Changes in / [eef8dfb:bdfc032]
- Files:
-
- 191 added
- 547 deleted
- 392 edited
-
.gitignore (modified) (4 diffs)
-
Jenkins/Distribute (deleted)
-
Jenkins/FullBuild (modified) (6 diffs)
-
Jenkins/tools.groovy (deleted)
-
Jenkinsfile (modified) (28 diffs)
-
Makefile.am (modified) (4 diffs)
-
Makefile.in (added)
-
aclocal.m4 (added)
-
autogen.sh (deleted)
-
automake/cfa.m4 (added)
-
automake/compile (added)
-
automake/config.guess (added)
-
automake/config.sub (added)
-
automake/depcomp (added)
-
automake/install-sh (added)
-
automake/libtool.m4 (added)
-
automake/ltmain.sh (added)
-
automake/ltoptions.m4 (added)
-
automake/ltsugar.m4 (added)
-
automake/ltversion.m4 (added)
-
automake/lt~obsolete.m4 (added)
-
automake/missing (added)
-
automake/test-driver (added)
-
automake/ylwrap (added)
-
benchmark/Cargo.toml.in (deleted)
-
benchmark/Makefile.am (modified) (21 diffs)
-
benchmark/Makefile.in (added)
-
benchmark/baselines/x64/schedint.csv (deleted)
-
benchmark/baselines/x64/signal.csv (added)
-
benchmark/baselines/x86/schedint.csv (deleted)
-
benchmark/baselines/x86/signal.csv (added)
-
benchmark/bench.rs (deleted)
-
benchmark/benchcltr.hfa (deleted)
-
benchmark/creation/JavaThread.java (modified) (3 diffs)
-
benchmark/creation/cfa_gen.cfa (modified) (2 diffs)
-
benchmark/creation/node_cor.js (modified) (1 diff)
-
benchmark/creation/rust_tokio_thrd.rs (deleted)
-
benchmark/ctxswitch/JavaThread.java (modified) (2 diffs)
-
benchmark/ctxswitch/cfa_cor.cfa (modified) (1 diff)
-
benchmark/ctxswitch/cfa_gen.cfa (modified) (1 diff)
-
benchmark/ctxswitch/node_cor.js (modified) (1 diff)
-
benchmark/ctxswitch/rust_await.rs (deleted)
-
benchmark/ctxswitch/rust_tokio_thrd.rs (deleted)
-
benchmark/exclude (modified) (1 diff)
-
benchmark/io/batch-readv.c (deleted)
-
benchmark/io/http/Makefile.am (deleted)
-
benchmark/io/http/channel.cfa (deleted)
-
benchmark/io/http/channel.hfa (deleted)
-
benchmark/io/http/filecache.cfa (deleted)
-
benchmark/io/http/filecache.hfa (deleted)
-
benchmark/io/http/main.cfa (deleted)
-
benchmark/io/http/options.cfa (deleted)
-
benchmark/io/http/options.hfa (deleted)
-
benchmark/io/http/protocol.cfa (deleted)
-
benchmark/io/http/protocol.hfa (deleted)
-
benchmark/io/http/worker.cfa (deleted)
-
benchmark/io/http/worker.hfa (deleted)
-
benchmark/io/io_uring.h (deleted)
-
benchmark/io/readv-posix.c (deleted)
-
benchmark/io/readv.cfa (deleted)
-
benchmark/mutex/JavaThread.java (modified) (3 diffs)
-
benchmark/mutexC/JavaThread.java (modified) (4 diffs)
-
benchmark/plot.py (deleted)
-
benchmark/readyQ/bench.go (deleted)
-
benchmark/readyQ/cycle.cc (deleted)
-
benchmark/readyQ/cycle.cfa (deleted)
-
benchmark/readyQ/cycle.cpp (deleted)
-
benchmark/readyQ/cycle.go (deleted)
-
benchmark/readyQ/cycle.rs (deleted)
-
benchmark/readyQ/locality.cfa (deleted)
-
benchmark/readyQ/locality.cpp (deleted)
-
benchmark/readyQ/locality.go (deleted)
-
benchmark/readyQ/locality.rs (deleted)
-
benchmark/readyQ/rq_bench.hfa (deleted)
-
benchmark/readyQ/rq_bench.hpp (deleted)
-
benchmark/readyQ/yield.cfa (deleted)
-
benchmark/readyQ/yield.cpp (deleted)
-
benchmark/rmit.py (deleted)
-
benchmark/schedint/JavaThread.java (modified) (2 diffs)
-
benchmark/size/size.cfa (deleted)
-
configure (added)
-
configure.ac (modified) (8 diffs)
-
doc/LaTeXmacros/common.tex (modified) (5 diffs)
-
doc/LaTeXmacros/lstlang.sty (modified) (3 diffs)
-
doc/bibliography/pl.bib (modified) (65 diffs)
-
doc/man/cfa.1 (modified) (6 diffs)
-
doc/papers/AMA/AMA-stix/ama/WileyNJD-v2.cls (modified) (1 diff)
-
doc/papers/concurrency/Paper.tex (modified) (87 diffs)
-
doc/papers/concurrency/annex/local.bib (modified) (3 diffs)
-
doc/papers/concurrency/examples/DatingServiceThread.cfa (deleted)
-
doc/papers/concurrency/examples/Fib.js (deleted)
-
doc/papers/concurrency/examples/Fib.py (modified) (2 diffs)
-
doc/papers/concurrency/examples/Fib2.c (modified) (1 diff)
-
doc/papers/concurrency/examples/Fib2.py (modified) (2 diffs)
-
doc/papers/concurrency/examples/Fib3.c (modified) (1 diff)
-
doc/papers/concurrency/examples/FibRefactor.py (modified) (1 diff)
-
doc/papers/concurrency/examples/Format.c (modified) (2 diffs)
-
doc/papers/concurrency/examples/Format.cc (modified) (2 diffs)
-
doc/papers/concurrency/examples/Format.cfa (modified) (1 diff)
-
doc/papers/concurrency/examples/Format.data (modified) (1 diff)
-
doc/papers/concurrency/examples/Format.js (deleted)
-
doc/papers/concurrency/examples/Format.py (modified) (2 diffs)
-
doc/papers/concurrency/examples/Format1.c (modified) (1 diff)
-
doc/papers/concurrency/examples/PingPong.c (modified) (3 diffs)
-
doc/papers/concurrency/examples/Pingpong.py (modified) (1 diff)
-
doc/papers/concurrency/examples/ProdCons.py (modified) (1 diff)
-
doc/papers/concurrency/examples/RWMonitor.cfa (added)
-
doc/papers/concurrency/examples/RWMonitorEXT.cfa (deleted)
-
doc/papers/concurrency/examples/RWMonitorINT.cfa (deleted)
-
doc/papers/concurrency/examples/Refactor.py (modified) (1 diff)
-
doc/papers/concurrency/examples/channels.go (deleted)
-
doc/papers/concurrency/examples/channels.rs (deleted)
-
doc/papers/concurrency/examples/future.rs (deleted)
-
doc/papers/concurrency/figures/FullCoroutinePhases.fig (modified) (2 diffs)
-
doc/papers/concurrency/figures/RunTimeStructure.fig (modified) (1 diff)
-
doc/papers/concurrency/mail (modified) (2 diffs)
-
doc/papers/concurrency/mail2 (modified) (1 diff)
-
doc/papers/concurrency/response (deleted)
-
doc/papers/concurrency/response2 (deleted)
-
doc/papers/concurrency/response3 (deleted)
-
doc/papers/ibm_CASCON19/ThreadingModels.fig (deleted)
-
doc/papers/ibm_CASCON19/ThreadingModels.png (deleted)
-
doc/papers/ibm_CASCON19/ThreadingModels.svg (deleted)
-
doc/papers/ibm_CASCON19/abstract.txt (deleted)
-
doc/papers/ibm_CASCON19/client.cfa (deleted)
-
doc/papers/ibm_CASCON19/server.cfa (deleted)
-
doc/papers/ibm_CASCON19/slides.pdf (deleted)
-
doc/proposals/ZeroCostPreemption.md (deleted)
-
doc/proposals/function_type_change.md (deleted)
-
doc/proposals/vtable.md (modified) (3 diffs)
-
doc/refrat/refrat.tex (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/.gitignore (deleted)
-
doc/theses/andrew_beach_MMath/Makefile (deleted)
-
doc/theses/andrew_beach_MMath/cfalab.sty (deleted)
-
doc/theses/andrew_beach_MMath/existing.tex (deleted)
-
doc/theses/andrew_beach_MMath/features.tex (deleted)
-
doc/theses/andrew_beach_MMath/future.tex (deleted)
-
doc/theses/andrew_beach_MMath/glossaries.tex (deleted)
-
doc/theses/andrew_beach_MMath/thesis-frontpgs.tex (deleted)
-
doc/theses/andrew_beach_MMath/thesis.bib (deleted)
-
doc/theses/andrew_beach_MMath/thesis.tex (deleted)
-
doc/theses/andrew_beach_MMath/unwinding.tex (deleted)
-
doc/theses/andrew_beach_MMath/uw-ethesis.cls (deleted)
-
doc/theses/fangren_yu_COOP_S20/Makefile (deleted)
-
doc/theses/fangren_yu_COOP_S20/Report.tex (deleted)
-
doc/theses/fangren_yu_COOP_S20/cfa_developer_reference.pdf (deleted)
-
doc/theses/fangren_yu_COOP_S20/figures/DeepNodeSharing.fig (deleted)
-
doc/theses/fangren_yu_COOP_S20/figures/DeepNodeSharing.fig.bak (deleted)
-
doc/theses/thierry_delisle_PhD/.gitignore (deleted)
-
doc/theses/thierry_delisle_PhD/code/assert.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/prefetch.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/processor.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/processor_list.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/processor_list_fast.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/processor_list_good.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/Makefile (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/proto-gui/main.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/Makefile (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/cforall.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/fibre.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/pthread.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/thread.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/thread.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/Makefile (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/assert.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/bitbench/select.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/bts.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/bts_test.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/links.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/prefetch.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/process.sh (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list_fast.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list_good.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/randbit.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list_layout.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/runperf.sh (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/scale.sh (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzi-packed.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzi.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzm.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/utils.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/work_stealing.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/utils.hpp (added)
-
doc/theses/thierry_delisle_PhD/comp_II/Makefile (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/comp_II.tex (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/comp_II_PAB.tex (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/comp_II_too_big.tex (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/glossary.tex (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/base.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/empty.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/emptybit.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/emptytls.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/emptytree.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/resize.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/img/system.fig (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/local.bib (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/presentation.tex (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/presentationstyle.sty (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/Makefile (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/base.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/empty.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/emptybit.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/emptytls.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/emptytree.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/fairness.py (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/resize.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/fig/system.fig (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/glossary.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/local.bib (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/core.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/existing.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/front.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/intro.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/io.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/practice.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/text/runtime.tex (deleted)
-
doc/theses/thierry_delisle_PhD/thesis/thesis.tex (deleted)
-
doc/user/Makefile (modified) (1 diff)
-
doc/user/user.tex (modified) (24 diffs)
-
doc/working/unified_semaphores/semaphore.cfa (deleted)
-
doc/working/unified_semaphores/semaphore.hfa (deleted)
-
driver/Makefile.am (modified) (1 diff)
-
driver/Makefile.in (added)
-
driver/cc1.cc (modified) (22 diffs)
-
driver/cfa.cc (modified) (22 diffs)
-
example/io/cat.c (deleted)
-
example/io/filereader.c (deleted)
-
example/io/simple/client.c (deleted)
-
example/io/simple/server.c (deleted)
-
example/io/simple/server.cfa (deleted)
-
example/io/simple/server_epoll.c (deleted)
-
examples/ArrayN.c (added)
-
examples/Initialization.c (added)
-
examples/Initialization2.c (added)
-
examples/Makefile.example (added)
-
examples/Members.c (added)
-
examples/Misc.c (added)
-
examples/MiscError.c (added)
-
examples/Rank2.c (added)
-
examples/Tuple.c (added)
-
examples/abstype.c (added)
-
examples/constructors.c (added)
-
examples/forward.c (added)
-
examples/gc_no_raii/.gitignore (added)
-
examples/gc_no_raii/bug-repro/assert.c (added)
-
examples/gc_no_raii/bug-repro/blockers.tar.gz (added)
-
examples/gc_no_raii/bug-repro/blockers/explicit_cast.c (added)
-
examples/gc_no_raii/bug-repro/blockers/file_scope.c (added)
-
examples/gc_no_raii/bug-repro/blockers/recursive_realloc.c (added)
-
examples/gc_no_raii/bug-repro/crash.c (added)
-
examples/gc_no_raii/bug-repro/deref.c (added)
-
examples/gc_no_raii/bug-repro/field.c (added)
-
examples/gc_no_raii/bug-repro/find.c (added)
-
examples/gc_no_raii/bug-repro/inline.c (added)
-
examples/gc_no_raii/bug-repro/malloc.c (added)
-
examples/gc_no_raii/bug-repro/not_equal.c (added)
-
examples/gc_no_raii/bug-repro/oddtype.c (added)
-
examples/gc_no_raii/bug-repro/push_back.c (added)
-
examples/gc_no_raii/bug-repro/push_back.h (added)
-
examples/gc_no_raii/bug-repro/realloc.c (added)
-
examples/gc_no_raii/bug-repro/return.c (added)
-
examples/gc_no_raii/bug-repro/return_template.c (added)
-
examples/gc_no_raii/bug-repro/slow_malloc.c (added)
-
examples/gc_no_raii/bug-repro/static_const_local.c (added)
-
examples/gc_no_raii/bug-repro/test-assert.cpp (added)
-
examples/gc_no_raii/bug-repro/void_pointer.c (added)
-
examples/gc_no_raii/bug-repro/while.c (added)
-
examples/gc_no_raii/bug-repro/zero.c (added)
-
examples/gc_no_raii/pool-alloc/allocate-malign.c (added)
-
examples/gc_no_raii/pool-alloc/allocate-malloc.c (added)
-
examples/gc_no_raii/pool-alloc/allocate-mmap.c (added)
-
examples/gc_no_raii/pool-alloc/allocate-win-valloc.c (added)
-
examples/gc_no_raii/premake4.lua (added)
-
examples/gc_no_raii/src/allocate-pool.c (added)
-
examples/gc_no_raii/src/allocate-pool.h (added)
-
examples/gc_no_raii/src/gc.h (added)
-
examples/gc_no_raii/src/gcpointers.c (added)
-
examples/gc_no_raii/src/gcpointers.h (added)
-
examples/gc_no_raii/src/internal/card_table.h (added)
-
examples/gc_no_raii/src/internal/collector.c (added)
-
examples/gc_no_raii/src/internal/collector.h (added)
-
examples/gc_no_raii/src/internal/gc_tools.h (added)
-
examples/gc_no_raii/src/internal/globals.h (added)
-
examples/gc_no_raii/src/internal/memory_pool.c (added)
-
examples/gc_no_raii/src/internal/memory_pool.h (added)
-
examples/gc_no_raii/src/internal/object_header.c (added)
-
examples/gc_no_raii/src/internal/object_header.h (added)
-
examples/gc_no_raii/src/internal/state.c (added)
-
examples/gc_no_raii/src/internal/state.h (added)
-
examples/gc_no_raii/src/test_include.c (added)
-
examples/gc_no_raii/src/tools.h (added)
-
examples/gc_no_raii/src/tools/checks.h (added)
-
examples/gc_no_raii/src/tools/print.c (added)
-
examples/gc_no_raii/src/tools/print.h (added)
-
examples/gc_no_raii/src/tools/worklist.h (added)
-
examples/gc_no_raii/test/badlll.c (added)
-
examples/gc_no_raii/test/gctest.c (added)
-
examples/gc_no_raii/test/operators.c (added)
-
examples/huge.c (added)
-
examples/includes.c (added)
-
examples/index.h (added)
-
examples/it_out.c (added)
-
examples/multicore.c (added)
-
examples/new.c (added)
-
examples/poly-bench.c (added)
-
examples/prolog.c (added)
-
examples/quad.c (added)
-
examples/s.c (added)
-
examples/simplePoly.c (added)
-
examples/simpler.c (added)
-
examples/specialize.c (added)
-
examples/square.c (added)
-
examples/twice.c (added)
-
examples/wrapper/.gitignore (added)
-
examples/wrapper/premake4.lua (added)
-
examples/wrapper/src/main.c (added)
-
examples/wrapper/src/pointer.h (added)
-
examples/zero_one.c (added)
-
libcfa/Makefile.dist.am (deleted)
-
libcfa/Makefile.in (added)
-
libcfa/aclocal.m4 (added)
-
libcfa/automake/compile (added)
-
libcfa/automake/config.guess (added)
-
libcfa/automake/config.sub (added)
-
libcfa/automake/depcomp (added)
-
libcfa/automake/install-sh (added)
-
libcfa/automake/libtool.m4 (added)
-
libcfa/automake/ltmain.sh (added)
-
libcfa/automake/ltoptions.m4 (added)
-
libcfa/automake/ltsugar.m4 (added)
-
libcfa/automake/ltversion.m4 (added)
-
libcfa/automake/lt~obsolete.m4 (added)
-
libcfa/automake/missing (added)
-
libcfa/configure (added)
-
libcfa/configure.ac (modified) (7 diffs)
-
libcfa/prelude/Makefile.am (modified) (4 diffs)
-
libcfa/prelude/Makefile.in (added)
-
libcfa/prelude/bootloader.cf (modified) (1 diff)
-
libcfa/prelude/builtins.c (modified) (3 diffs)
-
libcfa/prelude/defines.hfa.in (deleted)
-
libcfa/prelude/extras.regx (modified) (1 diff)
-
libcfa/prelude/extras.regx2 (deleted)
-
libcfa/prelude/prototypes.awk (modified) (4 diffs)
-
libcfa/src/Makefile.am (modified) (7 diffs)
-
libcfa/src/Makefile.in (added)
-
libcfa/src/assert.cfa (modified) (3 diffs)
-
libcfa/src/bitmanip.hfa (deleted)
-
libcfa/src/bits/collection.hfa (deleted)
-
libcfa/src/bits/containers.hfa (modified) (8 diffs)
-
libcfa/src/bits/debug.cfa (modified) (3 diffs)
-
libcfa/src/bits/debug.hfa (modified) (6 diffs)
-
libcfa/src/bits/defs.hfa (modified) (3 diffs)
-
libcfa/src/bits/locks.hfa (modified) (7 diffs)
-
libcfa/src/bits/queue.hfa (deleted)
-
libcfa/src/bits/random.hfa (deleted)
-
libcfa/src/bits/sequence.hfa (deleted)
-
libcfa/src/bits/signal.hfa (modified) (3 diffs)
-
libcfa/src/bits/stack.hfa (deleted)
-
libcfa/src/common.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/CtxSwitch-arm.S (added)
-
libcfa/src/concurrency/CtxSwitch-arm32.S (deleted)
-
libcfa/src/concurrency/CtxSwitch-arm64.S (deleted)
-
libcfa/src/concurrency/CtxSwitch-i386.S (modified) (3 diffs)
-
libcfa/src/concurrency/CtxSwitch-x86_64.S (modified) (3 diffs)
-
libcfa/src/concurrency/alarm.cfa (modified) (6 diffs)
-
libcfa/src/concurrency/alarm.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/clib/cfathread.cfa (deleted)
-
libcfa/src/concurrency/clib/cfathread.h (deleted)
-
libcfa/src/concurrency/coroutine.cfa (modified) (13 diffs)
-
libcfa/src/concurrency/coroutine.hfa (modified) (10 diffs)
-
libcfa/src/concurrency/exception.cfa (deleted)
-
libcfa/src/concurrency/exception.hfa (deleted)
-
libcfa/src/concurrency/future.hfa (deleted)
-
libcfa/src/concurrency/invoke.c (modified) (10 diffs)
-
libcfa/src/concurrency/invoke.h (modified) (12 diffs)
-
libcfa/src/concurrency/io.cfa (deleted)
-
libcfa/src/concurrency/io/call.cfa.in (deleted)
-
libcfa/src/concurrency/io/setup.cfa (deleted)
-
libcfa/src/concurrency/io/types.hfa (deleted)
-
libcfa/src/concurrency/iofwd.hfa (deleted)
-
libcfa/src/concurrency/kernel.cfa (modified) (13 diffs)
-
libcfa/src/concurrency/kernel.hfa (modified) (11 diffs)
-
libcfa/src/concurrency/kernel/fwd.hfa (deleted)
-
libcfa/src/concurrency/kernel/startup.cfa (deleted)
-
libcfa/src/concurrency/kernel_private.hfa (modified) (4 diffs)
-
libcfa/src/concurrency/locks.cfa (deleted)
-
libcfa/src/concurrency/locks.hfa (deleted)
-
libcfa/src/concurrency/monitor.cfa (modified) (41 diffs)
-
libcfa/src/concurrency/monitor.hfa (modified) (7 diffs)
-
libcfa/src/concurrency/mutex.cfa (modified) (10 diffs)
-
libcfa/src/concurrency/mutex.hfa (modified) (3 diffs)
-
libcfa/src/concurrency/preemption.cfa (modified) (22 diffs)
-
libcfa/src/concurrency/preemption.hfa (modified) (1 diff)
-
libcfa/src/concurrency/ready_queue.cfa (deleted)
-
libcfa/src/concurrency/ready_subqueue.hfa (deleted)
-
libcfa/src/concurrency/snzi.hfa (deleted)
-
libcfa/src/concurrency/stats.cfa (deleted)
-
libcfa/src/concurrency/stats.hfa (deleted)
-
libcfa/src/concurrency/thread.cfa (modified) (4 diffs)
-
libcfa/src/concurrency/thread.hfa (modified) (3 diffs)
-
libcfa/src/containers/list.hfa (deleted)
-
libcfa/src/containers/stackLockFree.hfa (deleted)
-
libcfa/src/containers/vector.hfa (modified) (1 diff)
-
libcfa/src/exception.c (modified) (16 diffs)
-
libcfa/src/exception.h (modified) (2 diffs)
-
libcfa/src/exception.hfa (deleted)
-
libcfa/src/executor.cfa (modified) (1 diff)
-
libcfa/src/fstream.cfa (modified) (14 diffs)
-
libcfa/src/fstream.hfa (modified) (5 diffs)
-
libcfa/src/gmp.hfa (modified) (5 diffs)
-
libcfa/src/heap.cfa (modified) (59 diffs)
-
libcfa/src/heap.hfa (deleted)
-
libcfa/src/interpose.cfa (modified) (10 diffs)
-
libcfa/src/iostream.cfa (modified) (44 diffs)
-
libcfa/src/iostream.hfa (modified) (20 diffs)
-
libcfa/src/math.hfa (modified) (2 diffs)
-
libcfa/src/memory.cfa (deleted)
-
libcfa/src/memory.hfa (deleted)
-
libcfa/src/parseargs.cfa (deleted)
-
libcfa/src/parseargs.hfa (deleted)
-
libcfa/src/rational.cfa (modified) (2 diffs)
-
libcfa/src/startup.cfa (modified) (3 diffs)
-
libcfa/src/stdhdr/assert.h (modified) (3 diffs)
-
libcfa/src/stdhdr/bfdlink.h (modified) (1 diff)
-
libcfa/src/stdhdr/hwloc.h (modified) (1 diff)
-
libcfa/src/stdhdr/krb5.h (modified) (1 diff)
-
libcfa/src/stdhdr/malloc.h (modified) (1 diff)
-
libcfa/src/stdhdr/math.h (modified) (1 diff)
-
libcfa/src/stdhdr/sys/mman.h (deleted)
-
libcfa/src/stdhdr/sys/time.h (deleted)
-
libcfa/src/stdhdr/sys/ucontext.h (added)
-
libcfa/src/stdhdr/unwind.h (deleted)
-
libcfa/src/stdlib.cfa (modified) (9 diffs)
-
libcfa/src/stdlib.hfa (modified) (9 diffs)
-
libcfa/src/time.cfa (modified) (2 diffs)
-
libcfa/src/time.hfa (modified) (4 diffs)
-
libcfa/src/vec/vec.hfa (modified) (1 diff)
-
libcfa/src/vec/vec2.hfa (modified) (1 diff)
-
libcfa/src/vec/vec3.hfa (modified) (1 diff)
-
libcfa/src/vec/vec4.hfa (modified) (1 diff)
-
longrun_tests/Makefile.am (modified) (2 diffs)
-
longrun_tests/Makefile.in (added)
-
longrun_tests/locks.cfa (deleted)
-
src/AST/Attribute.hpp (modified) (1 diff)
-
src/AST/CVQualifiers.hpp (modified) (2 diffs)
-
src/AST/Convert.cpp (modified) (42 diffs)
-
src/AST/Convert.hpp (modified) (1 diff)
-
src/AST/Copy.hpp (deleted)
-
src/AST/Decl.cpp (modified) (1 diff)
-
src/AST/Decl.hpp (modified) (8 diffs)
-
src/AST/DeclReplacer.cpp (modified) (3 diffs)
-
src/AST/DeclReplacer.hpp (modified) (1 diff)
-
src/AST/Eval.hpp (deleted)
-
src/AST/Expr.cpp (modified) (13 diffs)
-
src/AST/Expr.hpp (modified) (23 diffs)
-
src/AST/Fwd.hpp (modified) (4 diffs)
-
src/AST/GenericSubstitution.cpp (modified) (2 diffs)
-
src/AST/Init.hpp (modified) (5 diffs)
-
src/AST/Node.cpp (modified) (4 diffs)
-
src/AST/Node.hpp (modified) (12 diffs)
-
src/AST/Pass.hpp (modified) (11 diffs)
-
src/AST/Pass.impl.hpp (modified) (124 diffs)
-
src/AST/Pass.proto.hpp (modified) (13 diffs)
-
src/AST/Print.cpp (modified) (11 diffs)
-
src/AST/Stmt.hpp (modified) (3 diffs)
-
src/AST/SymbolTable.cpp (modified) (12 diffs)
-
src/AST/SymbolTable.hpp (modified) (5 diffs)
-
src/AST/TranslationUnit.hpp (deleted)
-
src/AST/Type.cpp (modified) (7 diffs)
-
src/AST/Type.hpp (modified) (13 diffs)
-
src/AST/TypeEnvironment.cpp (modified) (20 diffs)
-
src/AST/TypeEnvironment.hpp (modified) (9 diffs)
-
src/AST/TypeSubstitution.cpp (modified) (9 diffs)
-
src/AST/TypeSubstitution.hpp (modified) (9 diffs)
-
src/AST/Visitor.hpp (modified) (1 diff)
-
src/AST/module.mk (modified) (1 diff)
-
src/AST/porting.md (modified) (6 diffs)
-
src/CodeGen/CodeGenerator.cc (modified) (25 diffs)
-
src/CodeGen/CodeGenerator.h (modified) (5 diffs)
-
src/CodeGen/FixMain.cc (modified) (2 diffs)
-
src/CodeGen/FixMain.h (modified) (2 diffs)
-
src/CodeGen/FixNames.cc (modified) (2 diffs)
-
src/CodeGen/GenType.h (modified) (2 diffs)
-
src/CodeGen/Generate.cc (modified) (2 diffs)
-
src/CodeGen/OperatorTable.cc (modified) (3 diffs)
-
src/CodeGen/OperatorTable.h (modified) (3 diffs)
-
src/CodeGen/Options.h (modified) (1 diff)
-
src/CodeGen/module.mk (modified) (1 diff)
-
src/CodeTools/ResolvProtoDump.cc (modified) (2 diffs)
-
src/CodeTools/TrackLoc.cc (modified) (3 diffs)
-
src/CodeTools/module.mk (modified) (1 diff)
-
src/Common/CodeLocation.h (modified) (1 diff)
-
src/Common/CodeLocationTools.cpp (deleted)
-
src/Common/CodeLocationTools.hpp (deleted)
-
src/Common/Eval.cc (modified) (1 diff)
-
src/Common/Examine.cc (deleted)
-
src/Common/Examine.h (deleted)
-
src/Common/PassVisitor.h (modified) (4 diffs)
-
src/Common/PassVisitor.impl.h (modified) (16 diffs)
-
src/Common/PassVisitor.proto.h (modified) (1 diff)
-
src/Common/ScopedMap.h (modified) (2 diffs)
-
src/Common/SemanticError.cc (modified) (1 diff)
-
src/Common/SemanticError.h (modified) (2 diffs)
-
src/Common/Stats/Heap.cc (modified) (6 diffs)
-
src/Common/Stats/Heap.h (modified) (1 diff)
-
src/Common/Stats/ResolveTime.cc (deleted)
-
src/Common/Stats/ResolveTime.h (deleted)
-
src/Common/Stats/Stats.cc (modified) (2 diffs)
-
src/Common/module.mk (modified) (1 diff)
-
src/Common/utility.h (modified) (4 diffs)
-
src/CompilationState.cc (modified) (2 diffs)
-
src/CompilationState.h (modified) (1 diff)
-
src/Concurrency/Keywords.cc (modified) (36 diffs)
-
src/Concurrency/Waitfor.cc (modified) (4 diffs)
-
src/Concurrency/module.mk (modified) (1 diff)
-
src/ControlStruct/ExceptTranslate.cc (modified) (22 diffs)
-
src/ControlStruct/ExceptTranslate.h (modified) (2 diffs)
-
src/ControlStruct/Mutate.cc (modified) (2 diffs)
-
src/ControlStruct/module.mk (modified) (1 diff)
-
src/GenPoly/GenPoly.cc (modified) (9 diffs)
-
src/GenPoly/GenPoly.h (modified) (4 diffs)
-
src/GenPoly/InstantiateGeneric.cc (modified) (6 diffs)
-
src/GenPoly/Specialize.cc (modified) (5 diffs)
-
src/GenPoly/module.mk (modified) (1 diff)
-
src/InitTweak/FixGlobalInit.cc (modified) (5 diffs)
-
src/InitTweak/FixGlobalInit.h (modified) (2 diffs)
-
src/InitTweak/FixInit.cc (modified) (4 diffs)
-
src/InitTweak/FixInit.h (modified) (2 diffs)
-
src/InitTweak/FixInitNew.cpp (deleted)
-
src/InitTweak/GenInit.cc (modified) (8 diffs)
-
src/InitTweak/GenInit.h (modified) (2 diffs)
-
src/InitTweak/InitTweak.cc (modified) (13 diffs)
-
src/InitTweak/InitTweak.h (modified) (4 diffs)
-
src/InitTweak/module.mk (modified) (1 diff)
-
src/MakeLibCfa.cc (modified) (3 diffs)
-
src/Makefile.am (modified) (2 diffs)
-
src/Makefile.in (added)
-
src/Parser/DeclarationNode.cc (modified) (6 diffs)
-
src/Parser/ExpressionNode.cc (modified) (8 diffs)
-
src/Parser/ParseNode.h (modified) (9 diffs)
-
src/Parser/ParserTypes.h (modified) (2 diffs)
-
src/Parser/StatementNode.cc (modified) (3 diffs)
-
src/Parser/TypeData.cc (modified) (2 diffs)
-
src/Parser/TypedefTable.cc (modified) (2 diffs)
-
src/Parser/TypedefTable.h (modified) (2 diffs)
-
src/Parser/lex.ll (modified) (7 diffs)
-
src/Parser/module.mk (modified) (2 diffs)
-
src/Parser/parser.yy (modified) (31 diffs)
-
src/ResolvExpr/AdjustExprType.cc (modified) (1 diff)
-
src/ResolvExpr/AlternativeFinder.cc (modified) (6 diffs)
-
src/ResolvExpr/Candidate.cpp (modified) (1 diff)
-
src/ResolvExpr/Candidate.hpp (modified) (2 diffs)
-
src/ResolvExpr/CandidateFinder.cpp (modified) (89 diffs)
-
src/ResolvExpr/CandidateFinder.hpp (modified) (4 diffs)
-
src/ResolvExpr/CastCost.cc (modified) (7 diffs)
-
src/ResolvExpr/CommonType.cc (modified) (5 diffs)
-
src/ResolvExpr/ConversionCost.cc (modified) (16 diffs)
-
src/ResolvExpr/ConversionCost.h (modified) (6 diffs)
-
src/ResolvExpr/CurrentObject.cc (modified) (18 diffs)
-
src/ResolvExpr/FindOpenVars.cc (modified) (1 diff)
-
src/ResolvExpr/PolyCost.cc (modified) (2 diffs)
-
src/ResolvExpr/PtrsAssignable.cc (modified) (3 diffs)
-
src/ResolvExpr/PtrsCastable.cc (modified) (3 diffs)
-
src/ResolvExpr/RenameVars.cc (modified) (10 diffs)
-
src/ResolvExpr/RenameVars.h (modified) (1 diff)
-
src/ResolvExpr/ResolvMode.h (modified) (1 diff)
-
src/ResolvExpr/ResolveAssertions.cc (modified) (1 diff)
-
src/ResolvExpr/ResolveTypeof.cc (modified) (8 diffs)
-
src/ResolvExpr/ResolveTypeof.h (modified) (2 diffs)
-
src/ResolvExpr/Resolver.cc (modified) (30 diffs)
-
src/ResolvExpr/Resolver.h (modified) (3 diffs)
-
src/ResolvExpr/SatisfyAssertions.cpp (modified) (21 diffs)
-
src/ResolvExpr/SatisfyAssertions.hpp (modified) (1 diff)
-
src/ResolvExpr/SpecCost.cc (modified) (5 diffs)
-
src/ResolvExpr/TypeEnvironment.cc (modified) (3 diffs)
-
src/ResolvExpr/TypeEnvironment.h (modified) (1 diff)
-
src/ResolvExpr/Unify.cc (modified) (30 diffs)
-
src/ResolvExpr/module.mk (modified) (2 diffs)
-
src/ResolvExpr/typeops.h (modified) (3 diffs)
-
src/SymTab/Autogen.cc (modified) (5 diffs)
-
src/SymTab/Autogen.h (modified) (7 diffs)
-
src/SymTab/Demangle.cc (modified) (4 diffs)
-
src/SymTab/FixFunction.cc (modified) (4 diffs)
-
src/SymTab/Mangler.cc (modified) (42 diffs)
-
src/SymTab/Validate.cc (modified) (17 diffs)
-
src/SymTab/module.mk (modified) (1 diff)
-
src/SynTree/AggregateDecl.cc (modified) (2 diffs)
-
src/SynTree/ApplicationExpr.cc (modified) (1 diff)
-
src/SynTree/Attribute.h (modified) (2 diffs)
-
src/SynTree/Declaration.h (modified) (3 diffs)
-
src/SynTree/Expression.cc (modified) (3 diffs)
-
src/SynTree/Expression.h (modified) (4 diffs)
-
src/SynTree/LinkageSpec.cc (modified) (2 diffs)
-
src/SynTree/LinkageSpec.h (modified) (2 diffs)
-
src/SynTree/Mutator.h (modified) (1 diff)
-
src/SynTree/NamedTypeDecl.cc (modified) (6 diffs)
-
src/SynTree/ReferenceToType.cc (modified) (2 diffs)
-
src/SynTree/Statement.cc (modified) (1 diff)
-
src/SynTree/Statement.h (modified) (2 diffs)
-
src/SynTree/SynTree.h (modified) (1 diff)
-
src/SynTree/Type.cc (modified) (1 diff)
-
src/SynTree/Type.h (modified) (1 diff)
-
src/SynTree/TypeDecl.cc (modified) (2 diffs)
-
src/SynTree/Visitor.h (modified) (1 diff)
-
src/SynTree/module.mk (modified) (3 diffs)
-
src/Tuples/Explode.cc (modified) (4 diffs)
-
src/Tuples/Explode.h (modified) (2 diffs)
-
src/Tuples/TupleAssignment.cc (modified) (6 diffs)
-
src/Tuples/TupleExpansion.cc (modified) (1 diff)
-
src/Tuples/Tuples.cc (modified) (2 diffs)
-
src/Tuples/module.mk (modified) (1 diff)
-
src/Validate/module.mk (modified) (1 diff)
-
src/Virtual/ExpandCasts.cc (modified) (10 diffs)
-
src/Virtual/Tables.cc (deleted)
-
src/Virtual/Tables.h (deleted)
-
src/Virtual/module.mk (modified) (1 diff)
-
src/cfa.make (added)
-
src/config.h.in (modified) (1 diff)
-
src/main.cc (modified) (14 diffs)
-
tests/.expect/KRfunctions.nast.arm64.txt (deleted)
-
tests/.expect/KRfunctions.nast.x64.txt (deleted)
-
tests/.expect/KRfunctions.nast.x86.txt (deleted)
-
tests/.expect/KRfunctions.oast.arm64.txt (deleted)
-
tests/.expect/KRfunctions.oast.x64.txt (deleted)
-
tests/.expect/KRfunctions.oast.x86.txt (deleted)
-
tests/.expect/KRfunctions.x64.txt (added)
-
tests/.expect/KRfunctions.x86.txt (added)
-
tests/.expect/abs.arm64.txt (deleted)
-
tests/.expect/abs.txt (added)
-
tests/.expect/abs.x64.txt (deleted)
-
tests/.expect/abs.x86.txt (deleted)
-
tests/.expect/alloc-ERROR.nast.txt (deleted)
-
tests/.expect/alloc-ERROR.oast.txt (deleted)
-
tests/.expect/alloc-ERROR.txt (added)
-
tests/.expect/alloc.txt (modified) (2 diffs)
-
tests/.expect/alloc.txt.old (deleted)
-
tests/.expect/alloc2.txt (deleted)
-
tests/.expect/array.txt (modified) (1 diff)
-
tests/.expect/ato.arm64.txt (deleted)
-
tests/.expect/ato.txt (added)
-
tests/.expect/ato.x64.txt (deleted)
-
tests/.expect/ato.x86.txt (deleted)
-
tests/.expect/attributes.nast.arm64.txt (deleted)
-
tests/.expect/attributes.nast.x64.txt (deleted)
-
tests/.expect/attributes.nast.x86.txt (deleted)
-
tests/.expect/attributes.oast.arm64.txt (deleted)
-
tests/.expect/attributes.oast.x64.txt (deleted)
-
tests/.expect/attributes.oast.x86.txt (deleted)
-
tests/.expect/attributes.x64.txt (added)
-
tests/.expect/attributes.x86.txt (added)
-
tests/.expect/bitmanip1.arm64.txt (deleted)
-
tests/.expect/bitmanip1.x64.txt (deleted)
-
tests/.expect/bitmanip1.x86.txt (deleted)
-
tests/.expect/bitmanip2.arm64.txt (deleted)
-
tests/.expect/bitmanip2.x64.txt (deleted)
-
tests/.expect/bitmanip2.x86.txt (deleted)
-
tests/.expect/bitmanip3.arm64.txt (deleted)
-
tests/.expect/bitmanip3.x64.txt (deleted)
-
tests/.expect/bitmanip3.x86.txt (deleted)
-
tests/.expect/cast.txt (modified) (1 diff)
-
tests/.expect/castError.nast.txt (deleted)
-
tests/.expect/castError.oast.txt (deleted)
-
tests/.expect/castError.txt (added)
-
tests/.expect/completeTypeError.txt (added)
-
tests/.expect/const-init.txt (deleted)
-
tests/.expect/copyfile.txt (modified) (1 diff)
-
tests/.expect/declarationErrors.txt (added)
-
tests/.expect/declarationSpecifier.arm64.txt (deleted)
-
tests/.expect/declarationSpecifier.x64.txt (modified) (1 diff)
-
tests/.expect/declarationSpecifier.x86.txt (modified) (1 diff)
-
tests/.expect/enum.txt (modified) (1 diff)
-
tests/.expect/expression.txt (modified) (1 diff)
-
tests/.expect/extension.arm64.txt (deleted)
-
tests/.expect/forall.txt (modified) (1 diff)
-
tests/.expect/functions.nast.arm64.txt (deleted)
-
tests/.expect/functions.nast.x64.txt (deleted)
-
tests/.expect/functions.nast.x86.txt (deleted)
-
tests/.expect/functions.oast.arm64.txt (deleted)
-
tests/.expect/functions.oast.x64.txt (deleted)
-
tests/.expect/functions.oast.x86.txt (deleted)
-
tests/.expect/functions.x64.txt (added)
-
tests/.expect/functions.x86.txt (added)
-
tests/.expect/gccExtensions.arm64.txt (deleted)
-
tests/.expect/gccExtensions.x64.txt (modified) (1 diff)
-
tests/.expect/gccExtensions.x86.txt (modified) (1 diff)
-
tests/.expect/global-monomorph.txt (deleted)
-
tests/.expect/heap.txt (modified) (1 diff)
-
tests/.expect/identFuncDeclarator.txt (modified) (1 diff)
-
tests/.expect/identParamDeclarator.txt (modified) (1 diff)
-
tests/.expect/init1-ERROR.nast.txt (deleted)
-
tests/.expect/init1-ERROR.oast.txt (deleted)
-
tests/.expect/init1.txt (deleted)
-
tests/.expect/labelledExit.txt (modified) (1 diff)
-
tests/.expect/limits.txt (modified) (1 diff)
-
tests/.expect/malloc.txt (deleted)
-
tests/.expect/manipulatorsInput.arm64.txt (deleted)
-
tests/.expect/manipulatorsInput.txt (added)
-
tests/.expect/manipulatorsInput.x64.txt (deleted)
-
tests/.expect/manipulatorsInput.x86.txt (deleted)
-
tests/.expect/manipulatorsOutput1.arm64.txt (deleted)
-
tests/.expect/manipulatorsOutput1.txt (added)
-
tests/.expect/manipulatorsOutput1.x64.txt (deleted)
-
tests/.expect/manipulatorsOutput1.x86.txt (deleted)
-
tests/.expect/manipulatorsOutput2.arm64.txt (deleted)
-
tests/.expect/manipulatorsOutput3.arm64.txt (deleted)
-
tests/.expect/manipulatorsOutput3.x64.txt (deleted)
-
tests/.expect/math1.arm64.txt (deleted)
-
tests/.expect/math1.txt (added)
-
tests/.expect/math1.x64.txt (deleted)
-
tests/.expect/math1.x86.txt (deleted)
-
tests/.expect/math2.arm64.txt (deleted)
-
tests/.expect/math2.txt (added)
-
tests/.expect/math2.x64.txt (deleted)
-
tests/.expect/math2.x86.txt (deleted)
-
tests/.expect/math3.arm64.txt (deleted)
-
tests/.expect/math3.txt (added)
-
tests/.expect/math3.x64.txt (deleted)
-
tests/.expect/math3.x86.txt (deleted)
-
tests/.expect/math4.arm64.txt (deleted)
-
tests/.expect/math4.txt (added)
-
tests/.expect/math4.x64.txt (deleted)
-
tests/.expect/math4.x86.txt (deleted)
-
tests/.expect/maybe.txt (modified) (1 diff)
-
tests/.expect/minmax.txt (modified) (2 diffs)
-
tests/.expect/nested-types-ERR1.txt (modified) (1 diff)
-
tests/.expect/nested-types-ERR2.txt (modified) (1 diff)
-
tests/.expect/nested-types.txt (modified) (1 diff)
-
tests/.expect/numericConstants.txt (modified) (1 diff)
-
tests/.expect/operators.txt (modified) (1 diff)
-
tests/.expect/poly-d-cycle.txt (deleted)
-
tests/.expect/poly-o-cycle.txt (deleted)
-
tests/.expect/random.arm64.txt (deleted)
-
tests/.expect/random.txt (added)
-
tests/.expect/random.x64.txt (deleted)
-
tests/.expect/random.x86.txt (deleted)
-
tests/.expect/rational.txt (modified) (1 diff)
-
tests/.expect/result.txt (modified) (1 diff)
-
tests/.expect/scopeErrors.txt (added)
-
tests/.expect/stdincludes.txt (modified) (1 diff)
-
tests/.expect/switch.txt (modified) (1 diff)
-
tests/.expect/time.txt (modified) (1 diff)
-
tests/.expect/typedefRedef-ERR1.txt (modified) (1 diff)
-
tests/.expect/typedefRedef.txt (modified) (1 diff)
-
tests/.expect/typeof.txt (modified) (1 diff)
-
tests/.expect/userLiterals.txt (deleted)
-
tests/.expect/variableDeclarator.txt (modified) (1 diff)
-
tests/.expect/voidPtr.txt (modified) (1 diff)
-
tests/.in/copyfile.txt (modified) (1 diff)
-
tests/.in/manipulatorsInput.txt (modified) (1 diff)
-
tests/Makefile.am (modified) (9 diffs)
-
tests/Makefile.in (added)
-
tests/abort.cfa (added)
-
tests/alloc.cfa (modified) (12 diffs)
-
tests/alloc2.cfa (deleted)
-
tests/array.cfa (modified) (2 diffs)
-
tests/avltree/avl1.cfa (modified) (1 diff)
-
tests/bitmanip1.cfa (deleted)
-
tests/bitmanip2.cfa (deleted)
-
tests/bitmanip3.cfa (deleted)
-
tests/bugs/10.cfa (deleted)
-
tests/bugs/104.cfa (deleted)
-
tests/bugs/140.cfa (deleted)
-
tests/bugs/194.cfa (deleted)
-
tests/bugs/196.cfa (deleted)
-
tests/bugs/20.cfa (deleted)
-
tests/bugs/203-2.cfa (deleted)
-
tests/bugs/203-7.cfa (deleted)
-
tests/bugs/203-9.cfa (deleted)
-
tests/bugs/44.cfa (deleted)
-
tests/bugs/46.cfa (deleted)
-
tests/bugs/5.cfa (deleted)
-
tests/bugs/66.cfa (deleted)
-
tests/bugs/7.cfa (deleted)
-
tests/bugs/91.cfa (deleted)
-
tests/bugs/92.cfa (deleted)
-
tests/bugs/95.cfa (deleted)
-
tests/builtins/.expect/sync.txt (modified) (1 diff)
-
tests/builtins/sync.cfa (modified) (2 diffs)
-
tests/cast.cfa (modified) (1 diff)
-
tests/castError.cfa (modified) (2 diffs)
-
tests/collections/.expect/multi_list.txt (deleted)
-
tests/collections/.expect/queue.txt (deleted)
-
tests/collections/.expect/sequence.txt (deleted)
-
tests/collections/.expect/stack.txt (deleted)
-
tests/collections/multi_list.cfa (deleted)
-
tests/collections/queue.cfa (deleted)
-
tests/collections/sequence.cfa (deleted)
-
tests/collections/stack.cfa (deleted)
-
tests/completeTypeError.cfa (added)
-
tests/complex.cfa (modified) (1 diff)
-
tests/concurrent/.expect/clib.txt (deleted)
-
tests/concurrent/.expect/cluster.txt (deleted)
-
tests/concurrent/.expect/join.txt (deleted)
-
tests/concurrent/.expect/joinerror.sed (deleted)
-
tests/concurrent/.expect/monitor.txt (modified) (1 diff)
-
tests/concurrent/.expect/suspend_then.txt (deleted)
-
tests/concurrent/clib.c (deleted)
-
tests/concurrent/cluster.cfa (deleted)
-
tests/concurrent/coroutineThen.cfa (added)
-
tests/concurrent/coroutineYield.cfa (modified) (1 diff)
-
tests/concurrent/examples/.expect/datingService.txt (modified) (1 diff)
-
tests/concurrent/examples/boundedBufferEXT.cfa (modified) (2 diffs)
-
tests/concurrent/examples/datingService.cfa (modified) (5 diffs)
-
tests/concurrent/examples/quickSort.cfa (modified) (3 diffs)
-
tests/concurrent/futures/.expect/abandon.txt (deleted)
-
tests/concurrent/futures/.expect/basic.txt (deleted)
-
tests/concurrent/futures/.expect/multi.txt (deleted)
-
tests/concurrent/futures/.expect/typed.txt (deleted)
-
tests/concurrent/futures/abandon.cfa (deleted)
-
tests/concurrent/futures/basic.cfa (deleted)
-
tests/concurrent/futures/multi.cfa (deleted)
-
tests/concurrent/futures/typed.cfa (deleted)
-
tests/concurrent/join.cfa (deleted)
-
tests/concurrent/joinerror.cfa (deleted)
-
tests/concurrent/monitor.cfa (modified) (1 diff)
-
tests/concurrent/multi-monitor.cfa (modified) (1 diff)
-
tests/concurrent/park/.expect/contention.txt (deleted)
-
tests/concurrent/park/.expect/force_preempt.txt (deleted)
-
tests/concurrent/park/.expect/start_parked.txt (deleted)
-
tests/concurrent/park/contention.cfa (deleted)
-
tests/concurrent/park/force_preempt.cfa (deleted)
-
tests/concurrent/park/start_parked.cfa (deleted)
-
tests/concurrent/signal/block.cfa (modified) (2 diffs)
-
tests/concurrent/signal/disjoint.cfa (modified) (6 diffs)
-
tests/concurrent/suspend_then.cfa (deleted)
-
tests/concurrent/waitfor/when.cfa (modified) (1 diff)
-
tests/config.py.in (modified) (1 diff)
-
tests/const-init.cfa (deleted)
-
tests/context.cfa (added)
-
tests/copyfile.cfa (modified) (1 diff)
-
tests/coroutine/.expect/fmtLines.txt (modified) (1 diff)
-
tests/coroutine/.in/fmtLines.txt (modified) (1 diff)
-
tests/coroutine/cntparens.cfa (modified) (3 diffs)
-
tests/coroutine/devicedriver.cfa (modified) (5 diffs)
-
tests/coroutine/fibonacci.cfa (modified) (1 diff)
-
tests/coroutine/fibonacci_1.cfa (modified) (2 diffs)
-
tests/coroutine/fmtLines.cfa (modified) (1 diff)
-
tests/coroutine/raii.cfa (modified) (1 diff)
-
tests/coroutine/runningTotal.cfa (modified) (1 diff)
-
tests/coroutine/suspend_then.cfa (modified) (1 diff)
-
tests/declarationErrors.cfa (added)
-
tests/enum.cfa (modified) (1 diff)
-
tests/errors/.expect/completeType.nast.arm64.txt (deleted)
-
tests/errors/.expect/completeType.nast.x64.txt (deleted)
-
tests/errors/.expect/completeType.nast.x86.txt (deleted)
-
tests/errors/.expect/completeType.oast.arm64.txt (deleted)
-
tests/errors/.expect/completeType.oast.x64.txt (deleted)
-
tests/errors/.expect/completeType.oast.x86.txt (deleted)
-
tests/errors/.expect/declaration.txt (deleted)
-
tests/errors/.expect/scope.txt (deleted)
-
tests/errors/.expect/signature.txt (deleted)
-
tests/errors/completeType.cfa (deleted)
-
tests/errors/declaration.cfa (deleted)
-
tests/errors/scope.cfa (deleted)
-
tests/errors/signature.cfa (deleted)
-
tests/errors/suspend.cfa (deleted)
-
tests/exceptions/.expect/conditional-threads.txt (deleted)
-
tests/exceptions/.expect/conditional.txt (deleted)
-
tests/exceptions/.expect/data-except.txt (deleted)
-
tests/exceptions/.expect/defaults-threads.txt (deleted)
-
tests/exceptions/.expect/defaults.txt (deleted)
-
tests/exceptions/.expect/finally-threads.txt (deleted)
-
tests/exceptions/.expect/finally.txt (deleted)
-
tests/exceptions/.expect/interact.txt (deleted)
-
tests/exceptions/.expect/polymorphic.txt (deleted)
-
tests/exceptions/.expect/resume-threads.txt (deleted)
-
tests/exceptions/.expect/resume.txt (deleted)
-
tests/exceptions/.expect/terminate-threads.txt (deleted)
-
tests/exceptions/.expect/terminate.txt (deleted)
-
tests/exceptions/.expect/trash.txt (deleted)
-
tests/exceptions/.expect/type-check.txt (deleted)
-
tests/exceptions/.expect/virtual-cast.txt (deleted)
-
tests/exceptions/.expect/virtual-poly.txt (deleted)
-
tests/exceptions/cancel/.expect/coroutine.txt (deleted)
-
tests/exceptions/cancel/.expect/thread.txt (deleted)
-
tests/exceptions/cancel/coroutine.cfa (deleted)
-
tests/exceptions/cancel/thread.cfa (deleted)
-
tests/exceptions/conditional.cfa (deleted)
-
tests/exceptions/data-except.cfa (deleted)
-
tests/exceptions/defaults.cfa (deleted)
-
tests/exceptions/except-0.cfa (added)
-
tests/exceptions/except-1.cfa (added)
-
tests/exceptions/except-2.cfa (added)
-
tests/exceptions/except-3.cfa (added)
-
tests/exceptions/except-io.hfa (deleted)
-
tests/exceptions/except-mac.hfa (added)
-
tests/exceptions/finally.cfa (deleted)
-
tests/exceptions/interact.cfa (deleted)
-
tests/exceptions/polymorphic.cfa (deleted)
-
tests/exceptions/resume.cfa (deleted)
-
tests/exceptions/terminate.cfa (deleted)
-
tests/exceptions/trash.cfa (deleted)
-
tests/exceptions/type-check.cfa (deleted)
-
tests/exceptions/virtual-cast.cfa (deleted)
-
tests/exceptions/virtual-poly.cfa (deleted)
-
tests/exceptions/with-threads.hfa (deleted)
-
tests/expression.cfa (modified) (1 diff)
-
tests/forall.cfa (modified) (3 diffs)
-
tests/generator/.expect/fibonacci.txt (deleted)
-
tests/generator/.expect/fmtLines.txt (deleted)
-
tests/generator/.expect/suspend_then.txt (deleted)
-
tests/generator/.in/fmtLines.txt (deleted)
-
tests/generator/fibonacci.cfa (deleted)
-
tests/generator/fmtLines.cfa (deleted)
-
tests/generator/suspend_then.cfa (deleted)
-
tests/global-monomorph.cfa (deleted)
-
tests/heap.cfa (modified) (30 diffs)
-
tests/identFuncDeclarator.cfa (modified) (2 diffs)
-
tests/identParamDeclarator.cfa (modified) (2 diffs)
-
tests/init1.cfa (deleted)
-
tests/io2.cfa (modified) (1 diff)
-
tests/labelledExit.cfa (modified) (3 diffs)
-
tests/limits.cfa (modified) (2 diffs)
-
tests/linking/.expect/exception-nothreads.txt (deleted)
-
tests/linking/.expect/exception-withthreads.txt (deleted)
-
tests/linking/.expect/nostdlib.txt (deleted)
-
tests/linking/exception-nothreads.cfa (deleted)
-
tests/linking/exception-withthreads.cfa (deleted)
-
tests/linking/nostdlib.cfa (deleted)
-
tests/linking/withthreads.cfa (modified) (1 diff)
-
tests/list/.expect/dlist-insert-remove.txt (deleted)
-
tests/list/dlist-insert-remove.cfa (deleted)
-
tests/literals.cfa (modified) (5 diffs)
-
tests/malloc.cfa (deleted)
-
tests/manipulatorsInput.cfa (modified) (2 diffs)
-
tests/manipulatorsOutput1.cfa (modified) (6 diffs)
-
tests/manipulatorsOutput2.cfa (modified) (2 diffs)
-
tests/manipulatorsOutput3.cfa (deleted)
-
tests/math4.cfa (modified) (4 diffs)
-
tests/maybe.cfa (modified) (2 diffs)
-
tests/meta/.expect/archVast.nast.arm64.txt (deleted)
-
tests/meta/.expect/archVast.nast.x64.txt (deleted)
-
tests/meta/.expect/archVast.nast.x86.txt (deleted)
-
tests/meta/.expect/archVast.oast.arm64.txt (deleted)
-
tests/meta/.expect/archVast.oast.x64.txt (deleted)
-
tests/meta/.expect/archVast.oast.x86.txt (deleted)
-
tests/meta/archVast.cfa (deleted)
-
tests/minmax.cfa (modified) (3 diffs)
-
tests/namedParmArg.cfa (added)
-
tests/nested-types.cfa (modified) (2 diffs)
-
tests/numericConstants.cfa (modified) (2 diffs)
-
tests/occursError.cfa (added)
-
tests/operators.cfa (modified) (1 diff)
-
tests/poly-d-cycle.cfa (deleted)
-
tests/poly-o-cycle.cfa (deleted)
-
tests/pybin/settings.py (modified) (6 diffs)
-
tests/pybin/test_run.py (modified) (4 diffs)
-
tests/pybin/tools.py (modified) (9 diffs)
-
tests/quotedKeyword.cfa (modified) (2 diffs)
-
tests/raii/.expect/ctor-autogen-ERR1.nast.txt (deleted)
-
tests/raii/.expect/ctor-autogen-ERR1.oast.txt (deleted)
-
tests/raii/.expect/ctor-autogen-ERR1.txt (added)
-
tests/raii/.expect/ctor-autogen.txt (modified) (1 diff)
-
tests/raii/.expect/init_once.txt (modified) (1 diff)
-
tests/raii/.expect/memberCtors-ERR1.nast.txt (deleted)
-
tests/raii/.expect/memberCtors-ERR1.oast.txt (deleted)
-
tests/raii/.expect/memberCtors-ERR1.txt (added)
-
tests/raii/ctor-autogen.cfa (modified) (1 diff)
-
tests/raii/init_once.cfa (modified) (2 diffs)
-
tests/rational.cfa (modified) (5 diffs)
-
tests/references.cfa (modified) (1 diff)
-
tests/resolutionErrors.cfa (deleted)
-
tests/result.cfa (modified) (2 diffs)
-
tests/scope.cfa (added)
-
tests/scopeErrors.cfa (added)
-
tests/searchsort.cfa (modified) (1 diff)
-
tests/smart-pointers.cfa (deleted)
-
tests/stdincludes.cfa (modified) (2 diffs)
-
tests/structMember.cfa (added)
-
tests/subrange.cfa (added)
-
tests/switch.cfa (modified) (2 diffs)
-
tests/test.py (modified) (16 diffs)
-
tests/time.cfa (modified) (2 diffs)
-
tests/tuple/tupleAssign.cfa (modified) (2 diffs)
-
tests/typeGenerator.cfa (added)
-
tests/typedef.cfa (added)
-
tests/typedefDeclarator.cfa (added)
-
tests/typedefRedef.cfa (modified) (2 diffs)
-
tests/typeof.cfa (modified) (1 diff)
-
tests/unified_locking/.expect/locks.txt (deleted)
-
tests/unified_locking/locks.cfa (deleted)
-
tests/unified_locking/timeout_lock.cfa (deleted)
-
tests/unified_locking/timeout_lock.txt (deleted)
-
tests/userLiterals.cfa (modified) (6 diffs)
-
tests/variableDeclarator.cfa (modified) (8 diffs)
-
tests/vector.cfa (modified) (2 diffs)
-
tests/virtualCast.cfa (added)
-
tests/voidPtr.cfa (modified) (1 diff)
-
tests/warnings/.expect/self-assignment.nast.txt (deleted)
-
tests/warnings/.expect/self-assignment.oast.txt (deleted)
-
tests/warnings/.expect/self-assignment.txt (added)
-
tests/warnings/self-assignment.cfa (modified) (2 diffs)
-
tests/withStatement.cfa (added)
-
tests/zombies/ArrayN.c (deleted)
-
tests/zombies/Initialization.c (deleted)
-
tests/zombies/Initialization2.c (deleted)
-
tests/zombies/Makefile.example (deleted)
-
tests/zombies/Members.c (deleted)
-
tests/zombies/Misc.c (deleted)
-
tests/zombies/MiscError.c (deleted)
-
tests/zombies/Rank2.c (deleted)
-
tests/zombies/Tuple.c (deleted)
-
tests/zombies/abstype.c (deleted)
-
tests/zombies/constructors.c (deleted)
-
tests/zombies/context.cfa (deleted)
-
tests/zombies/forward.c (deleted)
-
tests/zombies/gc_no_raii/.gitignore (deleted)
-
tests/zombies/gc_no_raii/bug-repro/assert.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/blockers.tar.gz (deleted)
-
tests/zombies/gc_no_raii/bug-repro/blockers/explicit_cast.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/blockers/file_scope.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/blockers/recursive_realloc.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/crash.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/deref.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/field.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/find.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/inline.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/malloc.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/not_equal.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/oddtype.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/push_back.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/push_back.h (deleted)
-
tests/zombies/gc_no_raii/bug-repro/realloc.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/return.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/return_template.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/slow_malloc.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/static_const_local.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/test-assert.cpp (deleted)
-
tests/zombies/gc_no_raii/bug-repro/void_pointer.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/while.c (deleted)
-
tests/zombies/gc_no_raii/bug-repro/zero.c (deleted)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-malign.c (deleted)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-malloc.c (deleted)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-mmap.c (deleted)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-win-valloc.c (deleted)
-
tests/zombies/gc_no_raii/premake4.lua (deleted)
-
tests/zombies/gc_no_raii/src/allocate-pool.c (deleted)
-
tests/zombies/gc_no_raii/src/allocate-pool.h (deleted)
-
tests/zombies/gc_no_raii/src/gc.h (deleted)
-
tests/zombies/gc_no_raii/src/gcpointers.c (deleted)
-
tests/zombies/gc_no_raii/src/gcpointers.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/card_table.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/collector.c (deleted)
-
tests/zombies/gc_no_raii/src/internal/collector.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/gc_tools.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/globals.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/memory_pool.c (deleted)
-
tests/zombies/gc_no_raii/src/internal/memory_pool.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/object_header.c (deleted)
-
tests/zombies/gc_no_raii/src/internal/object_header.h (deleted)
-
tests/zombies/gc_no_raii/src/internal/state.c (deleted)
-
tests/zombies/gc_no_raii/src/internal/state.h (deleted)
-
tests/zombies/gc_no_raii/src/test_include.c (deleted)
-
tests/zombies/gc_no_raii/src/tools.h (deleted)
-
tests/zombies/gc_no_raii/src/tools/checks.h (deleted)
-
tests/zombies/gc_no_raii/src/tools/print.c (deleted)
-
tests/zombies/gc_no_raii/src/tools/print.h (deleted)
-
tests/zombies/gc_no_raii/src/tools/worklist.h (deleted)
-
tests/zombies/gc_no_raii/test/badlll.c (deleted)
-
tests/zombies/gc_no_raii/test/gctest.c (deleted)
-
tests/zombies/gc_no_raii/test/operators.c (deleted)
-
tests/zombies/hashtable.cfa (deleted)
-
tests/zombies/hashtable2.cfa (deleted)
-
tests/zombies/huge.c (deleted)
-
tests/zombies/includes.c (deleted)
-
tests/zombies/index.h (deleted)
-
tests/zombies/io_uring.txt (deleted)
-
tests/zombies/it_out.c (deleted)
-
tests/zombies/multicore.c (deleted)
-
tests/zombies/namedParmArg.cfa (deleted)
-
tests/zombies/new.c (deleted)
-
tests/zombies/occursError.cfa (deleted)
-
tests/zombies/poly-bench.c (deleted)
-
tests/zombies/prolog.c (deleted)
-
tests/zombies/quad.c (deleted)
-
tests/zombies/s.c (deleted)
-
tests/zombies/scope.cfa (deleted)
-
tests/zombies/simplePoly.c (deleted)
-
tests/zombies/simpler.c (deleted)
-
tests/zombies/specialize.c (deleted)
-
tests/zombies/square.c (deleted)
-
tests/zombies/structMember.cfa (deleted)
-
tests/zombies/subrange.cfa (deleted)
-
tests/zombies/twice.c (deleted)
-
tests/zombies/typeGenerator.cfa (deleted)
-
tests/zombies/typedef.cfa (deleted)
-
tests/zombies/typedefDeclarator.cfa (deleted)
-
tests/zombies/withStatement.cfa (deleted)
-
tests/zombies/wrapper/.gitignore (deleted)
-
tests/zombies/wrapper/premake4.lua (deleted)
-
tests/zombies/wrapper/src/main.c (deleted)
-
tests/zombies/wrapper/src/pointer.h (deleted)
-
tests/zombies/zero_one.c (deleted)
-
tools/Makefile.am (modified) (1 diff)
-
tools/Makefile.in (added)
-
tools/auto-complete.md (deleted)
-
tools/build/cfa.m4 (deleted)
-
tools/build/cfa.make (deleted)
-
tools/build/push2dist.sh (modified) (2 diffs)
-
tools/busy (added)
-
tools/cfa.nanorc (modified) (3 diffs)
-
tools/error (added)
-
tools/gdb/.gdbinit (deleted)
-
tools/gdb/README (deleted)
-
tools/gdb/utils-gdb.gdb (deleted)
-
tools/gdb/utils-gdb.py (deleted)
-
tools/langserver/src/json.hpp (deleted)
-
tools/langserver/src/log.cpp (deleted)
-
tools/langserver/src/log.hpp (deleted)
-
tools/langserver/src/main.cpp (deleted)
-
tools/langserver/src/server.cpp (deleted)
-
tools/langserver/src/server.hpp (deleted)
-
tools/perf/process_halts.sh (deleted)
-
tools/perf/view_halts.py (deleted)
-
tools/prettyprinter/Makefile.am (modified) (1 diff)
-
tools/prettyprinter/Makefile.in (added)
-
tools/stat.py (modified) (2 diffs)
-
tools/vscode/uwaterloo.cforall-0.1.0/.gitignore (deleted)
-
tools/vscode/uwaterloo.cforall-0.1.0/client/main.js (deleted)
-
tools/vscode/uwaterloo.cforall-0.1.0/images/icon.png (deleted)
-
tools/vscode/uwaterloo.cforall-0.1.0/package.json (modified) (4 diffs)
-
tools/vscode/uwaterloo.cforall-0.1.0/server/out/server (deleted)
-
tools/vscode/uwaterloo.cforall-0.1.0/syntaxes/cfa.tmLanguage.json (deleted)
-
tools/watchdog (added)
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
reef8dfb rbdfc032 4 4 5 5 # generated by configure 6 aclocal.m47 automake8 6 autom4te.cache 9 7 config.h … … 11 9 config.log 12 10 config.py 13 configure 11 stamp-h1 14 12 libtool 15 stamp-h116 13 /Makefile 17 /Makefile.in18 14 **/Makefile 19 **/Makefile.in20 **/Makefile.dist.in21 15 /version 22 16 … … 48 42 libcfa/x64-debug/ 49 43 libcfa/x64-nodebug/ 44 libcfa/x64-nolib/ 50 45 libcfa/x86-debug/ 51 46 libcfa/x86-nodebug/ 52 libcfa/arm64-debug/ 53 libcfa/arm64-nodebug/ 47 libcfa/x86-nolib/ 48 libcfa/arm-debug/ 49 libcfa/arm-nodebug/ 50 libcfa/arm-nolib/ 54 51 55 52 # generated by bison and lex from parser.yy and lex.ll … … 76 73 doc/user/pointer2.tex 77 74 doc/user/EHMHierarchy.tex 78 79 # generated by npm80 package-lock.json -
Jenkins/FullBuild
reef8dfb rbdfc032 8 8 def err = null 9 9 10 final scmVars = checkout scm11 final commitId = scmVars.GIT_COMMIT12 13 10 try { 14 11 //Wrap build to add timestamp to command line … … 17 14 stage('Build') { 18 15 16 results = [null, null] 17 19 18 parallel ( 20 gcc_8_x86_old: { trigger_build( 'gcc-8', 'x86', false ) }, 21 gcc_7_x86_old: { trigger_build( 'gcc-7', 'x86', false ) }, 22 gcc_6_x86_old: { trigger_build( 'gcc-6', 'x86', false ) }, 23 gcc_9_x64_old: { trigger_build( 'gcc-9', 'x64', false ) }, 24 gcc_8_x64_old: { trigger_build( 'gcc-8', 'x64', false ) }, 25 gcc_7_x64_old: { trigger_build( 'gcc-7', 'x64', false ) }, 26 gcc_6_x64_old: { trigger_build( 'gcc-6', 'x64', false ) }, 27 gcc_5_x64_old: { trigger_build( 'gcc-5', 'x64', false ) }, 28 clang_x64_old: { trigger_build( 'clang', 'x64', false ) }, 29 clang_x64_new: { trigger_build( 'clang', 'x64', true ) }, 19 clang_x86: { trigger_build( 'gcc-8', 'x86' ) }, 20 gcc_5_x86: { trigger_build( 'gcc-7', 'x86' ) }, 21 gcc_6_x86: { trigger_build( 'gcc-6', 'x86' ) }, 22 gcc_9_x64: { trigger_build( 'gcc-9', 'x64' ) }, 23 gcc_8_x64: { trigger_build( 'gcc-8', 'x64' ) }, 24 gcc_7_x64: { trigger_build( 'gcc-7', 'x64' ) }, 25 gcc_6_x64: { trigger_build( 'gcc-6', 'x64' ) }, 26 gcc_5_x64: { trigger_build( 'gcc-5', 'x64' ) }, 27 clang_x64: { trigger_build( 'clang', 'x64' ) }, 30 28 ) 31 }32 33 stage('Package') {34 trigger_dist( commitId, currentBuild.number.toString() )35 29 } 36 30 } … … 65 59 //=========================================================================================================== 66 60 67 def trigger_build(String cc, String arch , boolean new_ast) {61 def trigger_build(String cc, String arch) { 68 62 def result = build job: 'Cforall/master', \ 69 63 parameters: [ \ … … 74 68 name: 'Architecture', \ 75 69 value: arch], \ 76 [$class: 'BooleanParameterValue', \77 name: 'NewAST', \78 value: new_ast], \79 70 [$class: 'BooleanParameterValue', \ 80 71 name: 'RunAllTests', \ … … 88 79 [$class: 'BooleanParameterValue', \ 89 80 name: 'Publish', \ 90 value: true], \81 value: true], \ 91 82 [$class: 'BooleanParameterValue', \ 92 83 name: 'Silent', \ … … 103 94 } 104 95 105 def trigger_dist(String commitId, String buildNum) { 106 def result = build job: 'Cforall_Distribute_Ref', \ 107 parameters: [ \ 108 string(name: 'GitRef', value: commitId), \ 109 string(name: 'Build' , value: buildNum) \ 110 ], \ 111 propagate: false 96 //Helper routine to collect information about the git history 97 def collect_git_info() { 112 98 113 echo(result.result) 99 //create the temporary output directory in case it doesn't already exist 100 def out_dir = pwd tmp: true 101 sh "mkdir -p ${out_dir}" 114 102 115 if(result.result != 'SUCCESS') {116 sh("wget -q -O - https://cforall.uwaterloo.ca/jenkins/job/Cforall_Distribute_Ref/${result.number}/consoleText")117 error(result.result)103 //parse git logs to find what changed 104 dir("../Cforall_Full_Build@script") { 105 sh "git reflog > ${out_dir}/GIT_COMMIT" 118 106 } 107 git_reflog = readFile("${out_dir}/GIT_COMMIT") 108 gitRefOldValue = (git_reflog =~ /moving from (.+) to (.+)/)[0][1] 109 gitRefNewValue = (git_reflog =~ /moving from (.+) to (.+)/)[0][2] 119 110 } 120 111 -
Jenkinsfile
reef8dfb rbdfc032 2 2 3 3 import groovy.transform.Field 4 5 // For skipping stages 6 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 4 7 5 8 //=========================================================================================================== … … 12 15 SrcDir = pwd tmp: false 13 16 Settings = null 14 Tools = null17 StageName = '' 15 18 16 19 // Local variables … … 30 33 SrcDir = pwd tmp: false 31 34 32 Tools.Clean()33 34 Tools.Checkout()35 clean() 36 37 checkout() 35 38 36 39 build() … … 54 57 //attach the build log to the email 55 58 catch (Exception caughtError) { 56 // Store the result of the build log 57 currentBuild.result = "FAILURE" 58 59 // An error has occured, the build log is relevent 59 //rethrow error later 60 err = caughtError 61 62 echo err.toString() 63 64 //An error has occured, the build log is relevent 60 65 log_needed = true 61 66 62 // rethrow error later 63 err = caughtError 64 65 // print the error so it shows in the log 66 echo err.toString() 67 //Store the result of the build log 68 currentBuild.result = "${StageName} FAILURE".trim() 67 69 } 68 70 … … 82 84 // Main compilation routines 83 85 //=========================================================================================================== 86 def clean() { 87 build_stage('Cleanup', true) { 88 // clean the build by wipping the build directory 89 dir(BuildDir) { 90 deleteDir() 91 } 92 } 93 } 94 95 //Compilation script is done here but environnement set-up and error handling is done in main loop 96 def checkout() { 97 build_stage('Checkout', true) { 98 //checkout the source code and clean the repo 99 final scmVars = checkout scm 100 Settings.GitNewRef = scmVars.GIT_COMMIT 101 Settings.GitOldRef = scmVars.GIT_PREVIOUS_COMMIT 102 103 echo GitLogMessage() 104 105 // This is a complete hack but it solves problems with automake thinking it needs to regenerate makefiles 106 // We fudged automake/missing to handle that but automake stills bakes prints inside the makefiles 107 // and these cause more problems. 108 sh 'find . -name Makefile.in -exec touch {} +' 109 } 110 } 111 84 112 def build() { 85 113 debug = true 86 114 release = Settings.RunAllTests || Settings.RunBenchmark 87 Tools.BuildStage('Build : configure', true) { 88 // Configure must be run inside the tree 89 dir (SrcDir) { 90 // Generate the necessary build files 91 sh './autogen.sh' 92 } 93 115 build_stage('Build : configure', true) { 94 116 // Build outside of the src tree to ease cleaning 95 117 dir (BuildDir) { 96 //Configure the co mpilation (Output is not relevant)118 //Configure the conpilation (Output is not relevant) 97 119 //Use the current directory as the installation target so nothing escapes the sandbox 98 120 //Also specify the compiler by hand … … 104 126 } 105 127 106 ast = Settings.NewAST ? "--enable-new-ast" : "--disable-new-ast" 107 108 sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} AR=gcc-ar RANLIB=gcc-ranlib ${targets} ${ast} --quiet --prefix=${BuildDir}" 128 sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} ${targets} --quiet" 109 129 110 130 // Configure libcfa … … 113 133 } 114 134 115 Tools.BuildStage('Build : cfa-cpp', true) {135 build_stage('Build : cfa-cpp', true) { 116 136 // Build outside of the src tree to ease cleaning 117 137 dir (BuildDir) { … … 124 144 } 125 145 126 Tools.BuildStage('Build : libcfa(debug)', debug) {146 build_stage('Build : libcfa(debug)', debug) { 127 147 // Build outside of the src tree to ease cleaning 128 148 dir (BuildDir) { … … 131 151 } 132 152 133 Tools.BuildStage('Build : libcfa(nodebug)', release) {153 build_stage('Build : libcfa(nodebug)', release) { 134 154 // Build outside of the src tree to ease cleaning 135 155 dir (BuildDir) { … … 137 157 } 138 158 } 139 140 Tools.BuildStage('Build : install', true) {141 // Build outside of the src tree to ease cleaning142 dir (BuildDir) {143 sh "make -j 8 --no-print-directory install"144 }145 }146 159 } 147 160 148 161 def test() { 149 162 try { 150 Tools.BuildStage('Test: short', !Settings.RunAllTests) {163 build_stage('Test: short', !Settings.RunAllTests) { 151 164 dir (BuildDir) { 152 165 //Run the tests from the tests directory … … 155 168 } 156 169 157 Tools.BuildStage('Test: full', Settings.RunAllTests) {170 build_stage('Test: full', Settings.RunAllTests) { 158 171 dir (BuildDir) { 159 172 //Run the tests from the tests directory … … 166 179 echo "Archiving core dumps" 167 180 dir (BuildDir) { 168 archiveArtifacts artifacts: "tests/crashes/**/* ,lib/**/lib*.so*", fingerprint: true181 archiveArtifacts artifacts: "tests/crashes/**/*", fingerprint: true 169 182 } 170 183 throw err … … 173 186 174 187 def benchmark() { 175 Tools.BuildStage('Benchmark', Settings.RunBenchmark) {188 build_stage('Benchmark', Settings.RunBenchmark) { 176 189 dir (BuildDir) { 177 190 //Append bench results … … 182 195 183 196 def build_doc() { 184 Tools.BuildStage('Documentation', Settings.BuildDocumentation) {197 build_stage('Documentation', Settings.BuildDocumentation) { 185 198 dir ('doc/user') { 186 199 make_doc() … … 194 207 195 208 def publish() { 196 Tools.BuildStage('Publish', true) {209 build_stage('Publish', true) { 197 210 198 211 if( Settings.Publish && !Settings.RunBenchmark ) { echo 'No results to publish!!!' } … … 202 215 203 216 //Then publish the results 204 do_plot(Settings.RunBenchmark && Settings.Publish, 'compile' , groupCompile , false, 'Compilation')205 do_plot(Settings.RunBenchmark && Settings.Publish, 'compile.diff' , groupCompile , true , 'Compilation (relative)')206 do_plot(Settings.RunBenchmark && Settings.Publish, 'ctxswitch' , groupConcurrency, false, 'Context Switching')207 do_plot(Settings.RunBenchmark && Settings.Publish, 'ctxswitch.diff' , groupConcurrency, true , 'Context Switching (relative)')208 do_plot(Settings.RunBenchmark && Settings.Publish, 'mutex' , groupConcurrency, false, 'Mutual Exclusion')209 do_plot(Settings.RunBenchmark && Settings.Publish, 'mutex.diff' , groupConcurrency, true , 'Mutual Exclusion (relative)')210 do_plot(Settings.RunBenchmark && Settings.Publish, 's cheduling', groupConcurrency, false, 'Internal and External Scheduling')211 do_plot(Settings.RunBenchmark && Settings.Publish, 's cheduling.diff', groupConcurrency, true , 'Internal and External Scheduling (relative)')217 do_plot(Settings.RunBenchmark && Settings.Publish, 'compile' , groupCompile , false, 'Compilation') 218 do_plot(Settings.RunBenchmark && Settings.Publish, 'compile.diff' , groupCompile , true , 'Compilation (relative)') 219 do_plot(Settings.RunBenchmark && Settings.Publish, 'ctxswitch' , groupConcurrency, false, 'Context Switching') 220 do_plot(Settings.RunBenchmark && Settings.Publish, 'ctxswitch.diff', groupConcurrency, true , 'Context Switching (relative)') 221 do_plot(Settings.RunBenchmark && Settings.Publish, 'mutex' , groupConcurrency, false, 'Mutual Exclusion') 222 do_plot(Settings.RunBenchmark && Settings.Publish, 'mutex.diff' , groupConcurrency, true , 'Mutual Exclusion (relative)') 223 do_plot(Settings.RunBenchmark && Settings.Publish, 'signal' , groupConcurrency, false, 'Internal and External Scheduling') 224 do_plot(Settings.RunBenchmark && Settings.Publish, 'signal.diff' , groupConcurrency, true , 'Internal and External Scheduling (relative)') 212 225 } 213 226 } … … 216 229 //Routine responsible of sending the email notification once the build is completed 217 230 //=========================================================================================================== 231 @NonCPS 232 def SplitLines(String text) { 233 def list = [] 234 235 text.eachLine { 236 list += it 237 } 238 239 return list 240 } 241 242 def GitLogMessage() { 243 if (!Settings || !Settings.GitOldRef || !Settings.GitNewRef) return "\nERROR retrieveing git information!\n" 244 245 def oldRef = Settings.GitOldRef 246 def newRef = Settings.GitNewRef 247 248 def revText = sh(returnStdout: true, script: "git rev-list ${oldRef}..${newRef}").trim() 249 def revList = SplitLines( revText ) 250 251 def gitUpdate = "" 252 revList.each { rev -> 253 def type = sh(returnStdout: true, script: "git cat-file -t ${rev}").trim() 254 gitUpdate = gitUpdate + " via ${rev} (${type})" 255 } 256 257 def rev = oldRef 258 def type = sh(returnStdout: true, script: "git cat-file -t ${rev}").trim() 259 gitUpdate = gitUpdate + " from ${rev} (${type})" 260 261 def gitLog = sh(returnStdout: true, script: "git rev-list --format=short ${oldRef}...${newRef}").trim() 262 263 def gitDiff = sh(returnStdout: true, script: "git diff --stat --color ${newRef} ${oldRef}").trim() 264 gitDiff = gitDiff.replace('[32m', '<span style="color: #00AA00;">') 265 gitDiff = gitDiff.replace('[31m', '<span style="color: #AA0000;">') 266 gitDiff = gitDiff.replace('[m', '</span>') 267 268 return """ 269 <pre> 270 The branch ${env.BRANCH_NAME} has been updated. 271 ${gitUpdate} 272 </pre> 273 274 <p>Check console output at ${env.BUILD_URL} to view the results.</p> 275 276 <p>- Status --------------------------------------------------------------</p> 277 278 <p>BUILD# ${env.BUILD_NUMBER} - ${currentBuild.result}</p> 279 280 <p>- Log -----------------------------------------------------------------</p> 281 282 <pre> 283 ${gitLog} 284 </pre> 285 286 <p>-----------------------------------------------------------------------</p> 287 <pre> 288 Summary of changes: 289 ${gitDiff} 290 </pre> 291 """ 292 } 293 218 294 //Standard build email notification 219 295 def email(boolean log) { … … 227 303 generated because of a git hooks/post-receive script following 228 304 a ref change which was pushed to the C\u2200 repository.</p> 229 """ + Tools.GitLogMessage()305 """ + GitLogMessage() 230 306 231 307 def email_to = !Settings.IsSandbox ? "cforall@lists.uwaterloo.ca" : "tdelisle@uwaterloo.ca" … … 249 325 public String CXX 250 326 public String CC 251 public String lto 252 253 CC_Desc(String name, String CXX, String CC, String lto) { 327 328 CC_Desc(String name, String CXX, String CC) { 254 329 this.name = name 255 330 this.CXX = CXX 256 this.CC = CC 257 this.lto = lto 331 this.CC = CC 258 332 } 259 333 } … … 275 349 public final CC_Desc Compiler 276 350 public final Arch_Desc Architecture 277 public final Boolean NewAST278 351 public final Boolean RunAllTests 279 352 public final Boolean RunBenchmark … … 291 364 switch( param.Compiler ) { 292 365 case 'gcc-9': 293 this.Compiler = new CC_Desc('gcc-9', 'g++-9', 'gcc-9' , '-flto=auto')366 this.Compiler = new CC_Desc('gcc-9', 'g++-9', 'gcc-9') 294 367 break 295 368 case 'gcc-8': 296 this.Compiler = new CC_Desc('gcc-8', 'g++-8', 'gcc-8' , '-flto=auto')369 this.Compiler = new CC_Desc('gcc-8', 'g++-8', 'gcc-8') 297 370 break 298 371 case 'gcc-7': 299 this.Compiler = new CC_Desc('gcc-7', 'g++-7', 'gcc-7' , '-flto=auto')372 this.Compiler = new CC_Desc('gcc-7', 'g++-7', 'gcc-7') 300 373 break 301 374 case 'gcc-6': 302 this.Compiler = new CC_Desc('gcc-6', 'g++-6', 'gcc-6' , '-flto=auto')375 this.Compiler = new CC_Desc('gcc-6', 'g++-6', 'gcc-6') 303 376 break 304 377 case 'gcc-5': 305 this.Compiler = new CC_Desc('gcc-5', 'g++-5', 'gcc-5' , '-flto=auto')378 this.Compiler = new CC_Desc('gcc-5', 'g++-5', 'gcc-5') 306 379 break 307 380 case 'gcc-4.9': 308 this.Compiler = new CC_Desc('gcc-4.9', 'g++-4.9', 'gcc-4.9' , '-flto=auto')381 this.Compiler = new CC_Desc('gcc-4.9', 'g++-4.9', 'gcc-4.9') 309 382 break 310 383 case 'clang': 311 this.Compiler = new CC_Desc('clang', 'clang++- 10', 'gcc-9', '-flto=thin -flto-jobs=0')384 this.Compiler = new CC_Desc('clang', 'clang++-6.0', 'gcc-6') 312 385 break 313 386 default : … … 327 400 328 401 this.IsSandbox = (branch == "jenkins-sandbox") 329 this.NewAST = param.NewAST330 402 this.RunAllTests = param.RunAllTests 331 403 this.RunBenchmark = param.RunBenchmark … … 337 409 this.DescShort = "${ this.Compiler.name }:${ this.Architecture.name }${full}" 338 410 339 final ast = this.NewAST ? "New AST" : "Old AST"340 411 this.DescLong = """Compiler : ${ this.Compiler.name } (${ this.Compiler.CXX }/${ this.Compiler.CC }) 341 AST Version : ${ ast.toString() }342 412 Architecture : ${ this.Architecture.name } 343 413 Arc Flags : ${ this.Architecture.flags } … … 369 439 // prepare the properties 370 440 properties ([ \ 371 buildDiscarder(logRotator( \372 artifactDaysToKeepStr: '', \373 artifactNumToKeepStr: '', \374 daysToKeepStr: '730', \375 numToKeepStr: '1000' \376 )), \377 441 [$class: 'ParametersDefinitionProperty', \ 378 442 parameterDefinitions: [ \ … … 380 444 description: 'Which compiler to use', \ 381 445 name: 'Compiler', \ 382 choices: 'gcc-9\ngcc-8\ngcc-7\ngcc-6\ngcc-5\ngcc-4.9\nclang', \446 choices: 'gcc-9\ngcc-8\ngcc-7\ngcc-6\ngcc-5\ngcc-4.9\nclang', \ 383 447 defaultValue: 'gcc-8', \ 384 448 ], \ … … 389 453 defaultValue: 'x64', \ 390 454 ], \ 391 [$class: 'BooleanParameterDefinition', \392 description: 'If true, build compiler using new AST', \393 name: 'NewAST', \394 defaultValue: true, \395 ], \396 455 [$class: 'BooleanParameterDefinition', \ 397 456 description: 'If false, only the quick test suite is ran', \ … … 422 481 ]]) 423 482 424 // It's unfortunate but it looks like we need to checkout the entire repo just to get 425 // - the pretty git printer 426 // - Jenkins.tools 483 // It's unfortunate but it looks like we need to checkout the entire repo just to get the pretty git printer 427 484 checkout scm 428 429 Tools = load "Jenkins/tools.groovy"430 485 431 486 final settings = new BuildSettings(params, env.BRANCH_NAME) … … 435 490 436 491 return settings 492 } 493 494 def build_stage(String name, boolean run, Closure block ) { 495 StageName = name 496 echo " -------- ${StageName} -------- " 497 if(run) { 498 stage(name, block) 499 } else { 500 stage(name) { Utils.markStageSkippedForConditional(STAGE_NAME) } 501 } 437 502 } 438 503 -
Makefile.am
reef8dfb rbdfc032 19 19 20 20 MAINTAINERCLEANFILES = lib/* bin/* tests/.deps/* tests/.out/* # order important 21 DISTCLEANFILES = version22 21 23 22 SUBDIRS = driver src . @LIBCFA_TARGET_DIRS@ 24 DIST_SUBDIRS = driver src . libcfa tests25 23 26 24 @LIBCFA_TARGET_MAKEFILES@ : Makefile $(srcdir)/libcfa/configure … … 28 26 @ls $(config_file) || (echo "Missing config.data, re-run configure script again" && false) 29 27 @$(eval config_data = $(shell cat $(config_file))) 30 @echo "Configuring libcfa ($(abs_top_srcdir)/libcfa/configure) with '$(config_data)' from $(shell pwd) / $(dir $@)"28 @echo "Configuring libcfa with '$(config_data)''" 31 29 @cd $(dir $@) && $(abs_top_srcdir)/libcfa/configure $(config_data) 32 30 … … 34 32 35 33 man1_MANS = doc/man/cfa.1 36 37 EXTRA_DIST = LICENSE doc/man/cfa.1 libcfa/configure libcfa/Makefile.dist.am libcfa/Makefile.dist.in tools/build/distcc_hash tools/build/push2dist.sh38 34 39 35 debug=yes … … 51 47 @./config.status --config | sed "s/ /\n\t/g; s/\t'/\t/g; s/'\n/\n/g; s/^'//g; s/'$$//g" 52 48 @find libcfa -name config.status -printf "\n%h\n\t" -exec {} --config \; | sed "s/ /\n\t/g; s/\t'/\t/g; s/'\n/\n/g; s/^'//g; s/'$$//g" 53 54 mostlyclean-local: @LIBCFA_TARGET_MAKEFILES@55 for dir in @LIBCFA_TARGET_DIRS@; do \56 $(MAKE) -C $${dir} mostlyclean; \57 done58 59 clean-local: @LIBCFA_TARGET_MAKEFILES@60 for dir in @LIBCFA_TARGET_DIRS@; do \61 $(MAKE) -C $${dir} clean; \62 done63 64 distclean-local: @LIBCFA_TARGET_MAKEFILES@65 for dir in @LIBCFA_TARGET_DIRS@; do \66 $(MAKE) -C $${dir} distclean; \67 rm $${dir}/config.data; \68 done -
benchmark/Makefile.am
reef8dfb rbdfc032 11 11 ## Created On : Sun May 31 09:08:15 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Tue Mar 10 11:41:18202014 ## Update Count : 25 813 ## Last Modified On : Sat Jan 25 09:20:44 2020 14 ## Update Count : 255 15 15 ############################################################################### 16 16 … … 19 19 20 20 # applies to both programs 21 include $(top_srcdir)/ tools/build/cfa.make21 include $(top_srcdir)/src/cfa.make 22 22 23 23 AM_CFLAGS = -O2 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror … … 30 30 BENCH_V_UPP = $(__bench_v_UPP_$(__quiet)) 31 31 BENCH_V_GOC = $(__bench_v_GOC_$(__quiet)) 32 BENCH_V_PY = $(__bench_v_PY_$(__quiet))33 32 BENCH_V_RUSTC = $(__bench_v_RUSTC_$(__quiet)) 34 33 BENCH_V_NODEJS = $(__bench_v_NODEJS_$(__quiet)) … … 48 47 __bench_v_UPP_verbose = $(AM_V_UPP) 49 48 __bench_v_GOC_verbose = $(AM_V_GOC) 50 __bench_v_PY_verbose = $(AM_V_PY) 51 __bench_v_RUSTC_verbose = $(AM_V_RUST) 49 __bench_v_RUSTC_verbose = $(AM_V_RUSTC) 52 50 __bench_v_NODEJS_verbose = $(AM_V_NODEJS) 53 51 __bench_v_JAVAC_verbose = $(AM_V_JAVAC) … … 66 64 # Dummy hack tricks 67 65 EXTRA_PROGRAMS = dummy # build but do not install 68 nodist_dummy_SOURCES = dummyC.c dummyCXX.cpp66 dummy_SOURCES = dummyC.c dummyCXX.cpp 69 67 70 68 dummyC.c: … … 74 72 echo "int main() { return 0; }" > ${@} 75 73 76 .SILENT: # do not print recipe 74 #.SILENT: # do not print recipe 75 .ONESHELL: # use one shell to execute recipe 77 76 .NOTPARALLEL: 78 .PHONY: jenkins cleancsv 79 80 ## ========================================================================================================= 81 82 # all is used by make dist so ignore it 83 all: 84 85 all-bench : basic$(EXEEXT) ctxswitch$(EXEEXT) mutex$(EXEEXT) schedint$(EXEEXT) schedext$(EXEEXT) creation$(EXEEXT) 77 .PHONY: compile.csv basic.csv ctxswitch.csv mutex.csv schedint.csv 78 79 ## ========================================================================================================= 80 81 all : basic$(EXEEXT) ctxswitch$(EXEEXT) mutex$(EXEEXT) schedint$(EXEEXT) schedext$(EXEEXT) creation$(EXEEXT) 86 82 87 83 basic_loop_DURATION = 15000000000 … … 111 107 creation_cfa_coroutine_DURATION = 100000000 112 108 creation_cfa_coroutine_eager_DURATION = 10000000 113 creation_cfa_generator_DURATION = 1000000000114 109 creation_upp_coroutine_DURATION = ${creation_cfa_coroutine_eager_DURATION} 110 creation_cfa_thread_DURATION = 10000000 111 creation_upp_thread_DURATION = ${creation_cfa_thread_DURATION} 115 112 creation_DURATION = 10000000 116 113 … … 145 142 FIX_NEW_LINES = cat $@ | tr "\n" "\t" | sed -r 's/\t,/,/' | tr "\t" "\n" > $@ 146 143 147 cleancsv: 148 rm -f compile.csv basic.csv ctxswitch.csv mutex.csv schedint.csv 149 150 jenkins$(EXEEXT): cleancsv 144 jenkins$(EXEEXT): 151 145 @DOifskipcompile@ 152 146 +make compile.csv 153 147 -+make compile.diff.csv 154 148 @DOendif@ 149 +make basic.csv 150 -+make basic.diff.csv 155 151 +make ctxswitch.csv 156 152 -+make ctxswitch.diff.csv … … 163 159 -cat compile.diff.csv 164 160 @DOendif@ 161 cat basic.csv 162 -cat basic.diff.csv 165 163 cat ctxswitch.csv 166 164 -cat ctxswitch.diff.csv … … 171 169 172 170 compile.csv: 173 echo "building $@"174 171 echo "array,attributes,empty,expression,io,monitor,operators,typeof" > $@ 175 172 +make TIME_FORMAT='%e,' PRINT_FORMAT='' compile-array.make >> $@ … … 183 180 $(srcdir)/fixcsv.sh $@ 184 181 182 basic.csv: 183 echo "generator,coroutine,thread" > $@ 184 +make basic-cfa_generator.runquiet >> $@ && echo -n ',' >> $@ 185 +make basic-cfa_coroutine.runquiet >> $@ && echo -n ',' >> $@ 186 +make basic-cfa_thread.runquiet >> $@ 187 $(srcdir)/fixcsv.sh $@ 188 185 189 ctxswitch.csv: 186 echo "building $@"187 190 echo "generator,coroutine,thread" > $@ 188 191 +make ctxswitch-cfa_generator.runquiet >> $@ && echo -n ',' >> $@ … … 192 195 193 196 mutex.csv: 194 echo "building $@"195 197 echo "1-monitor,2-monitor" > $@ 196 198 +make mutex-cfa1.runquiet >> $@ && echo -n ',' >> $@ … … 199 201 200 202 schedint.csv: 201 echo "building $@"202 203 echo "schedint-1,schedint-2,schedext-1,schedext-2" > $@ 203 204 +make schedint-cfa1.runquiet >> $@ && echo -n ',' >> $@ … … 288 289 289 290 ctxswitch-python_coroutine$(EXEEXT): 290 $(BENCH_V_PY)echo "#!/bin/sh" > a.out291 echo "python3 $(srcdir)/ctxswitch/python_cor.py \"$$""@\"" >> a.out291 echo "#!/bin/sh" > a.out 292 echo "python3.7 $(srcdir)/ctxswitch/python_cor.py" >> a.out 292 293 chmod a+x a.out 293 294 294 295 ctxswitch-nodejs_coroutine$(EXEEXT): 295 $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out296 echo "nodejs $(srcdir)/ctxswitch/node_cor.js \"$$""@\"" >> a.out296 echo "#!/bin/sh" > a.out 297 echo "nodejs $(srcdir)/ctxswitch/node_cor.js" >> a.out 297 298 chmod a+x a.out 298 299 299 300 ctxswitch-nodejs_await$(EXEEXT): 300 $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out301 echo "nodejs $(srcdir)/ctxswitch/node_await.js \"$$""@\"" >> a.out301 echo "#!/bin/sh" > a.out 302 echo "nodejs $(srcdir)/ctxswitch/node_await.js" >> a.out 302 303 chmod a+x a.out 303 304 … … 311 312 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/ctxswitch/JavaThread.java 312 313 echo "#!/bin/sh" > a.out 313 echo "java JavaThread \"$$""@\"" >> a.out314 echo "java JavaThread" >> a.out 314 315 chmod a+x a.out 315 316 … … 353 354 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/mutex/JavaThread.java 354 355 echo "#!/bin/sh" > a.out 355 echo "java JavaThread \"$$""@\"" >> a.out356 echo "java JavaThread" >> a.out 356 357 chmod a+x a.out 357 358 … … 385 386 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/schedint/JavaThread.java 386 387 echo "#!/bin/sh" > a.out 387 echo "java JavaThread \"$$""@\"" >> a.out388 echo "java JavaThread" >> a.out 388 389 chmod a+x a.out 389 390 … … 451 452 452 453 creation-python_coroutine$(EXEEXT): 453 $(BENCH_V_PY)echo "#!/bin/sh" > a.out454 echo "python3 $(srcdir)/creation/python_cor.py \"$$""@\"" >> a.out454 echo "#!/bin/sh" > a.out 455 echo "python3.7 $(srcdir)/creation/python_cor.py" >> a.out 455 456 chmod a+x a.out 456 457 457 458 creation-nodejs_coroutine$(EXEEXT): 458 $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out459 echo "nodejs $(srcdir)/creation/node_cor.js \"$$""@\"" >> a.out459 echo "#!/bin/sh" > a.out 460 echo "nodejs $(srcdir)/creation/node_cor.js" >> a.out 460 461 chmod a+x a.out 461 462 … … 469 470 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/creation/JavaThread.java 470 471 echo "#!/bin/sh" > a.out 471 echo "java JavaThread \"$$""@\"" >> a.out472 echo "java JavaThread" >> a.out 472 473 chmod a+x a.out 473 474 … … 477 478 ## ========================================================================================================= 478 479 479 bcompile$(EXEEXT) : \480 compile$(EXEEXT) : \ 480 481 compile-array.make \ 481 482 compile-attributes.make \ … … 490 491 491 492 compile-array$(EXEEXT): 492 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/array.cfa493 $(CFACOMPILE) -fsyntax-only -w $(testdir)/array.cfa 493 494 494 495 compile-attributes$(EXEEXT): 495 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/attributes.cfa496 $(CFACOMPILE) -fsyntax-only -w $(testdir)/attributes.cfa 496 497 497 498 compile-empty$(EXEEXT): 498 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(srcdir)/compile/empty.cfa499 $(CFACOMPILE) -fsyntax-only -w $(srcdir)/compile/empty.cfa 499 500 500 501 compile-expression$(EXEEXT): 501 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/expression.cfa502 $(CFACOMPILE) -fsyntax-only -w $(testdir)/expression.cfa 502 503 503 504 compile-io$(EXEEXT): 504 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/io1.cfa505 $(CFACOMPILE) -fsyntax-only -w $(testdir)/io1.cfa 505 506 506 507 compile-monitor$(EXEEXT): 507 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/concurrent/monitor.cfa508 $(CFACOMPILE) -fsyntax-only -w $(testdir)/concurrent/monitor.cfa 508 509 509 510 compile-operators$(EXEEXT): 510 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/operators.cfa511 $(CFACOMPILE) -fsyntax-only -w $(testdir)/operators.cfa 511 512 512 513 compile-thread$(EXEEXT): 513 $(CFACOMPILE) - DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/concurrent/thread.cfa514 $(CFACOMPILE) -fsyntax-only -w $(testdir)/concurrent/thread.cfa 514 515 515 516 compile-typeof$(EXEEXT): 516 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/typeof.cfa 517 518 ## ========================================================================================================= 519 520 size$(EXEEXT) : size-cfa.runquiet 521 522 size-cfa$(EXEEXT): 523 $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/size/size.cfa 524 525 ## ========================================================================================================= 526 527 %-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs 528 cd $(builddir) && cargo build --release 529 cp $(builddir)/target/release/$(basename $@) $@ 517 $(CFACOMPILE) -fsyntax-only -w $(testdir)/typeof.cfa -
benchmark/creation/JavaThread.java
reef8dfb rbdfc032 1 1 public class JavaThread { 2 2 // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator. 3 // Bijective 3 // Bijective 4 4 // Cycle length for non-zero values is 4G-1. 5 5 // 0 is absorbing and should be avoided -- fixed point. 6 6 // The returned value is typically masked to produce a positive value. 7 static volatile int Ticket = 0 ; 7 static volatile int Ticket = 0 ; 8 8 9 9 private static int nextRandom (int x) { 10 if (x == 0) { 10 if (x == 0) { 11 11 // reseed the PRNG 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 20 20 } 21 21 x ^= x << 6; 22 22 x ^= x >>> 21; 23 23 x ^= x << 7; 24 return x ; 24 return x ; 25 25 } 26 26 static int x = 2; 27 27 28 static private long times = Long.parseLong("10000") ;28 static private int times = Integer.parseInt("10000") ; 29 29 30 30 public static class MyThread extends Thread { … … 33 33 } 34 34 public static void helper() throws InterruptedException { 35 for( longi = 1; i <= times; i += 1) {35 for(int i = 1; i <= times; i += 1) { 36 36 MyThread m = new MyThread(); 37 37 x = nextRandom( x ); … … 47 47 } 48 48 public static void main(String[] args) throws InterruptedException { 49 if ( args.length > 1) System.exit( 1 );50 if ( args.length == 1 ) { times = Long.parseLong(args[0]); }49 if ( args.length > 2 ) System.exit( 1 ); 50 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 51 51 52 for (int i = Integer.parseInt("5"); --i >= 0 ; ) { 52 for (int i = Integer.parseInt("5"); --i >= 0 ; ) { 53 53 InnerMain(); 54 54 Thread.sleep(2000); // 2 seconds -
benchmark/creation/cfa_gen.cfa
reef8dfb rbdfc032 1 #include " ../bench.h"1 #include "bench.h" 2 2 3 generator G{3 struct C { 4 4 volatile int restart; // ensure compiler does not optimize away all the code 5 5 }; 6 void ?{}( G & g ) { g.restart = 0; }7 void main( G& ) {}6 void ?{}( C & c ) { c.restart = 0; } 7 void main( C & ) {} 8 8 9 9 int main( int argc, char * argv[] ) { … … 11 11 BENCH( 12 12 for ( times ) { 13 G g;13 C c; 14 14 }, 15 15 result -
benchmark/creation/node_cor.js
reef8dfb rbdfc032 5 5 6 6 function * coroutine() { yield } 7 8 for ( var i = 0; i < times; i += 1 ) { // warm jit9 cor = coroutine()10 }11 12 7 var hrstart = process.hrtime() 13 8 for ( var i = 0; i < times; i += 1 ) { -
benchmark/ctxswitch/JavaThread.java
reef8dfb rbdfc032 1 1 public class JavaThread { 2 2 // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator. 3 // Bijective 3 // Bijective 4 4 // Cycle length for non-zero values is 4G-1. 5 5 // 0 is absorbing and should be avoided -- fixed point. 6 6 // The returned value is typically masked to produce a positive value. 7 static volatile int Ticket = 0 ; 7 static volatile int Ticket = 0 ; 8 8 9 9 private static int nextRandom (int x) { 10 if (x == 0) { 10 if (x == 0) { 11 11 // reseed the PRNG 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 20 20 } 21 21 x ^= x << 6; 22 22 x ^= x >>> 21; 23 23 x ^= x << 7; 24 return x ; 24 return x ; 25 25 } 26 26 static int x = 2; 27 27 28 static private long times = Long.parseLong("100000");28 static private int times = Integer.parseInt("100000"); 29 29 30 30 public static void helper() { 31 for( longi = 1; i <= times; i += 1) {31 for(int i = 1; i <= times; i += 1) { 32 32 Thread.yield(); 33 33 } … … 40 40 } 41 41 public static void main(String[] args) throws InterruptedException { 42 if ( args.length > 1) System.exit( 1 );43 if ( args.length == 1 ) { times = Long.parseLong(args[0]); }42 if ( args.length > 2 ) System.exit( 1 ); 43 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 44 44 45 45 for (int i = Integer.parseInt("5"); --i >= 0 ; ) { -
benchmark/ctxswitch/cfa_cor.cfa
reef8dfb rbdfc032 2 2 #include <thread.hfa> 3 3 4 #include " ../bench.h"4 #include "bench.h" 5 5 6 coroutine C {} ;6 coroutine C {} c; 7 7 void main( __attribute__((unused)) C & ) { 8 for() {9 suspend ;8 while () { 9 suspend(); 10 10 } 11 11 } 12 12 int main( int argc, char * argv[] ) { 13 C c;14 13 BENCH_START() 15 14 BENCH( -
benchmark/ctxswitch/cfa_gen.cfa
reef8dfb rbdfc032 1 1 #include "../bench.h" 2 2 3 generator G {}; 4 void main( G & ) { 3 typedef struct { 4 void * next; 5 } C; 6 7 void comain( C * c ) { 8 if ( __builtin_expect(c->next != 0, 1) ) goto *(c->next); 9 c->next = &&s1; 5 10 for () { 6 suspend; 11 return; 12 s1: ; 7 13 } 8 14 } 9 15 10 16 int main( int argc, char * argv[] ) { 11 G g;12 17 BENCH_START() 18 C c = { 0 }; 13 19 BENCH( 14 20 for ( times ) { 15 resume( g);21 comain( &c ); 16 22 }, 17 23 result -
benchmark/ctxswitch/node_cor.js
reef8dfb rbdfc032 10 10 } 11 11 cor = coroutine() 12 13 for ( var i = 0; i < times; i += 1 ) { // warm git14 cor.next();15 }16 12 17 13 var hrstart = process.hrtime() -
benchmark/exclude
reef8dfb rbdfc032 10 10 interrupt_linux.c 11 11 exclude 12 io13 12 Monitor.c -
benchmark/mutex/JavaThread.java
reef8dfb rbdfc032 1 1 public class JavaThread { 2 2 // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator. 3 // Bijective 3 // Bijective 4 4 // Cycle length for non-zero values is 4G-1. 5 5 // 0 is absorbing and should be avoided -- fixed point. 6 6 // The returned value is typically masked to produce a positive value. 7 static volatile int Ticket = 0 ; 7 static volatile int Ticket = 0 ; 8 8 9 9 private static int nextRandom (int x) { 10 if (x == 0) { 10 if (x == 0) { 11 11 // reseed the PRNG 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 20 20 } 21 21 x ^= x << 6; 22 22 x ^= x >>> 21; 23 23 x ^= x << 7; 24 return x ; 24 return x ; 25 25 } 26 26 static int x = 2; 27 27 28 static private long times = Long.parseLong("100000000");28 static private int times = Integer.parseInt("100000000"); 29 29 30 30 public synchronized void noop() { … … 34 34 JavaThread j = new JavaThread(); 35 35 // Inhibit biased locking ... 36 x = (j.hashCode() ^ System.identityHashCode(j)) | 1 ; 37 for( longi = 1; i <= times; i += 1) {36 x = (j.hashCode() ^ System.identityHashCode(j)) | 1 ; 37 for(int i = 1; i <= times; i += 1) { 38 38 x = nextRandom(x); 39 39 j.noop(); … … 47 47 } 48 48 public static void main(String[] args) throws InterruptedException { 49 if ( args.length > 1) System.exit( 1 );50 if ( args.length == 1 ) { times = Long.parseLong(args[0]); }49 if ( args.length > 2 ) System.exit( 1 ); 50 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 51 51 52 for (int n = Integer.parseInt("5"); --n >= 0 ; ) { 52 for (int n = Integer.parseInt("5"); --n >= 0 ; ) { 53 53 InnerMain(); 54 54 Thread.sleep(2000); // 2 seconds -
benchmark/mutexC/JavaThread.java
reef8dfb rbdfc032 1 1 class Noop { 2 2 // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator. 3 // Bijective 3 // Bijective 4 4 // Cycle length for non-zero values is 4G-1. 5 5 // 0 is absorbing and should be avoided -- fixed point. 6 6 // The returned value is typically masked to produce a positive value. 7 static volatile int Ticket = 0 ; 7 static volatile int Ticket = 0 ; 8 8 9 9 public static int nextRandom( int x ) { 10 if (x == 0) { 10 if (x == 0) { 11 11 // reseed the PRNG 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 12 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 13 // Note that we use a non-atomic racy increment -- the race is rare and benign. 14 // If the race is a concern switch to an AtomicInteger. 15 // In addition accesses to the RW volatile global "Ticket" variable are not 16 // (readily) predictable at compile-time so the JIT will not be able to elide 17 // nextRandom() invocations. 18 x = ++Ticket ; 19 if (x == 0) x = 1 ; 20 20 } 21 21 x ^= x << 6; 22 22 x ^= x >>> 21; 23 23 x ^= x << 7; 24 return x ; 24 return x ; 25 25 } 26 26 } … … 47 47 static int x = 2; 48 48 49 static private long times = Long.parseLong("10000000");49 static private int times = Integer.parseInt("10000000"); 50 50 51 51 public static void call( Monitor m ) throws InterruptedException { … … 53 53 m.go = true; 54 54 //while ( ! m.go2 ); 55 for ( longi = 0; i < times; i += 1 ) {55 for ( int i = 0; i < times; i += 1 ) { 56 56 m.call(); 57 57 x = Noop.nextRandom( x ); … … 71 71 public static void main( String[] args ) throws InterruptedException { 72 72 if ( args.length > 2 ) System.exit( 1 ); 73 if ( args.length == 2 ) { times = Long.parseLong(args[1]); }73 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 74 74 75 for ( int i = Integer.parseInt("5"); --i >= 0 ; ) { 75 if ( args.length > 2 ) System.exit( 1 ); 76 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 77 78 for ( int i = Integer.parseInt("5"); --i >= 0 ; ) { 76 79 InnerMain(); 77 80 // Thread.sleep(2000); // 2 seconds -
benchmark/schedint/JavaThread.java
reef8dfb rbdfc032 24 24 public class JavaThread { 25 25 // Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator. 26 // Bijective 26 // Bijective 27 27 // Cycle length for non-zero values is 4G-1. 28 28 // 0 is absorbing and should be avoided -- fixed point. 29 29 // The returned value is typically masked to produce a positive value. 30 static volatile int Ticket = 0 ; 30 static volatile int Ticket = 0 ; 31 31 32 32 private static int nextRandom (int x) { 33 if (x == 0) { 33 if (x == 0) { 34 34 // reseed the PRNG 35 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 36 // Note that we use a non-atomic racy increment -- the race is rare and benign. 37 // If the race is a concern switch to an AtomicInteger. 38 // In addition accesses to the RW volatile global "Ticket" variable are not 39 // (readily) predictable at compile-time so the JIT will not be able to elide 40 // nextRandom() invocations. 41 x = ++Ticket ; 42 if (x == 0) x = 1 ; 35 // Ticket is accessed infrequently and does not constitute a coherence hot-spot. 36 // Note that we use a non-atomic racy increment -- the race is rare and benign. 37 // If the race is a concern switch to an AtomicInteger. 38 // In addition accesses to the RW volatile global "Ticket" variable are not 39 // (readily) predictable at compile-time so the JIT will not be able to elide 40 // nextRandom() invocations. 41 x = ++Ticket ; 42 if (x == 0) x = 1 ; 43 43 } 44 44 x ^= x << 6; 45 45 x ^= x >>> 21; 46 46 x ^= x << 7; 47 return x ; 47 return x ; 48 48 } 49 49 static int x = 2; 50 50 51 static private long times = Long.parseLong("1000000");51 static private int times = Integer.parseInt("1000000"); 52 52 53 53 public static void helper( Monitor m ) throws InterruptedException { 54 for( longi = 1; i <= times; i += 1) {54 for(int i = 1; i <= times; i += 1) { 55 55 m.wait(); // relase monitor lock 56 56 m.next = true; … … 75 75 } 76 76 public static void main(String[] args) throws InterruptedException { 77 if ( args.length > 1) System.exit( 1 );78 if ( args.length == 1 ) { times = Long.parseLong(args[0]); }77 if ( args.length > 2 ) System.exit( 1 ); 78 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 79 79 80 for (int n = Integer.parseInt("5"); --n >= 0 ; ) { 80 for (int n = Integer.parseInt("5"); --n >= 0 ; ) { 81 81 InnerMain(); 82 82 Thread.sleep(2000); // 2 seconds -
configure.ac
reef8dfb rbdfc032 3 3 4 4 AC_PREREQ([2.68]) 5 AC_INIT([cfa-cc],[1.0.0 ],[cforall@plg.uwaterloo.ca])5 AC_INIT([cfa-cc],[1.0.0.0],[cforall@plg.uwaterloo.ca]) 6 6 AC_CONFIG_AUX_DIR([automake]) 7 7 AC_CONFIG_MACRO_DIRS([automake]) 8 #AC_CONFIG_SRCDIR([src/main.cc]) 8 9 AC_CONFIG_HEADERS([config.h:src/config.h.in]) 9 10 AM_SILENT_RULES([yes]) 10 11 11 m4_include([ tools/build/cfa.m4])12 m4_include([automake/cfa.m4]) 12 13 13 14 # don't use the default CFLAGS as they unconditonnaly add -O2 14 15 : ${CFLAGS=""} 15 : ${CXXFLAGS=""}16 16 17 17 AM_INIT_AUTOMAKE([subdir-objects]) … … 24 24 #Trasforming cc1 will break compilation 25 25 M4CFA_PROGRAM_NAME 26 27 #==============================================================================28 # New AST toggling support29 AH_TEMPLATE([CFA_USE_NEW_AST],[Sets whether or not to use the new-ast, this is adefault value and can be overrided by --old-ast and --new-ast])30 DEFAULT_NEW_AST="True"31 AC_ARG_ENABLE(new-ast,32 [ --enable-new-ast whether or not to use new ast as the default AST algorithm],33 [case "${enableval}" in34 yes) newast=true ; DEFAULT_NEW_AST="True" ;;35 no) newast=false; DEFAULT_NEW_AST="False" ;;36 *) AC_MSG_ERROR([bad value ${enableval} for --enable-new-ast]) ;;37 esac],[newast=true])38 AC_DEFINE_UNQUOTED([CFA_USE_NEW_AST], $newast)39 AC_SUBST(DEFAULT_NEW_AST)40 26 41 27 #============================================================================== … … 78 64 enable_distcc=$enableval, enable_distcc=no) 79 65 80 AC_ARG_WITH(bwlimit,81 [ --with-bwlimit=RATE RATE the maximum rate at which rsync will be limited when using distributed builds],82 [], [])83 84 66 AM_CONDITIONAL([ENABLE_DISTCC], [test x$enable_distcc = xyes]) 85 67 HAS_DISTCC="False" … … 103 85 # Create variables for commonly used targets 104 86 105 TOP_SRCDIR="$(readlink - e $ac_abs_confdir/)/"106 TOP_BUILDDIR="$(readlink - e$ac_pwd/)/"87 TOP_SRCDIR="$(readlink -m $ac_confdir/)/" 88 TOP_BUILDDIR="$(readlink -m $ac_pwd/)/" 107 89 108 90 AC_DEFINE_UNQUOTED(TOP_SRCDIR, "$TOP_SRCDIR", [Top src directory]) … … 139 121 \'--enable-gprofiler=*) ;; 140 122 \'--disable-gprofiler) ;; 141 142 # skip the target hosts143 \'--enable-new-ast=*) ;;144 \'--disable-new-ast) ;;145 146 # skip this, it only causes problems147 \'--srcdir=*) ;;148 123 149 124 # append all other arguments to the sub configure arguments … … 211 186 212 187 LIBCFA_TARGET_DIRS="${LIBCFA_TARGET_DIRS} ${lib_dir}" 213 LIBCFA_1TARGET_DIR="${lib_dir}"214 188 LIBCFA_TARGET_MAKEFILES="${LIBCFA_TARGET_MAKEFILES} ${lib_dir}/Makefile" 215 189 … … 223 197 224 198 AC_SUBST(LIBCFA_TARGET_DIRS) 225 AC_SUBST(LIBCFA_1TARGET_DIR)226 199 AC_SUBST(LIBCFA_TARGET_MAKEFILES) 227 200 … … 289 262 driver/Makefile 290 263 src/Makefile 291 libcfa/Makefile:libcfa/Makefile.dist.in264 benchmark/Makefile 292 265 tests/Makefile 266 longrun_tests/Makefile 267 tools/Makefile 268 tools/prettyprinter/Makefile 293 269 ]) 294 295 # Some of our makefile don't need to be distributed296 AM_CONDITIONAL([CFORALL_DISTRIBUTE], [test -e $TOP_SRCDIR/autogen.sh])297 AM_COND_IF([CFORALL_DISTRIBUTE], [298 AC_CONFIG_FILES([299 longrun_tests/Makefile300 benchmark/Makefile301 benchmark/io/http/Makefile302 tools/Makefile303 tools/prettyprinter/Makefile304 ])305 306 AC_OUTPUT(benchmark/Cargo.toml)307 ])308 270 309 271 AC_CONFIG_LINKS([tests/test.py:tests/test.py]) -
doc/LaTeXmacros/common.tex
reef8dfb rbdfc032 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Oct 5 09:34:46 202014 %% Update Count : 46413 %% Last Modified On : Fri May 24 07:59:54 2019 14 %% Update Count : 382 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 36 36 % Names used in the document. 37 37 38 \usepackage{xspace}39 38 \newcommand{\CFAIcon}{\textsf{C}\raisebox{\depth}{\rotatebox{180}{\textsf{A}}}\xspace} % Cforall symbolic name 40 39 \newcommand{\CFA}{\protect\CFAIcon} % safe for section/caption … … 55 54 \newlength{\parindentlnth} 56 55 \setlength{\parindentlnth}{\parindent} 56 57 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}} 58 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}} 59 \newcommand{\LstCommentStyle}[1]{{\lst@basicstyle{\lst@commentstyle{#1}}}} 60 61 \newlength{\gcolumnposn} % temporary hack because lstlisting does not handle tabs correctly 62 \newlength{\columnposn} 63 \setlength{\gcolumnposn}{2.75in} 64 \setlength{\columnposn}{\gcolumnposn} 65 \newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@basicstyle{\LstCommentStyle{#2}}}} 66 \newcommand{\CRT}{\global\columnposn=\gcolumnposn} 67 68 % allow escape sequence in lstinline 69 %\usepackage{etoolbox} 70 %\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}} 57 71 58 72 \usepackage{pslatex} % reduce size of san serif font … … 227 241 }% 228 242 229 \usepackage{listings} % format program code230 243 \usepackage{lstlang} 231 \makeatletter 232 233 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}} 234 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}} 235 \newcommand{\LstCommentStyle}[1]{{\lst@basicstyle{\lst@commentstyle{#1}}}} 236 237 \newlength{\gcolumnposn} % temporary hack because lstlisting does not handle tabs correctly 238 \newlength{\columnposn} 239 \setlength{\gcolumnposn}{2.75in} 240 \setlength{\columnposn}{\gcolumnposn} 241 \newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@basicstyle{\LstCommentStyle{#2}}}} 242 \newcommand{\CRT}{\global\columnposn=\gcolumnposn} 243 244 % allow escape sequence in lstinline 245 %\usepackage{etoolbox} 246 %\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}} 247 248 % allow adding to lst literate 249 \def\addToLiterate#1{\protect\edef\lst@literate{\unexpanded\expandafter{\lst@literate}\unexpanded{#1}}} 250 \lst@Key{add to literate}{}{\addToLiterate{#1}} 251 \makeatother 252 253 \newcommand{\CFAStyle}{% 244 245 \newcommand{\CFADefaults}{% 254 246 \lstset{ 247 language=CFA, 255 248 columns=fullflexible, 256 249 basicstyle=\linespread{0.9}\sf, % reduce line spacing and use sanserif font … … 267 260 belowskip=3pt, 268 261 % replace/adjust listing characters that look bad in sanserif 269 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0. 75ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1262 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1 270 263 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 271 264 {<-}{$\leftarrow$}2 {=>}{$\Rightarrow$}2 {->}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.075ex}}}\kern-0.2ex\textgreater}2, 272 }% lstset273 }% CFAStyle274 275 \ifdefined\CFALatin% extra Latin-1 escape characters276 \lstnewenvironment{cfa}[1][]{277 \lstset{278 language=CFA,279 265 moredelim=**[is][\color{red}]{Ā®}{Ā®}, % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-. 280 266 moredelim=**[is][\color{blue}]{Ć}{Ć}, % blue highlighting Ć...Ć (sharp s symbol) emacs: C-q M-_ 281 267 moredelim=**[is][\color{OliveGreen}]{Ā¢}{Ā¢}, % green highlighting Ā¢...Ā¢ (cent symbol) emacs: C-q M-" 282 268 moredelim=[is][\lstset{keywords={}}]{¶}{¶}, % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 283 % replace/adjust listing characters that look bad in sanserif284 add to literate={`}{\ttfamily\upshape\hspace*{-0.1ex}`}1285 269 }% lstset 286 \lstset{#1} 287 }{} 270 }% CFADefaults 271 \newcommand{\CFAStyle}{% 272 \CFADefaults 288 273 % inline code Ā©...Ā© (copyright symbol) emacs: C-q M-) 289 274 \lstMakeShortInlineĀ© % single-character for \lstinline 290 \else% regular ASCI characters 291 \lstnewenvironment{cfa}[1][]{ 292 \lstset{ 293 language=CFA, 294 escapechar=\$, % LaTeX escape in CFA code 295 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 296 }% lstset 297 \lstset{#1} 298 }{} 299 % inline code @...@ (at symbol) 300 \lstMakeShortInline@ % single-character for \lstinline 301 \fi% 275 }% CFAStyle 276 277 \lstnewenvironment{cfa}[1][] 278 {\CFADefaults\lstset{#1}} 279 {} 302 280 303 281 % Local Variables: % -
doc/LaTeXmacros/lstlang.sty
reef8dfb rbdfc032 8 8 %% Created On : Sat May 13 16:34:42 2017 9 9 %% Last Modified By : Peter A. Buhr 10 %% Last Modified On : Wed Sep 23 22:40:04 202011 %% Update Count : 2 410 %% Last Modified On : Tue Jan 8 14:40:33 2019 11 %% Update Count : 21 12 12 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 13 … … 115 115 auto, _Bool, catch, catchResume, choose, _Complex, __complex, __complex__, __const, __const__, 116 116 coroutine, disable, dtype, enable, exception, __extension__, fallthrough, fallthru, finally, 117 __float80, float80, __float128, float128, forall, ftype, generator,_Generic, _Imaginary, __imag, __imag__,117 __float80, float80, __float128, float128, forall, ftype, _Generic, _Imaginary, __imag, __imag__, 118 118 inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or, 119 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend,thread,119 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, thread, 120 120 _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__, 121 121 virtual, __volatile, __volatile__, waitfor, when, with, zero_t, … … 125 125 126 126 % C++ programming language 127 \lstdefinelanguage{C++}[ANSI]{C++}{ 128 morekeywords={nullptr,} 129 } 127 \lstdefinelanguage{C++}[ANSI]{C++}{} 130 128 131 129 % uC++ programming language, based on ANSI C++ -
doc/bibliography/pl.bib
reef8dfb rbdfc032 9 9 % Predefined journal names: 10 10 % acmcs: Computing Surveys acta: Acta Infomatica 11 @string{acta="Acta Infomatica"} 11 12 % cacm: Communications of the ACM 12 13 % ibmjrd: IBM J. Research & Development ibmsj: IBM Systems Journal … … 21 22 % tcs: Theoretical Computer Science 22 23 23 @string{acta="Acta Infomatica"}24 24 string{ieeepds="IEEE Transactions on Parallel and Distributed Systems"} 25 25 @string{ieeepds="IEEE Trans. Parallel Distrib. Syst."} … … 124 124 series = {ACM Distinguished Dissertations}, 125 125 year = 1983, 126 }127 128 @article{Zhang19,129 keywords = {Algebraic effects, dynamic scoping, exceptions, parametricity, type systems},130 author = {Zhang, Yizhou and Myers, Andrew C.},131 title = {Abstraction-safe Effect Handlers via Tunneling},132 journal = {Proc. ACM Program. Lang.},133 issue_date = {January 2019},134 volume = {3},135 number = {POPL},136 month = jan,137 year = {2019},138 issn = {2475-1421},139 pages = {5:1--5:29},140 articleno = {5},141 publisher = {ACM},142 address = {New York, NY, USA},143 }144 145 @inproceedings{Zhang16,146 keywords = {Exception tunneling, Genus, exception handling},147 author = {Zhang, Yizhou and Salvaneschi, Guido and Beightol, Quinn and Liskov, Barbara and Myers, Andrew C.},148 title = {Accepting Blame for Safe Tunneled Exceptions},149 booktitle = {Proceedings of the 37th ACM SIGPLAN Conference on Programming Language Design and Implementation},150 series = {PLDI'16},151 year = {2016},152 location = {Santa Barbara, CA, USA},153 pages = {281--295},154 publisher = {ACM},155 address = {New York, NY, USA},156 126 } 157 127 … … 428 398 journal = sigplan, 429 399 year = 1981, 430 month = feb, 431 volume = 16, 432 number = 2, 433 pages = {48-52}, 400 month = feb, volume = 16, number = 2, pages = {48-52}, 434 401 comment = { 435 402 A one-pass, top-down algorithm for overload resolution. Input is a … … 510 477 title = {An Alternative to Subclassing}, 511 478 journal = sigplan, 512 volume = {21}, 513 number = {11}, 479 volume = {21}, number = {11}, 514 480 pages = {424-428}, 515 month = nov, 516 year = 1986, 481 month = nov, year = 1986, 517 482 comment = { 518 483 The Smalltalk class hierarchy has three uses: factoring out code; … … 568 533 isbn = {3-540-66538-2}, 569 534 location = {Toulouse, France}, 535 doi = {http://doi.acm.org/10.1145/318773.319251}, 570 536 publisher = {Springer}, 571 537 address = {London, UK}, … … 665 631 year = 2010, 666 632 pages = {39--50}, 633 numpages = {12}, 667 634 publisher = {IEEE Computer Society}, 668 635 address = {Washington, DC, USA}, … … 955 922 } 956 923 957 @manual{C99,958 keywords = {ISO/IEC C 9899},959 contributer = {pabuhr@plg},960 key = {C99},961 title = {C Programming Language {ISO/IEC} 9899:1999(E)},962 edition = {2nd},963 organization= {International Standard Organization},964 address = {Geneva, Switzerland},965 year = 1999,966 note = {\href{https://webstore.ansi.org/Standards/INCITS/INCITSISOIEC98991999R2005}{https://webstore.ansi.org/\-Standards/\-INCITS/\-INCITSISOIEC98991999R2005}},967 }968 969 924 @manual{C11, 970 925 keywords = {ISO/IEC C 11}, … … 973 928 title = {C Programming Language {ISO/IEC} 9889:2011-12}, 974 929 edition = {3rd}, 975 organization= {International Standard Organization},976 address = { Geneva, Switzerland},930 publisher = {International Standard Organization}, 931 address = {\href{https://www.iso.org/standard/57853.html}{https://\-www.iso.org/\-standard/\-57853.html}}, 977 932 year = 2012, 978 note = {\href{https://www.iso.org/standard/57853.html}{https://\-www.iso.org/\-standard/\-57853.html}},979 933 } 980 934 … … 984 938 key = {Concepts}, 985 939 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming language -- Extensions for concepts {ISO/IEC} {TS} 19217:2015}, 986 organization= {International Standard Organization},987 address = { Geneva, Switzerland},940 publisher = {International Standard Organization}, 941 address = {\href{https://www.iso.org/standard/64031.html}{https://\-www.iso.org/\-standard/\-64031.html}}, 988 942 year = 2015, 989 note = {\href{https://www.iso.org/standard/64031.html}{https://\-www.iso.org/\-standard/\-64031.html}},990 943 } 991 944 … … 1004 957 } 1005 958 1006 @misc{Cforall ConcurrentBenchmarks,959 @misc{CforallBenchMarks, 1007 960 contributer = {pabuhr@plg}, 1008 961 key = {Cforall Benchmarks}, 1009 962 author = {{\textsf{C}{$\mathbf{\forall}$} Benchmarks}}, 1010 howpublished= {\href{https:// github.com/cforall/ConcurrentBenchmarks_SPE20}{https://\-github.com/\-cforall/\-ConcurrentBenchmarks\_SPE20}},963 howpublished= {\href{https://plg.uwaterloo.ca/~cforall/benchmark.tar}{https://\-plg.uwaterloo.ca/\-$\sim$cforall/\-benchmark.tar}}, 1011 964 } 1012 965 … … 1152 1105 title = {C\# Language Specification, Standard ECMA-334}, 1153 1106 organization= {ECMA International Standardizing Information and Communication Systems}, 1154 address = {Geneva, Switzerland},1155 1107 month = jun, 1156 1108 year = 2006, … … 1302 1254 title = {Programming Languages -- {Cobol} ISO/IEC 1989:2014}, 1303 1255 edition = {2nd}, 1304 organization= {International Standard Organization},1305 address = { Geneva, Switzerland},1256 institution = {International Standard Organization}, 1257 address = {\href{https://www.iso.org/standard/51416.html}{https://\-www.iso.org/\-standard/\-51416.html}}, 1306 1258 year = 2014, 1307 note = {\href{https://www.iso.org/standard/51416.html}{https://\-www.iso.org/\-standard/\-51416.html}},1308 1259 } 1309 1260 … … 1354 1305 location = {London, United Kingdom}, 1355 1306 pages = {41--53}, 1307 numpages = {13}, 1308 url = {http://doi.acm.org/10.1145/360204.360207}, 1309 doi = {10.1145/360204.360207}, 1310 acmid = {360207}, 1356 1311 publisher = {ACM}, 1357 1312 address = {New York, NY, USA}, … … 1659 1614 title = {$\mu${C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Annotated Reference Manual, Version 7.0.0}, 1660 1615 organization= {University of Waterloo}, 1661 address = {Waterloo Ontario, Canada},1662 1616 month = sep, 1663 1617 year = 2018, … … 1979 1933 title = {Cooperating Sequential Processes}, 1980 1934 institution = {Technological University}, 1981 address = {Eindhoven, Neth .},1935 address = {Eindhoven, Netherlands}, 1982 1936 year = 1965, 1983 1937 note = {Reprinted in \cite{Genuys68} pp. 43--112.} … … 1988 1942 author = {Adya, Atul and Howell, Jon and Theimer, Marvin and Bolosky, William J. and Douceur, John R.}, 1989 1943 title = {Cooperative Task Management Without Manual Stack Management}, 1990 booktitle = {Proc . of the General Track USENIX Tech. Conf.},1944 booktitle = {Proceedings of the General Track of the Annual Conference on USENIX Annual Technical Conference}, 1991 1945 series = {ATEC '02}, 1992 1946 year = {2002}, … … 2092 2046 author = {Walter Bright and Andrei Alexandrescu}, 2093 2047 organization= {Digital Mars}, 2094 address = {Vienna Virginia, U.S.A.},2095 2048 year = 2016, 2096 2049 note = {\href{http://dlang.org/spec/spec.html}{http://\-dlang.org/\-spec/\-spec.html}}, … … 2455 2408 year = 1993, 2456 2409 pages = {201--208}, 2410 url = {http://doi.acm.org/10.1145/155360.155580}, 2457 2411 publisher = {ACM}, 2458 2412 address = {New York, NY, USA}, … … 2652 2606 location = {Boulder, Colorado, USA}, 2653 2607 pages = {91--97}, 2608 numpages = {7}, 2654 2609 publisher = {ACM}, 2655 2610 address = {New York, NY, USA}, … … 2682 2637 issn = {0004-5411}, 2683 2638 pages = {215--225}, 2639 numpages = {11}, 2640 url = {http://doi.acm.org/10.1145/321879.321884}, 2641 doi = {10.1145/321879.321884}, 2642 acmid = {321884}, 2684 2643 publisher = {ACM}, 2685 2644 address = {New York, NY, USA}, … … 2749 2708 } 2750 2709 2751 @misc{Drepper13,2752 keywords = {thread-local storage},2753 contributer = {pabuhr@plg},2754 author = {Ulrich Drepper},2755 title = {{ELF} Handling For Thread-Local Storage},2756 year = 2013,2757 month = aug,2758 note = {WikipediA},2759 howpublished= {\href{http://www.akkadia.org/drepper/tls.pdf}2760 {http://\-www.akkadia.org/\-drepper/\-tls.pdf}},2761 }2762 2763 2710 @misc{Turley99, 2764 2711 keywords = {embedded system, micrprocessor}, … … 2771 2718 howpublished= {\href{https://www.eetimes.com/author.asp?sectionid=36&doc_id=1287712} 2772 2719 {https://\-www.eetimes.com/\-author.asp?sectionid=\-36&doc_id=1287712}}, 2773 }2774 2775 @article{Xiao19,2776 keywords = {bug classification, fault trigger, Linux operating system, regression bug},2777 contributer = {pabuhr@plg},2778 author = {Guanping Xiao and Zheng Zheng and Beibei Yin and Kishor S. Trivedi and Xiaoting Du and Kai-Yuan Cai},2779 title = {An Empirical Study of Fault Triggers in the Linux Operating System: An Evolutionary Perspective},2780 journal = {IEEE Transactions on Reliability},2781 month = dec,2782 year = 2019,2783 volume = 68,2784 number = 4,2785 pages = {1356-1383},2786 2720 } 2787 2721 … … 3203 3137 } 3204 3138 3205 @inproceedings{Palix11,3206 keywords = {Linux, fault-finding tools},3207 contributer = {pabuhr@plg},3208 author = {Nicolas Palix and Ga\"el Thomas and Suman Saha and Christophe Calv\`es and Julia Lawall and Gilles Muller},3209 title = {Faults in Linux: Ten Years Later},3210 booktitle = {Proc. of the 16 International Conf. on Arch. Support for Prog. Lang. and Oper. Sys.},3211 series = {ASPLOS'11},3212 month = mar,3213 year = 2011,3214 location = {Newport Beach, California, USA},3215 pages = {305-318},3216 publisher = {ACM},3217 address = {New York, NY, USA},3218 }3219 3220 3139 @article{Lamport87, 3221 3140 keywords = {software solutions, mutual exclusion, fast}, … … 3339 3258 issn = {0001-0782}, 3340 3259 pages = {107--115}, 3260 numpages = {9}, 3261 url = {http://doi.acm.org/10.1145/1538788.1538814}, 3262 doi = {10.1145/1538788.1538814}, 3263 acmid = {1538814}, 3341 3264 publisher = {ACM}, 3342 3265 address = {New York, NY, USA}, … … 3360 3283 title = {Programming Languages -- {Fortran} Part 1:Base Language ISO/IEC 1539-1:2010}, 3361 3284 edition = {3rd}, 3362 organization= {International Standard Organization},3363 address = { Geneva, Switzerland},3285 publisher = {International Standard Organization}, 3286 address = {\href{https://www.iso.org/standard/50459.html}{https://\-www.iso.org/\-standard/\-50459.html}}, 3364 3287 year = 2010, 3365 note = {\href{https://www.iso.org/standard/50459.html}{https://\-www.iso.org/\-standard/\-50459.html}},3366 3288 } 3367 3289 … … 3372 3294 title = {Programming Languages -- {Fortran} Part 1:Base Language ISO/IEC 1539-1:2018}, 3373 3295 edition = {4rd}, 3374 organization= {International Standard Organization},3375 address = { Geneva, Switzerland},3296 publisher = {International Standard Organization}, 3297 address = {\href{https://www.iso.org/standard/72320.html}{https://\-www.iso.org/\-standard/\-72320.html}}, 3376 3298 year = 2018, 3377 note = {\href{https://www.iso.org/standard/72320.html}{https://\-www.iso.org/\-standard/\-72320.html}},3378 3299 } 3379 3300 … … 3743 3664 } 3744 3665 3745 @mastersthesis{Radhakrishnan19,3746 author = {Srihari Radhakrishnan},3747 title = {High Performance Web Servers: A Study In Concurrent Programming Models},3748 school = {School of Computer Sc., University of Waterloo},3749 year = 2019,3750 optaddress = {Waterloo, Ontario, Canada, N2L 3G1},3751 note = {\href{https://uwspace.uwaterloo.ca/handle/10012/14706}{https://\-uwspace.uwaterloo.ca/\-handle/\-10012/\-14706}},3752 }3753 3754 3666 @article{katzenelson83b, 3755 3667 contributer = {gjditchfield@plg}, … … 3785 3697 pages = {115-138}, 3786 3698 year = 1971, 3787 }3788 3789 @inproceedings{Hagersten03,3790 keywords = {cache storage, parallel architectures, performance evaluation, shared memory systems},3791 author = {Zoran Radovi\'{c} and Erik Hagersten},3792 title = {Hierarchical backoff locks for nonuniform communication architectures},3793 booktitle = {Proceedings of the Ninth International Symposium on High-Performance Computer Architecture},3794 year = {2003},3795 location = {Anaheim, CA, USA},3796 pages = {241-252},3797 publisher = {IEEE},3798 3699 } 3799 3700 … … 4464 4365 } 4465 4366 4466 @misc{gccValueLabels,4467 keywords = {gcc extension, value labels},4468 contributer = {pabuhr@plg},4469 key = {Labels as Values},4470 author = {{gcc Extension}},4471 title = {Labels as Values},4472 year = {since gcc-3},4473 howpublished= {\href{https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html}4474 {https:\-//gcc.gnu.org/\-onlinedocs/\-gcc/\-Labels-as-Values.html}},4475 }4476 4477 4367 @mastersthesis{Clarke90, 4478 4368 keywords = {concurrency, postponing requests}, … … 4533 4423 } 4534 4424 4535 @misc{libfibre,4536 key = {libfibre},4537 author = {Martin Karsten},4538 title = {{libfibre:~User-Level Threading Runtime}},4539 howpublished= {\href{https://git.uwaterloo.ca/mkarsten/libfibre}4540 {https://\-git.uwaterloo.ca/\-mkarsten/\-libfibre}},4541 note = {[Online; accessed 2020-04-15]},4542 }4543 4544 4425 @article{Linda, 4545 4426 keywords = {Linda, concurrency}, … … 4575 4456 } 4576 4457 4577 @inproceedings{Fang06,4578 author = {Fang, Yi and McMillan, Kenneth L. and Pnueli, Amir and Zuck, Lenore D.},4579 editor = {Najm, Elie and Pradat-Peyre, Jean-Fran{\c{c}}ois and Donzeau-Gouge, V{\'e}ronique Vigui{\'e}},4580 title = {Liveness by Invisible Invariants},4581 booktitle = {Formal Techniques for Networked and Distributed Systems - FORTE 2006},4582 year = 2006,4583 publisher = {Springer Berlin Heidelberg},4584 address = {Berlin, Heidelberg},4585 pages = {356--371},4586 }4587 4588 4458 @article{Pierce00, 4589 keywords = {Scala , polymorphism, subtyping, type inference},4459 keywords = {Scala}, 4590 4460 contributer = {a3moss@uwaterloo.ca}, 4591 4461 author = {Pierce, Benjamin C. and Turner, David N.}, … … 4599 4469 issn = {0164-0925}, 4600 4470 pages = {1--44}, 4471 numpages = {44}, 4472 url = {http://doi.acm.org/10.1145/345099.345100}, 4473 doi = {10.1145/345099.345100}, 4474 acmid = {345100}, 4601 4475 publisher = {ACM}, 4602 4476 address = {New York, NY, USA}, 4477 keywords = {polymorphism, subtyping, type inference}, 4603 4478 } 4604 4605 @article{Dice15,4606 keywords = {Concurrency, NUMA, hierarchical locks, locks, multicore, mutex, mutual exclusion, spin locks},4607 author = {Dice, David and Marathe, Virendra J. and Shavit, Nir},4608 title = {Lock Cohorting: A General Technique for Designing NUMA Locks},4609 journal = {ACM Trans. Parallel Comput.},4610 issue_date = {January 2015},4611 volume = 1,4612 number = 2,4613 month = feb,4614 year = 2015,4615 pages = {13:1--13:42},4616 publisher = {ACM},4617 address = {New York, NY, USA},4618 }4619 4479 4620 4480 @article{Sundell08, … … 4694 4554 journal = sigplan, 4695 4555 year = 1989, 4696 month = jun, 4697 volume = 24, 4698 number = 6, 4699 pages = {37-48}, 4556 month = jun, volume = 24, number = 6, pages = {37-48}, 4700 4557 abstract = { 4701 4558 This paper describes a scheme we have used to manage a large … … 4753 4610 address = {New York, NY, USA}, 4754 4611 } 4755 4756 4612 @techreport{Mesa, 4757 4613 keywords = {monitors, packages}, … … 4760 4616 title = {Mesa Language Manual}, 4761 4617 institution = {Xerox Palo Alto Research Center}, 4762 address = {Palo Alto, California, U.S.A.},4763 4618 number = {CSL--79--3}, 4764 4619 month = apr, … … 4770 4625 contributer = {pabuhr@plg}, 4771 4626 author = {Gregory R. Andrews}, 4772 title = {A Method for Solving Syn chronization Problems},4627 title = {A Method for Solving Synronization Problems}, 4773 4628 journal = scp, 4774 4629 volume = 13, … … 5095 4950 title = {Multiple Inheritance for {C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}}}, 5096 4951 booktitle = {Proceedings of the Spring '87 EUUG Conference}, 5097 month = may, 5098 year = 1987, 4952 month = may, year = 1987 5099 4953 } 5100 4954 … … 5141 4995 year = 1986, 5142 4996 pages = {313--326}, 4997 numpages = {14}, 5143 4998 publisher = {ACM}, 5144 4999 address = {New York, NY, USA}, … … 5156 5011 year = 1986, 5157 5012 pages = {327--348}, 5013 numpages = {22}, 5158 5014 publisher = {ACM}, 5159 5015 address = {New York, NY, USA}, … … 5352 5208 year = 2005, 5353 5209 pages = {146-196}, 5210 numpages = {51}, 5354 5211 publisher = {ACM}, 5355 5212 address = {New York, NY, USA}, … … 5497 5354 year = 2000, 5498 5355 pages = {29-46}, 5499 note = {OOPSLA'00, Oct. 15--19, 2000, Minneapolis, Minn ., U.S.A.},5356 note = {OOPSLA'00, Oct. 15--19, 2000, Minneapolis, Minnesota, U.S.A.}, 5500 5357 } 5501 5358 … … 5611 5468 location = {San Diego, California, USA}, 5612 5469 pages = {101--112}, 5470 numpages = {12}, 5471 url = {http://doi.acm.org/10.1145/2535838.2535878}, 5472 doi = {10.1145/2535838.2535878}, 5473 acmid = {2535878}, 5613 5474 publisher = {ACM}, 5614 5475 address = {New York, NY, USA}, … … 5714 5575 issn = {0362-1340}, 5715 5576 pages = {30--42}, 5577 numpages = {13}, 5578 url = {http://doi.acm.org/10.1145/947586.947589}, 5579 doi = {10.1145/947586.947589}, 5716 5580 publisher = {ACM}, 5717 5581 address = {New York, NY, USA} … … 6250 6114 } 6251 6115 6252 @article{Bauer15,6253 keywords = {resumption exceptions, theory},6254 contributer = {pabuhr@plg},6255 author = {Andrej Bauer and Matija Pretnar},6256 title = {Programming with Algebraic Effects and Handlers},6257 journal = {Journal of Logical and Algebraic Methods in Programming},6258 publisher = {Elsevier BV},6259 volume = 84,6260 number = 1,6261 month = jan,6262 year = 2015,6263 pages = {108-123},6264 }6265 6266 6116 @book{Butenhof97, 6267 6117 keywords = {PThreads, concurrency}, … … 6312 6162 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:1998}, 6313 6163 edition = {1st}, 6314 organization= {International Standard Organization},6315 address = { Geneva, Switzerland},6164 publisher = {International Standard Organization}, 6165 address = {\href{https://www.iso.org/standard/25845.html}{https://\-www.iso.org/\-standard/\-25845.html}}, 6316 6166 year = 1998, 6317 note = {\href{https://www.iso.org/standard/25845.html}{https://\-www.iso.org/\-standard/\-25845.html}},6318 6167 } 6319 6168 … … 6324 6173 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:2014}, 6325 6174 edition = {4th}, 6326 organization= {International Standard Organization},6327 address = { Geneva, Switzerland},6175 publisher = {International Standard Organization}, 6176 address = {\href{https://www.iso.org/standard/64029.html}{https://\-www.iso.org/\-standard/\-64029.html}}, 6328 6177 year = 2014, 6329 note = {\href{https://www.iso.org/standard/64029.html}{https://\-www.iso.org/\-standard/\-64029.html}},6330 6178 } 6331 6179 … … 6336 6184 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:2017}, 6337 6185 edition = {5th}, 6338 organization= {International Standard Organization},6339 address = { Geneva, Switzerland},6186 publisher = {International Standard Organization}, 6187 address = {\href{https://www.iso.org/standard/68564.html}{https://\-www.iso.org/\-standard/\-68564.html}}, 6340 6188 year = 2017, 6341 note = {\href{https://www.iso.org/standard/68564.html}{https://\-www.iso.org/\-standard/\-68564.html}},6342 6189 } 6343 6190 … … 6471 6318 title = {The Programming Language Concurrent Pascal}, 6472 6319 journal = ieeese, 6473 volume = {SE-1}, 6474 number = 2, 6320 volume = 2, 6475 6321 month = jun, 6476 6322 year = 1975, 6477 pages = {199-20 7}6323 pages = {199-206} 6478 6324 } 6479 6325 … … 6653 6499 issn = {0164-0925}, 6654 6500 pages = {429-475}, 6501 url = {http://doi.acm.org/10.1145/1133651.1133653}, 6502 doi = {10.1145/1133651.1133653}, 6503 acmid = {1133653}, 6655 6504 publisher = {ACM}, 6656 6505 address = {New York, NY, USA}, … … 6682 6531 } 6683 6532 6684 @article{Aravind09,6685 author = {Alex A. Aravind and Wim H. Hesselink},6686 title = {A Queue Based Mutual Exclusion Algorithm},6687 journal = acta,6688 volume = 46,6689 pages = {73--86},6690 year = 2009,6691 }6692 6693 6533 % R 6694 6534 … … 6734 6574 title = {Programming languages -- {Ada} ISO/IEC 8652:2012}, 6735 6575 edition = {3rd}, 6736 organization= {International Standard Organization},6737 address = { Geneva, Switzerland},6576 publisher = {International Standard Organization}, 6577 address = {\href{https://www.iso.org/standard/61507.html}{https://\-www.iso.org/\-standard/\-61507.html}}, 6738 6578 year = 2012, 6739 note = {\href{https://www.iso.org/standard/61507.html}{https://\-www.iso.org/\-standard/\-61507.html}},6740 6579 } 6741 6580 … … 7040 6879 issn = {0001-0782}, 7041 6880 pages = {565--569}, 6881 numpages = {5}, 6882 url = {http://doi.acm.org/10.1145/359545.359566}, 6883 doi = {10.1145/359545.359566}, 6884 acmid = {359566}, 7042 6885 publisher = {ACM}, 7043 6886 address = {New York, NY, USA} … … 7057 6900 issn = {0362-1340}, 7058 6901 pages = {145--147}, 6902 numpages = {3}, 6903 url = {http://doi.acm.org/10.1145/122598.122614}, 6904 doi = {10.1145/122598.122614}, 6905 acmid = {122614}, 7059 6906 publisher = {ACM}, 7060 6907 address = {New York, NY, USA}, … … 7159 7006 issn = {0362-1340}, 7160 7007 pages = {82--87}, 7008 numpages = {6}, 7009 url = {http://doi.acm.org/10.1145/947680.947688}, 7010 doi = {10.1145/947680.947688}, 7161 7011 publisher = {ACM}, 7162 7012 address = {New York, NY, USA}, … … 7303 7153 } 7304 7154 7305 @article{Cascaval08,7306 author = {Cascaval, Calin and Blundell, Colin and Michael, Maged and Cain, Harold W. and Wu, Peng and Chiras, Stefanie and Chatterjee, Siddhartha},7307 title = {Software Transactional Memory: Why Is It Only a Research Toy?},7308 journal = {Queue},7309 volume = {6},7310 number = {5},7311 month = sep,7312 year = {2008},7313 pages = {40:46--40:58},7314 publisher = {ACM},7315 address = {New York, NY, USA},7316 }7317 7318 7155 @article{Dijkstra65a, 7319 7156 keywords = {N-thread software-solution mutual exclusion}, … … 7526 7363 year = 1974, 7527 7364 pages = {261-301}, 7365 issn = {0360-0300}, 7366 doi = {http://doi.acm.org/10.1145/356635.356640}, 7528 7367 publisher = {ACM}, 7529 7368 address = {New York, NY, USA}, … … 7615 7454 publisher = {ACM Press}, 7616 7455 address = {New York, NY, USA}, 7456 doi = {http://doi.acm.org/10.1145/356586.356588}, 7617 7457 } 7618 7458 … … 7742 7582 title = {The Thoth System: Multi-Process Structuring and Portability}, 7743 7583 publisher = {American Elsevier}, 7744 address = {New York, New York, U.S.A.},7745 7584 year = 1982 7746 7585 } … … 7916 7755 howpublished= {\href{https://projects.eclipse.org/proposals/trace-compass}{https://\-projects.eclipse.org/\-proposals/\-trace-compass}}, 7917 7756 } 7918 7919 @inproceedings{Boehm09, 7920 author = {Boehm, Hans-J.}, 7921 title = {Transactional Memory Should Be an Implementation Technique, Not a Programming Interface}, 7922 booktitle = {Proceedings of the First USENIX Conference on Hot Topics in Parallelism}, 7923 series = {HotPar'09}, 7924 year = {2009}, 7925 location = {Berkeley, California}, 7926 publisher = {USENIX Association}, 7927 address = {Berkeley, CA, USA}, 7928 } 7929 7757 7930 7758 @article{Leroy00, 7931 7759 keywords = {type-systems, exceptions}, … … 7977 7805 number = {2}, 7978 7806 pages = {204-214}, 7979 month = apr, 7980 year = 1988, 7807 month = apr, year = 1988, 7981 7808 comment = { 7982 7809 Extended record types add fields to their base record. Assignment … … 8077 7904 } 8078 7905 8079 @article{Karsten20,8080 author = {Karsten, Martin and Barghi, Saman},8081 title = {{User-level Threading: Have Your Cake and Eat It Too}},8082 year = {2020},8083 issue_date = {March 2020},8084 publisher = {Association for Computing Machinery},8085 address = {New York, NY, USA},8086 volume = {4},8087 number = {1},8088 url = {https://doi.org/10.1145/3379483},8089 doi = {10.1145/3379483},8090 journal = {Proc. ACM Meas. Anal. Comput. Syst.},8091 month = mar,8092 numpages = {30},8093 }8094 8095 7906 @techreport{Harmony, 8096 7907 keywords = {messages, concurrency}, … … 8108 7919 contributer = {gjditchfield@plg}, 8109 7920 author = {Henry Lieverman}, 8110 title = {Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems}, 7921 title = {Using Prototypical Objects to Implement Shared Behavior in 7922 Object Oriented Systems}, 8111 7923 journal = sigplan, 8112 month = nov, 8113 year = 1986, 8114 volume = 21, 8115 number = 11, 8116 pages = {214-223} 7924 month = nov, year = 1986, 7925 volume = 21, number = 11, pages = {214-223} 8117 7926 } 8118 7927 … … 8301 8110 issn = {0004-5411}, 8302 8111 pages = {245--281}, 8112 numpages = {37}, 8113 url = {http://doi.acm.org/10.1145/62.2160}, 8114 doi = {10.1145/62.2160}, 8115 acmid = {2160}, 8303 8116 publisher = {ACM}, 8304 8117 address = {New York, NY, USA}, … … 8313 8126 contributer = {pabuhr@plg}, 8314 8127 author = {Boehm, Hans-J. and Adve, Sarita V.}, 8315 title = {You Don' tKnow Jack About Shared Variables or Memory Models},8128 title = {You Don'T Know Jack About Shared Variables or Memory Models}, 8316 8129 journal = cacm, 8317 8130 volume = 55, -
doc/man/cfa.1
reef8dfb rbdfc032 11 11 .\" Created On : Wed Jul 26 22:34:47 2017 12 12 .\" Last Modified By : Peter A. Buhr 13 .\" Last Modified On : Wed Sep 2 17:59:53 202014 .\" Update Count : 7813 .\" Last Modified On : Thu Jul 27 10:29:29 2017 14 .\" Update Count : 44 15 15 .\" 16 16 .\" nroff -man cfa.1 … … 23 23 .ds Cf "Cforall 24 24 .\" 25 .TH CFA 1 "2020-09-2" cfa-\*(Mg "\*(Cf Project"25 .TH cfa 1 2017-07-27 cfa-\*(Mg 26 26 .SH NAME 27 cfa \- \*(Cf project translator and runtime library to enhance C27 cfa \- \*(Cf Translator and Runtime Library 28 28 .SH SYNOPSIS 29 cfa [cfa/gcc-options] 30 [cfa/c source-files] 31 [assembler/loader files] 29 cfa [gcc-options] [C/\*(Cf source-files] [assembler/loader files] 32 30 .SH DESCRIPTION 33 \*(Cf (C-for-all) is an open-source project extending ISO C with modern safety and productivity features, while still ensuring backwards compatibility with C and its programmers.34 35 31 The cfa command compiles C and \*(Cf source files and links C/\*(Cf object 36 32 files named on the command line. … … 38 34 The cfa command introduces a translator pass over the specified source files 39 35 after the C preprocessor but before the C compilation. The translator converts 40 new \*(Cf constructs into C statements. The cfa command also provides a fully 41 concurrent (user-level threads) runtime library, which is linked with the 42 \*(Cf application. 36 new \*(Cf constructs into C statements. The cfa command also provides the 37 runtime library, which is linked with each \*(Cf application. 43 38 44 39 The command line options depend on the particular C compiler used (gcc/clang 45 40 supported). As with most C compilers, the output is sent to the file a.out(5) 46 41 unless the -o option is present on the command line. See the reference pages 47 for gcc(1) for more information on command line options.42 for gcc(1) for more information. 48 43 .SH OPTIONS 49 44 When multiple conflicting options appear on the command line, e.g., … … 55 50 All of the options available to the gcc compiler are available to the cfa 56 51 translator. The following gcc flags are implicitly turned on: 57 .IP "-std=gnu11" 3 58 The 2011 C standard plus GNU extensions. 59 .IP "-fgnu89-inline" 60 Use the traditional GNU semantics for inline routines in C11 mode, which allows inline routines in header files. 61 .IP "-imacros stdbool.h" 62 Include stdbool.h to get defines for bool/true/false. 63 .IP "-latomic -lm" 64 Provide access to double-wide CAS instruction and math library. 52 .IP -std=gnu99 3 53 The 1999 C standard plus GNU extensions. 54 .IP -fgnu89-inline 55 Use the traditional GNU semantics for inline routines in C99 mode, which allows inline routines in header files. 65 56 .LP 66 57 The following additional options are available: 67 .IP "-CFA"358 .IP -CFA 3 68 59 Only the C preprocessor and the \*(Cf translator steps are performed and the transformed program is written to standard output, which makes it possible to examine the code generated by the \*(Cf translator. 69 60 The generated code starts with the standard \*(Cf prelude. 70 .IP "-debug"61 .IP -debug 71 62 The program is linked with the debugging version of the runtime system. 72 63 The debug version performs runtime checks to help during the debugging phase of a \*(Cf program, but can substantially slow program execution. 73 64 The runtime checks should only be removed after the program is completely debugged. 74 65 .B This option is the default. 75 .IP "-nodebug"66 .IP -nodebug 76 67 The program is linked with the non-debugging version of the runtime system, so the execution of the program is faster. 77 68 .I However, no runtime checks or asserts are performed so errors usually result in abnormal program behaviour or termination. 78 .IP "-help"69 .IP -help 79 70 Information about the set of \*(Cf compilation flags is printed. 80 .IP "-nohelp"71 .IP -nohelp 81 72 Information about the set of \*(Cf compilation flags is not printed. 82 73 .B This option is the default. 83 .IP "-quiet"74 .IP -quiet 84 75 The \*(Cf compilation message is not printed at the beginning of a compilation. 85 .IP "-noquiet"76 .IP -noquiet 86 77 The \*(Cf compilation message is printed at the beginning of a compilation. 87 78 .B This option is the default. … … 90 81 available. These variables allow conditional compilation of programs that must 91 82 work differently in these situations. 92 .IP "__CFA_MAJOR__"383 .IP __CFA_MAJOR__ 3 93 84 is available during preprocessing and its value is the major version number of \*(Cf. 94 .IP "__CFA_MINOR__"85 .IP __CFA_MINOR__ 95 86 is available during preprocessing and its value is the minor version number of \*(Cf. 96 .IP "__CFA_PATCH__"87 .IP __CFA_PATCH__ 97 88 is available during preprocessing and its value is the patch level number of \*(Cf. 98 89 .IP "__CFA__, __CFORALL__, and __cforall" 99 90 are always available during preprocessing and have no value. 100 .IP "__CFA_DEBUG__"91 .IP __CFA_DEBUG__ 101 92 is available during preprocessing if the -debug compilation option is 102 93 specified. … … 125 116 .SH REFERENCES 126 117 .HP 3 127 .I \*(Cf Home Page 118 \*(Cf Reference and Rational Manual 128 119 .br 129 http s://cforall.uwaterloo.ca120 http://plg.uwaterloo.ca/~cforall/refrat.pdf 130 121 .HP 131 122 .I \*(Cf User Manual 132 123 .br 133 https://cforall.uwaterloo.ca/doc/user.pdf 134 .SH BUILDS 135 Nightly builds are available here https://cforall.uwaterloo.ca/jenkins 124 http://plg.uwaterloo.ca/~cforall/user.pdf 136 125 .SH BUGS 137 Bugs reportss are available here https://cforall.uwaterloo.ca/trac126 Bugs should be reported to trac@plg.cs.uwaterloo.ca. 138 127 .SH COPYRIGHT 139 128 \*(Cf is covered under the licence agreement in the distribution. 140 129 .SH AUTHORS 141 130 Andrew Beach, Richard Bilson, Peter A. Buhr, Thierry Delisle, Glen Ditchfield, 142 Rodolfo G. Esteves, Aaron Moss, Rob Schluntz , Mubeen Zulfiqar131 Rodolfo G. Esteves, Aaron Moss, Rob Schluntz -
doc/papers/AMA/AMA-stix/ama/WileyNJD-v2.cls
reef8dfb rbdfc032 2444 2444 \@afterheading} 2445 2445 2446 \renewcommand\section{\@startsection{section}{1}{\z@}{-2 0pt \@plus -2pt \@minus -2pt}{7\p@}{\sectionfont}}%2447 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{- 18pt \@plus -2pt \@minus -2pt}{5\p@}{\subsectionfont}}%2448 \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{- 16pt \@plus -2pt \@minus -2pt}{2\p@}{\subsubsectionfont}}%2446 \renewcommand\section{\@startsection{section}{1}{\z@}{-25pt \@plus -2pt \@minus -2pt}{12\p@}{\sectionfont}}% 2447 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{-22pt \@plus -2pt \@minus -2pt}{5\p@}{\subsectionfont}}% 2448 \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{-20pt \@plus -2pt \@minus -2pt}{2\p@}{\subsubsectionfont}}% 2449 2449 % 2450 2450 \newskip\secruleskip\secruleskip8.5\p@% -
doc/papers/concurrency/Paper.tex
reef8dfb rbdfc032 61 61 \newcommand{\CCseventeen}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}17\xspace} % C++17 symbolic name 62 62 \newcommand{\CCtwenty}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}20\xspace} % C++20 symbolic name 63 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\ large$^\sharp$}\xspace} % C# symbolic name63 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\Large$^\sharp$}\xspace} % C# symbolic name 64 64 65 65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 99 99 \newcommand{\CRT}{\global\columnposn=\gcolumnposn} 100 100 101 % Denote newterms in particular font and index them without particular font and in lowercase, \eg\newterm{abc}.102 % The option parameter provides an index term different from the new term, \eg\newterm[\texttt{abc}]{abc}101 % Denote newterms in particular font and index them without particular font and in lowercase, e.g., \newterm{abc}. 102 % The option parameter provides an index term different from the new term, e.g., \newterm[\texttt{abc}]{abc} 103 103 % The star version does not lowercase the index information, e.g., \newterm*{IBM}. 104 104 \newcommand{\newtermFontInline}{\emph} … … 110 110 \newcommand{\abbrevFont}{\textit} % set empty for no italics 111 111 \@ifundefined{eg}{ 112 %\newcommand{\EG}{\abbrevFont{e}\abbrevFont{g}} 113 \newcommand{\EG}{for example} 112 \newcommand{\EG}{\abbrevFont{e}\abbrevFont{g}} 114 113 \newcommand*{\eg}{% 115 114 \@ifnextchar{,}{\EG}% … … 118 117 }}{}% 119 118 \@ifundefined{ie}{ 120 %\newcommand{\IE}{\abbrevFont{i}\abbrevFont{e}} 121 \newcommand{\IE}{that is} 119 \newcommand{\IE}{\abbrevFont{i}\abbrevFont{e}} 122 120 \newcommand*{\ie}{% 123 121 \@ifnextchar{,}{\IE}% … … 129 127 \newcommand*{\etc}{% 130 128 \@ifnextchar{.}{\ETC}% 131 {\ETC.\xspace}%129 {\ETC.\xspace}% 132 130 }}{}% 133 131 \@ifundefined{etal}{ 134 132 \newcommand{\ETAL}{\abbrevFont{et}~\abbrevFont{al}} 135 133 \newcommand*{\etal}{% 136 \@ifnextchar{.}{\ ETAL}%137 {\ ETAL.\xspace}%134 \@ifnextchar{.}{\protect\ETAL}% 135 {\protect\ETAL.\xspace}% 138 136 }}{}% 139 137 \@ifundefined{viz}{ … … 165 163 __float80, float80, __float128, float128, forall, ftype, generator, _Generic, _Imaginary, __imag, __imag__, 166 164 inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or, 167 otype, restrict, resume, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, thread,165 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, thread, 168 166 _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__, 169 167 virtual, __volatile, __volatile__, waitfor, when, with, zero_t}, 170 168 moredirectives={defined,include_next}, 171 169 % replace/adjust listing characters that look bad in sanserif 172 literate={-}{\makebox[1ex][c]{\raisebox{0. 5ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1170 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1 173 171 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 % {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 174 172 {<}{\textrm{\textless}}1 {>}{\textrm{\textgreater}}1 … … 199 197 _Else, _Enable, _Event, _Finally, _Monitor, _Mutex, _Nomutex, _PeriodicTask, _RealTimeTask, 200 198 _Resume, _Select, _SporadicTask, _Task, _Timeout, _When, _With, _Throw}, 199 } 200 \lstdefinelanguage{Golang}{ 201 morekeywords=[1]{package,import,func,type,struct,return,defer,panic,recover,select,var,const,iota,}, 202 morekeywords=[2]{string,uint,uint8,uint16,uint32,uint64,int,int8,int16,int32,int64, 203 bool,float32,float64,complex64,complex128,byte,rune,uintptr, error,interface}, 204 morekeywords=[3]{map,slice,make,new,nil,len,cap,copy,close,true,false,delete,append,real,imag,complex,chan,}, 205 morekeywords=[4]{for,break,continue,range,goto,switch,case,fallthrough,if,else,default,}, 206 morekeywords=[5]{Println,Printf,Error,}, 207 sensitive=true, 208 morecomment=[l]{//}, 209 morecomment=[s]{/*}{*/}, 210 morestring=[b]', 211 morestring=[b]", 212 morestring=[s]{`}{`}, 201 213 } 202 214 … … 226 238 {} 227 239 \lstnewenvironment{C++}[1][] % use C++ style 228 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{`}{`} }\lstset{#1}}240 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 229 241 {} 230 242 \lstnewenvironment{uC++}[1][] 231 {\lstset{ language=uC++,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}}243 {\lstset{#1}} 232 244 {} 233 245 \lstnewenvironment{Go}[1][] 234 {\lstset{language=Golang,moredelim=**[is][\protect\color{red}]{`}{`} }\lstset{#1}}246 {\lstset{language=Golang,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 235 247 {} 236 248 \lstnewenvironment{python}[1][] 237 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 238 {} 239 \lstnewenvironment{java}[1][] 240 {\lstset{language=java,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 249 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 241 250 {} 242 251 … … 253 262 } 254 263 255 \new savebox{\myboxA}256 \new savebox{\myboxB}257 \new savebox{\myboxC}258 \new savebox{\myboxD}264 \newbox\myboxA 265 \newbox\myboxB 266 \newbox\myboxC 267 \newbox\myboxD 259 268 260 269 \title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}} … … 266 275 \address[1]{\orgdiv{Cheriton School of Computer Science}, \orgname{University of Waterloo}, \orgaddress{\state{Waterloo, ON}, \country{Canada}}} 267 276 268 \corres{*Peter A. Buhr, Cheriton School of Computer Science, University of Waterloo, 200 University Avenue West, Waterloo, ON N2L 3G1, Canada. \email{pabuhr{\char`\@}uwaterloo.ca}}277 \corres{*Peter A. Buhr, Cheriton School of Computer Science, University of Waterloo, 200 University Avenue West, Waterloo, ON, N2L 3G1, Canada. \email{pabuhr{\char`\@}uwaterloo.ca}} 269 278 270 279 % \fundingInfo{Natural Sciences and Engineering Research Council of Canada} 271 280 272 281 \abstract[Summary]{ 273 \CFA is a polymorphic, non object-oriented, concurrent, backwardscompatible extension of the C programming language.282 \CFA is a polymorphic, non-object-oriented, concurrent, backwards-compatible extension of the C programming language. 274 283 This paper discusses the design philosophy and implementation of its advanced control-flow and concurrent/parallel features, along with the supporting runtime written in \CFA. 275 These features are created from scratch as ISO C has only low-level and/or unimplemented concurrency, so C programmers continue to rely on library approaches like pthreads.284 These features are created from scratch as ISO C has only low-level and/or unimplemented concurrency, so C programmers continue to rely on library features like pthreads. 276 285 \CFA introduces modern language-level control-flow mechanisms, like generators, coroutines, user-level threading, and monitors for mutual exclusion and synchronization. 277 286 % Library extension for executors, futures, and actors are built on these basic mechanisms. 278 287 The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and monitor barging. 279 The runtime also ensures multiple monitors can be safely acquired in a deadlock-free way, and this feature is fully integrated with all monitor synchronization mechanisms.288 The runtime also ensures multiple monitors can be safely acquired \emph{simultaneously} (deadlock free), and this feature is fully integrated with all monitor synchronization mechanisms. 280 289 All control-flow features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers. 281 290 Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming languages. 282 291 }% 283 292 284 \keywords{ C \CFA (Cforall) coroutine concurrency generator monitor parallelism runtime thread}293 \keywords{generator, coroutine, concurrency, parallelism, thread, monitor, runtime, C, \CFA (Cforall)} 285 294 286 295 287 296 \begin{document} 288 %\linenumbers% comment out to turn off line numbering297 \linenumbers % comment out to turn off line numbering 289 298 290 299 \maketitle … … 293 302 \section{Introduction} 294 303 295 \CFA~\cite{Moss18,Cforall} is a modern, polymorphic, nonobject-oriented\footnote{ 296 \CFA has object-oriented features, such as constructors, destructors, and simple trait/interface inheritance. 297 % Go interfaces, Rust traits, Swift Protocols, Haskell Type Classes and Java Interfaces. 298 % "Trait inheritance" works for me. "Interface inheritance" might also be a good choice, and distinguish clearly from implementation inheritance. 299 % You'll want to be a little bit careful with terms like "structural" and "nominal" inheritance as well. CFA has structural inheritance (I think Go as well) -- it's inferred based on the structure of the code. 300 % Java, Rust, and Haskell (not sure about Swift) have nominal inheritance, where there needs to be a specific statement that "this type inherits from this type". 301 However, functions \emph{cannot} be nested in structures and there is no mechanism to designate a function parameter as a receiver, \lstinline@this@, parameter.}, 302 , backward-compatible extension of the C programming language. 303 In many ways, \CFA is to C as Scala~\cite{Scala} is to Java, providing a vehicle for new typing and control-flow capabilities on top of a highly popular programming language\footnote{ 304 The TIOBE index~\cite{TIOBE} for May 2020 ranks the top five \emph{popular} programming languages as C 17\%, Java 16\%, Python 9\%, \CC 6\%, and \Csharp 4\% = 52\%, and over the past 30 years, C has always ranked either first or second in popularity.} 305 allowing immediate dissemination. 306 This paper discusses the design philosophy and implementation of \CFA's advanced control-flow and concurrent/parallel features, along with the supporting runtime written in \CFA. 307 308 % The call/return extensions retain state between callee and caller versus losing the callee's state on return; 309 % the concurrency extensions allow high-level management of threads. 310 311 The \CFA control-flow framework extends ISO \Celeven~\cite{C11} with new call/return and concurrent/parallel control-flow. 312 Call/return control-flow with argument and parameter passing appeared in the first programming languages. 313 Over the past 50 years, call/return has been augmented with features like static and dynamic call, exceptions (multilevel return) and generators/coroutines (see Section~\ref{s:StatefulFunction}). 314 While \CFA has mechanisms for dynamic call (algebraic effects~\cite{Zhang19}) and exceptions\footnote{ 315 \CFA exception handling will be presented in a separate paper. 316 The key feature that dovetails with this paper is nonlocal exceptions allowing exceptions to be raised across stacks, with synchronous exceptions raised among coroutines and asynchronous exceptions raised among threads, similar to that in \uC~\cite[\S~5]{uC++}} 317 , this work only discusses retaining state between calls via generators and coroutines. 318 \newterm{Coroutining} was introduced by Conway~\cite{Conway63}, discussed by Knuth~\cite[\S~1.4.2]{Knuth73V1}, implemented in Simula67~\cite{Simula67}, formalized by Marlin~\cite{Marlin80}, and is now popular and appears in old and new programming languages: CLU~\cite{CLU}, \Csharp~\cite{Csharp}, Ruby~\cite{Ruby}, Python~\cite{Python}, JavaScript~\cite{JavaScript}, Lua~\cite{Lua}, \CCtwenty~\cite{C++20Coroutine19}. 319 Coroutining is sequential execution requiring direct handoff among coroutines, \ie only the programmer is controlling execution order. 320 If coroutines transfer to an internal event-engine for scheduling the next coroutines (as in async-await), the program transitions into the realm of concurrency~\cite[\S~3]{Buhr05a}. 321 Coroutines are only a stepping stone toward concurrency where the commonality is that coroutines and threads retain state between calls. 322 323 \Celeven and \CCeleven define concurrency~\cite[\S~7.26]{C11}, but it is largely wrappers for a subset of the pthreads library~\cite{Pthreads}.\footnote{Pthreads concurrency is based on simple thread fork and join in a function and mutex or condition locks, which is low-level and error-prone} 324 Interestingly, almost a decade after the \Celeven standard, the most recent versions of gcc, clang, and msvc do not support the \Celeven include @threads.h@, indicating no interest in the C11 concurrency approach (possibly because of the recent effort to add concurrency to \CC). 325 While the \Celeven standard does not state a threading model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}, as for \CC. 304 This paper discusses the design philosophy and implementation of advanced language-level control-flow and concurrent/parallel features in \CFA~\cite{Moss18,Cforall} and its runtime, which is written entirely in \CFA. 305 \CFA is a modern, polymorphic, non-object-oriented\footnote{ 306 \CFA has features often associated with object-oriented programming languages, such as constructors, destructors, virtuals and simple inheritance. 307 However, functions \emph{cannot} be nested in structures, so there is no lexical binding between a structure and set of functions (member/method) implemented by an implicit \lstinline@this@ (receiver) parameter.}, 308 backwards-compatible extension of the C programming language. 309 In many ways, \CFA is to C as Scala~\cite{Scala} is to Java, providing a \emph{research vehicle} for new typing and control-flow capabilities on top of a highly popular programming language allowing immediate dissemination. 310 Within the \CFA framework, new control-flow features are created from scratch because ISO \Celeven defines only a subset of the \CFA extensions, where the overlapping features are concurrency~\cite[\S~7.26]{C11}. 311 However, \Celeven concurrency is largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}, and \Celeven and pthreads concurrency is simple, based on thread fork/join in a function and mutex/condition locks, which is low-level and error-prone; 312 no high-level language concurrency features are defined. 313 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-9 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C11 concurrency approach (possibly because the effort to add concurrency to \CC). 314 Finally, while the \Celeven standard does not state a threading model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}. 315 326 316 In contrast, there has been a renewed interest during the past decade in user-level (M:N, green) threading in old and new programming languages. 327 As multi core hardware became available in the 1980/1990s, both user and kernel threading were examined.317 As multi-core hardware became available in the 1980/90s, both user and kernel threading were examined. 328 318 Kernel threading was chosen, largely because of its simplicity and fit with the simpler operating systems and hardware architectures at the time, which gave it a performance advantage~\cite{Drepper03}. 329 319 Libraries like pthreads were developed for C, and the Solaris operating-system switched from user (JDK 1.1~\cite{JDK1.1}) to kernel threads. 330 As a result, many languages adopt the 1:1 kernel-threading model, like Java (Scala), Objective-C~\cite{obj-c-book}, \CCeleven~\cite{C11}, C\#~\cite{Csharp} and Rust~\cite{Rust}, with a variety of presentation mechanisms.331 From 2000 onward , several language implementations have championed the M:N user-threading model, like Go~\cite{Go}, Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, D~\cite{D}, and \uC~\cite{uC++,uC++book}, including putting green threads back into Java~\cite{Quasar}, and many user-threading libraries have appeared~\cite{Qthreads,MPC,Marcel}.332 The main argument for user-level threading is that it is lighter weight than kernel threading because locking and context switching do not cross the kernel boundary, so there is less restriction on programming styles that encourages large numbers of threads performing medium-sized workto facilitate load balancing by the runtime~\cite{Verch12}.320 As a result, languages like Java, Scala, Objective-C~\cite{obj-c-book}, \CCeleven~\cite{C11}, and C\#~\cite{Csharp} adopt the 1:1 kernel-threading model, with a variety of presentation mechanisms. 321 From 2000 onwards, languages like Go~\cite{Go}, Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, D~\cite{D}, and \uC~\cite{uC++,uC++book} have championed the M:N user-threading model, and many user-threading libraries have appeared~\cite{Qthreads,MPC,Marcel}, including putting green threads back into Java~\cite{Quasar}. 322 The main argument for user-level threading is that it is lighter weight than kernel threading (locking and context switching do not cross the kernel boundary), so there is less restriction on programming styles that encourage large numbers of threads performing medium work units to facilitate load balancing by the runtime~\cite{Verch12}. 333 323 As well, user-threading facilitates a simpler concurrency approach using thread objects that leverage sequential patterns versus events with call-backs~\cite{Adya02,vonBehren03}. 334 Finally, performant user-threading implementations, both in time and space, meet or exceed direct kernel-threading implementations, while achieving the programming advantages of high concurrency levels and safety. 335 336 A further effort over the past two decades is the development of language memory models to deal with the conflict between language features and compiler/hardware optimizations, \eg some language features are unsafe in the presence of aggressive sequential optimizations~\cite{Buhr95a,Boehm05}. 337 The consequence is that a language must provide sufficient tools to program around safety issues, as inline and library code is compiled as sequential without any explicit concurrent directive. 338 One solution is low-level qualifiers and functions, \eg @volatile@ and atomics, allowing \emph{programmers} to explicitly write safe, race-free~\cite{Boehm12} programs. 339 A safer solution is high-level language constructs so the \emph{compiler} knows the concurrency boundaries, \ie where mutual exclusion and synchronization are acquired and released, and provide implicit safety at and across these boundaries. 340 While the optimization problem is best known with respect to concurrency, it applies to other complex control-flows like exceptions and coroutines. 341 As well, language solutions allow matching the language paradigm with the approach, \eg matching the functional paradigm with data-flow programming or the imperative paradigm with thread programming. 324 Finally, performant user-threading implementations (both time and space) meet or exceed direct kernel-threading implementations, while achieving the programming advantages of high concurrency levels and safety. 325 326 A further effort over the past two decades is the development of language memory models to deal with the conflict between language features and compiler/hardware optimizations, \ie some language features are unsafe in the presence of aggressive sequential optimizations~\cite{Buhr95a,Boehm05}. 327 The consequence is that a language must provide sufficient tools to program around safety issues, as inline and library code is all sequential to the compiler. 328 One solution is low-level qualifiers and functions (\eg @volatile@ and atomics) allowing \emph{programmers} to explicitly write safe (race-free~\cite{Boehm12}) programs. 329 A safer solution is high-level language constructs so the \emph{compiler} knows the optimization boundaries, and hence, provides implicit safety. 330 This problem is best known with respect to concurrency, but applies to other complex control-flow, like exceptions\footnote{ 331 \CFA exception handling will be presented in a separate paper. 332 The key feature that dovetails with this paper is nonlocal exceptions allowing exceptions to be raised across stacks, with synchronous exceptions raised among coroutines and asynchronous exceptions raised among threads, similar to that in \uC~\cite[\S~5]{uC++} 333 } and coroutines. 334 Finally, language solutions allow matching constructs with language paradigm, \ie imperative and functional languages often have different presentations of the same concept to fit their programming model. 342 335 343 336 Finally, it is important for a language to provide safety over performance \emph{as the default}, allowing careful reduction of safety for performance when necessary. 344 Two concurrency violations of this philosophy are \emph{spurious} or \emph{random wakeup}~\cite[\S~9]{Buhr05a}, and \emph{barging}\footnote{ 345 Barging is competitive succession instead of direct handoff, \ie after a lock is released both arriving and preexisting waiter threads compete to acquire the lock. 346 Hence, an arriving thread can temporally \emph{barge} ahead of threads already waiting for an event, which can repeat indefinitely leading to starvation of waiter threads. 347 } or signals-as-hints~\cite[\S~8]{Buhr05a}, where one is a consequence of the other, \ie once there is spurious wakeup, barging follows. 348 (Author experience teaching concurrency is that students are confused by these semantics.) 349 However, spurious wakeup is \emph{not} a foundational concurrency property~\cite[\S~9]{Buhr05a}; 350 it is a performance design choice. 351 We argue removing spurious wakeup and signals-as-hints make concurrent programming simpler and safer as there is less local nondeterminism to manage. 352 If barging acquisition is allowed, its specialized performance advantage should be available as an option not the default. 353 354 \CFA embraces language extensions for advanced control-flow, user-level threading, and safety as the default. 355 We present comparative examples to support our argument that the \CFA control-flow extensions are as expressive and safe as those in other concurrent imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms. 337 Two concurrency violations of this philosophy are \emph{spurious wakeup} (random wakeup~\cite[\S~8]{Buhr05a}) and \emph{barging}\footnote{ 338 The notion of competitive succession instead of direct handoff, \ie a lock owner releases the lock and an arriving thread acquires it ahead of preexisting waiter threads. 339 } (signals-as-hints~\cite[\S~8]{Buhr05a}), where one is a consequence of the other, \ie once there is spurious wakeup, signals-as-hints follow. 340 However, spurious wakeup is \emph{not} a foundational concurrency property~\cite[\S~8]{Buhr05a}, it is a performance design choice. 341 Similarly, signals-as-hints are often a performance decision. 342 We argue removing spurious wakeup and signals-as-hints make concurrent programming significantly safer because it removes local non-determinism and matches with programmer expectation. 343 (Author experience teaching concurrency is that students are highly confused by these semantics.) 344 Clawing back performance, when local non-determinism is unimportant, should be an option not the default. 345 346 \begin{comment} 347 Most augmented traditional (Fortran 18~\cite{Fortran18}, Cobol 14~\cite{Cobol14}, Ada 12~\cite{Ada12}, Java 11~\cite{Java11}) and new languages (Go~\cite{Go}, Rust~\cite{Rust}, and D~\cite{D}), except \CC, diverge from C with different syntax and semantics, only interoperate indirectly with C, and are not systems languages, for those with managed memory. 348 As a result, there is a significant learning curve to move to these languages, and C legacy-code must be rewritten. 349 While \CC, like \CFA, takes an evolutionary approach to extend C, \CC's constantly growing complex and interdependent features-set (\eg objects, inheritance, templates, etc.) mean idiomatic \CC code is difficult to use from C, and C programmers must expend significant effort learning \CC. 350 Hence, rewriting and retraining costs for these languages, even \CC, are prohibitive for companies with a large C software-base. 351 \CFA with its orthogonal feature-set, its high-performance runtime, and direct access to all existing C libraries circumvents these problems. 352 \end{comment} 353 354 \CFA embraces user-level threading, language extensions for advanced control-flow, and safety as the default. 355 We present comparative examples so the reader can judge if the \CFA control-flow extensions are better and safer than those in other concurrent, imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms. 356 356 The main contributions of this work are: 357 \begin{itemize}[topsep=3pt,itemsep= 0pt]357 \begin{itemize}[topsep=3pt,itemsep=1pt] 358 358 \item 359 a set of fundamental execution properties that dictate which language-level control-flow features need to be supported, 360 359 language-level generators, coroutines and user-level threading, which respect the expectations of C programmers. 361 360 \item 362 integration of these language-level control-flow features, while respecting the style and expectations of C programmers, 363 361 monitor synchronization without barging, and the ability to safely acquiring multiple monitors \emph{simultaneously} (deadlock free), while seamlessly integrating these capabilities with all monitor synchronization mechanisms. 364 362 \item 365 monitor synchronization without barging, and the ability to safely acquiring multiple monitors in a deadlock-free way, while seamlessly integrating these capabilities with all monitor synchronization mechanisms, 366 367 \item 368 providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features, 369 363 providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features. 370 364 % \item 371 365 % library extensions for executors, futures, and actors built on the basic mechanisms. 372 373 366 \item 374 a runtime system without spurious wake-up and no performance loss, 375 367 a runtime system with no spurious wakeup. 376 368 \item 377 a dynamic partitioning mechanism to segregate groups of executing user and kernel threads performing specialized work, \eg web-server or compute engine, or requiring different scheduling, \eg NUMA or real-time. 378 369 a dynamic partitioning mechanism to segregate the execution environment for specialized requirements. 379 370 % \item 380 % a nonblocking I/O library 381 371 % a non-blocking I/O library 382 372 \item 383 experimental results showing comparable performance of the \CFA features with similar mechanisms in otherlanguages.373 experimental results showing comparable performance of the new features with similar mechanisms in other programming languages. 384 374 \end{itemize} 385 375 386 Section~\ref{s:FundamentalExecutionProperties} presents the compositional hierarchy of execution properties directing the design of control-flow features in \CFA. 387 Section~\ref{s:StatefulFunction} begins advanced control by introducing sequential functions that retain data and execution state between calls producing constructs @generator@ and @coroutine@. 388 Section~\ref{s:Concurrency} begins concurrency, or how to create (fork) and destroy (join) a thread producing the @thread@ construct. 389 Section~\ref{s:MutualExclusionSynchronization} discusses the two mechanisms to restricted nondeterminism when controlling shared access to resources, called mutual exclusion, and timing relationships among threads, called synchronization. 376 Section~\ref{s:StatefulFunction} begins advanced control by introducing sequential functions that retain data and execution state between calls, which produces constructs @generator@ and @coroutine@. 377 Section~\ref{s:Concurrency} begins concurrency, or how to create (fork) and destroy (join) a thread, which produces the @thread@ construct. 378 Section~\ref{s:MutualExclusionSynchronization} discusses the two mechanisms to restricted nondeterminism when controlling shared access to resources (mutual exclusion) and timing relationships among threads (synchronization). 390 379 Section~\ref{s:Monitor} shows how both mutual exclusion and synchronization are safely embedded in the @monitor@ and @thread@ constructs. 391 Section~\ref{s:CFARuntimeStructure} describes the large-scale mechanism to structure threads and virtual processors (kernel threads). 392 Section~\ref{s:Performance} uses microbenchmarks to compare \CFA threading with pthreads, Java 11.0.6, Go 1.12.6, Rust 1.37.0, Python 3.7.6, Node.js v12.18.0, and \uC 7.0.0. 393 394 395 \section{Fundamental Execution Properties} 396 \label{s:FundamentalExecutionProperties} 397 398 The features in a programming language should be composed of a set of fundamental properties rather than an ad hoc collection chosen by the designers. 399 To this end, the control-flow features created for \CFA are based on the fundamental properties of any language with function-stack control-flow (see also \uC~\cite[pp.~140-142]{uC++}). 400 The fundamental properties are execution state, thread, and mutual-exclusion/synchronization. 401 These independent properties can be used to compose different language features, forming a compositional hierarchy, where the combination of all three is the most advanced feature, called a thread. 402 While it is possible for a language to only provide threads for composing programs~\cite{Hermes90}, this unnecessarily complicates and makes inefficient solutions to certain classes of problems. 403 As is shown, each of the non-rejected composed language features solves a particular set of problems, and hence, has a defensible position in a programming language. 404 If a compositional feature is missing, a programmer has too few fundamental properties resulting in a complex and/or inefficient solution. 405 406 In detail, the fundamental properties are: 407 \begin{description}[leftmargin=\parindent,topsep=3pt,parsep=0pt] 408 \item[\newterm{execution state}:] 409 It is the state information needed by a control-flow feature to initialize and manage both compute data and execution location(s), and de-initialize. 410 For example, calling a function initializes a stack frame including contained objects with constructors, manages local data in blocks and return locations during calls, and de-initializes the frame by running any object destructors and management operations. 411 State is retained in fixed-sized aggregate structures (objects) and dynamic-sized stack(s), often allocated in the heap(s) managed by the runtime system. 412 The lifetime of state varies with the control-flow feature, where longer life-time and dynamic size provide greater power but also increase usage complexity and cost. 413 Control-flow transfers among execution states in multiple ways, such as function call, context switch, asynchronous await, etc. 414 Because the programming language determines what constitutes an execution state, implicitly manages this state, and defines movement mechanisms among states, execution state is an elementary property of the semantics of a programming language. 415 % An execution-state is related to the notion of a process continuation \cite{Hieb90}. 416 417 \item[\newterm{threading}:] 418 It is execution of code that occurs independently of other execution, where an individual thread's execution is sequential. 419 Multiple threads provide \emph{concurrent execution}; 420 concurrent execution becomes parallel when run on multiple processing units, \eg hyper-threading, cores, or sockets. 421 A programmer needs mechanisms to create, block and unblock, and join with a thread, even if these basic mechanisms are supplied indirectly through high-level features. 422 423 \item[\newterm{mutual-exclusion / synchronization (MES)}:] 424 It is the concurrency mechanism to perform an action without interruption and establish timing relationships among multiple threads. 425 We contented these two properties are independent, \ie mutual exclusion cannot provide synchronization and vice versa without introducing additional threads~\cite[\S~4]{Buhr05a}. 426 Limiting MES functionality results in contrived solutions and inefficiency on multicore von Neumann computers where shared memory is a foundational aspect of its design. 427 \end{description} 428 These properties are fundamental as they cannot be built from existing language features, \eg a basic programming language like C99~\cite{C99} cannot create new control-flow features, concurrency, or provide MES without (atomic) hardware mechanisms. 429 430 431 \subsection{Structuring execution properties} 432 433 Programming languages seldom present the fundamental execution properties directly to programmers. 434 Instead, the properties are packaged into higher-level constructs that encapsulate details and provide safety to these low-level mechanisms. 435 Interestingly, language designers often pick and choose among these execution properties proving a varying subset of constructs. 436 437 Table~\ref{t:ExecutionPropertyComposition} shows all combinations of the three fundamental execution properties available to language designers. 438 (When doing combination case-analysis, not all combinations are meaningful.) 439 The combinations of state, thread, and MES compose a hierarchy of control-flow features all of which have appeared in prior programming languages, where each of these languages have found the feature useful. 440 To understand the table, it is important to review the basic von Neumann execution requirement of at least one thread and execution state providing some form of call stack. 441 For table entries missing these minimal components, the property is borrowed from the invoker (caller). 442 Each entry in the table, numbered \textbf{1}--\textbf{12}, is discussed with respect to how the execution properties combine to generate a high-level language construct. 443 444 \begin{table} 445 \caption{Execution property composition} 446 \centering 447 \label{t:ExecutionPropertyComposition} 448 \renewcommand{\arraystretch}{1.25} 449 %\setlength{\tabcolsep}{5pt} 450 \vspace*{-5pt} 451 \begin{tabular}{c|c||l|l} 452 \multicolumn{2}{c||}{Execution properties} & \multicolumn{2}{c}{Mutual exclusion / synchronization} \\ 453 \hline 454 stateful & thread & \multicolumn{1}{c|}{No} & \multicolumn{1}{c}{Yes} \\ 455 \hline 456 \hline 457 No & No & \textbf{1}\ \ \ @struct@ & \textbf{2}\ \ \ @mutex@ @struct@ \\ 458 \hline 459 Yes (stackless) & No & \textbf{3}\ \ \ @generator@ & \textbf{4}\ \ \ @mutex@ @generator@ \\ 460 \hline 461 Yes (stackful) & No & \textbf{5}\ \ \ @coroutine@ & \textbf{6}\ \ \ @mutex@ @coroutine@ \\ 462 \hline 463 No & Yes & \textbf{7}\ \ \ {\color{red}rejected} & \textbf{8}\ \ \ {\color{red}rejected} \\ 464 \hline 465 Yes (stackless) & Yes & \textbf{9}\ \ \ {\color{red}rejected} & \textbf{10}\ \ \ {\color{red}rejected} \\ 466 \hline 467 Yes (stackful) & Yes & \textbf{11}\ \ \ @thread@ & \textbf{12}\ \ @mutex@ @thread@ \\ 468 \end{tabular} 469 \vspace*{-8pt} 470 \end{table} 471 472 Case 1 is a structure where access functions borrow local state (stack frame/activation) and thread from the invoker and retain this state across \emph{callees}, \ie function local-variables are retained on the borrowed stack during calls. 473 Structures are a foundational mechanism for data organization, and access functions provide interface abstraction and code sharing in all programming languages. 474 Case 2 is case 1 with thread safety to a structure's state where access functions provide serialization (mutual exclusion) and scheduling among calling threads (synchronization). 475 A @mutex@ structure, often called a \newterm{monitor}, provides a high-level interface for race-free access of shared data in concurrent programming languages. 476 Case 3 is case 1 where the structure can implicitly retain execution state and access functions use this execution state to resume/suspend across \emph{callers}, but resume/suspend does not retain a function's local state. 477 A stackless structure, often called a \newterm{generator} or \emph{iterator}, is \newterm{stackless} because it still borrows the caller's stack and thread, but the stack is used only to preserve state across its callees not callers. 478 Generators provide the first step toward directly solving problems like finite-state machines (FSMs) that retain data and execution state between calls, whereas normal functions restart on each call. 479 Case 4 is cases 2 and 3 with thread safety during execution of the generator's access functions. 480 A @mutex@ generator extends generators into the concurrent domain. 481 Cases 5 and 6 are like cases 3 and 4 where the structure is extended with an implicit separate stack, so only the thread is borrowed by access functions. 482 A stackful generator, often called a \newterm{coroutine}, is \newterm{stackful} because resume/suspend now context switch to/from the caller's and coroutine's stack. 483 A coroutine extends the state retained between calls beyond the generator's structure to arbitrary call depth in the access functions. 484 Cases 7, 8, 9 and 10 are rejected because a new thread must have its own stack, where the thread begins and stack frames are stored for calls, \ie it is unrealistic for a thread to borrow a stack. 485 For cases 9 and 10, the stackless frame is not growable, precluding accepting nested calls, making calls, blocking as it requires calls, or preemption as it requires pushing an interrupt frame, all of which compound to require an unknown amount of execution state. 486 Hence, if this kind of uninterruptable thread exists, it must execute to completion, \ie computation only, which severely restricts runtime management. 487 Cases 11 and 12 are a stackful thread with and without safe access to shared state. 488 A thread is the language mechanism to start another thread of control in a program with growable execution state for call/return execution. 489 In general, language constructs with more execution properties increase the cost of creation and execution along with complexity of usage. 490 491 Given the execution-properties taxonomy, programmers now ask three basic questions: is state necessary across callers and how much, is a separate thread necessary, is thread safety necessary. 492 Table~\ref{t:ExecutionPropertyComposition} then suggests the optimal language feature needed for implementing a programming problem. 493 The following sections describe how \CFA fills in \emph{all} the nonrejected table entries with language features, while other programming languages may only provide a subset of the table. 494 495 496 \subsection{Design requirements} 497 498 The following design requirements largely stem from building \CFA on top of C. 499 \begin{itemize}[topsep=3pt,parsep=0pt] 500 \item 501 All communication must be statically type checkable for early detection of errors and efficient code generation. 502 This requirement is consistent with the fact that C is a statically typed programming language. 503 504 \item 505 Direct interaction among language features must be possible allowing any feature to be selected without restricting comm\-unication. 506 For example, many concurrent languages do not provide direct communication calls among threads, \ie threads only communicate indirectly through monitors, channels, messages, and/or futures. 507 Indirect communication increases the number of objects, consuming more resources, and requires additional synchronization and possibly data transfer. 508 509 \item 510 All communication is performed using function calls, \ie data are transmitted from argument to parameter and results are returned from function calls. 511 Alternative forms of communication, such as call-backs, message passing, channels, or communication ports, step outside of C's normal form of communication. 512 513 \item 514 All stateful features must follow the same declaration scopes and lifetimes as other language data. 515 For C that means at program startup, during block and function activation, and on demand using dynamic allocation. 516 517 \item 518 MES must be available implicitly in language constructs, \eg Java built-in monitors, as well as explicitly for specialized requirements, \eg @java.util.concurrent@, because requiring programmers to build MES using low-level locks often leads to incorrect programs. 519 Furthermore, reducing synchronization scope by encapsulating it within language constructs further reduces errors in concurrent programs. 520 521 \item 522 Both synchronous and asynchronous communication are needed. 523 However, we believe the best way to provide asynchrony, such as call-buffering/chaining and/or returning futures~\cite{multilisp}, is building it from expressive synchronous features. 524 525 \item 526 Synchronization must be able to control the service order of requests including prioritizing selection from different kinds of outstanding requests, and postponing a request for an unspecified time while continuing to accept new requests. 527 Otherwise, certain concurrency problems are difficult, \eg web server, disk scheduling, and the amount of concurrency is inhibited~\cite{Gentleman81}. 528 \end{itemize} 529 We have satisfied these requirements in \CFA while maintaining backwards compatibility with the huge body of legacy C programs. 530 % In contrast, other new programming languages must still access C programs (\eg operating-system service routines), but do so through fragile C interfaces. 531 532 533 \subsection{Asynchronous await / call} 534 535 Asynchronous await/call is a caller mechanism for structuring programs and/or increasing concurrency, where the caller (client) postpones an action into the future, which is subsequently executed by a callee (server). 536 The caller detects the action's completion through a \newterm{future} or \newterm{promise}. 537 The benefit is asynchronous caller execution with respect to the callee until future resolution. 538 For single-threaded languages like JavaScript, an asynchronous call passes a callee action, which is queued in the event-engine, and continues execution with a promise. 539 When the caller needs the promise to be fulfilled, it executes @await@. 540 A promise-completion call-back can be part of the callee action or the caller is rescheduled; 541 in either case, the call back is executed after the promise is fulfilled. 542 While asynchronous calls generate new callee (server) events, we contend this mechanism is insufficient for advanced control-flow mechanisms like generators or coroutines, which are discussed next. 543 Specifically, control between caller and callee occurs indirectly through the event-engine precluding direct handoff and cycling among events, and requires complex resolution of a control promise and data. 544 Note, @async-await@ is just syntactic-sugar over the event engine so it does not solve these deficiencies. 545 For multithreaded languages like Java, the asynchronous call queues a callee action with an executor (server), which subsequently executes the work by a thread in the executor thread-pool. 546 The problem is when concurrent work-units need to interact and/or block as this effects the executor by stopping threads. 547 While it is possible to extend this approach to support the necessary mechanisms, \eg message passing in Actors, we show monitors and threads provide an equally competitive approach that does not deviate from normal call communication and can be used to build asynchronous call, as is done in Java. 380 Section~\ref{s:CFARuntimeStructure} describes the large-scale mechanism to structure (cluster) threads and virtual processors (kernel threads). 381 Section~\ref{s:Performance} uses a series of microbenchmarks to compare \CFA threading with pthreads, Java OpenJDK-9, Go 1.12.6 and \uC 7.0.0. 548 382 549 383 … … 551 385 \label{s:StatefulFunction} 552 386 553 A \emph{stateful function} has the ability to remember state between calls, where state can be either data or execution, \eg plugin, device driver, FSM. 554 A simple technique to retain data state between calls is @static@ declarations within a function, which is often implemented by hoisting the declarations to the global scope but hiding the names within the function using name mangling. 555 However, each call starts the function at the top making it difficult to determine the last point of execution in an algorithm, and requiring multiple flag variables and testing to reestablish the continuation point. 556 Hence, the next step of generalizing function state is implicitly remembering the return point between calls and reentering the function at this point rather than the top, called \emph{generators}\,/\,\emph{iterators} or \emph{stackless coroutines}. 557 For example, a Fibonacci generator retains data and execution state allowing it to remember prior values needed to generate the next value and the location in the algorithm to compute that value. 558 The next step of generalization is instantiating the function to allow multiple named instances, \eg multiple Fibonacci generators, where each instance has its own state, and hence, can generate an independent sequence of values. 559 Note, a subset of generator state is a function \emph{closure}, \ie the technique of capturing lexical references when returning a nested function. 560 A further generalization is adding a stack to a generator's state, called a \emph{coroutine}, so it can suspend outside of itself, \eg call helper functions to arbitrary depth before suspending back to its resumer without unwinding these calls. 561 For example, a coroutine iterator for a binary tree can stop the traversal at the visit point (pre, infix, post traversal), return the node value to the caller, and then continue the recursive traversal from the current node on the next call. 562 563 There are two styles of activating a stateful function, \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles). 564 These styles \emph{do not} cause incremental stack growth, \eg a million resume/suspend or resume/resume cycles do not remember each cycle just the last resumer for each cycle. 565 Selecting between stackless/stackful semantics and asymmetric/symmetric style is a tradeoff between programming requirements, performance, and design, where stackless is faster and smaller using modified call/return between closures, stackful is more general but slower and larger using context switching between distinct stacks, and asymmetric is simpler control-flow than symmetric. 566 Additionally, storage management for the closure/stack must be factored into design and performance, especially in unmanaged languages without garbage collection. 567 Note, creation cost (closure/stack) is amortized across usage, so activation cost (resume/suspend) is usually the dominant factor. 568 569 % The stateful function is an old idea~\cite{Conway63,Marlin80} that is new again~\cite{C++20Coroutine19}, where execution is temporarily suspended and later resumed, \eg plugin, device driver, finite-state machine. 570 % Hence, a stateful function may not end when it returns to its caller, allowing it to be restarted with the data and execution location present at the point of suspension. 571 % If the closure is fixed size, we call it a \emph{generator} (or \emph{stackless}), and its control flow is restricted, \eg suspending outside the generator is prohibited. 572 % If the closure is variable size, we call it a \emph{coroutine} (or \emph{stackful}), and as the names implies, often implemented with a separate stack with no programming restrictions. 573 % Hence, refactoring a stackless coroutine may require changing it to stackful. 574 % A foundational property of all \emph{stateful functions} is that resume/suspend \emph{do not} cause incremental stack growth, \ie resume/suspend operations are remembered through the closure not the stack. 575 % As well, activating a stateful function is \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles). 576 % A fixed closure activated by modified call/return is faster than a variable closure activated by context switching. 577 % Additionally, any storage management for the closure (especially in unmanaged languages, \ie no garbage collection) must also be factored into design and performance. 578 % Therefore, selecting between stackless and stackful semantics is a tradeoff between programming requirements and performance, where stackless is faster and stackful is more general. 579 % nppNote, creation cost is amortized across usage, so activation cost is usually the dominant factor. 580 581 For example, Python presents asymmetric generators as a function object, \uC presents symmetric coroutines as a \lstinline[language=C++]|class|-like object, and many languages present threading using function pointers, @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}. 582 \begin{center} 583 \begin{tabular}{@{}l|l|l@{}} 584 \multicolumn{1}{@{}c|}{Python asymmetric generator} & \multicolumn{1}{c|}{\uC symmetric coroutine} & \multicolumn{1}{c@{}}{Pthreads thread} \\ 585 \hline 586 \begin{python} 587 `def Gen():` $\LstCommentStyle{\color{red}// function}$ 588 ... yield val ... 589 gen = Gen() 590 for i in range( 10 ): 591 print( next( gen ) ) 592 \end{python} 593 & 594 \begin{uC++} 595 `_Coroutine Cycle {` $\LstCommentStyle{\color{red}// class}$ 596 Cycle * p; 597 void main() { p->cycle(); } 598 void cycle() { resume(); } `};` 599 Cycle c1, c2; c1.p=&c2; c2.p=&c1; c1.cycle(); 600 \end{uC++} 601 & 602 \begin{cfa} 603 void * `rtn`( void * arg ) { ... } 604 int i = 3, rc; 605 pthread_t t; $\C{// thread id}$ 606 $\LstCommentStyle{\color{red}// function pointer}$ 607 rc=pthread_create(&t, `rtn`, (void *)i); 608 \end{cfa} 609 \end{tabular} 610 \end{center} 611 \CFA's preferred presentation model for generators/coroutines/threads is a hybrid of functions and classes, giving an object-oriented flavor. 612 Essentially, the generator/coroutine/thread function is semantically coupled with a generator/coroutine/thread custom type via the type's name. 613 The custom type solves several issues, while accessing the underlying mechanisms used by the custom types is still allowed for flexibility reasons. 614 Each custom type is discussed in detail in the following sections. 615 616 617 \subsection{Generator} 618 619 Stackless generators (Table~\ref{t:ExecutionPropertyComposition} case 3) have the potential to be very small and fast, \ie as small and fast as function call/return for both creation and execution. 620 The \CFA goal is to achieve this performance target, possibly at the cost of some semantic complexity. 621 A series of different kinds of generators and their implementation demonstrate how this goal is accomplished.\footnote{ 622 The \CFA operator syntax uses \lstinline|?| to denote operands, which allows precise definitions for pre, post, and infix operators, \eg \lstinline|?++|, \lstinline|++?|, and \lstinline|?+?|, in addition \lstinline|?\{\}| denotes a constructor, as in \lstinline|foo `f` = `\{`...`\}`|, \lstinline|^?\{\}| denotes a destructor, and \lstinline|?()| is \CC function call \lstinline|operator()|. 623 Operator \lstinline+|+ is overloaded for printing, like bit-shift \lstinline|<<| in \CC. 624 The \CFA \lstinline|with| clause opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics; 625 multiple aggregates may be opened. 626 \CFA has rebindable references \lstinline|int i, & ip = i, j; `&ip = &j;`| and nonrebindable references \lstinline|int i, & `const` ip = i, j; `&ip = &j;` // disallowed|. 627 }% 387 The stateful function is an old idea~\cite{Conway63,Marlin80} that is new again~\cite{C++20Coroutine19}, where execution is temporarily suspended and later resumed, \eg plugin, device driver, finite-state machine. 388 Hence, a stateful function may not end when it returns to its caller, allowing it to be restarted with the data and execution location present at the point of suspension. 389 This capability is accomplished by retaining a data/execution \emph{closure} between invocations. 390 If the closure is fixed size, we call it a \emph{generator} (or \emph{stackless}), and its control flow is restricted, \eg suspending outside the generator is prohibited. 391 If the closure is variable size, we call it a \emph{coroutine} (or \emph{stackful}), and as the names implies, often implemented with a separate stack with no programming restrictions. 392 Hence, refactoring a stackless coroutine may require changing it to stackful. 393 A foundational property of all \emph{stateful functions} is that resume/suspend \emph{do not} cause incremental stack growth, \ie resume/suspend operations are remembered through the closure not the stack. 394 As well, activating a stateful function is \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles). 395 A fixed closure activated by modified call/return is faster than a variable closure activated by context switching. 396 Additionally, any storage management for the closure (especially in unmanaged languages, \ie no garbage collection) must also be factored into design and performance. 397 Therefore, selecting between stackless and stackful semantics is a tradeoff between programming requirements and performance, where stackless is faster and stackful is more general. 398 Note, creation cost is amortized across usage, so activation cost is usually the dominant factor. 628 399 629 400 \begin{figure} … … 639 410 640 411 641 642 643 412 int fn = f->fn; f->fn = f->fn1; 644 413 f->fn1 = f->fn + fn; 645 414 return fn; 415 646 416 } 647 417 int main() { … … 662 432 void `main(Fib & fib)` with(fib) { 663 433 664 665 434 [fn1, fn] = [1, 0]; 666 435 for () { … … 682 451 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 683 452 typedef struct { 684 int `restart`, fn1, fn;453 int fn1, fn; void * `next`; 685 454 } Fib; 686 #define FibCtor { `0`, 1, 0}455 #define FibCtor { 1, 0, NULL } 687 456 Fib * comain( Fib * f ) { 688 `static void * states[] = {&&s0, &&s1};` 689 `goto *states[f->restart];` 690 s0: f->`restart` = 1; 457 if ( f->next ) goto *f->next; 458 f->next = &&s1; 691 459 for ( ;; ) { 692 460 return f; 693 461 s1:; int fn = f->fn + f->fn1; 694 f->fn1 = f->fn; f->fn = fn;462 f->fn1 = f->fn; f->fn = fn; 695 463 } 696 464 } … … 704 472 \end{lrbox} 705 473 706 \subfloat[C ]{\label{f:CFibonacci}\usebox\myboxA}474 \subfloat[C asymmetric generator]{\label{f:CFibonacci}\usebox\myboxA} 707 475 \hspace{3pt} 708 476 \vrule 709 477 \hspace{3pt} 710 \subfloat[\CFA ]{\label{f:CFAFibonacciGen}\usebox\myboxB}478 \subfloat[\CFA asymmetric generator]{\label{f:CFAFibonacciGen}\usebox\myboxB} 711 479 \hspace{3pt} 712 480 \vrule 713 481 \hspace{3pt} 714 \subfloat[C generat ed code for \CFA version]{\label{f:CFibonacciSim}\usebox\myboxC}715 \caption{Fibonacci outputasymmetric generator}482 \subfloat[C generator implementation]{\label{f:CFibonacciSim}\usebox\myboxC} 483 \caption{Fibonacci (output) asymmetric generator} 716 484 \label{f:FibonacciAsymmetricGenerator} 717 485 … … 725 493 }; 726 494 void ?{}( Fmt & fmt ) { `resume(fmt);` } // constructor 727 void ^?{}( Fmt & f ) with(f) { $\C[ 2.25in]{// destructor}$495 void ^?{}( Fmt & f ) with(f) { $\C[1.75in]{// destructor}$ 728 496 if ( g != 0 || b != 0 ) sout | nl; } 729 497 void `main( Fmt & f )` with(f) { … … 731 499 for ( ; g < 5; g += 1 ) { $\C{// groups}$ 732 500 for ( ; b < 4; b += 1 ) { $\C{// blocks}$ 733 do {`suspend;` $\C{// wait for character}$734 while ( ch == '\n' ) ; // ignore newline735 sout | ch; $\C{// print character}$736 } sout | " "; $\C{// block separator}$737 } sout | nl; $\C{// group separator}$501 `suspend;` $\C{// wait for character}$ 502 while ( ch == '\n' ) `suspend;` // ignore 503 sout | ch; // newline 504 } sout | " "; // block spacer 505 } sout | nl; // group newline 738 506 } 739 507 } … … 753 521 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 754 522 typedef struct { 755 int `restart`, g, b;523 void * next; 756 524 char ch; 525 int g, b; 757 526 } Fmt; 758 527 void comain( Fmt * f ) { 759 `static void * states[] = {&&s0, &&s1};` 760 `goto *states[f->restart];` 761 s0: f->`restart` = 1; 528 if ( f->next ) goto *f->next; 529 f->next = &&s1; 762 530 for ( ;; ) { 763 531 for ( f->g = 0; f->g < 5; f->g += 1 ) { 764 532 for ( f->b = 0; f->b < 4; f->b += 1 ) { 765 do { return; s1:;766 } while ( f->ch == '\n' );533 return; 534 s1:; while ( f->ch == '\n' ) return; 767 535 printf( "%c", f->ch ); 768 536 } printf( " " ); … … 771 539 } 772 540 int main() { 773 Fmt fmt = { `0`}; comain( &fmt ); // prime541 Fmt fmt = { NULL }; comain( &fmt ); // prime 774 542 for ( ;; ) { 775 543 scanf( "%c", &fmt.ch ); … … 782 550 \end{lrbox} 783 551 784 \subfloat[\CFA ]{\label{f:CFAFormatGen}\usebox\myboxA}785 \hspace{3 5pt}552 \subfloat[\CFA asymmetric generator]{\label{f:CFAFormatGen}\usebox\myboxA} 553 \hspace{3pt} 786 554 \vrule 787 555 \hspace{3pt} 788 \subfloat[C generat ed code for \CFA version]{\label{f:CFormatGenImpl}\usebox\myboxB}556 \subfloat[C generator simulation]{\label{f:CFormatSim}\usebox\myboxB} 789 557 \hspace{3pt} 790 \caption{Formatter inputasymmetric generator}558 \caption{Formatter (input) asymmetric generator} 791 559 \label{f:FormatterAsymmetricGenerator} 792 560 \end{figure} 793 561 794 Figure~\ref{f:FibonacciAsymmetricGenerator} shows an unbounded asymmetric generator for an infinite sequence of Fibonacci numbers written left to right in C, \CFA, and showing the underlying C implementation for the \CFA version. 562 Stateful functions appear as generators, coroutines, and threads, where presentations are based on function objects or pointers~\cite{Butenhof97, C++14, MS:VisualC++, BoostCoroutines15}. 563 For example, Python presents generators as a function object: 564 \begin{python} 565 def Gen(): 566 ... `yield val` ... 567 gen = Gen() 568 for i in range( 10 ): 569 print( next( gen ) ) 570 \end{python} 571 Boost presents coroutines in terms of four functor object-types: 572 \begin{cfa} 573 asymmetric_coroutine<>::pull_type 574 asymmetric_coroutine<>::push_type 575 symmetric_coroutine<>::call_type 576 symmetric_coroutine<>::yield_type 577 \end{cfa} 578 and many languages present threading using function pointers, @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}, \eg pthreads: 579 \begin{cfa} 580 void * rtn( void * arg ) { ... } 581 int i = 3, rc; 582 pthread_t t; $\C{// thread id}$ 583 `rc = pthread_create( &t, rtn, (void *)i );` $\C{// create and initialized task, type-unsafe input parameter}$ 584 \end{cfa} 585 % void mycor( pthread_t cid, void * arg ) { 586 % int * value = (int *)arg; $\C{// type unsafe, pointer-size only}$ 587 % // thread body 588 % } 589 % int main() { 590 % int input = 0, output; 591 % coroutine_t cid = coroutine_create( &mycor, (void *)&input ); $\C{// type unsafe, pointer-size only}$ 592 % coroutine_resume( cid, (void *)input, (void **)&output ); $\C{// type unsafe, pointer-size only}$ 593 % } 594 \CFA's preferred presentation model for generators/coroutines/threads is a hybrid of objects and functions, with an object-oriented flavour. 595 Essentially, the generator/coroutine/thread function is semantically coupled with a generator/coroutine/thread custom type. 596 The custom type solves several issues, while accessing the underlying mechanisms used by the custom types is still allowed. 597 598 599 \subsection{Generator} 600 601 Stackless generators have the potential to be very small and fast, \ie as small and fast as function call/return for both creation and execution. 602 The \CFA goal is to achieve this performance target, possibly at the cost of some semantic complexity. 603 A series of different kinds of generators and their implementation demonstrate how this goal is accomplished. 604 605 Figure~\ref{f:FibonacciAsymmetricGenerator} shows an unbounded asymmetric generator for an infinite sequence of Fibonacci numbers written in C and \CFA, with a simple C implementation for the \CFA version. 795 606 This generator is an \emph{output generator}, producing a new result on each resumption. 796 607 To compute Fibonacci, the previous two values in the sequence are retained to generate the next value, \ie @fn1@ and @fn@, plus the execution location where control restarts when the generator is resumed, \ie top or middle. 797 An additional requirement is the ability to create an arbitrary number of generators of any kind, \ie retaining one state in global variables is insufficient;608 An additional requirement is the ability to create an arbitrary number of generators (of any kind), \ie retaining one state in global variables is insufficient; 798 609 hence, state is retained in a closure between calls. 799 610 Figure~\ref{f:CFibonacci} shows the C approach of manually creating the closure in structure @Fib@, and multiple instances of this closure provide multiple Fibonacci generators. 800 611 The C version only has the middle execution state because the top execution state is declaration initialization. 801 612 Figure~\ref{f:CFAFibonacciGen} shows the \CFA approach, which also has a manual closure, but replaces the structure with a custom \CFA @generator@ type. 802 Each generator type must have a function named \lstinline|main|, 803 % \footnote{ 804 % The name \lstinline|main| has special meaning in C, specifically the function where a program starts execution. 805 % Leveraging starting semantics to this name for generator/coroutine/thread is a logical extension.} 806 called a \emph{generator main} (leveraging the starting semantics for program @main@ in C), which is connected to the generator type via its single reference parameter. 613 This generator type is then connected to a function that \emph{must be named \lstinline|main|},\footnote{ 614 The name \lstinline|main| has special meaning in C, specifically the function where a program starts execution. 615 Hence, overloading this name for other starting points (generator/coroutine/thread) is a logical extension.} 616 called a \emph{generator main},which takes as its only parameter a reference to the generator type. 807 617 The generator main contains @suspend@ statements that suspend execution without ending the generator versus @return@. 808 For the Fibonacci generator-main, the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@. 618 For the Fibonacci generator-main,\footnote{ 619 The \CFA \lstinline|with| opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics. 620 Multiple aggregates may be opened.} 621 the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@. 809 622 Any local variables in @main@ \emph{are not retained} between calls; 810 623 hence local variables are only for temporary computations \emph{between} suspends. … … 814 627 Resuming an ended (returned) generator is undefined. 815 628 Function @resume@ returns its argument generator so it can be cascaded in an expression, in this case to print the next Fibonacci value @fn@ computed in the generator instance. 816 Figure~\ref{f:CFibonacciSim} shows the C implementation of the \CFA asymmetric generator. 817 Only one execution-state field, @restart@, is needed to subscript the suspension points in the generator. 818 At the start of the generator main, the @static@ declaration, @states@, is initialized to the N suspend points in the generator, where operator @&&@ dereferences or references a label~\cite{gccValueLabels}. 819 Next, the computed @goto@ selects the last suspend point and branches to it. 820 The cost of setting @restart@ and branching via the computed @goto@ adds very little cost to the suspend and resume calls. 821 822 An advantage of the \CFA explicit generator type is the ability to allow multiple type-safe interface functions taking and returning arbitrary types. 629 Figure~\ref{f:CFibonacciSim} shows the C implementation of the \CFA generator only needs one additional field, @next@, to handle retention of execution state. 630 The computed @goto@ at the start of the generator main, which branches after the previous suspend, adds very little cost to the resume call. 631 Finally, an explicit generator type provides both design and performance benefits, such as multiple type-safe interface functions taking and returning arbitrary types.\footnote{ 632 The \CFA operator syntax uses \lstinline|?| to denote operands, which allows precise definitions for pre, post, and infix operators, \eg \lstinline|++?|, \lstinline|?++|, and \lstinline|?+?|, in addition \lstinline|?\{\}| denotes a constructor, as in \lstinline|foo `f` = `\{`...`\}`|, \lstinline|^?\{\}| denotes a destructor, and \lstinline|?()| is \CC function call \lstinline|operator()|. 633 }% 823 634 \begin{cfa} 824 635 int ?()( Fib & fib ) { return `resume( fib )`.fn; } $\C[3.9in]{// function-call interface}$ 825 int ?()( Fib & fib, int N ) { for ( N - 1 ) `fib()`; return `fib()`; } $\C{// add parameter to skip N values}$ 826 double ?()( Fib & fib ) { return (int)`fib()` / 3.14159; } $\C{// different return type, cast prevents recursive call}$ 827 Fib f; int i; double d; 828 i = f(); i = f( 2 ); d = f(); $\C{// alternative interfaces}\CRT$ 636 int ?()( Fib & fib, int N ) { for ( N - 1 ) `fib()`; return `fib()`; } $\C{// use function-call interface to skip N values}$ 637 double ?()( Fib & fib ) { return (int)`fib()` / 3.14159; } $\C{// different return type, cast prevents recursive call}\CRT$ 638 sout | (int)f1() | (double)f1() | f2( 2 ); // alternative interface, cast selects call based on return type, step 2 values 829 639 \end{cfa} 830 640 Now, the generator can be a separately compiled opaque-type only accessed through its interface functions. 831 641 For contrast, Figure~\ref{f:PythonFibonacci} shows the equivalent Python Fibonacci generator, which does not use a generator type, and hence only has a single interface, but an implicit closure. 832 642 833 \begin{figure} 834 %\centering 835 \newbox\myboxA 836 \begin{lrbox}{\myboxA} 837 \begin{python}[aboveskip=0pt,belowskip=0pt] 838 def Fib(): 839 fn1, fn = 0, 1 840 while True: 841 `yield fn1` 842 fn1, fn = fn, fn1 + fn 843 f1 = Fib() 844 f2 = Fib() 845 for i in range( 10 ): 846 print( next( f1 ), next( f2 ) ) 847 848 849 850 851 852 853 854 855 856 857 \end{python} 858 \end{lrbox} 859 860 \newbox\myboxB 861 \begin{lrbox}{\myboxB} 862 \begin{python}[aboveskip=0pt,belowskip=0pt] 863 def Fmt(): 864 try: 865 while True: $\C[2.5in]{\# until destructor call}$ 866 for g in range( 5 ): $\C{\# groups}$ 867 for b in range( 4 ): $\C{\# blocks}$ 868 while True: 869 ch = (yield) $\C{\# receive from send}$ 870 if '\n' not in ch: $\C{\# ignore newline}$ 871 break 872 print( ch, end='' ) $\C{\# print character}$ 873 print( ' ', end='' ) $\C{\# block separator}$ 874 print() $\C{\# group separator}$ 875 except GeneratorExit: $\C{\# destructor}$ 876 if g != 0 | b != 0: $\C{\# special case}$ 877 print() 878 fmt = Fmt() 879 `next( fmt )` $\C{\# prime, next prewritten}$ 880 for i in range( 41 ): 881 `fmt.send( 'a' );` $\C{\# send to yield}$ 882 \end{python} 883 \end{lrbox} 884 885 \hspace{30pt} 886 \subfloat[Fibonacci]{\label{f:PythonFibonacci}\usebox\myboxA} 887 \hspace{3pt} 888 \vrule 889 \hspace{3pt} 890 \subfloat[Formatter]{\label{f:PythonFormatter}\usebox\myboxB} 891 \caption{Python generator} 892 \label{f:PythonGenerator} 893 \end{figure} 894 895 Having to manually create the generator closure by moving local-state variables into the generator type is an additional programmer burden (removed by the coroutine in Section~\ref{s:Coroutine}). 896 This manual requirement follows from the generality of allowing variable-size local-state, \eg local state with a variable-length array requires dynamic allocation as the array size is unknown at compile time. 643 Having to manually create the generator closure by moving local-state variables into the generator type is an additional programmer burden. 644 (This restriction is removed by the coroutine in Section~\ref{s:Coroutine}.) 645 This requirement follows from the generality of variable-size local-state, \eg local state with a variable-length array requires dynamic allocation because the array size is unknown at compile time. 897 646 However, dynamic allocation significantly increases the cost of generator creation/destruction and is a showstopper for embedded real-time programming. 898 647 But more importantly, the size of the generator type is tied to the local state in the generator main, which precludes separate compilation of the generator main, \ie a generator must be inlined or local state must be dynamically allocated. 899 With respect to safety, we believe static analysis can discriminate persistent generator state from temporary generator-main state and raise a compile-time error for temporary usage spanning suspend points.900 Our experience using generators is that theproblems have simple data state, including local state, but complex execution state, so the burden of creating the generator type is small.901 As well, C programmers are not afraid of this kind of semantic programming requirement, if it results in very small andfast generators.648 With respect to safety, we believe static analysis can discriminate local state from temporary variables in a generator, \ie variable usage spanning @suspend@, and generate a compile-time error. 649 Finally, our current experience is that most generator problems have simple data state, including local state, but complex execution state, so the burden of creating the generator type is small. 650 As well, C programmers are not afraid of this kind of semantic programming requirement, if it results in very small, fast generators. 902 651 903 652 Figure~\ref{f:CFAFormatGen} shows an asymmetric \newterm{input generator}, @Fmt@, for restructuring text into groups of characters of fixed-size blocks, \ie the input on the left is reformatted into the output on the right, where newlines are ignored. … … 920 669 The example takes advantage of resuming a generator in the constructor to prime the loops so the first character sent for formatting appears inside the nested loops. 921 670 The destructor provides a newline, if formatted text ends with a full line. 922 Figure~\ref{f:CFormatGenImpl} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@. 923 For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the \CFA format generator. 924 925 % https://dl-acm-org.proxy.lib.uwaterloo.ca/ 926 927 An important application for the asymmetric generator is a device-driver, because device drivers are a significant source of operating-system errors: 85\% in Windows XP~\cite[p.~78]{Swift05} and 51.6\% in Linux~\cite[p.~1358,]{Xiao19}. %\cite{Palix11} 928 Swift \etal~\cite[p.~86]{Swift05} restructure device drivers using the Extension Procedure Call (XPC) within the kernel via functions @nooks_driver_call@ and @nooks_kernel_call@, which have coroutine properties context switching to separate stacks with explicit hand-off calls; 929 however, the calls do not retain execution state, and hence always start from the top. 930 The alternative approach for implementing device drivers is using stack-ripping. 931 However, Adya \etal~\cite{Adya02} argue against stack ripping in Section 3.2 and suggest a hybrid approach in Section 4 using cooperatively scheduled \emph{fibers}, which is coroutining. 932 933 Figure~\ref{f:DeviceDriverGen} shows the generator advantages in implementing a simple network device-driver with the following protocol: 671 Figure~\ref{f:CFormatSim} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@. 672 For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the Fibonacci generator. 673 674 Figure~\ref{f:DeviceDriverGen} shows a \emph{killer} asymmetric generator, a device-driver, because device drivers caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}. 675 Device drives follow the pattern of simple data state but complex execution state, \ie finite state-machine (FSM) parsing a protocol. 676 For example, the following protocol: 934 677 \begin{center} 935 678 \ldots\, STX \ldots\, message \ldots\, ESC ETX \ldots\, message \ldots\, ETX 2-byte crc \ldots 936 679 \end{center} 937 where the network message begins with the control character STX, ends with an ETX, and is followed by a two-byte cyclic-redundancy check.680 is a network message beginning with the control character STX, ending with an ETX, and followed by a 2-byte cyclic-redundancy check. 938 681 Control characters may appear in a message if preceded by an ESC. 939 682 When a message byte arrives, it triggers an interrupt, and the operating system services the interrupt by calling the device driver with the byte read from a hardware register. 940 The device driver returns a status code of its current state, and when a complete message is obtained, the operating system reads the message accumulated in the supplied buffer. 941 Hence, the device driver is an input/output generator, where the cost of resuming the device-driver generator is the same as call and return, so performance in an operating-system kernel is excellent. 942 The key benefits of using a generator are correctness, safety, and maintenance because the execution states are transcribed directly into the programming language rather than table lookup or stack ripping. 943 % The conclusion is that FSMs are complex and occur in important domains, so direct generator support is important in a system programming language. 683 The device driver returns a status code of its current state, and when a complete message is obtained, the operating system knows the message is in the message buffer. 684 Hence, the device driver is an input/output generator. 685 686 Note, the cost of creating and resuming the device-driver generator, @Driver@, is virtually identical to call/return, so performance in an operating-system kernel is excellent. 687 As well, the data state is small, where variables @byte@ and @msg@ are communication variables for passing in message bytes and returning the message, and variables @lnth@, @crc@, and @sum@ are local variable that must be retained between calls and are manually hoisted into the generator type. 688 % Manually, detecting and hoisting local-state variables is easy when the number is small. 689 In contrast, the execution state is large, with one @resume@ and seven @suspend@s. 690 Hence, the key benefits of the generator are correctness, safety, and maintenance because the execution states are transcribed directly into the programming language rather than using a table-driven approach. 691 Because FSMs can be complex and frequently occur in important domains, direct generator support is important in a system programming language. 944 692 945 693 \begin{figure} 946 694 \centering 695 \newbox\myboxA 696 \begin{lrbox}{\myboxA} 697 \begin{python}[aboveskip=0pt,belowskip=0pt] 698 def Fib(): 699 fn1, fn = 0, 1 700 while True: 701 `yield fn1` 702 fn1, fn = fn, fn1 + fn 703 f1 = Fib() 704 f2 = Fib() 705 for i in range( 10 ): 706 print( next( f1 ), next( f2 ) ) 707 708 709 710 711 712 713 \end{python} 714 \end{lrbox} 715 716 \newbox\myboxB 717 \begin{lrbox}{\myboxB} 718 \begin{python}[aboveskip=0pt,belowskip=0pt] 719 def Fmt(): 720 try: 721 while True: 722 for g in range( 5 ): 723 for b in range( 4 ): 724 print( `(yield)`, end='' ) 725 print( ' ', end='' ) 726 print() 727 except GeneratorExit: 728 if g != 0 | b != 0: 729 print() 730 fmt = Fmt() 731 `next( fmt )` # prime, next prewritten 732 for i in range( 41 ): 733 `fmt.send( 'a' );` # send to yield 734 \end{python} 735 \end{lrbox} 736 \subfloat[Fibonacci]{\label{f:PythonFibonacci}\usebox\myboxA} 737 \hspace{3pt} 738 \vrule 739 \hspace{3pt} 740 \subfloat[Formatter]{\label{f:PythonFormatter}\usebox\myboxB} 741 \caption{Python generator} 742 \label{f:PythonGenerator} 743 744 \bigskip 745 947 746 \begin{tabular}{@{}l|l@{}} 948 747 \begin{cfa}[aboveskip=0pt,belowskip=0pt] … … 951 750 `generator` Driver { 952 751 Status status; 953 char byte, * msg; // communication954 int lnth, sum; // local state955 short int crc;752 unsigned char byte, * msg; // communication 753 unsigned int lnth, sum; // local state 754 unsigned short int crc; 956 755 }; 957 756 void ?{}( Driver & d, char * m ) { d.msg = m; } … … 998 797 \end{figure} 999 798 1000 Generators can also have symmetric activation using resume/resume to create control-flow cycles among generators.799 Figure~\ref{f:CFAPingPongGen} shows a symmetric generator, where the generator resumes another generator, forming a resume/resume cycle. 1001 800 (The trivial cycle is a generator resuming itself.) 1002 801 This control flow is similar to recursion for functions but without stack growth. 1003 Figure~\ref{f:PingPongFullCoroutineSteps} shows the steps for symmetric control-flow using for the ping/pong program in Figure~\ref{f:CFAPingPongGen}. 1004 The program starts by creating the generators, @ping@ and @pong@, and then assigns the partners that form the cycle. 802 The steps for symmetric control-flow are creating, executing, and terminating the cycle. 1005 803 Constructing the cycle must deal with definition-before-use to close the cycle, \ie, the first generator must know about the last generator, which is not within scope. 1006 804 (This issue occurs for any cyclic data structure.) 1007 % (Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle.) 1008 Once the cycle is formed, the program main resumes one of the generators, @ping@, and the generators can then traverse an arbitrary number of cycles using @resume@ to activate partner generator(s). 805 % The example creates all the generators and then assigns the partners that form the cycle. 806 % Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle. 807 Once the cycle is formed, the program main resumes one of the generators, and the generators can then traverse an arbitrary cycle using @resume@ to activate partner generator(s). 1009 808 Terminating the cycle is accomplished by @suspend@ or @return@, both of which go back to the stack frame that started the cycle (program main in the example). 1010 Note, the creator and starter may be different, \eg if the creator calls another function that starts the cycle.1011 809 The starting stack-frame is below the last active generator because the resume/resume cycle does not grow the stack. 1012 Also, since local variables are not retained in the generator function, there are no objects with destructors to be called, so the cost is the same as a function return. 1013 Destructor cost occurs when the generator instance is deallocated by the creator. 1014 1015 \begin{figure} 1016 \centering 1017 \input{FullCoroutinePhases.pstex_t} 1018 \vspace*{-10pt} 1019 \caption{Symmetric coroutine steps: Ping / Pong} 1020 \label{f:PingPongFullCoroutineSteps} 1021 \end{figure} 810 Also, since local variables are not retained in the generator function, it does not contain any objects with destructors that must be called, so the cost is the same as a function return. 811 Destructor cost occurs when the generator instance is deallocated, which is easily controlled by the programmer. 812 813 Figure~\ref{f:CPingPongSim} shows the implementation of the symmetric generator, where the complexity is the @resume@, which needs an extension to the calling convention to perform a forward rather than backward jump. 814 This jump-starts at the top of the next generator main to re-execute the normal calling convention to make space on the stack for its local variables. 815 However, before the jump, the caller must reset its stack (and any registers) equivalent to a @return@, but subsequently jump forward. 816 This semantics is basically a tail-call optimization, which compilers already perform. 817 The example shows the assembly code to undo the generator's entry code before the direct jump. 818 This assembly code depends on what entry code is generated, specifically if there are local variables and the level of optimization. 819 To provide this new calling convention requires a mechanism built into the compiler, which is beyond the scope of \CFA at this time. 820 Nevertheless, it is possible to hand generate any symmetric generators for proof of concept and performance testing. 821 A compiler could also eliminate other artifacts in the generator simulation to further increase performance, \eg LLVM has various coroutine support~\cite{CoroutineTS}, and \CFA can leverage this support should it fork @clang@. 1022 822 1023 823 \begin{figure} … … 1026 826 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1027 827 `generator PingPong` { 1028 int N, i; // local state1029 828 const char * name; 829 int N; 830 int i; // local state 1030 831 PingPong & partner; // rebindable reference 1031 832 }; 1032 833 1033 834 void `main( PingPong & pp )` with(pp) { 1034 1035 1036 835 for ( ; i < N; i += 1 ) { 1037 836 sout | name | i; … … 1051 850 \begin{cfa}[escapechar={},aboveskip=0pt,belowskip=0pt] 1052 851 typedef struct PingPong { 1053 int restart, N, i;1054 852 const char * name; 853 int N, i; 1055 854 struct PingPong * partner; 855 void * next; 1056 856 } PingPong; 1057 #define PPCtor(name, N) { 0, N, 0, name,NULL}857 #define PPCtor(name, N) {name,N,0,NULL,NULL} 1058 858 void comain( PingPong * pp ) { 1059 static void * states[] = {&&s0, &&s1}; 1060 goto *states[pp->restart]; 1061 s0: pp->restart = 1; 859 if ( pp->next ) goto *pp->next; 860 pp->next = &&cycle; 1062 861 for ( ; pp->i < pp->N; pp->i += 1 ) { 1063 862 printf( "%s %d\n", pp->name, pp->i ); 1064 863 asm( "mov %0,%%rdi" : "=m" (pp->partner) ); 1065 864 asm( "mov %rdi,%rax" ); 1066 asm( "add $16, %rsp" ); 1067 asm( "popq %rbp" ); 865 asm( "popq %rbx" ); 1068 866 asm( "jmp comain" ); 1069 s1: ;867 cycle: ; 1070 868 } 1071 869 } … … 1083 881 \end{figure} 1084 882 1085 Figure~\ref{f:CPingPongSim} shows the C implementation of the \CFA symmetric generator, where there is still only one additional field, @restart@, but @resume@ is more complex because it does a forward rather than backward jump. 1086 Before the jump, the parameter for the next call @partner@ is placed into the register used for the first parameter, @rdi@, and the remaining registers are reset for a return. 1087 The @jmp comain@ restarts the function but with a different parameter, so the new call's behavior depends on the state of the coroutine type, \ie branch to restart location with different data state. 1088 While the semantics of call forward is a tail-call optimization, which compilers perform, the generator state is different on each call rather a common state for a tail-recursive function (\ie the parameter to the function never changes during the forward calls). 1089 However, this assembler code depends on what entry code is generated, specifically if there are local variables and the level of optimization. 1090 Hence, internal compiler support is necessary for any forward call or backwards return, \eg LLVM has various coroutine support~\cite{CoroutineTS}, and \CFA can leverage this support should it eventually fork @clang@. 1091 For this reason, \CFA does not support general symmetric generators at this time, but, it is possible to hand generate any symmetric generators, as in Figure~\ref{f:CPingPongSim}, for proof of concept and performance testing. 1092 1093 Finally, part of this generator work was inspired by the recent \CCtwenty coroutine proposal~\cite{C++20Coroutine19}, which uses the general term coroutine to mean generator. 883 Finally, part of this generator work was inspired by the recent \CCtwenty generator proposal~\cite{C++20Coroutine19} (which they call coroutines). 1094 884 Our work provides the same high-performance asymmetric generators as \CCtwenty, and extends their work with symmetric generators. 1095 885 An additional \CCtwenty generator feature allows @suspend@ and @resume@ to be followed by a restricted compound statement that is executed after the current generator has reset its stack but before calling the next generator, specified with \CFA syntax: … … 1106 896 \label{s:Coroutine} 1107 897 1108 Stackful coroutines (Table~\ref{t:ExecutionPropertyComposition} case 5) extend generator semantics with an implicit closure and @suspend@ may appear in a helper function called from the coroutine main because of the separate stack. 1109 Note, simulating coroutines with stacks of generators, \eg Python with @yield from@ cannot handle symmetric control-flow. 1110 Furthermore, all stack components must be of generators, so it is impossible to call a library function passing a generator that yields. 1111 Creating a generator copy of the library function maybe impossible because the library function is opaque. 1112 1113 A \CFA coroutine is specified by replacing @generator@ with @coroutine@ for the type. 1114 Coroutine generality results in higher cost for creation, due to dynamic stack allocation, for execution, due to context switching among stacks, and for terminating, due to possible stack unwinding and dynamic stack deallocation. 898 Stackful coroutines extend generator semantics, \ie there is an implicit closure and @suspend@ may appear in a helper function called from the coroutine main. 899 A coroutine is specified by replacing @generator@ with @coroutine@ for the type. 900 Coroutine generality results in higher cost for creation, due to dynamic stack allocation, execution, due to context switching among stacks, and terminating, due to possible stack unwinding and dynamic stack deallocation. 1115 901 A series of different kinds of coroutines and their implementations demonstrate how coroutines extend generators. 1116 902 1117 903 First, the previous generator examples are converted to their coroutine counterparts, allowing local-state variables to be moved from the generator type into the coroutine main. 1118 Now the coroutine type only contains communication variables between interface functions and the coroutine main. 1119 \begin{center} 1120 \begin{tabular}{@{}l|l|l|l@{}} 1121 \multicolumn{1}{c|}{Fibonacci} & \multicolumn{1}{c|}{Formatter} & \multicolumn{1}{c|}{Device Driver} & \multicolumn{1}{c}{PingPong} \\ 1122 \hline 904 \begin{description} 905 \item[Fibonacci] 906 Move the declaration of @fn1@ to the start of coroutine main. 1123 907 \begin{cfa}[xleftmargin=0pt] 1124 void main( Fib & fib ) ...908 void main( Fib & fib ) with(fib) { 1125 909 `int fn1;` 1126 1127 1128 \end{cfa} 1129 & 910 \end{cfa} 911 \item[Formatter] 912 Move the declaration of @g@ and @b@ to the for loops in the coroutine main. 1130 913 \begin{cfa}[xleftmargin=0pt] 1131 914 for ( `g`; 5 ) { 1132 915 for ( `b`; 4 ) { 1133 1134 1135 \end{cfa} 1136 & 916 \end{cfa} 917 \item[Device Driver] 918 Move the declaration of @lnth@ and @sum@ to their points of initialization. 1137 919 \begin{cfa}[xleftmargin=0pt] 1138 status = CONT; 1139 `int lnth = 0, sum = 0;` 1140 ... 1141 `short int crc = byte << 8;` 1142 \end{cfa} 1143 & 920 status = CONT; 921 `unsigned int lnth = 0, sum = 0;` 922 ... 923 `unsigned short int crc = byte << 8;` 924 \end{cfa} 925 \item[PingPong] 926 Move the declaration of @i@ to the for loop in the coroutine main. 1144 927 \begin{cfa}[xleftmargin=0pt] 1145 void main( PingPong & pp ) ...928 void main( PingPong & pp ) with(pp) { 1146 929 for ( `i`; N ) { 1147 1148 1149 \end{cfa} 1150 \end{tabular} 1151 \end{center} 930 \end{cfa} 931 \end{description} 1152 932 It is also possible to refactor code containing local-state and @suspend@ statements into a helper function, like the computation of the CRC for the device driver. 1153 933 \begin{cfa} 1154 int Crc() { 1155 `suspend;` short int crc = byte << 8; 1156 `suspend;` status = (crc | byte) == sum ? MSG : ECRC; 934 unsigned int Crc() { 935 `suspend;` 936 unsigned short int crc = byte << 8; 937 `suspend;` 938 status = (crc | byte) == sum ? MSG : ECRC; 1157 939 return crc; 1158 940 } 1159 941 \end{cfa} 1160 A call to this function is placed at the end of the d evice driver's coroutine-main.1161 For complex FSMs, refactoring is part of normal program abstraction, especially when code is used in multiple places.942 A call to this function is placed at the end of the driver's coroutine-main. 943 For complex finite-state machines, refactoring is part of normal program abstraction, especially when code is used in multiple places. 1162 944 Again, this complexity is usually associated with execution state rather than data state. 1163 945 1164 946 \begin{comment} 1165 Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface functions, \eg @ restart@.1166 Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the overloadedcoroutine main.947 Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface functions, \eg @next@. 948 Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the (overloaded) coroutine main. 1167 949 The coroutine main's stack holds the state for the next generation, @f1@ and @f2@, and the code represents the three states in the Fibonacci formula via the three suspend points, to context switch back to the caller's @resume@. 1168 The interface function @ restart@, takes a Fibonacci instance and context switches to it using @resume@;950 The interface function @next@, takes a Fibonacci instance and context switches to it using @resume@; 1169 951 on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned. 1170 952 The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack; … … 1332 1114 \begin{figure} 1333 1115 \centering 1116 \lstset{language=CFA,escapechar={},moredelim=**[is][\protect\color{red}]{`}{`}}% allow $ 1334 1117 \begin{tabular}{@{}l@{\hspace{2\parindentlnth}}l@{}} 1335 1118 \begin{cfa} 1336 1119 `coroutine` Prod { 1337 Cons & c; $\C[1.5in]{// communication}$1120 Cons & c; // communication 1338 1121 int N, money, receipt; 1339 1122 }; 1340 1123 void main( Prod & prod ) with( prod ) { 1341 for ( i; N ) { $\C{// 1st resume}\CRT$ 1124 // 1st resume starts here 1125 for ( i; N ) { 1342 1126 int p1 = random( 100 ), p2 = random( 100 ); 1127 sout | p1 | " " | p2; 1343 1128 int status = delivery( c, p1, p2 ); 1129 sout | " $" | money | nl | status; 1344 1130 receipt += 1; 1345 1131 } 1346 1132 stop( c ); 1133 sout | "prod stops"; 1347 1134 } 1348 1135 int payment( Prod & prod, int money ) { … … 1365 1152 \begin{cfa} 1366 1153 `coroutine` Cons { 1367 Prod & p; $\C[1.5in]{// communication}$1154 Prod & p; // communication 1368 1155 int p1, p2, status; 1369 1156 bool done; 1370 1157 }; 1371 1158 void ?{}( Cons & cons, Prod & p ) { 1372 &cons.p = &p; $\C{// reassignable reference}$1159 &cons.p = &p; // reassignable reference 1373 1160 cons.[status, done ] = [0, false]; 1374 1161 } 1375 1162 void main( Cons & cons ) with( cons ) { 1376 int money = 1, receipt; $\C{// 1st resume}\CRT$ 1163 // 1st resume starts here 1164 int money = 1, receipt; 1377 1165 for ( ; ! done; ) { 1166 sout | p1 | " " | p2 | nl | " $" | money; 1378 1167 status += 1; 1379 1168 receipt = payment( p, money ); 1169 sout | " #" | receipt; 1380 1170 money += 1; 1381 1171 } 1172 sout | "cons stops"; 1382 1173 } 1383 1174 int delivery( Cons & cons, int p1, int p2 ) { … … 1398 1189 1399 1190 Figure~\ref{f:ProdCons} shows the ping-pong example in Figure~\ref{f:CFAPingPongGen} extended into a producer/consumer symmetric-coroutine performing bidirectional communication. 1400 This example is illustrative because both producer and consumer have two interface functions with @resume@s that suspend execution in these interfacefunctions.1191 This example is illustrative because both producer/consumer have two interface functions with @resume@s that suspend execution in these interface (helper) functions. 1401 1192 The program main creates the producer coroutine, passes it to the consumer coroutine in its initialization, and closes the cycle at the call to @start@ along with the number of items to be produced. 1402 The call to @start@ is the first @resume@ of @prod@, which remembers the program main as the starter and creates @prod@'s stack with a frame for @prod@'s coroutine main at the top, and context switches to it. 1403 @prod@'s coroutine main starts, creates local-state variables that are retained between coroutine activations, and executes $N$ iterations, each generating two random values, calling the consumer's @deliver@ function to transfer the values, and printing the status returned from the consumer. 1404 The producer's call to @delivery@ transfers values into the consumer's communication variables, resumes the consumer, and returns the consumer status. 1405 Similarly on the first resume, @cons@'s stack is created and initialized, holding local-state variables retained between subsequent activations of the coroutine. 1406 The symmetric coroutine cycle forms when the consumer calls the producer's @payment@ function, which resumes the producer in the consumer's delivery function. 1407 When the producer calls @delivery@ again, it resumes the consumer in the @payment@ function. 1408 Both interface functions then return to their corresponding coroutine-main functions for the next cycle. 1193 The first @resume@ of @prod@ creates @prod@'s stack with a frame for @prod@'s coroutine main at the top, and context switches to it. 1194 @prod@'s coroutine main starts, creates local-state variables that are retained between coroutine activations, and executes $N$ iterations, each generating two random values, calling the consumer to deliver the values, and printing the status returned from the consumer. 1195 1196 The producer call to @delivery@ transfers values into the consumer's communication variables, resumes the consumer, and returns the consumer status. 1197 On the first resume, @cons@'s stack is created and initialized, holding local-state variables retained between subsequent activations of the coroutine. 1198 The consumer iterates until the @done@ flag is set, prints the values delivered by the producer, increments status, and calls back to the producer via @payment@, and on return from @payment@, prints the receipt from the producer and increments @money@ (inflation). 1199 The call from the consumer to @payment@ introduces the cycle between producer and consumer. 1200 When @payment@ is called, the consumer copies values into the producer's communication variable and a resume is executed. 1201 The context switch restarts the producer at the point where it last context switched, so it continues in @delivery@ after the resume. 1202 @delivery@ returns the status value in @prod@'s coroutine main, where the status is printed. 1203 The loop then repeats calling @delivery@, where each call resumes the consumer coroutine. 1204 The context switch to the consumer continues in @payment@. 1205 The consumer increments and returns the receipt to the call in @cons@'s coroutine main. 1206 The loop then repeats calling @payment@, where each call resumes the producer coroutine. 1409 1207 Figure~\ref{f:ProdConsRuntimeStacks} shows the runtime stacks of the program main, and the coroutine mains for @prod@ and @cons@ during the cycling. 1410 As a consequence of a coroutine retaining its last resumer for suspending back, these reverse pointers allow @suspend@ to cycle \emph{backwards} around a symmetric coroutine cycle.1411 1208 1412 1209 \begin{figure} … … 1417 1214 \caption{Producer / consumer runtime stacks} 1418 1215 \label{f:ProdConsRuntimeStacks} 1216 1217 \medskip 1218 1219 \begin{center} 1220 \input{FullCoroutinePhases.pstex_t} 1221 \end{center} 1222 \vspace*{-10pt} 1223 \caption{Ping / Pong coroutine steps} 1224 \label{f:PingPongFullCoroutineSteps} 1419 1225 \end{figure} 1420 1226 1421 1227 Terminating a coroutine cycle is more complex than a generator cycle, because it requires context switching to the program main's \emph{stack} to shutdown the program, whereas generators started by the program main run on its stack. 1422 Furthermore, each deallocated coroutine must execute all destructors for objects allocated in the coroutine type \emph{and} allocated on the coroutine's stack at the point of suspension, which can be arbitrarily deep. 1423 In the example, termination begins with the producer's loop stopping after N iterations and calling the consumer's @stop@ function, which sets the @done@ flag, resumes the consumer in function @payment@, terminating the call, and the consumer's loop in its coroutine main. 1424 % (Not shown is having @prod@ raise a nonlocal @stop@ exception at @cons@ after it finishes generating values and suspend back to @cons@, which catches the @stop@ exception to terminate its loop.) 1425 When the consumer's main ends, its stack is already unwound so any stack allocated objects with destructors are finalized. 1426 The question now is where does control continue? 1427 1428 The na\"{i}ve semantics for coroutine-cycle termination is to context switch to the last resumer, like executing a @suspend@ or @return@ in a generator. 1228 Furthermore, each deallocated coroutine must guarantee all destructors are run for object allocated in the coroutine type \emph{and} allocated on the coroutine's stack at the point of suspension, which can be arbitrarily deep. 1229 When a coroutine's main ends, its stack is already unwound so any stack allocated objects with destructors have been finalized. 1230 The na\"{i}ve semantics for coroutine-cycle termination is to context switch to the last resumer, like executing a @suspend@/@return@ in a generator. 1429 1231 However, for coroutines, the last resumer is \emph{not} implicitly below the current stack frame, as for generators, because each coroutine's stack is independent. 1430 1232 Unfortunately, it is impossible to determine statically if a coroutine is in a cycle and unrealistic to check dynamically (graph-cycle problem). 1431 1233 Hence, a compromise solution is necessary that works for asymmetric (acyclic) and symmetric (cyclic) coroutines. 1432 Our solution is to retain a coroutine's starter (first resumer), and context switch back to the starter when the coroutine ends. 1433 Hence, the consumer restarts its first resumer, @prod@, in @stop@, and when the producer ends, it restarts its first resumer, program main, in @start@ (see dashed lines from the end of the coroutine mains in Figure~\ref{f:ProdConsRuntimeStacks}).1234 1235 Our solution is to context switch back to the first resumer (starter) once the coroutine ends. 1434 1236 This semantics works well for the most common asymmetric and symmetric coroutine usage patterns. 1435 For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer; 1436 for symmetric coroutines, it is common for the cycle creator to persist for the lifetime of the cycle. 1437 For other scenarios, it is always possible to devise a solution with additional programming effort, such as forcing the cycle forward or backward to a safe point before starting termination. 1438 1439 Note, the producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first. 1440 Assume generator @PingPong@ in Figure~\ref{f:PingPongSymmetricGenerator} is converted to a coroutine. 1441 Unlike generators, coroutines have a starter structure with multiple levels, where the program main starts @ping@ and @ping@ starts @pong@. 1442 By adjusting $N$ for either @ping@ or @pong@, it is possible to have either finish first. 1443 If @pong@ ends first, it resumes its starter @ping@ in its coroutine main, then @ping@ ends and resumes its starter the program main on return; 1444 if @ping@ ends first, it resumes its starter the program main on return. 1445 Regardless of the cycle complexity, the starter structure always leads back to the program main, but the path can be entered at an arbitrary point. 1446 Once back at the program main (creator), coroutines @ping@ and @pong@ are deallocated, running any destructors for objects within the coroutine and possibly deallocating any coroutine stacks for non-terminated coroutines, where stack deallocation implies stack unwinding to find destructors for allocated objects on the stack. 1447 Hence, the \CFA termination semantics for the generator and coroutine ensure correct deallocation semantics, regardless of the coroutine's state (terminated or active), like any other aggregate object. 1448 1449 1450 \subsection{Generator / coroutine implementation} 1451 1452 A significant implementation challenge for generators and coroutines (and threads in Section~\ref{s:threads}) is adding extra fields to the custom types and related functions, \eg inserting code after/before the coroutine constructor/destructor and @main@ to create/initialize/de-initialize/destroy any extra fields, \eg the coroutine stack. 1453 There are several solutions to this problem, which follow from the object-oriented flavor of adopting custom types. 1237 For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer. 1238 All previous generators converted to coroutines have this property. 1239 For symmetric coroutines, it is common for the cycle creator to persist for the lifetime of the cycle. 1240 Hence, the starter coroutine is remembered on the first resume and ending the coroutine resumes the starter. 1241 Figure~\ref{f:ProdConsRuntimeStacks} shows this semantic by the dashed lines from the end of the coroutine mains: @prod@ starts @cons@ so @cons@ resumes @prod@ at the end, and the program main starts @prod@ so @prod@ resumes the program main at the end. 1242 For other scenarios, it is always possible to devise a solution with additional programming effort, such as forcing the cycle forward (backward) to a safe point before starting termination. 1243 1244 The producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first. 1245 Assume generator @PingPong@ is converted to a coroutine. 1246 Figure~\ref{f:PingPongFullCoroutineSteps} shows the creation, starter, and cyclic execution steps of the coroutine version. 1247 The program main creates (declares) coroutine instances @ping@ and @pong@. 1248 Next, program main resumes @ping@, making it @ping@'s starter, and @ping@'s main resumes @pong@'s main, making it @pong@'s starter. 1249 Execution forms a cycle when @pong@ resumes @ping@, and cycles $N$ times. 1250 By adjusting $N$ for either @ping@/@pong@, it is possible to have either one finish first, instead of @pong@ always ending first. 1251 If @pong@ ends first, it resumes its starter @ping@ in its coroutine main, then @ping@ ends and resumes its starter the program main in function @start@. 1252 If @ping@ ends first, it resumes its starter the program main in function @start@. 1253 Regardless of the cycle complexity, the starter stack always leads back to the program main, but the stack can be entered at an arbitrary point. 1254 Once back at the program main, coroutines @ping@ and @pong@ are deallocated. 1255 For generators, deallocation runs the destructors for all objects in the generator type. 1256 For coroutines, deallocation deals with objects in the coroutine type and must also run the destructors for any objects pending on the coroutine's stack for any unterminated coroutine. 1257 Hence, if a coroutine's destructor detects the coroutine is not ended, it implicitly raises a cancellation exception (uncatchable exception) at the coroutine and resumes it so the cancellation exception can propagate to the root of the coroutine's stack destroying all local variable on the stack. 1258 So the \CFA semantics for the generator and coroutine, ensure both can be safely deallocated at any time, regardless of their current state, like any other aggregate object. 1259 Explicitly raising normal exceptions at another coroutine can replace flag variables, like @stop@, \eg @prod@ raises a @stop@ exception at @cons@ after it finishes generating values and resumes @cons@, which catches the @stop@ exception to terminate its loop. 1260 1261 Finally, there is an interesting effect for @suspend@ with symmetric coroutines. 1262 A coroutine must retain its last resumer to suspend back because the resumer is on a different stack. 1263 These reverse pointers allow @suspend@ to cycle \emph{backwards}, which may be useful in certain cases. 1264 However, there is an anomaly if a coroutine resumes itself, because it overwrites its last resumer with itself, losing the ability to resume the last external resumer. 1265 To prevent losing this information, a self-resume does not overwrite the last resumer. 1266 1267 1268 \subsection{Generator / Coroutine Implementation} 1269 1270 A significant implementation challenge for generators/coroutines (and threads in Section~\ref{s:threads}) is adding extra fields to the custom types and related functions, \eg inserting code after/before the coroutine constructor/destructor and @main@ to create/initialize/de-initialize/destroy any extra fields, \eg stack. 1271 There are several solutions to these problem, which follow from the object-oriented flavour of adopting custom types. 1454 1272 1455 1273 For object-oriented languages, inheritance is used to provide extra fields and code via explicit inheritance: … … 1458 1276 \end{cfa} 1459 1277 % The problem is that the programming language and its tool chain, \eg debugger, @valgrind@, need to understand @baseCoroutine@ because it infers special property, so type @baseCoroutine@ becomes a de facto keyword and all types inheriting from it are implicitly custom types. 1460 The problem is that some special properties are not handled by existing language semantics, \eg the execution of constructors anddestructors is in the wrong order to implicitly start threads because the thread must start \emph{after} all constructors as it relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived.1278 The problem is that some special properties are not handled by existing language semantics, \eg the execution of constructors/destructors is in the wrong order to implicitly start threads because the thread must start \emph{after} all constructors as it relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived. 1461 1279 Alternatives, such as explicitly starting threads as in Java, are repetitive and forgetting to call start is a common source of errors. 1462 1280 An alternative is composition: … … 1476 1294 Users wanting to extend custom types or build their own can only do so in ways offered by the language. 1477 1295 Furthermore, implementing custom types without language support may display the power of a programming language. 1478 \CFA blends the two approaches, providing custom type for idiomatic \CFA code, while extending and building new custom types is still possible, similar to Java concurrency with builtin and library (@java.util.concurrent@) monitors.1296 \CFA blends the two approaches, providing custom type for idiomatic \CFA code, while extending and building new custom types is still possible, similar to Java concurrency with builtin and library. 1479 1297 1480 1298 Part of the mechanism to generalize custom types is the \CFA trait~\cite[\S~2.3]{Moss18}, \eg the definition for custom-type @coroutine@ is anything satisfying the trait @is_coroutine@, and this trait both enforces and restricts the coroutine-interface functions. … … 1486 1304 forall( `dtype` T | is_coroutine(T) ) void $suspend$( T & ), resume( T & ); 1487 1305 \end{cfa} 1488 Note, copying generators, coroutines, and threads is undefined because multiple objects cannot execute on a shared stack and stack copying does not work in unmanaged languages (no garbage collection), like C, because the stack may contain pointers to objects within it that require updating for the copy. 1489 The \CFA @dtype@ property provides no \emph{implicit} copying operations and the @is_coroutine@ trait provides no \emph{explicit} copying operations, so all coroutines must be passed by reference or pointer. 1490 The function definitions ensure there is a statically typed @main@ function that is the starting point (first stack frame) of a coroutine, and a mechanism to read the coroutine descriptor from its handle. 1491 The @main@ function has no return value or additional parameters because the coroutine type allows an arbitrary number of interface functions with arbitrary typed input and output values versus fixed ones. 1306 Note, copying generators/coroutines/threads is not meaningful. 1307 For example, both the resumer and suspender descriptors can have bidirectional pointers; 1308 copying these coroutines does not update the internal pointers so behaviour of both copies would be difficult to understand. 1309 Furthermore, two coroutines cannot logically execute on the same stack. 1310 A deep coroutine copy, which copies the stack, is also meaningless in an unmanaged language (no garbage collection), like C, because the stack may contain pointers to object within it that require updating for the copy. 1311 The \CFA @dtype@ property provides no \emph{implicit} copying operations and the @is_coroutine@ trait provides no \emph{explicit} copying operations, so all coroutines must be passed by reference (pointer). 1312 The function definitions ensure there is a statically typed @main@ function that is the starting point (first stack frame) of a coroutine, and a mechanism to get (read) the coroutine descriptor from its handle. 1313 The @main@ function has no return value or additional parameters because the coroutine type allows an arbitrary number of interface functions with corresponding arbitrary typed input/output values versus fixed ones. 1492 1314 The advantage of this approach is that users can easily create different types of coroutines, \eg changing the memory layout of a coroutine is trivial when implementing the @get_coroutine@ function, and possibly redefining \textsf{suspend} and @resume@. 1493 1315 … … 1530 1352 The combination of custom types and fundamental @trait@ description of these types allows a concise specification for programmers and tools, while more advanced programmers can have tighter control over memory layout and initialization. 1531 1353 1532 Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a t hreadis similar).1533 The coroutine handle is the @coroutine@ instance containing programmer specified type global andcommunication variables across interface functions.1354 Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a task is similar). 1355 The coroutine handle is the @coroutine@ instance containing programmer specified type global/communication variables across interface functions. 1534 1356 The coroutine descriptor contains all implicit declarations needed by the runtime, \eg @suspend@/@resume@, and can be part of the coroutine handle or separate. 1535 1357 The coroutine stack can appear in a number of locations and be fixed or variable sized. 1536 Hence, the coroutine's stack could be a variable-length structure (VLS) 1537 % \footnote{ 1538 % We are examining VLSs, where fields can be variable-sized structures or arrays. 1539 % Once allocated, a VLS is fixed sized.} 1358 Hence, the coroutine's stack could be a VLS\footnote{ 1359 We are examining variable-sized structures (VLS), where fields can be variable-sized structures or arrays. 1360 Once allocated, a VLS is fixed sized.} 1540 1361 on the allocating stack, provided the allocating stack is large enough. 1541 For a VLS stack allocation and deallocation is an inexpensive adjustment of the stack pointer, modulo any stack constructor costs to initial frame setup.1542 For stack allocation in the heap, allocation and deallocation is an expensive allocation, where the heap can be a shared resource, modulo any stack constructor costs.1543 It is also possible to use a split or segmented stack calling convention, available with gcc and clang, allowing a variable-sized stack via a set of connected blocks in the heap.1544 Currently, \CFA supports stack andheap allocated descriptors but only fixed-sized heap allocated stacks.1362 For a VLS stack allocation/deallocation is an inexpensive adjustment of the stack pointer, modulo any stack constructor costs (\eg initial frame setup). 1363 For heap stack allocation, allocation/deallocation is an expensive heap allocation (where the heap can be a shared resource), modulo any stack constructor costs. 1364 With heap stack allocation, it is also possible to use a split (segmented) stack calling convention, available with gcc and clang, so the stack is variable sized. 1365 Currently, \CFA supports stack/heap allocated descriptors but only fixed-sized heap allocated stacks. 1545 1366 In \CFA debug-mode, the fixed-sized stack is terminated with a write-only page, which catches most stack overflows. 1546 1367 Experience teaching concurrency with \uC~\cite{CS343} shows fixed-sized stacks are rarely an issue for students. 1547 Split-stack allocation is under development but requires recompilation of legacy code, which is not alwayspossible.1368 Split-stack allocation is under development but requires recompilation of legacy code, which may be impossible. 1548 1369 1549 1370 \begin{figure} … … 1559 1380 1560 1381 Concurrency is nondeterministic scheduling of independent sequential execution paths (threads), where each thread has its own stack. 1561 A single thread with multiple stacks, \ie coroutining, does \emph{not} imply concurrency~\cite[\S~3]{Buhr05a}.1562 Coroutiningself-schedule the thread across stacks so execution is deterministic.1382 A single thread with multiple call stacks, \newterm{coroutining}~\cite{Conway63,Marlin80}, does \emph{not} imply concurrency~\cite[\S~2]{Buhr05a}. 1383 In coroutining, coroutines self-schedule the thread across stacks so execution is deterministic. 1563 1384 (It is \emph{impossible} to generate a concurrency error when coroutining.) 1564 1565 The transition to concurrency, even for a single thread with multiple stacks, occurs when coroutines context switch to a \newterm{scheduling coroutine}, introducing non-determinism from the coroutine perspective~\cite[\S~3]{Buhr05a}. 1385 However, coroutines are a stepping stone towards concurrency. 1386 1387 The transition to concurrency, even for a single thread with multiple stacks, occurs when coroutines context switch to a \newterm{scheduling coroutine}, introducing non-determinism from the coroutine perspective~\cite[\S~3,]{Buhr05a}. 1566 1388 Therefore, a minimal concurrency system requires coroutines \emph{in conjunction with a nondeterministic scheduler}. 1567 The resulting execution system now follows a cooperative threading-model~\cite{Adya02,libdill} because context-switching points to the scheduler are known, but the next unblocking point is unknown due to the scheduler. 1568 Adding \newterm{preemption} introduces \newterm{non-cooperative} or \newterm{preemptive} scheduling, where context switching points to the scheduler are unknown as they can occur randomly between any two instructions often based on a timer interrupt. 1389 The resulting execution system now follows a cooperative threading model~\cite{Adya02,libdill}, called \newterm{non-preemptive scheduling}. 1390 Adding \newterm{preemption} introduces non-cooperative scheduling, where context switching occurs randomly between any two instructions often based on a timer interrupt, called \newterm{preemptive scheduling}. 1391 While a scheduler introduces uncertain execution among explicit context switches, preemption introduces uncertainty by introducing implicit context switches. 1569 1392 Uncertainty gives the illusion of parallelism on a single processor and provides a mechanism to access and increase performance on multiple processors. 1570 The reason is that the scheduler andruntime have complete knowledge about resources and how to best utilized them.1571 However, the introduction of unrestricted nondeterminism results in the need for \newterm{mutual exclusion} and \newterm{synchronization} ~\cite[\S~4]{Buhr05a}, which restrict nondeterminism for correctness;1393 The reason is that the scheduler/runtime have complete knowledge about resources and how to best utilized them. 1394 However, the introduction of unrestricted nondeterminism results in the need for \newterm{mutual exclusion} and \newterm{synchronization}, which restrict nondeterminism for correctness; 1572 1395 otherwise, it is impossible to write meaningful concurrent programs. 1573 1396 Optimal concurrent performance is often obtained by having as much nondeterminism as mutual exclusion and synchronization correctness allow. 1574 1397 1575 A scheduler can also bestackless or stackful.1398 A scheduler can either be a stackless or stackful. 1576 1399 For stackless, the scheduler performs scheduling on the stack of the current coroutine and switches directly to the next coroutine, so there is one context switch. 1577 1400 For stackful, the current coroutine switches to the scheduler, which performs scheduling, and it then switches to the next coroutine, so there are two context switches. … … 1582 1405 \label{s:threads} 1583 1406 1584 Threading (Table~\ref{t:ExecutionPropertyComposition} case 11) needs the ability to start a thread and wait for its completion, where a common API is @fork@ and @join@. 1585 \vspace{4pt} 1586 \par\noindent 1587 \begin{tabular}{@{}l|l|l@{}} 1588 \multicolumn{1}{c|}{\textbf{Java}} & \multicolumn{1}{c|}{\textbf{\Celeven}} & \multicolumn{1}{c}{\textbf{pthreads}} \\ 1589 \hline 1590 \begin{cfa} 1591 class MyThread extends Thread {...} 1592 mythread t = new MyThread(...); 1407 Threading needs the ability to start a thread and wait for its completion. 1408 A common API for this ability is @fork@ and @join@. 1409 \begin{cquote} 1410 \begin{tabular}{@{}lll@{}} 1411 \multicolumn{1}{c}{\textbf{Java}} & \multicolumn{1}{c}{\textbf{\Celeven}} & \multicolumn{1}{c}{\textbf{pthreads}} \\ 1412 \begin{cfa} 1413 class MyTask extends Thread {...} 1414 mytask t = new MyTask(...); 1593 1415 `t.start();` // start 1594 1416 // concurrency … … 1597 1419 & 1598 1420 \begin{cfa} 1599 class MyT hread{ ... } // functor1600 MyT hread mythread;1601 `thread t( myt hread, ... );` // start1421 class MyTask { ... } // functor 1422 MyTask mytask; 1423 `thread t( mytask, ... );` // start 1602 1424 // concurrency 1603 1425 `t.join();` // wait … … 1612 1434 \end{cfa} 1613 1435 \end{tabular} 1614 \vspace{1pt} 1615 \par\noindent 1616 \CFA has a simpler approach using a custom @thread@ type and leveraging declaration semantics, allocation and deallocation, where threads implicitly @fork@ after construction and @join@ before destruction. 1617 \begin{cfa} 1618 thread MyThread {}; 1619 void main( MyThread & this ) { ... } 1436 \end{cquote} 1437 \CFA has a simpler approach using a custom @thread@ type and leveraging declaration semantics (allocation/deallocation), where threads implicitly @fork@ after construction and @join@ before destruction. 1438 \begin{cfa} 1439 thread MyTask {}; 1440 void main( MyTask & this ) { ... } 1620 1441 int main() { 1621 MyT hreadteam`[10]`; $\C[2.5in]{// allocate stack-based threads, implicit start after construction}$1442 MyTask team`[10]`; $\C[2.5in]{// allocate stack-based threads, implicit start after construction}$ 1622 1443 // concurrency 1623 1444 } $\C{// deallocate stack-based threads, implicit joins before destruction}$ 1624 1445 \end{cfa} 1625 This semantic ensures a thread is started and stopped exactly once, eliminating some programming error, and scales to multiple threads for basic terminationsynchronization.1626 For block allocation to arbitrary depth, including recursion, threads are created anddestroyed in a lattice structure (tree with top and bottom).1446 This semantic ensures a thread is started and stopped exactly once, eliminating some programming error, and scales to multiple threads for basic (termination) synchronization. 1447 For block allocation to arbitrary depth, including recursion, threads are created/destroyed in a lattice structure (tree with top and bottom). 1627 1448 Arbitrary topologies are possible using dynamic allocation, allowing threads to outlive their declaration scope, identical to normal dynamic allocation. 1628 1449 \begin{cfa} 1629 MyT hread* factory( int N ) { ... return `anew( N )`; } $\C{// allocate heap-based threads, implicit start after construction}$1450 MyTask * factory( int N ) { ... return `anew( N )`; } $\C{// allocate heap-based threads, implicit start after construction}$ 1630 1451 int main() { 1631 MyT hread* team = factory( 10 );1452 MyTask * team = factory( 10 ); 1632 1453 // concurrency 1633 ` adelete( team );` $\C{// deallocate heap-based threads, implicit joins before destruction}\CRT$1454 `delete( team );` $\C{// deallocate heap-based threads, implicit joins before destruction}\CRT$ 1634 1455 } 1635 1456 \end{cfa} … … 1672 1493 1673 1494 1674 \subsection{Thread implementation}1495 \subsection{Thread Implementation} 1675 1496 1676 1497 Threads in \CFA are user level run by runtime kernel threads (see Section~\ref{s:CFARuntimeStructure}), where user threads provide concurrency and kernel threads provide parallelism. 1677 Like coroutines, and for the same design reasons, \CFA provides a custom @thread@ type and a @trait@ to enforce and restrict the t hread-interface functions.1498 Like coroutines, and for the same design reasons, \CFA provides a custom @thread@ type and a @trait@ to enforce and restrict the task-interface functions. 1678 1499 \begin{cquote} 1679 1500 \begin{tabular}{@{}c@{\hspace{3\parindentlnth}}c@{}} … … 1695 1516 \end{tabular} 1696 1517 \end{cquote} 1697 Like coroutines, the @dtype@ property prevents \emph{implicit} copy operations and the @is_thread@ trait provides no \emph{explicit} copy operations, so threads must be passed by reference or pointer.1698 Similarly, the function definitions ensure there is a statically typed @main@ function that is the thread starting point (first stack frame), a mechanism to readthe thread descriptor from its handle, and a special destructor to prevent deallocation while the thread is executing.1518 Like coroutines, the @dtype@ property prevents \emph{implicit} copy operations and the @is_thread@ trait provides no \emph{explicit} copy operations, so threads must be passed by reference (pointer). 1519 Similarly, the function definitions ensure there is a statically typed @main@ function that is the thread starting point (first stack frame), a mechanism to get (read) the thread descriptor from its handle, and a special destructor to prevent deallocation while the thread is executing. 1699 1520 (The qualifier @mutex@ for the destructor parameter is discussed in Section~\ref{s:Monitor}.) 1700 1521 The difference between the coroutine and thread is that a coroutine borrows a thread from its caller, so the first thread resuming a coroutine creates the coroutine's stack and starts running the coroutine main on the stack; 1701 1522 whereas, a thread is scheduling for execution in @main@ immediately after its constructor is run. 1702 No return value or additional parameters are necessary for this function because the @thread@ type allows an arbitrary number of interface functions with corresponding arbitrary typed input andoutput values.1523 No return value or additional parameters are necessary for this function because the @thread@ type allows an arbitrary number of interface functions with corresponding arbitrary typed input/output values. 1703 1524 1704 1525 … … 1706 1527 \label{s:MutualExclusionSynchronization} 1707 1528 1708 Unrestricted nondeterminism is meaningless as there is no way to know when a result is completed and safe to access.1529 Unrestricted nondeterminism is meaningless as there is no way to know when the result is completed without synchronization. 1709 1530 To produce meaningful execution requires clawing back some determinism using mutual exclusion and synchronization, where mutual exclusion provides access control for threads using shared data, and synchronization is a timing relationship among threads~\cite[\S~4]{Buhr05a}. 1710 The shared data protected by mutual exclusion is called a \newterm{critical section}~\cite{Dijkstra65}, and the protection can be simple, only 1 thread, or complex, only N kinds of threads, \eg group~\cite{Joung00} or readers/writer~\cite{Courtois71} problems. 1711 Without synchronization control in a critical section, an arriving thread can barge ahead of preexisting waiter threads resulting in short/long-term starvation, staleness and freshness problems, and incorrect transfer of data. 1712 Preventing or detecting barging is a challenge with low-level locks, but made easier through higher-level constructs. 1713 This challenge is often split into two different approaches: barging \emph{avoidance} and \emph{prevention}. 1714 Approaches that unconditionally releasing a lock for competing threads to acquire must use barging avoidance with flag/counter variable(s) to force barging threads to wait; 1715 approaches that conditionally hold locks during synchronization, \eg baton-passing~\cite{Andrews89}, prevent barging completely. 1716 1717 At the lowest level, concurrent control is provided by atomic operations, upon which different kinds of locking mechanisms are constructed, \eg spin locks, semaphores~\cite{Dijkstra68b}, barriers, and path expressions~\cite{Campbell74}. 1531 Some concurrent systems eliminate mutable shared-state by switching to stateless communication like message passing~\cite{Thoth,Harmony,V-Kernel,MPI} (Erlang, MPI), channels~\cite{CSP} (CSP,Go), actors~\cite{Akka} (Akka, Scala), or functional techniques (Haskell). 1532 However, these approaches introduce a new communication mechanism for concurrency different from the standard communication using function call/return. 1533 Hence, a programmer must learn and manipulate two sets of design/programming patterns. 1534 While this distinction can be hidden away in library code, effective use of the library still has to take both paradigms into account. 1535 In contrast, approaches based on stateful models more closely resemble the standard call/return programming model, resulting in a single programming paradigm. 1536 1537 At the lowest level, concurrent control is implemented by atomic operations, upon which different kinds of locking mechanisms are constructed, \eg semaphores~\cite{Dijkstra68b}, barriers, and path expressions~\cite{Campbell74}. 1718 1538 However, for productivity it is always desirable to use the highest-level construct that provides the necessary efficiency~\cite{Hochstein05}. 1719 A significant challenge with locks is composability because it takes careful organization for multiple locks to be used while preventing deadlock. 1539 A newer approach for restricting non-determinism is transactional memory~\cite{Herlihy93}. 1540 While this approach is pursued in hardware~\cite{Nakaike15} and system languages, like \CC~\cite{Cpp-Transactions}, the performance and feature set is still too restrictive to be the main concurrency paradigm for system languages, which is why it is rejected as the core paradigm for concurrency in \CFA. 1541 1542 One of the most natural, elegant, and efficient mechanisms for mutual exclusion and synchronization for shared-memory systems is the \emph{monitor}. 1543 First proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}, many concurrent programming languages provide monitors as an explicit language construct: \eg Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}. 1544 In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as mutex locks or semaphores to simulate monitors. 1545 For these reasons, \CFA selected monitors as the core high-level concurrency construct, upon which higher-level approaches can be easily constructed. 1546 1547 1548 \subsection{Mutual Exclusion} 1549 1550 A group of instructions manipulating a specific instance of shared data that must be performed atomically is called a \newterm{critical section}~\cite{Dijkstra65}, which is enforced by \newterm{simple mutual-exclusion}. 1551 The generalization is called a \newterm{group critical-section}~\cite{Joung00}, where multiple tasks with the same session use the resource simultaneously and different sessions are segregated, which is enforced by \newterm{complex mutual-exclusion} providing the correct kind and number of threads using a group critical-section. 1552 The readers/writer problem~\cite{Courtois71} is an instance of a group critical-section, where readers share a session but writers have a unique session. 1553 1554 However, many solutions exist for mutual exclusion, which vary in terms of performance, flexibility and ease of use. 1555 Methods range from low-level locks, which are fast and flexible but require significant attention for correctness, to higher-level concurrency techniques, which sacrifice some performance to improve ease of use. 1556 Ease of use comes by either guaranteeing some problems cannot occur, \eg deadlock free, or by offering a more explicit coupling between shared data and critical section. 1557 For example, the \CC @std::atomic<T>@ offers an easy way to express mutual-exclusion on a restricted set of operations, \eg reading/writing, for numerical types. 1558 However, a significant challenge with locks is composability because it takes careful organization for multiple locks to be used while preventing deadlock. 1720 1559 Easing composability is another feature higher-level mutual-exclusion mechanisms can offer. 1721 Some concurrent systems eliminate mutable shared-state by switching to non-shared communication like message passing~\cite{Thoth,Harmony,V-Kernel,MPI} (Erlang, MPI), channels~\cite{CSP} (CSP,Go), actors~\cite{Akka} (Akka, Scala), or functional techniques (Haskell). 1722 However, these approaches introduce a new communication mechanism for concurrency different from the standard communication using function call/return. 1723 Hence, a programmer must learn and manipulate two sets of design and programming patterns. 1724 While this distinction can be hidden away in library code, effective use of the library still has to take both paradigms into account. 1725 In contrast, approaches based on shared-state models more closely resemble the standard call and return programming model, resulting in a single programming paradigm. 1726 Finally, a newer approach for restricting non-determinism is transactional memory~\cite{Herlihy93}. 1727 While this approach is pursued in hardware~\cite{Nakaike15} and system languages, like \CC~\cite{Cpp-Transactions}, the performance and feature set is still too restrictive~\cite{Cascaval08,Boehm09} to be the main concurrency paradigm for system languages. 1560 1561 1562 \subsection{Synchronization} 1563 1564 Synchronization enforces relative ordering of execution, and synchronization tools provide numerous mechanisms to establish these timing relationships. 1565 Low-level synchronization primitives offer good performance and flexibility at the cost of ease of use; 1566 higher-level mechanisms often simplify usage by adding better coupling between synchronization and data, \eg receive-specific versus receive-any thread in message passing or offering specialized solutions, \eg barrier lock. 1567 Often synchronization is used to order access to a critical section, \eg ensuring a waiting writer thread enters the critical section before a calling reader thread. 1568 If the calling reader is scheduled before the waiting writer, the reader has barged. 1569 Barging can result in staleness/freshness problems, where a reader barges ahead of a writer and reads temporally stale data, or a writer barges ahead of another writer overwriting data with a fresh value preventing the previous value from ever being read (lost computation). 1570 Preventing or detecting barging is an involved challenge with low-level locks, which is made easier through higher-level constructs. 1571 This challenge is often split into two different approaches: barging avoidance and prevention. 1572 Algorithms that unconditionally releasing a lock for competing threads to acquire use barging avoidance during synchronization to force a barging thread to wait; 1573 algorithms that conditionally hold locks during synchronization, \eg baton-passing~\cite{Andrews89}, prevent barging completely. 1728 1574 1729 1575 … … 1731 1577 \label{s:Monitor} 1732 1578 1733 One of the most natural, elegant, efficient, high-level mechanisms for mutual exclusion and synchronization for shared-memory systems is the \emph{monitor} (Table~\ref{t:ExecutionPropertyComposition} case 2). 1734 First proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}, many concurrent programming languages provide monitors as an explicit language construct: \eg Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}. 1735 In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as mutex locks or semaphores to manually implement a monitor. 1736 For these reasons, \CFA selected monitors as the core high-level concurrency construct, upon which higher-level approaches can be easily constructed. 1737 1738 Figure~\ref{f:AtomicCounter} compares a \CFA and Java monitor implementing an atomic counter. 1739 (Like other concurrent programming languages, \CFA and Java have performant specializations for the basic types using atomic instructions.) 1740 A \newterm{monitor} is a set of functions that ensure mutual exclusion when accessing shared state. 1741 (Note, in \CFA, @monitor@ is short-hand for @mutex struct@.) 1742 More precisely, a monitor is a programming technique that implicitly binds mutual exclusion to static function scope by call and return, as opposed to locks, where mutual exclusion is defined by acquire/release calls, independent of lexical context (analogous to block and heap storage allocation). 1743 Restricting acquire and release points eases programming, comprehension, and maintenance, at a slight cost in flexibility and efficiency. 1744 As for other special types, \CFA has a custom @monitor@ type. 1745 1746 \begin{figure} 1747 \centering 1748 1749 \begin{lrbox}{\myboxA} 1750 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1751 `monitor` Aint { // atomic integer counter 1752 int cnt; 1753 }; 1754 int ++?( Aint & `mutex` this ) with(this) { return ++cnt; } 1755 int ?=?( Aint & `mutex` lhs, int rhs ) with(lhs) { cnt = rhs; } 1756 int ?=?(int & lhs, Aint & rhs) with(rhs) { lhs = cnt; } 1757 1579 A \textbf{monitor} is a set of functions that ensure mutual exclusion when accessing shared state. 1580 More precisely, a monitor is a programming technique that implicitly binds mutual exclusion to static function scope, as opposed to locks, where mutual-exclusion is defined by acquire/release calls, independent of lexical context (analogous to block and heap storage allocation). 1581 Restricting acquire/release points eases programming, comprehension, and maintenance, at a slight cost in flexibility and efficiency. 1582 \CFA uses a custom @monitor@ type and leverages declaration semantics (deallocation) to protect active or waiting threads in a monitor. 1583 1584 The following is a \CFA monitor implementation of an atomic counter. 1585 \begin{cfa}[morekeywords=nomutex] 1586 `monitor` Aint { int cnt; }; $\C[4.25in]{// atomic integer counter}$ 1587 int ++?( Aint & `mutex`$\(_{opt}\)$ this ) with( this ) { return ++cnt; } $\C{// increment}$ 1588 int ?=?( Aint & `mutex`$\(_{opt}\)$ lhs, int rhs ) with( lhs ) { cnt = rhs; } $\C{// conversions with int}\CRT$ 1589 int ?=?( int & lhs, Aint & `mutex`$\(_{opt}\)$ rhs ) with( rhs ) { lhs = cnt; } 1590 \end{cfa} 1591 % The @Aint@ constructor, @?{}@, uses the \lstinline[morekeywords=nomutex]@nomutex@ qualifier indicating mutual exclusion is unnecessary during construction because an object is inaccessible (private) until after it is initialized. 1592 % (While a constructor may publish its address into a global variable, doing so generates a race-condition.) 1593 The prefix increment operation, @++?@, is normally @mutex@, indicating mutual exclusion is necessary during function execution, to protect the incrementing from race conditions, unless there is an atomic increment instruction for the implementation type. 1594 The assignment operators provide bidirectional conversion between an atomic and normal integer without accessing field @cnt@; 1595 these operations only need @mutex@, if reading/writing the implementation type is not atomic. 1596 The atomic counter is used without any explicit mutual-exclusion and provides thread-safe semantics, which is similar to the \CC template @std::atomic@. 1597 \begin{cfa} 1758 1598 int i = 0, j = 0, k = 5; 1759 Aint x = { 0 }, y = { 0 }, z = { 5 }; // no mutex 1760 ++x; ++y; ++z; // mutex 1761 x = 2; y = i; z = k; // mutex 1762 i = x; j = y; k = z; // no mutex 1763 \end{cfa} 1764 \end{lrbox} 1765 1766 \begin{lrbox}{\myboxB} 1767 \begin{java}[aboveskip=0pt,belowskip=0pt] 1768 class Aint { 1769 private int cnt; 1770 public Aint( int init ) { cnt = init; } 1771 `synchronized` public int inc() { return ++cnt; } 1772 `synchronized` public void set( int rhs ) {cnt=rhs;} 1773 public int get() { return cnt; } 1774 } 1775 int i = 0, j = 0, k = 5; 1776 Aint x=new Aint(0), y=new Aint(0), z=new Aint(5); 1777 x.inc(); y.inc(); z.inc(); 1778 x.set( 2 ); y.set( i ); z.set( k ); 1779 i = x.get(); j = y.get(); k = z.get(); 1780 \end{java} 1781 \end{lrbox} 1782 1783 \subfloat[\CFA]{\label{f:AtomicCounterCFA}\usebox\myboxA} 1784 \hspace{3pt} 1785 \vrule 1786 \hspace{3pt} 1787 \subfloat[Java]{\label{f:AtomicCounterJava}\usebox\myboxB} 1788 \caption{Atomic counter} 1789 \label{f:AtomicCounter} 1790 \end{figure} 1791 1792 Like Java, \CFA monitors have \newterm{multi-acquire} semantics so the thread in the monitor may acquire it multiple times without deadlock, allowing recursion and calling other interface functions. 1793 % \begin{cfa} 1794 % monitor M { ... } m; 1795 % void foo( M & mutex m ) { ... } $\C{// acquire mutual exclusion}$ 1796 % void bar( M & mutex m ) { $\C{// acquire mutual exclusion}$ 1797 % ... `bar( m );` ... `foo( m );` ... $\C{// reacquire mutual exclusion}$ 1798 % } 1799 % \end{cfa} 1800 \CFA monitors also ensure the monitor lock is released regardless of how an acquiring function ends, normal or exceptional, and returning a shared variable is safe via copying before the lock is released. 1801 Similar safety is offered by \emph{explicit} opt-in disciplines like \CC RAII versus the monitor \emph{implicit} language-enforced safety guarantee ensuring no programmer usage errors. 1802 However, RAII mechanisms cannot handle complex synchronization within a monitor, where the monitor lock may not be released on function exit because it is passed to an unblocking thread; 1599 Aint x = { 0 }, y = { 0 }, z = { 5 }; $\C{// no mutex required}$ 1600 ++x; ++y; ++z; $\C{// safe increment by multiple threads}$ 1601 x = 2; y = i; z = k; $\C{// conversions}$ 1602 i = x; j = y; k = z; 1603 \end{cfa} 1604 1605 \CFA monitors have \newterm{multi-acquire} semantics so the thread in the monitor may acquire it multiple times without deadlock, allowing recursion and calling other interface functions. 1606 \begin{cfa} 1607 monitor M { ... } m; 1608 void foo( M & mutex m ) { ... } $\C{// acquire mutual exclusion}$ 1609 void bar( M & mutex m ) { $\C{// acquire mutual exclusion}$ 1610 ... `bar( m );` ... `foo( m );` ... $\C{// reacquire mutual exclusion}$ 1611 } 1612 \end{cfa} 1613 \CFA monitors also ensure the monitor lock is released regardless of how an acquiring function ends (normal or exceptional), and returning a shared variable is safe via copying before the lock is released. 1614 Similar safety is offered by \emph{explicit} mechanisms like \CC RAII; 1615 monitor \emph{implicit} safety ensures no programmer usage errors. 1616 Furthermore, RAII mechanisms cannot handle complex synchronization within a monitor, where the monitor lock may not be released on function exit because it is passed to an unblocking thread; 1803 1617 RAII is purely a mutual-exclusion mechanism (see Section~\ref{s:Scheduling}). 1804 1618 1805 Both Java and \CFA use a keyword @mutex@/\lstinline[language=java]|synchronized| to designate functions that implicitly acquire/release the monitor lock on call/return providing mutual exclusion to the stared data. 1806 Non-designated functions provide no mutual exclusion for read-only access or as an interface to a multi-step protocol requiring several steps of acquiring and releasing the monitor. 1807 Monitor objects can be passed through multiple helper functions without acquiring mutual exclusion, until a designated function associated with the object is called. 1808 \CFA designated functions are marked by an explicitly parameter-only pointer/reference qualifier @mutex@ (discussed further in Section\ref{s:MutexAcquisition}). 1809 Whereas, Java designated members are marked with \lstinline[language=java]|synchronized| that applies to the implicit reference parameter @this@. 1810 In the example, the increment and setter operations need mutual exclusion while the read-only getter operation can be nonmutex if reading the implementation is atomic. 1811 1812 1813 \subsection{Monitor implementation} 1619 1620 \subsection{Monitor Implementation} 1814 1621 1815 1622 For the same design reasons, \CFA provides a custom @monitor@ type and a @trait@ to enforce and restrict the monitor-interface functions. … … 1831 1638 \end{tabular} 1832 1639 \end{cquote} 1833 The @dtype@ property prevents \emph{implicit} copy operations and the @is_monitor@ trait provides no \emph{explicit} copy operations, so monitors must be passed by reference or pointer. 1834 Similarly, the function definitions ensure there is a mechanism to read the monitor descriptor from its handle, and a special destructor to prevent deallocation if a thread is using the shared data. 1640 The @dtype@ property prevents \emph{implicit} copy operations and the @is_monitor@ trait provides no \emph{explicit} copy operations, so monitors must be passed by reference (pointer). 1641 % Copying a lock is insecure because it is possible to copy an open lock and then use the open copy when the original lock is closed to simultaneously access the shared data. 1642 % Copying a monitor is secure because both the lock and shared data are copies, but copying the shared data is meaningless because it no longer represents a unique entity. 1643 Similarly, the function definitions ensures there is a mechanism to get (read) the monitor descriptor from its handle, and a special destructor to prevent deallocation if a thread using the shared data. 1835 1644 The custom monitor type also inserts any locks needed to implement the mutual exclusion semantics. 1836 \CFA relies heavily on traits as an abstraction mechanism, so the @mutex@ qualifier prevents coincidentally matching of a monitor trait with a type that is not a monitor, similar to coincidental inheritance where a shape and playing card can both be drawable. 1837 1838 1839 \subsection{Mutex acquisition} 1645 1646 1647 \subsection{Mutex Acquisition} 1840 1648 \label{s:MutexAcquisition} 1841 1649 1842 For object-oriented programming languages, the mutex property applies to one object, the implicit pointer/reference to the monitor type. 1843 Because \CFA uses a pointer qualifier, other possibilities exist, \eg: 1844 \begin{cfa} 1845 monitor M { ... }; 1650 While the monitor lock provides mutual exclusion for shared data, there are implementation options for when and where the locking/unlocking occurs. 1651 (Much of this discussion also applies to basic locks.) 1652 For example, a monitor may be passed through multiple helper functions before it is necessary to acquire the monitor's mutual exclusion. 1653 1654 The benefit of mandatory monitor qualifiers is self-documentation, but requiring both @mutex@ and \lstinline[morekeywords=nomutex]@nomutex@ for all monitor parameters is redundant. 1655 Instead, the semantics has one qualifier as the default and the other required. 1656 For example, make the safe @mutex@ qualifier the default because assuming \lstinline[morekeywords=nomutex]@nomutex@ may cause subtle errors. 1657 Alternatively, make the unsafe \lstinline[morekeywords=nomutex]@nomutex@ qualifier the default because it is the \emph{normal} parameter semantics while @mutex@ parameters are rare. 1658 Providing a default qualifier implies knowing whether a parameter is a monitor. 1659 Since \CFA relies heavily on traits as an abstraction mechanism, types can coincidentally match the monitor trait but not be a monitor, similar to inheritance where a shape and playing card can both be drawable. 1660 For this reason, \CFA requires programmers to identify the kind of parameter with the @mutex@ keyword and uses no keyword to mean \lstinline[morekeywords=nomutex]@nomutex@. 1661 1662 The next semantic decision is establishing which parameter \emph{types} may be qualified with @mutex@. 1663 The following has monitor parameter types that are composed of multiple objects. 1664 \begin{cfa} 1665 monitor M { ... } 1846 1666 int f1( M & mutex m ); $\C{// single parameter object}$ 1847 1667 int f2( M * mutex m ); $\C{// single or multiple parameter object}$ … … 1849 1669 int f4( stack( M * ) & mutex m ); $\C{// multiple parameters object}$ 1850 1670 \end{cfa} 1851 Function @f1@ has a single object parameter, while functions @f2@ to @f4@ can be a single or multi-element parameter with statically unknown size. 1852 Because of the statically unknown size, \CFA only supports a single reference @mutex@ parameter, @f1@. 1853 1854 The \CFA @mutex@ qualifier does allow the ability to support multimonitor functions,\footnote{ 1671 Function @f1@ has a single parameter object, while @f2@'s indirection could be a single or multi-element array, where static array size is often unknown in C. 1672 Function @f3@ has a multiple object matrix, and @f4@ a multiple object data structure. 1673 While shown shortly, multiple object acquisition is possible, but the number of objects must be statically known. 1674 Therefore, \CFA only acquires one monitor per parameter with at most one level of indirection, excluding pointers as it is impossible to statically determine the size. 1675 1676 For object-oriented monitors, \eg Java, calling a mutex member \emph{implicitly} acquires mutual exclusion of the receiver object, @`rec`.foo(...)@. 1677 \CFA has no receiver, and hence, the explicit @mutex@ qualifier is used to specify which objects acquire mutual exclusion. 1678 A positive consequence of this design decision is the ability to support multi-monitor functions,\footnote{ 1855 1679 While object-oriented monitors can be extended with a mutex qualifier for multiple-monitor members, no prior example of this feature could be found.} 1856 where the number of acquisitions is statically known,called \newterm{bulk acquire}.1857 \CFA guarantees bulk acquisition order is consistent across calls to @mutex@ functions using the same monitors as arguments, so acquiring multiple monitors in a bulk acquireis safe from deadlock.1680 called \newterm{bulk acquire}. 1681 \CFA guarantees acquisition order is consistent across calls to @mutex@ functions using the same monitors as arguments, so acquiring multiple monitors is safe from deadlock. 1858 1682 Figure~\ref{f:BankTransfer} shows a trivial solution to the bank transfer problem~\cite{BankTransfer}, where two resources must be locked simultaneously, using \CFA monitors with implicit locking and \CC with explicit locking. 1859 1683 A \CFA programmer only has to manage when to acquire mutual exclusion; … … 1875 1699 void transfer( BankAccount & `mutex` my, 1876 1700 BankAccount & `mutex` your, int me2you ) { 1877 // bulk acquire 1701 1878 1702 deposit( my, -me2you ); // debit 1879 1703 deposit( your, me2you ); // credit … … 1905 1729 void transfer( BankAccount & my, 1906 1730 BankAccount & your, int me2you ) { 1907 `scoped_lock lock( my.m, your.m );` // bulk acquire1731 `scoped_lock lock( my.m, your.m );` 1908 1732 deposit( my, -me2you ); // debit 1909 1733 deposit( your, me2you ); // credit … … 1933 1757 \end{figure} 1934 1758 1935 Users can still force the acquiring order by using or not using @mutex@.1759 Users can still force the acquiring order by using @mutex@/\lstinline[morekeywords=nomutex]@nomutex@. 1936 1760 \begin{cfa} 1937 1761 void foo( M & mutex m1, M & mutex m2 ); $\C{// acquire m1 and m2}$ 1938 void bar( M & mutex m1, M & m2 ) { $\C{// onlyacquire m1}$1762 void bar( M & mutex m1, M & /* nomutex */ m2 ) { $\C{// acquire m1}$ 1939 1763 ... foo( m1, m2 ); ... $\C{// acquire m2}$ 1940 1764 } 1941 void baz( M & m1, M & mutex m2 ) { $\C{// onlyacquire m2}$1765 void baz( M & /* nomutex */ m1, M & mutex m2 ) { $\C{// acquire m2}$ 1942 1766 ... foo( m1, m2 ); ... $\C{// acquire m1}$ 1943 1767 } … … 1982 1806 % There are many aspects of scheduling in a concurrency system, all related to resource utilization by waiting threads, \ie which thread gets the resource next. 1983 1807 % Different forms of scheduling include access to processors by threads (see Section~\ref{s:RuntimeStructureCluster}), another is access to a shared resource by a lock or monitor. 1984 This section discusses scheduling for waiting threads eligible for monitor entry~\cite{Buhr95b}, \ie which user thread gets the shared resource next. 1985 (See Section~\ref{s:RuntimeStructureCluster} for scheduling kernel threads on virtual processors.) 1986 While monitor mutual-exclusion provides safe access to its shared data, the data may indicate a thread cannot proceed, \eg a bounded buffer may be full/\-empty so produce/consumer threads must block. 1987 Leaving the monitor and retrying (busy waiting) is impractical for high-level programming. 1988 1989 Monitors eliminate busy waiting by providing synchronization within the monitor critical-section to schedule threads needing access to the shared data, where threads block versus spin. 1808 This section discusses monitor scheduling for waiting threads eligible for entry, \ie which thread gets the shared resource next. (See Section~\ref{s:RuntimeStructureCluster} for scheduling threads on virtual processors.) 1809 While monitor mutual-exclusion provides safe access to shared data, the monitor data may indicate that a thread accessing it cannot proceed, \eg a bounded buffer may be full/empty so produce/consumer threads must block. 1810 Leaving the monitor and trying again (busy waiting) is impractical for high-level programming. 1811 Monitors eliminate busy waiting by providing synchronization to schedule threads needing access to the shared data, where threads block versus spinning. 1990 1812 Synchronization is generally achieved with internal~\cite{Hoare74} or external~\cite[\S~2.9.2]{uC++} scheduling. 1991 \newterm{Internal} largely schedules threads located \emph{inside} the monitor and is accomplished using condition variables with signal and wait. 1992 \newterm{External} largely schedules threads located \emph{outside} the monitor and is accomplished with the @waitfor@ statement. 1993 Note, internal scheduling has a small amount of external scheduling and vice versa, so the naming denotes where the majority of the block threads reside (inside or outside) for scheduling. 1994 For complex scheduling, the approaches can be combined, so there are threads waiting inside and outside. 1995 1996 \CFA monitors do not allow calling threads to barge ahead of signaled threads via barging prevention, which simplifies synchronization among threads in the monitor and increases correctness. 1997 A direct consequence of this semantics is that unblocked waiting threads are not required to recheck the waiting condition, \ie waits are not in a starvation-prone busy-loop as required by the signals-as-hints style with barging. 1998 Preventing barging comes directly from Hoare's semantics in the seminal paper on monitors~\cite[p.~550]{Hoare74}. 1813 \newterm{Internal scheduling} is characterized by each thread entering the monitor and making an individual decision about proceeding or blocking, while \newterm{external scheduling} is characterized by an entering thread making a decision about proceeding for itself and on behalf of other threads attempting entry. 1814 Finally, \CFA monitors do not allow calling threads to barge ahead of signalled threads, which simplifies synchronization among threads in the monitor and increases correctness. 1815 If barging is allowed, synchronization between a signaller and signallee is difficult, often requiring additional flags and multiple unblock/block cycles. 1816 In fact, signals-as-hints is completely opposite from that proposed by Hoare in the seminal paper on monitors~\cite[p.~550]{Hoare74}. 1999 1817 % \begin{cquote} 2000 1818 % However, we decree that a signal operation be followed immediately by resumption of a waiting program, without possibility of an intervening procedure call from yet a third program. 2001 % It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signal ing program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74}1819 % It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signalling program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74} 2002 1820 % \end{cquote} 2003 Furthermore, \CFA concurrency has no spurious wakeup~\cite[\S~9]{Buhr05a}, which eliminates an implicit self barging. 2004 2005 Monitor mutual-exclusion means signaling cannot have the signaller and signaled thread in the monitor simultaneously, so only the signaller or signallee can proceed and the other waits on an implicit urgent list~\cite[p.~551]{Hoare74}. 2006 Figure~\ref{f:MonitorScheduling} shows internal and external scheduling for the bounded-buffer examples in Figure~\ref{f:GenericBoundedBuffer}. 2007 For internal scheduling in Figure~\ref{f:BBInt}, the @signal@ moves the signallee, front thread of the specified condition queue, to the urgent list (see Figure~\ref{f:MonitorScheduling}) and the signaller continues (solid line). 2008 Multiple signals move multiple signallees to urgent until the condition queue is empty. 2009 When the signaller exits or waits, a thread is implicitly unblocked from urgent, if available, before unblocking a calling thread to prevent barging. 2010 (Java conceptually moves the signaled thread to the calling queue, and hence, allows barging.) 2011 Signal is used when the signaller is providing the cooperation needed by the signallee, \eg creating an empty slot in a buffer for a producer, and the signaller immediately exits the monitor to run concurrently consuming the buffer element, and passes control of the monitor to the signaled thread, which can immediately take advantage of the state change. 2012 Specifically, the @wait@ function atomically blocks the calling thread and implicitly releases the monitor lock(s) for all monitors in the function's parameter list. 2013 Signalling is unconditional because signaling an empty condition queue does nothing. 2014 It is common to declare condition queues as monitor fields to prevent shared access, hence no locking is required for access as the queues are protected by the monitor lock. 2015 In \CFA, a condition queue can be created and stored independently. 1821 Furthermore, \CFA concurrency has no spurious wakeup~\cite[\S~9]{Buhr05a}, which eliminates an implicit form of self barging. 1822 Hence, a \CFA @wait@ statement is not enclosed in a @while@ loop retesting a blocking predicate, which can cause thread starvation due to barging. 1823 1824 Figure~\ref{f:MonitorScheduling} shows general internal/external scheduling (for the bounded-buffer example in Figure~\ref{f:InternalExternalScheduling}). 1825 External calling threads block on the calling queue, if the monitor is occupied, otherwise they enter in FIFO order. 1826 Internal threads block on condition queues via @wait@ and reenter from the condition in FIFO order. 1827 Alternatively, internal threads block on urgent from the @signal_block@ or @waitfor@, and reenter implicitly when the monitor becomes empty, \ie, the thread in the monitor exits or waits. 1828 1829 There are three signalling mechanisms to unblock waiting threads to enter the monitor. 1830 Note, signalling cannot have the signaller and signalled thread in the monitor simultaneously because of the mutual exclusion, so either the signaller or signallee can proceed. 1831 For internal scheduling, threads are unblocked from condition queues using @signal@, where the signallee is moved to urgent and the signaller continues (solid line). 1832 Multiple signals move multiple signallees to urgent until the condition is empty. 1833 When the signaller exits or waits, a thread blocked on urgent is processed before calling threads to prevent barging. 1834 (Java conceptually moves the signalled thread to the calling queue, and hence, allows barging.) 1835 The alternative unblock is in the opposite order using @signal_block@, where the signaller is moved to urgent and the signallee continues (dashed line), and is implicitly unblocked from urgent when the signallee exits or waits. 1836 1837 For external scheduling, the condition queues are not used; 1838 instead threads are unblocked directly from the calling queue using @waitfor@ based on function names requesting mutual exclusion. 1839 (The linear search through the calling queue to locate a particular call can be reduced to $O(1)$.) 1840 The @waitfor@ has the same semantics as @signal_block@, where the signalled thread executes before the signallee, which waits on urgent. 1841 Executing multiple @waitfor@s from different signalled functions causes the calling threads to move to urgent. 1842 External scheduling requires urgent to be a stack, because the signaller expects to execute immediately after the specified monitor call has exited or waited. 1843 Internal scheduling behaves the same for an urgent stack or queue, except for multiple signalling, where the threads unblock from urgent in reverse order from signalling. 1844 If the restart order is important, multiple signalling by a signal thread can be transformed into daisy-chain signalling among threads, where each thread signals the next thread. 1845 We tried both a stack for @waitfor@ and queue for signalling, but that resulted in complex semantics about which thread enters next. 1846 Hence, \CFA uses a single urgent stack to correctly handle @waitfor@ and adequately support both forms of signalling. 2016 1847 2017 1848 \begin{figure} … … 2031 1862 \end{figure} 2032 1863 1864 Figure~\ref{f:BBInt} shows a \CFA generic bounded-buffer with internal scheduling, where producers/consumers enter the monitor, detect the buffer is full/empty, and block on an appropriate condition variable, @full@/@empty@. 1865 The @wait@ function atomically blocks the calling thread and implicitly releases the monitor lock(s) for all monitors in the function's parameter list. 1866 The appropriate condition variable is signalled to unblock an opposite kind of thread after an element is inserted/removed from the buffer. 1867 Signalling is unconditional, because signalling an empty condition variable does nothing. 1868 It is common to declare condition variables as monitor fields to prevent shared access, hence no locking is required for access as the conditions are protected by the monitor lock. 1869 In \CFA, a condition variable can be created/stored independently. 1870 % To still prevent expensive locking on access, a condition variable is tied to a \emph{group} of monitors on first use, called \newterm{branding}, resulting in a low-cost boolean test to detect sharing from other monitors. 1871 1872 % Signalling semantics cannot have the signaller and signalled thread in the monitor simultaneously, which means: 1873 % \begin{enumerate} 1874 % \item 1875 % The signalling thread returns immediately and the signalled thread continues. 1876 % \item 1877 % The signalling thread continues and the signalled thread is marked for urgent unblocking at the next scheduling point (exit/wait). 1878 % \item 1879 % The signalling thread blocks but is marked for urgent unblocking at the next scheduling point and the signalled thread continues. 1880 % \end{enumerate} 1881 % The first approach is too restrictive, as it precludes solving a reasonable class of problems, \eg dating service (see Figure~\ref{f:DatingService}). 1882 % \CFA supports the next two semantics as both are useful. 1883 2033 1884 \begin{figure} 2034 1885 \centering … … 2042 1893 T elements[10]; 2043 1894 }; 2044 void ?{}( Buffer(T) & buf ) with(buf) {1895 void ?{}( Buffer(T) & buffer ) with(buffer) { 2045 1896 front = back = count = 0; 2046 1897 } 2047 2048 void insert(Buffer(T) & mutex buf, T elm) with(buf){2049 if ( count == 10 ) `wait( empty )`; // full ?2050 // insert el m into buf1898 void insert( Buffer(T) & mutex buffer, T elem ) 1899 with(buffer) { 1900 if ( count == 10 ) `wait( empty )`; 1901 // insert elem into buffer 2051 1902 `signal( full )`; 2052 1903 } 2053 T remove( Buffer(T) & mutex buf ) with(buf) {2054 if ( count == 0 ) `wait( full )`; // empty ?2055 // remove el m from buf1904 T remove( Buffer(T) & mutex buffer ) with(buffer) { 1905 if ( count == 0 ) `wait( full )`; 1906 // remove elem from buffer 2056 1907 `signal( empty )`; 2057 return el m;1908 return elem; 2058 1909 } 2059 1910 } 2060 1911 \end{cfa} 2061 1912 \end{lrbox} 1913 1914 % \newbox\myboxB 1915 % \begin{lrbox}{\myboxB} 1916 % \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1917 % forall( otype T ) { // distribute forall 1918 % monitor Buffer { 1919 % 1920 % int front, back, count; 1921 % T elements[10]; 1922 % }; 1923 % void ?{}( Buffer(T) & buffer ) with(buffer) { 1924 % [front, back, count] = 0; 1925 % } 1926 % T remove( Buffer(T) & mutex buffer ); // forward 1927 % void insert( Buffer(T) & mutex buffer, T elem ) 1928 % with(buffer) { 1929 % if ( count == 10 ) `waitfor( remove, buffer )`; 1930 % // insert elem into buffer 1931 % 1932 % } 1933 % T remove( Buffer(T) & mutex buffer ) with(buffer) { 1934 % if ( count == 0 ) `waitfor( insert, buffer )`; 1935 % // remove elem from buffer 1936 % 1937 % return elem; 1938 % } 1939 % } 1940 % \end{cfa} 1941 % \end{lrbox} 2062 1942 2063 1943 \newbox\myboxB 2064 1944 \begin{lrbox}{\myboxB} 2065 1945 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2066 forall( otype T ) { // distribute forall2067 monitor Buffer {2068 2069 int front, back, count;2070 T elements[10];2071 };2072 void ?{}( Buffer(T) & buf ) with(buf) {2073 front = back = count = 0;2074 }2075 T remove( Buffer(T) & mutex buf ); // forward2076 void insert(Buffer(T) & mutex buf, T elm) with(buf){2077 if ( count == 10 ) `waitfor( remove : buf )`;2078 // insert elm into buf2079 2080 }2081 T remove( Buffer(T) & mutex buf ) with(buf) {2082 if ( count == 0 ) `waitfor( insert : buf )`;2083 // remove elm from buf2084 2085 return elm;2086 }2087 }2088 \end{cfa}2089 \end{lrbox}2090 2091 \subfloat[Internal scheduling]{\label{f:BBInt}\usebox\myboxA}2092 \hspace{1pt}2093 \vrule2094 \hspace{3pt}2095 \subfloat[External scheduling]{\label{f:BBExt}\usebox\myboxB}2096 2097 \caption{Generic bounded buffer}2098 \label{f:GenericBoundedBuffer}2099 \end{figure}2100 2101 The @signal_block@ provides the opposite unblocking order, where the signaller is moved to urgent and the signallee continues and a thread is implicitly unblocked from urgent when the signallee exits or waits (dashed line)~\cite[p.~551]{Hoare74}.2102 Signal block is used when the signallee is providing the cooperation needed by the signaller, \eg if the buffer is removed and a producer hands off an item to a consumer as in Figure~\ref{f:DatingSignalBlock}, so the signaller must wait until the signallee unblocks, provides the cooperation, exits the monitor to run concurrently, and passes control of the monitor to the signaller, which can immediately take advantage of the state change.2103 Using @signal@ or @signal_block@ can be a dynamic decision based on whether the thread providing the cooperation arrives before or after the thread needing the cooperation.2104 2105 For external scheduling in Figure~\ref{f:BBExt}, the internal scheduling is replaced, eliminating condition queues and @signal@/@wait@ (cases where it cannot are discussed shortly), and has existed in the programming language Ada for almost 40 years with variants in other languages~\cite{SR,ConcurrentC++,uC++}.2106 While prior languages use external scheduling solely for thread interaction, \CFA generalizes it to both monitors and threads.2107 External scheduling allows waiting for events from other threads while restricting unrelated events, that would otherwise have to wait on condition queues in the monitor.2108 Scheduling is controlled by the @waitfor@ statement, which atomically blocks the calling thread, releases the monitor lock, and restricts the function calls that can next acquire mutual exclusion.2109 Specifically, a thread calling the monitor is unblocked directly from the calling queue based on function names that can fulfill the cooperation required by the signaller.2110 (The linear search through the calling queue to locate a particular call can be reduced to $O(1)$.)2111 Hence, the @waitfor@ has the same semantics as @signal_block@, where the signallee thread from the calling queue executes before the signaller, which waits on urgent.2112 Now when a producer/consumer detects a full/empty buffer, the necessary cooperation for continuation is specified by indicating the next function call that can occur.2113 For example, a producer detecting a full buffer must have cooperation from a consumer to remove an item so function @remove@ is accepted, which prevents producers from entering the monitor, and after a consumer calls @remove@, the producer waiting on urgent is \emph{implicitly} unblocked because it can now continue its insert operation.2114 Hence, this mechanism is done in terms of control flow, next call, versus in terms of data, channels, as in Go and Rust @select@.2115 While both mechanisms have strengths and weaknesses, \CFA uses the control-flow mechanism to be consistent with other language features.2116 2117 Figure~\ref{f:ReadersWriterLock} shows internal and external scheduling for a readers/writer lock with no barging and threads are serviced in FIFO order to eliminate staleness and freshness among the reader/writer threads.2118 For internal scheduling in Figure~\ref{f:RWInt}, the readers and writers wait on the same condition queue in FIFO order, making it impossible to tell if a waiting thread is a reader or writer.2119 To clawback the kind of thread, a \CFA condition can store user data in the node for a blocking thread at the @wait@, \ie whether the thread is a @READER@ or @WRITER@.2120 An unblocked reader thread checks if the thread at the front of the queue is a reader and unblock it, \ie the readers daisy-chain signal the next group of readers demarcated by the next writer or end of the queue.2121 For external scheduling in Figure~\ref{f:RWExt}, a waiting reader checks if a writer is using the resource, and if so, restricts further calls until the writer exits by calling @EndWrite@.2122 The writer does a similar action for each reader or writer using the resource.2123 Note, no new calls to @StartRead@/@StartWrite@ may occur when waiting for the call to @EndRead@/@EndWrite@.2124 2125 \begin{figure}2126 \centering2127 \newbox\myboxA2128 \begin{lrbox}{\myboxA}2129 \begin{cfa}[aboveskip=0pt,belowskip=0pt]2130 enum RW { READER, WRITER };2131 1946 monitor ReadersWriter { 2132 int rcnt, wcnt; // readers/writer using resource 2133 `condition RWers;` 1947 int rcnt, wcnt; // readers/writer using resource 2134 1948 }; 2135 1949 void ?{}( ReadersWriter & rw ) with(rw) { … … 2138 1952 void EndRead( ReadersWriter & mutex rw ) with(rw) { 2139 1953 rcnt -= 1; 2140 if ( rcnt == 0 ) `signal( RWers )`;2141 1954 } 2142 1955 void EndWrite( ReadersWriter & mutex rw ) with(rw) { 2143 1956 wcnt = 0; 2144 `signal( RWers );`2145 1957 } 2146 1958 void StartRead( ReadersWriter & mutex rw ) with(rw) { 2147 if ( wcnt !=0 || ! empty( RWers ) ) 2148 `wait( RWers, READER )`; 1959 if ( wcnt > 0 ) `waitfor( EndWrite, rw );` 2149 1960 rcnt += 1; 2150 if ( ! empty(RWers) && `front(RWers) == READER` )2151 `signal( RWers )`; // daisy-chain signaling2152 1961 } 2153 1962 void StartWrite( ReadersWriter & mutex rw ) with(rw) { 2154 if ( wcnt != 0 || rcnt != 0 ) `wait( RWers, WRITER )`;2155 1963 if ( wcnt > 0 ) `waitfor( EndWrite, rw );` 1964 else while ( rcnt > 0 ) `waitfor( EndRead, rw );` 2156 1965 wcnt = 1; 2157 1966 } 1967 2158 1968 \end{cfa} 2159 1969 \end{lrbox} 2160 1970 2161 \newbox\myboxB 2162 \begin{lrbox}{\myboxB} 2163 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2164 2165 monitor ReadersWriter { 2166 int rcnt, wcnt; // readers/writer using resource 2167 2168 }; 2169 void ?{}( ReadersWriter & rw ) with(rw) { 2170 rcnt = wcnt = 0; 2171 } 2172 void EndRead( ReadersWriter & mutex rw ) with(rw) { 2173 rcnt -= 1; 2174 2175 } 2176 void EndWrite( ReadersWriter & mutex rw ) with(rw) { 2177 wcnt = 0; 2178 2179 } 2180 void StartRead( ReadersWriter & mutex rw ) with(rw) { 2181 if ( wcnt > 0 ) `waitfor( EndWrite : rw );` 2182 2183 rcnt += 1; 2184 2185 2186 } 2187 void StartWrite( ReadersWriter & mutex rw ) with(rw) { 2188 if ( wcnt > 0 ) `waitfor( EndWrite : rw );` 2189 else while ( rcnt > 0 ) `waitfor( EndRead : rw );` 2190 wcnt = 1; 2191 } 2192 \end{cfa} 2193 \end{lrbox} 2194 2195 \subfloat[Internal scheduling]{\label{f:RWInt}\usebox\myboxA} 2196 \hspace{1pt} 1971 \subfloat[Generic bounded buffer, internal scheduling]{\label{f:BBInt}\usebox\myboxA} 1972 \hspace{3pt} 2197 1973 \vrule 2198 1974 \hspace{3pt} 2199 \subfloat[ External scheduling]{\label{f:RWExt}\usebox\myboxB}2200 2201 \caption{ Readers / writer lock}2202 \label{f: ReadersWriterLock}1975 \subfloat[Readers / writer lock, external scheduling]{\label{f:RWExt}\usebox\myboxB} 1976 1977 \caption{Internal / external scheduling} 1978 \label{f:InternalExternalScheduling} 2203 1979 \end{figure} 2204 1980 2205 Finally, external scheduling requires urgent to be a stack, because the signaller expects to execute immediately after the specified monitor call has exited or waited. 2206 Internal scheduling performing multiple signaling results in unblocking from urgent in the reverse order from signaling. 2207 It is rare for the unblocking order to be important as an unblocked thread can be time-sliced immediately after leaving the monitor. 2208 If the unblocking order is important, multiple signaling can be restructured into daisy-chain signaling, where each thread signals the next thread. 2209 Hence, \CFA uses a single urgent stack to correctly handle @waitfor@ and adequately support both forms of signaling. 2210 (Advanced @waitfor@ features are discussed in Section~\ref{s:ExtendedWaitfor}.) 1981 Figure~\ref{f:BBInt} can be transformed into external scheduling by removing the condition variables and signals/waits, and adding the following lines at the locations of the current @wait@s in @insert@/@remove@, respectively. 1982 \begin{cfa}[aboveskip=2pt,belowskip=1pt] 1983 if ( count == 10 ) `waitfor( remove, buffer )`; | if ( count == 0 ) `waitfor( insert, buffer )`; 1984 \end{cfa} 1985 Here, the producers/consumers detects a full/\-empty buffer and prevents more producers/consumers from entering the monitor until there is a free/empty slot in the buffer. 1986 External scheduling is controlled by the @waitfor@ statement, which atomically blocks the calling thread, releases the monitor lock, and restricts the function calls that can next acquire mutual exclusion. 1987 If the buffer is full, only calls to @remove@ can acquire the buffer, and if the buffer is empty, only calls to @insert@ can acquire the buffer. 1988 Threads calling excluded functions block outside of (external to) the monitor on the calling queue, versus blocking on condition queues inside of (internal to) the monitor. 1989 Figure~\ref{f:RWExt} shows a readers/writer lock written using external scheduling, where a waiting reader detects a writer using the resource and restricts further calls until the writer exits by calling @EndWrite@. 1990 The writer does a similar action for each reader or writer using the resource. 1991 Note, no new calls to @StarRead@/@StartWrite@ may occur when waiting for the call to @EndRead@/@EndWrite@. 1992 External scheduling allows waiting for events from other threads while restricting unrelated events, that would otherwise have to wait on conditions in the monitor. 1993 The mechnaism can be done in terms of control flow, \eg Ada @accept@ or \uC @_Accept@, or in terms of data, \eg Go @select@ on channels. 1994 While both mechanisms have strengths and weaknesses, this project uses the control-flow mechanism to be consistent with other language features. 1995 % Two challenges specific to \CFA for external scheduling are loose object-definitions (see Section~\ref{s:LooseObjectDefinitions}) and multiple-monitor functions (see Section~\ref{s:Multi-MonitorScheduling}). 1996 1997 Figure~\ref{f:DatingService} shows a dating service demonstrating non-blocking and blocking signalling. 1998 The dating service matches girl and boy threads with matching compatibility codes so they can exchange phone numbers. 1999 A thread blocks until an appropriate partner arrives. 2000 The complexity is exchanging phone numbers in the monitor because of the mutual-exclusion property. 2001 For signal scheduling, the @exchange@ condition is necessary to block the thread finding the match, while the matcher unblocks to take the opposite number, post its phone number, and unblock the partner. 2002 For signal-block scheduling, the implicit urgent-queue replaces the explict @exchange@-condition and @signal_block@ puts the finding thread on the urgent condition and unblocks the matcher. 2003 The dating service is an example of a monitor that cannot be written using external scheduling because it requires knowledge of calling parameters to make scheduling decisions, and parameters of waiting threads are unavailable; 2004 as well, an arriving thread may not find a partner and must wait, which requires a condition variable, and condition variables imply internal scheduling. 2005 Furthermore, barging corrupts the dating service during an exchange because a barger may also match and change the phone numbers, invalidating the previous exchange phone number. 2006 Putting loops around the @wait@s does not correct the problem; 2007 the simple solution must be restructured to account for barging. 2211 2008 2212 2009 \begin{figure} … … 2222 2019 }; 2223 2020 int girl( DS & mutex ds, int phNo, int ccode ) { 2224 if ( empty( Boys[ccode] ) ) {2021 if ( is_empty( Boys[ccode] ) ) { 2225 2022 wait( Girls[ccode] ); 2226 2023 GirlPhNo = phNo; … … 2249 2046 }; 2250 2047 int girl( DS & mutex ds, int phNo, int ccode ) { 2251 if ( empty( Boys[ccode] ) ) { // no compatible2048 if ( is_empty( Boys[ccode] ) ) { // no compatible 2252 2049 wait( Girls[ccode] ); // wait for boy 2253 2050 GirlPhNo = phNo; // make phone number available … … 2269 2066 \qquad 2270 2067 \subfloat[\lstinline@signal_block@]{\label{f:DatingSignalBlock}\usebox\myboxB} 2271 \caption{Dating service Monitor}2272 \label{f:DatingService Monitor}2068 \caption{Dating service} 2069 \label{f:DatingService} 2273 2070 \end{figure} 2274 2071 2275 Figure~\ref{f:DatingServiceMonitor} shows a dating service demonstrating nonblocking and blocking signaling. 2276 The dating service matches girl and boy threads with matching compatibility codes so they can exchange phone numbers. 2277 A thread blocks until an appropriate partner arrives. 2278 The complexity is exchanging phone numbers in the monitor because of the mutual-exclusion property. 2279 For signal scheduling, the @exchange@ condition is necessary to block the thread finding the match, while the matcher unblocks to take the opposite number, post its phone number, and unblock the partner. 2280 For signal-block scheduling, the implicit urgent-queue replaces the explicit @exchange@-condition and @signal_block@ puts the finding thread on the urgent stack and unblocks the matcher. 2281 Note, barging corrupts the dating service during an exchange because a barger may also match and change the phone numbers, invalidating the previous exchange phone number. 2282 This situation shows rechecking the waiting condition and waiting again (signals-as-hints) fails, requiring significant restructured to account for barging. 2283 2284 Given external and internal scheduling, what guidelines can a programmer use to select between them? 2285 In general, external scheduling is easier to understand and code because only the next logical action (mutex function(s)) is stated, and the monitor implicitly handles all the details. 2286 Therefore, there are no condition variables, and hence, no wait and signal, which reduces coding complexity and synchronization errors. 2287 If external scheduling is simpler than internal, why not use it all the time? 2288 Unfortunately, external scheduling cannot be used if: scheduling depends on parameter value(s) or scheduling must block across an unknown series of calls on a condition variable, \ie internal scheduling. 2289 For example, the dating service cannot be written using external scheduling. 2290 First, scheduling requires knowledge of calling parameters to make matching decisions and parameters of calling threads are unavailable within the monitor. 2291 Specifically, a thread within the monitor cannot examine the @ccode@ of threads waiting on the calling queue to determine if there is a matching partner. 2292 (Similarly, if the bounded buffer or readers/writer are restructured with a single interface function with a parameter denoting producer/consumer or reader/write, they cannot be solved with external scheduling.) 2293 Second, a scheduling decision may be delayed across an unknown number of calls when there is no immediate match so the thread in the monitor must block on a condition. 2294 Specifically, if a thread determines there is no opposite calling thread with the same @ccode@, it must wait an unknown period until a matching thread arrives. 2295 For complex synchronization, both external and internal scheduling can be used to take advantage of best of properties of each. 2296 2297 Finally, both internal and external scheduling extend to multiple monitors in a natural way. 2072 In summation, for internal scheduling, non-blocking signalling (as in the producer/consumer example) is used when the signaller is providing the cooperation for a waiting thread; 2073 the signaller enters the monitor and changes state, detects a waiting threads that can use the state, performs a non-blocking signal on the condition queue for the waiting thread, and exits the monitor to run concurrently. 2074 The waiter unblocks next from the urgent queue, uses/takes the state, and exits the monitor. 2075 Blocking signal is the reverse, where the waiter is providing the cooperation for the signalling thread; 2076 the signaller enters the monitor, detects a waiting thread providing the necessary state, performs a blocking signal to place it on the urgent queue and unblock the waiter. 2077 The waiter changes state and exits the monitor, and the signaller unblocks next from the urgent queue to use/take the state. 2078 2079 Both internal and external scheduling extend to multiple monitors in a natural way. 2298 2080 \begin{cquote} 2299 \begin{tabular}{@{}l@{\hspace{ 2\parindentlnth}}l@{}}2081 \begin{tabular}{@{}l@{\hspace{3\parindentlnth}}l@{}} 2300 2082 \begin{cfa} 2301 2083 monitor M { `condition e`; ... }; … … 2308 2090 & 2309 2091 \begin{cfa} 2310 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); // overload rtn2092 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); 2311 2093 void rtn$\(_2\)$( M & mutex m1 ); 2312 2094 void bar( M & mutex m1, M & mutex m2 ) { 2313 ... waitfor( `rtn` ${\color{red}\(_1\)}$ ); ... // $\LstCommentStyle{waitfor( rtn\(_1\) :m1, m2 )}$2314 ... waitfor( `rtn ${\color{red}\(_2\)}$ : m1` ); ...2095 ... waitfor( `rtn` ); ... // $\LstCommentStyle{waitfor( rtn\(_1\), m1, m2 )}$ 2096 ... waitfor( `rtn, m1` ); ... // $\LstCommentStyle{waitfor( rtn\(_2\), m1 )}$ 2315 2097 } 2316 2098 \end{cfa} … … 2318 2100 \end{cquote} 2319 2101 For @wait( e )@, the default semantics is to atomically block the signaller and release all acquired mutex parameters, \ie @wait( e, m1, m2 )@. 2320 To override the implicit multi monitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@.2321 Wait cannot statically verif ythe released monitors are the acquired mutex-parameters without disallowing separately compiled helper functions calling @wait@.2322 While \CC supports bulk locking, @wait@ only accepts a single lock for a condition queue, so bulk locking with condition queues is asymmetric.2102 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@. 2103 Wait cannot statically verifies the released monitors are the acquired mutex-parameters without disallowing separately compiled helper functions calling @wait@. 2104 While \CC supports bulk locking, @wait@ only accepts a single lock for a condition variable, so bulk locking with condition variables is asymmetric. 2323 2105 Finally, a signaller, 2324 2106 \begin{cfa} … … 2327 2109 } 2328 2110 \end{cfa} 2329 must have acquired at least the same locks as the waiting thread signal ed from a condition queue to allow the locks to be passed, and hence, prevent barging.2330 2331 Similarly, for @waitfor( rtn )@, the default semantics is to atomically block the acceptor and release all acquired mutex parameters, \ie @waitfor( rtn :m1, m2 )@.2332 To override the implicit multi monitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn :m1 )@.2333 @waitfor@ does statically verify the monitor types passed are the same as the acquired mutex-parameters of the given function or function pointer, hence the prototype must be accessible.2111 must have acquired at least the same locks as the waiting thread signalled from a condition queue to allow the locks to be passed, and hence, prevent barging. 2112 2113 Similarly, for @waitfor( rtn )@, the default semantics is to atomically block the acceptor and release all acquired mutex parameters, \ie @waitfor( rtn, m1, m2 )@. 2114 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn, m1 )@. 2115 @waitfor@ does statically verify the monitor types passed are the same as the acquired mutex-parameters of the given function or function pointer, hence the function (pointer) prototype must be accessible. 2334 2116 % When an overloaded function appears in an @waitfor@ statement, calls to any function with that name are accepted. 2335 % The rationale is that functions with the same name should perform a similar actions, and therefore, all should be eligible to accept a call.2117 % The rationale is that members with the same name should perform a similar function, and therefore, all should be eligible to accept a call. 2336 2118 Overloaded functions can be disambiguated using a cast 2337 2119 \begin{cfa} 2338 2120 void rtn( M & mutex m ); 2339 2121 `int` rtn( M & mutex m ); 2340 waitfor( (`int` (*)( M & mutex ))rtn :m );2341 \end{cfa} 2342 2343 The ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock (see Section~\ref{s:MutexAcquisition}).2122 waitfor( (`int` (*)( M & mutex ))rtn, m ); 2123 \end{cfa} 2124 2125 The ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock. 2344 2126 \begin{cfa} 2345 2127 void foo( M & mutex m1, M & mutex m2 ) { 2346 ... wait( `e, m1` ); ... $\C{// release m1, keeping m2 acquired }$2347 void bar( M & mutex m1, M & mutex m2 ) { $\C{// must acquire m1 and m2 }$2128 ... wait( `e, m1` ); ... $\C{// release m1, keeping m2 acquired )}$ 2129 void bar( M & mutex m1, M & mutex m2 ) { $\C{// must acquire m1 and m2 )}$ 2348 2130 ... signal( `e` ); ... 2349 2131 \end{cfa} 2350 The @wait@ only releases @m1@ so the signaling thread cannot acquire @m1@ and @m2@ to enter @bar@ and @signal@ the condition. 2351 While deadlock can occur with multiple/nesting acquisition, this is a consequence of locks, and by extension monitor locking is not perfectly composable. 2132 The @wait@ only releases @m1@ so the signalling thread cannot acquire @m1@ and @m2@ to enter @bar@ and @signal@ the condition. 2133 While deadlock can occur with multiple/nesting acquisition, this is a consequence of locks, and by extension monitors, not being perfectly composable. 2134 2352 2135 2353 2136 2354 2137 \subsection{\texorpdfstring{Extended \protect\lstinline@waitfor@}{Extended waitfor}} 2355 \label{s:ExtendedWaitfor}2356 2138 2357 2139 Figure~\ref{f:ExtendedWaitfor} shows the extended form of the @waitfor@ statement to conditionally accept one of a group of mutex functions, with an optional statement to be performed \emph{after} the mutex function finishes. 2358 For a @waitfor@ clause to be executed, its @when@ must be true and an outstanding call to its corresponding function(s) must exist.2140 For a @waitfor@ clause to be executed, its @when@ must be true and an outstanding call to its corresponding member(s) must exist. 2359 2141 The \emph{conditional-expression} of a @when@ may call a function, but the function must not block or context switch. 2360 If there are multiple acceptable mutex calls, selection is prioritized top-to-bottomamong the @waitfor@ clauses, whereas some programming languages with similar mechanisms accept nondeterministically for this case, \eg Go \lstinline[morekeywords=select]@select@.2361 If some accept guards are true and there are no outstanding calls to these functions, the acceptor is blocked until a call to one of these functions is made.2142 If there are multiple acceptable mutex calls, selection occurs top-to-bottom (prioritized) among the @waitfor@ clauses, whereas some programming languages with similar mechanisms accept nondeterministically for this case, \eg Go \lstinline[morekeywords=select]@select@. 2143 If some accept guards are true and there are no outstanding calls to these members, the acceptor is blocked until a call to one of these members is made. 2362 2144 If there is a @timeout@ clause, it provides an upper bound on waiting. 2363 2145 If all the accept guards are false, the statement does nothing, unless there is a terminating @else@ clause with a true guard, which is executed instead. 2364 2146 Hence, the terminating @else@ clause allows a conditional attempt to accept a call without blocking. 2365 2147 If both @timeout@ and @else@ clause are present, the @else@ must be conditional, or the @timeout@ is never triggered. 2366 % There is also a traditional future wait queue (not shown) (\eg Microsoft @WaitForMultipleObjects@), to wait for a specified number of future elements in the queue. 2367 Finally, there is a shorthand for specifying multiple functions using the same set of monitors: @waitfor( f, g, h : m1, m2, m3 )@. 2148 There is also a traditional future wait queue (not shown) (\eg Microsoft (@WaitForMultipleObjects@)), to wait for a specified number of future elements in the queue. 2368 2149 2369 2150 \begin{figure} … … 2371 2152 \begin{cfa} 2372 2153 `when` ( $\emph{conditional-expression}$ ) $\C{// optional guard}$ 2373 waitfor( $\emph{mutex- function-name}$ ) $\emph{statement}$ $\C{// action after call}$2154 waitfor( $\emph{mutex-member-name}$ ) $\emph{statement}$ $\C{// action after call}$ 2374 2155 `or` `when` ( $\emph{conditional-expression}$ ) $\C{// any number of functions}$ 2375 waitfor( $\emph{mutex- function-name}$ ) $\emph{statement}$2156 waitfor( $\emph{mutex-member-name}$ ) $\emph{statement}$ 2376 2157 `or` ... 2377 2158 `when` ( $\emph{conditional-expression}$ ) $\C{// optional guard}$ … … 2391 2172 The left example only accepts @mem1@ if @C1@ is true or only @mem2@ if @C2@ is true. 2392 2173 The right example accepts either @mem1@ or @mem2@ if @C1@ and @C2@ are true. 2393 Hence, the @waitfor@ has parallel semantics, accepting any true @when@ clause. 2394 2395 An interesting use of @waitfor@ is accepting the @mutex@ destructor to know when an object is deallocated, \eg assume the bounded buffer is restructured from a monitor to a thread with the following @main@. 2174 2175 An interesting use of @waitfor@ is accepting the @mutex@ destructor to know when an object is deallocated, \eg assume the bounded buffer is restructred from a monitor to a thread with the following @main@. 2396 2176 \begin{cfa} 2397 2177 void main( Buffer(T) & buffer ) with(buffer) { 2398 2178 for () { 2399 `waitfor( ^?{} :buffer )` break;2400 or when ( count != 20 ) waitfor( insert :buffer ) { ... }2401 or when ( count != 0 ) waitfor( remove :buffer ) { ... }2179 `waitfor( ^?{}, buffer )` break; 2180 or when ( count != 20 ) waitfor( insert, buffer ) { ... } 2181 or when ( count != 0 ) waitfor( remove, buffer ) { ... } 2402 2182 } 2403 2183 // clean up … … 2412 2192 2413 2193 2414 \subsection{Bulk barging prevention}2415 2416 Figure~\ref{f:BulkBargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signal ing semantics.2194 \subsection{Bulk Barging Prevention} 2195 2196 Figure~\ref{f:BulkBargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signalling semantics. 2417 2197 The complexity begins at the end of the inner @mutex@ statement, where the semantics of internal scheduling need to be extended for multiple monitors. 2418 2198 The problem is that bulk acquire is used in the inner @mutex@ statement where one of the monitors is already acquired. 2419 When the signal ing thread reaches the end of the inner @mutex@ statement, it should transfer ownership of @m1@ and @m2@ to the waiting threads to prevent barging into the outer @mutex@ statement by another thread.2420 However, both the signal ing and waiting threads W1 and W2 need some subset of monitors @m1@ and @m2@.2199 When the signalling thread reaches the end of the inner @mutex@ statement, it should transfer ownership of @m1@ and @m2@ to the waiting threads to prevent barging into the outer @mutex@ statement by another thread. 2200 However, both the signalling and waiting threads W1 and W2 need some subset of monitors @m1@ and @m2@. 2421 2201 \begin{cquote} 2422 2202 condition c: (order 1) W2(@m2@), W1(@m1@,@m2@)\ \ \ or\ \ \ (order 2) W1(@m1@,@m2@), W2(@m2@) \\ … … 2485 2265 \end{figure} 2486 2266 2487 One scheduling solution is for the signaller S to keep ownership of all locks until the last lock is ready to be transferred, because this semantics fits most closely to the behavio r of single-monitor scheduling.2488 However, this solution is inefficient if W2 waited first and immediate passed @m2@ when released, while S retains @m1@ until completion of the outer mutex statement.2267 One scheduling solution is for the signaller S to keep ownership of all locks until the last lock is ready to be transferred, because this semantics fits most closely to the behaviour of single-monitor scheduling. 2268 However, this solution is inefficient if W2 waited first and can be immediate passed @m2@ when released, while S retains @m1@ until completion of the outer mutex statement. 2489 2269 If W1 waited first, the signaller must retain @m1@ amd @m2@ until completion of the outer mutex statement and then pass both to W1. 2490 2270 % Furthermore, there is an execution sequence where the signaller always finds waiter W2, and hence, waiter W1 starves. 2491 To support th ese efficient semantics and prevent barging, the implementation maintains a list of monitors acquired for each blocked thread.2492 When a signaller exits or waits in a m utex function orstatement, the front waiter on urgent is unblocked if all its monitors are released.2493 Implementing a fast subset check for the necessar ily released monitors is important and discussed in the following sections.2271 To support this efficient semantics (and prevent barging), the implementation maintains a list of monitors acquired for each blocked thread. 2272 When a signaller exits or waits in a monitor function/statement, the front waiter on urgent is unblocked if all its monitors are released. 2273 Implementing a fast subset check for the necessary released monitors is important. 2494 2274 % The benefit is encapsulating complexity into only two actions: passing monitors to the next owner when they should be released and conditionally waking threads if all conditions are met. 2495 2275 2496 2276 2497 \subsection{\texorpdfstring{\protect\lstinline@waitfor@ Implementation}{waitfor Implementation}} 2498 \label{s:waitforImplementation} 2499 2500 In a statically typed object-oriented programming language, a class has an exhaustive list of members, even when members are added via static inheritance (see Figure~\ref{f:uCinheritance}). 2501 Knowing all members at compilation, even separate compilation, allows uniquely numbered them so the accept-statement implementation can use a fast and compact bit mask with $O(1)$ compare. 2502 2503 \begin{figure} 2504 \centering 2505 \begin{lrbox}{\myboxA} 2506 \begin{uC++}[aboveskip=0pt,belowskip=0pt] 2507 $\emph{translation unit 1}$ 2508 _Monitor B { // common type in .h file 2509 _Mutex virtual void `f`( ... ); 2510 _Mutex virtual void `g`( ... ); 2511 _Mutex virtual void w1( ... ) { ... _Accept(`f`, `g`); ... } 2512 }; 2513 $\emph{translation unit 2}$ 2514 // include B 2515 _Monitor D : public B { // inherit 2516 _Mutex void `h`( ... ); // add 2517 _Mutex void w2( ... ) { ... _Accept(`f`, `h`); ... } 2518 }; 2519 \end{uC++} 2520 \end{lrbox} 2521 2522 \begin{lrbox}{\myboxB} 2523 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2524 $\emph{translation unit 1}$ 2525 monitor M { ... }; // common type in .h file 2526 void `f`( M & mutex m, ... ); 2527 void `g`( M & mutex m, ... ); 2528 void w1( M & mutex m, ... ) { ... waitfor(`f`, `g` : m); ... } 2529 2530 $\emph{translation unit 2}$ 2531 // include M 2532 extern void `f`( M & mutex m, ... ); // import f but not g 2533 void `h`( M & mutex m ); // add 2534 void w2( M & mutex m, ... ) { ... waitfor(`f`, `h` : m); ... } 2535 2536 \end{cfa} 2537 \end{lrbox} 2538 2539 \subfloat[\uC]{\label{f:uCinheritance}\usebox\myboxA} 2540 \hspace{3pt} 2541 \vrule 2542 \hspace{3pt} 2543 \subfloat[\CFA]{\label{f:CFinheritance}\usebox\myboxB} 2544 \caption{Member / function visibility} 2545 \label{f:MemberFunctionVisibility} 2546 \end{figure} 2547 2548 However, the @waitfor@ statement in translation unit 2 (see Figure~\ref{f:CFinheritance}) cannot see function @g@ in translation unit 1 precluding a unique numbering for a bit-mask because the monitor type only carries the protected shared data. 2277 \subsection{Loose Object Definitions} 2278 \label{s:LooseObjectDefinitions} 2279 2280 In an object-oriented programming language, a class includes an exhaustive list of operations. 2281 A new class can add members via static inheritance but the subclass still has an exhaustive list of operations. 2282 (Dynamic member adding, \eg JavaScript~\cite{JavaScript}, is not considered.) 2283 In the object-oriented scenario, the type and all its operators are always present at compilation (even separate compilation), so it is possible to number the operations in a bit mask and use an $O(1)$ compare with a similar bit mask created for the operations specified in a @waitfor@. 2284 2285 However, in \CFA, monitor functions can be statically added/removed in translation units, making a fast subset check difficult. 2286 \begin{cfa} 2287 monitor M { ... }; // common type, included in .h file 2288 translation unit 1 2289 void `f`( M & mutex m ); 2290 void g( M & mutex m ) { waitfor( `f`, m ); } 2291 translation unit 2 2292 void `f`( M & mutex m ); $\C{// replacing f and g for type M in this translation unit}$ 2293 void `g`( M & mutex m ); 2294 void h( M & mutex m ) { waitfor( `f`, m ) or waitfor( `g`, m ); } $\C{// extending type M in this translation unit}$ 2295 \end{cfa} 2296 The @waitfor@ statements in each translation unit cannot form a unique bit-mask because the monitor type does not carry that information. 2297 Hence, function pointers are used to identify the functions listed in the @waitfor@ statement, stored in a variable-sized array. 2298 Then, the same implementation approach used for the urgent stack is used for the calling queue. 2299 Each caller has a list of monitors acquired, and the @waitfor@ statement performs a (usually short) linear search matching functions in the @waitfor@ list with called functions, and then verifying the associated mutex locks can be transfers. 2549 2300 (A possible way to construct a dense mapping is at link or load-time.) 2550 Hence, function pointers are used to identify the functions listed in the @waitfor@ statement, stored in a variable-sized array. 2551 Then, the same implementation approach used for the urgent stack (see Section~\ref{s:Scheduling}) is used for the calling queue. 2552 Each caller has a list of monitors acquired, and the @waitfor@ statement performs a short linear search matching functions in the @waitfor@ list with called functions, and then verifying the associated mutex locks can be transferred. 2553 2554 2555 \subsection{Multimonitor scheduling} 2301 2302 2303 \subsection{Multi-Monitor Scheduling} 2556 2304 \label{s:Multi-MonitorScheduling} 2557 2305 2558 External scheduling, like internal scheduling, becomes significantly more complex for multi monitor semantics.2306 External scheduling, like internal scheduling, becomes significantly more complex for multi-monitor semantics. 2559 2307 Even in the simplest case, new semantics need to be established. 2560 2308 \begin{cfa} … … 2565 2313 The solution is for the programmer to disambiguate: 2566 2314 \begin{cfa} 2567 waitfor( f :`m2` ); $\C{// wait for call to f with argument m2}$2315 waitfor( f, `m2` ); $\C{// wait for call to f with argument m2}$ 2568 2316 \end{cfa} 2569 2317 Both locks are acquired by function @g@, so when function @f@ is called, the lock for monitor @m2@ is passed from @g@ to @f@, while @g@ still holds lock @m1@. 2570 This behavio r can be extended to the multimonitor @waitfor@ statement.2318 This behaviour can be extended to the multi-monitor @waitfor@ statement. 2571 2319 \begin{cfa} 2572 2320 monitor M { ... }; 2573 2321 void f( M & mutex m1, M & mutex m2 ); 2574 void g( M & mutex m1, M & mutex m2 ) { waitfor( f :`m1, m2` ); $\C{// wait for call to f with arguments m1 and m2}$2322 void g( M & mutex m1, M & mutex m2 ) { waitfor( f, `m1, m2` ); $\C{// wait for call to f with arguments m1 and m2}$ 2575 2323 \end{cfa} 2576 2324 Again, the set of monitors passed to the @waitfor@ statement must be entirely contained in the set of monitors already acquired by the accepting function. 2577 % Also, the order of the monitors in a @waitfor@ statement must match the order of the mutex parameters.2578 2579 Figure~\ref{f:UnmatchedMutexSets} shows internal and external scheduling with multiple monitors that must match exactly with a signaling or accepting thread, \ie partial matching results in waiting.2580 In both cases, the set of monitors is disjoint so unblocking is impossible.2325 Also, the order of the monitors in a @waitfor@ statement is unimportant. 2326 2327 Figure~\ref{f:UnmatchedMutexSets} shows an example where, for internal and external scheduling with multiple monitors, a signalling or accepting thread must match exactly, \ie partial matching results in waiting. 2328 For both examples, the set of monitors is disjoint so unblocking is impossible. 2581 2329 2582 2330 \begin{figure} … … 2607 2355 } 2608 2356 void g( M1 & mutex m1, M2 & mutex m2 ) { 2609 waitfor( f :m1, m2 );2357 waitfor( f, m1, m2 ); 2610 2358 } 2611 2359 g( `m11`, m2 ); // block on accept … … 2622 2370 \end{figure} 2623 2371 2372 2373 \subsection{\texorpdfstring{\protect\lstinline@mutex@ Threads}{mutex Threads}} 2374 2375 Threads in \CFA can also be monitors to allow \emph{direct communication} among threads, \ie threads can have mutex functions that are called by other threads. 2376 Hence, all monitor features are available when using threads. 2377 Figure~\ref{f:DirectCommunication} shows a comparison of direct call communication in \CFA with direct channel communication in Go. 2378 (Ada provides a similar mechanism to the \CFA direct communication.) 2379 The program main in both programs communicates directly with the other thread versus indirect communication where two threads interact through a passive monitor. 2380 Both direct and indirection thread communication are valuable tools in structuring concurrent programs. 2381 2624 2382 \begin{figure} 2625 2383 \centering … … 2628 2386 2629 2387 struct Msg { int i, j; }; 2630 mutexthread GoRtn { int i; float f; Msg m; };2388 thread GoRtn { int i; float f; Msg m; }; 2631 2389 void mem1( GoRtn & mutex gortn, int i ) { gortn.i = i; } 2632 2390 void mem2( GoRtn & mutex gortn, float f ) { gortn.f = f; } … … 2634 2392 void ^?{}( GoRtn & mutex ) {} 2635 2393 2636 void main( GoRtn & mutex gortn ) with(gortn) {// thread starts2394 void main( GoRtn & gortn ) with( gortn ) { // thread starts 2637 2395 2638 2396 for () { 2639 2397 2640 `waitfor( mem1 :gortn )` sout | i; // wait for calls2641 or `waitfor( mem2 :gortn )` sout | f;2642 or `waitfor( mem3 :gortn )` sout | m.i | m.j;2643 or `waitfor( ^?{} : gortn )` break; // low priority2398 `waitfor( mem1, gortn )` sout | i; // wait for calls 2399 or `waitfor( mem2, gortn )` sout | f; 2400 or `waitfor( mem3, gortn )` sout | m.i | m.j; 2401 or `waitfor( ^?{}, gortn )` break; 2644 2402 2645 2403 } … … 2695 2453 \hspace{3pt} 2696 2454 \subfloat[Go]{\label{f:Gochannel}\usebox\myboxB} 2697 \caption{Direct versus indirect communication} 2698 \label{f:DirectCommunicationComparison} 2699 2700 \medskip 2701 2702 \begin{cfa} 2703 mutex thread DatingService { 2704 condition Girls[CompCodes], Boys[CompCodes]; 2705 int girlPhoneNo, boyPhoneNo, ccode; 2706 }; 2707 int girl( DatingService & mutex ds, int phoneno, int code ) with( ds ) { 2708 girlPhoneNo = phoneno; ccode = code; 2709 `wait( Girls[ccode] );` $\C{// wait for boy}$ 2710 girlPhoneNo = phoneno; return boyPhoneNo; 2711 } 2712 int boy( DatingService & mutex ds, int phoneno, int code ) with( ds ) { 2713 boyPhoneNo = phoneno; ccode = code; 2714 `wait( Boys[ccode] );` $\C{// wait for girl}$ 2715 boyPhoneNo = phoneno; return girlPhoneNo; 2716 } 2717 void main( DatingService & ds ) with( ds ) { $\C{// thread starts, ds defaults to mutex}$ 2718 for () { 2719 waitfor( ^?{} ) break; $\C{// high priority}$ 2720 or waitfor( girl ) $\C{// girl called, compatible boy ? restart boy then girl}$ 2721 if ( ! is_empty( Boys[ccode] ) ) { `signal_block( Boys[ccode] ); signal_block( Girls[ccode] );` } 2722 or waitfor( boy ) { $\C{// boy called, compatible girl ? restart girl then boy}$ 2723 if ( ! is_empty( Girls[ccode] ) ) { `signal_block( Girls[ccode] ); signal_block( Boys[ccode] );` } 2724 } 2725 } 2726 \end{cfa} 2727 \caption{Direct communication dating service} 2728 \label{f:DirectCommunicationDatingService} 2455 \caption{Direct communication} 2456 \label{f:DirectCommunication} 2729 2457 \end{figure} 2730 2458 … … 2741 2469 void main( Ping & pi ) { 2742 2470 for ( 10 ) { 2743 `waitfor( ping :pi );`2471 `waitfor( ping, pi );` 2744 2472 `pong( po );` 2745 2473 } … … 2754 2482 for ( 10 ) { 2755 2483 `ping( pi );` 2756 `waitfor( pong :po );`2484 `waitfor( pong, po );` 2757 2485 } 2758 2486 } … … 2765 2493 % \label{f:pingpong} 2766 2494 % \end{figure} 2767 Note, the ping/pong threads are globally declared, @pi@/@po@, and hence, start and possibly completebefore the program main starts.2495 Note, the ping/pong threads are globally declared, @pi@/@po@, and hence, start (and possibly complete) before the program main starts. 2768 2496 \end{comment} 2769 2497 2770 2498 2771 \subsection{\texorpdfstring{\protect\lstinline@mutex@ Generators / coroutines / threads}{monitor Generators / coroutines / threads}} 2772 2773 \CFA generators, coroutines, and threads can also be @mutex@ (Table~\ref{t:ExecutionPropertyComposition} cases 4, 6, 12) allowing safe \emph{direct communication} with threads, \ie the custom types can have mutex functions that are called by other threads. 2774 All monitor features are available within these mutex functions. 2775 For example, if the formatter generator or coroutine equivalent in Figure~\ref{f:CFAFormatGen} is extended with the monitor property and this interface function is used to communicate with the formatter: 2776 \begin{cfa} 2777 void fmt( Fmt & mutex fmt, char ch ) { fmt.ch = ch; resume( fmt ) } 2778 \end{cfa} 2779 multiple threads can safely pass characters for formatting. 2780 2781 Figure~\ref{f:DirectCommunicationComparison} shows a comparison of direct call-communication in \CFA versus indirect channel-communication in Go. 2782 (Ada has a similar mechanism to \CFA direct communication.) 2783 % The thread main function is by default @mutex@, so the @mutex@ qualifier for the thread parameter is optional. 2784 % The reason is that the thread logically starts instantaneously in the thread main acquiring its mutual exclusion, so it starts before any calls to prepare for synchronizing these calls. 2785 The \CFA program @main@ uses the call/return paradigm to directly communicate with the @GoRtn main@, whereas Go switches to the unbuffered channel paradigm to indirectly communicate with the goroutine. 2786 Communication by multiple threads is safe for the @gortn@ thread via mutex calls in \CFA or channel assignment in Go. 2787 The difference between call and channel send occurs for buffered channels making the send asynchronous. 2788 In \CFA, asynchronous call and multiple buffers are provided using an administrator and worker threads~\cite{Gentleman81} and/or futures (not discussed). 2789 2790 Figure~\ref{f:DirectCommunicationDatingService} shows the dating-service problem in Figure~\ref{f:DatingServiceMonitor} extended from indirect monitor communication to direct thread communication. 2791 When converting a monitor to a thread (server), the coding pattern is to move as much code as possible from the accepted functions into the thread main so it does as much work as possible. 2792 Notice, the dating server is postponing requests for an unspecified time while continuing to accept new requests. 2793 For complex servers, \eg web-servers, there can be hundreds of lines of code in the thread main and safe interaction with clients can be complex. 2499 \subsection{Execution Properties} 2500 2501 Table~\ref{t:ObjectPropertyComposition} shows how the \CFA high-level constructs cover 3 fundamental execution properties: thread, stateful function, and mutual exclusion. 2502 Case 1 is a basic object, with none of the new execution properties. 2503 Case 2 allows @mutex@ calls to Case 1 to protect shared data. 2504 Case 3 allows stateful functions to suspend/resume but restricts operations because the state is stackless. 2505 Case 4 allows @mutex@ calls to Case 3 to protect shared data. 2506 Cases 5 and 6 are the same as 3 and 4 without restriction because the state is stackful. 2507 Cases 7 and 8 are rejected because a thread cannot execute without a stackful state in a preemptive environment when context switching from the signal handler. 2508 Cases 9 and 10 have a stackful thread without and with @mutex@ calls. 2509 For situations where threads do not require direct communication, case 9 provides faster creation/destruction by eliminating @mutex@ setup. 2510 2511 \begin{table} 2512 \caption{Object property composition} 2513 \centering 2514 \label{t:ObjectPropertyComposition} 2515 \renewcommand{\arraystretch}{1.25} 2516 %\setlength{\tabcolsep}{5pt} 2517 \begin{tabular}{c|c||l|l} 2518 \multicolumn{2}{c||}{object properties} & \multicolumn{2}{c}{mutual exclusion} \\ 2519 \hline 2520 thread & stateful & \multicolumn{1}{c|}{No} & \multicolumn{1}{c}{Yes} \\ 2521 \hline 2522 \hline 2523 No & No & \textbf{1}\ \ \ aggregate type & \textbf{2}\ \ \ @monitor@ aggregate type \\ 2524 \hline 2525 No & Yes (stackless) & \textbf{3}\ \ \ @generator@ & \textbf{4}\ \ \ @monitor@ @generator@ \\ 2526 \hline 2527 No & Yes (stackful) & \textbf{5}\ \ \ @coroutine@ & \textbf{6}\ \ \ @monitor@ @coroutine@ \\ 2528 \hline 2529 Yes & No / Yes (stackless) & \textbf{7}\ \ \ {\color{red}rejected} & \textbf{8}\ \ \ {\color{red}rejected} \\ 2530 \hline 2531 Yes & Yes (stackful) & \textbf{9}\ \ \ @thread@ & \textbf{10}\ \ @monitor@ @thread@ \\ 2532 \end{tabular} 2533 \end{table} 2794 2534 2795 2535 … … 2797 2537 2798 2538 For completeness and efficiency, \CFA provides a standard set of low-level locks: recursive mutex, condition, semaphore, barrier, \etc, and atomic instructions: @fetchAssign@, @fetchAdd@, @testSet@, @compareSet@, \etc. 2799 Some of these low-level mechanism s are used to build the \CFA runtime, but we alwaysadvocate using high-level mechanisms whenever possible.2539 Some of these low-level mechanism are used in the \CFA runtime, but we strongly advocate using high-level mechanisms whenever possible. 2800 2540 2801 2541 … … 2811 2551 % 2812 2552 % 2813 % \subsection{User threads}2553 % \subsection{User Threads} 2814 2554 % 2815 2555 % A direct improvement on kernel threads is user threads, \eg Erlang~\cite{Erlang} and \uC~\cite{uC++book}. … … 2826 2566 2827 2567 \begin{comment} 2828 \subsection{Thread pools}2568 \subsection{Thread Pools} 2829 2569 2830 2570 In contrast to direct threading is indirect \newterm{thread pools}, \eg Java @executor@, where small jobs (work units) are inserted into a work pool for execution. 2831 If the jobs are dependent, \ie interact, there is an implicit dependency graph that ties them together.2571 If the jobs are dependent, \ie interact, there is an implicit/explicit dependency graph that ties them together. 2832 2572 While removing direct concurrency, and hence the amount of context switching, thread pools significantly limit the interaction that can occur among jobs. 2833 2573 Indeed, jobs should not block because that also blocks the underlying thread, which effectively means the CPU utilization, and therefore throughput, suffers. … … 2840 2580 \begin{cfa} 2841 2581 struct Adder { 2842 int * row, cols;2582 int * row, cols; 2843 2583 }; 2844 2584 int operator()() { … … 2899 2639 \label{s:RuntimeStructureCluster} 2900 2640 2901 A \newterm{cluster} is a collection of user and kernel threads, where the kernel threads run the user threads from the cluster's ready queue, and the operating system runs the kernel threads on the processors from its ready queue~\cite{Buhr90a}. 2902 The term \newterm{virtual processor} is introduced as a synonym for kernel thread to disambiguate between user and kernel thread. 2903 From the language perspective, a virtual processor is an actual processor (core). 2904 2641 A \newterm{cluster} is a collection of threads and virtual processors (abstract kernel-thread) that execute the (user) threads from its own ready queue (like an OS executing kernel threads). 2905 2642 The purpose of a cluster is to control the amount of parallelism that is possible among threads, plus scheduling and other execution defaults. 2906 2643 The default cluster-scheduler is single-queue multi-server, which provides automatic load-balancing of threads on processors. 2907 However, the design allows changing the scheduler, \eg multi-queue multi server with work-stealing/sharing across the virtual processors.2644 However, the design allows changing the scheduler, \eg multi-queue multi-server with work-stealing/sharing across the virtual processors. 2908 2645 If several clusters exist, both threads and virtual processors, can be explicitly migrated from one cluster to another. 2909 2646 No automatic load balancing among clusters is performed by \CFA. … … 2915 2652 2916 2653 2917 \subsection{Virtual processor}2654 \subsection{Virtual Processor} 2918 2655 \label{s:RuntimeStructureProcessor} 2919 2656 2920 A virtual processor is implemented by a kernel thread , \eg UNIX process, which are scheduled for execution on a hardware processor by the underlying operating system.2657 A virtual processor is implemented by a kernel thread (\eg UNIX process), which are scheduled for execution on a hardware processor by the underlying operating system. 2921 2658 Programs may use more virtual processors than hardware processors. 2922 2659 On a multiprocessor, kernel threads are distributed across the hardware processors resulting in virtual processors executing in parallel. 2923 (It is possible to use affinity to lock a virtual processor onto a particular hardware processor~\cite{affinityLinux, affinityWindows}, which is used when caching issues occur or for heterogeneous hardware processors.) %, affinityFreebsd, affinityNetbsd, affinityMacosx2660 (It is possible to use affinity to lock a virtual processor onto a particular hardware processor~\cite{affinityLinux, affinityWindows, affinityFreebsd, affinityNetbsd, affinityMacosx}, which is used when caching issues occur or for heterogeneous hardware processors.) 2924 2661 The \CFA runtime attempts to block unused processors and unblock processors as the system load increases; 2925 balancing the workload with processors is difficult because it requires future knowledge, \ie what will the applicat ion workload do next.2662 balancing the workload with processors is difficult because it requires future knowledge, \ie what will the applicaton workload do next. 2926 2663 Preemption occurs on virtual processors rather than user threads, via operating-system interrupts. 2927 2664 Thus virtual processors execute user threads, where preemption frequency applies to a virtual processor, so preemption occurs randomly across the executed user threads. … … 2933 2670 \label{s:Implementation} 2934 2671 2935 A primary implementation challenge is avoiding contention from dynamically allocating memory because of bulk acquire, \eg the internal-scheduling design is almostfree of allocations.2672 A primary implementation challenge is avoiding contention from dynamically allocating memory because of bulk acquire, \eg the internal-scheduling design is (almost) free of allocations. 2936 2673 All blocking operations are made by parking threads onto queues, therefore all queues are designed with intrusive nodes, where each node has preallocated link fields for chaining. 2937 2674 Furthermore, several bulk-acquire operations need a variable amount of memory. 2938 2675 This storage is allocated at the base of a thread's stack before blocking, which means programmers must add a small amount of extra space for stacks. 2939 2676 2940 In \CFA, ordering of monitor acquisition relies on memory ordering to prevent deadlock~\cite{Havender68}, because all objects have distinct non overlapping memory layouts, and mutual-exclusion for a monitor is only defined for its lifetime.2677 In \CFA, ordering of monitor acquisition relies on memory ordering to prevent deadlock~\cite{Havender68}, because all objects have distinct non-overlapping memory layouts, and mutual-exclusion for a monitor is only defined for its lifetime. 2941 2678 When a mutex call is made, pointers to the concerned monitors are aggregated into a variable-length array and sorted. 2942 2679 This array persists for the entire duration of the mutual exclusion and is used extensively for synchronization operations. … … 2957 2694 2958 2695 Nondeterministic preemption provides fairness from long-running threads, and forces concurrent programmers to write more robust programs, rather than relying on code between cooperative scheduling to be atomic. 2959 This atomic reliance can fail on multi core machines, because execution across cores is nondeterministic.2960 A different reason for not supporting preemption is that it significantly complicates the runtime system, \eg Windowsruntime does not support interrupts and on Linux systems, interrupts are complex (see below).2696 This atomic reliance can fail on multi-core machines, because execution across cores is nondeterministic. 2697 A different reason for not supporting preemption is that it significantly complicates the runtime system, \eg Microsoft runtime does not support interrupts and on Linux systems, interrupts are complex (see below). 2961 2698 Preemption is normally handled by setting a countdown timer on each virtual processor. 2962 When the timer expires, an interrupt is delivered, and its signalhandler resets the countdown timer, and if the virtual processor is executing in user code, the signal handler performs a user-level context-switch, or if executing in the language runtime kernel, the preemption is ignored or rolled forward to the point where the runtime kernel context switches back to user code.2699 When the timer expires, an interrupt is delivered, and the interrupt handler resets the countdown timer, and if the virtual processor is executing in user code, the signal handler performs a user-level context-switch, or if executing in the language runtime kernel, the preemption is ignored or rolled forward to the point where the runtime kernel context switches back to user code. 2963 2700 Multiple signal handlers may be pending. 2964 2701 When control eventually switches back to the signal handler, it returns normally, and execution continues in the interrupted user thread, even though the return from the signal handler may be on a different kernel thread than the one where the signal is delivered. 2965 2702 The only issue with this approach is that signal masks from one kernel thread may be restored on another as part of returning from the signal handler; 2966 2703 therefore, the same signal mask is required for all virtual processors in a cluster. 2967 Because preemption interval is usually long (1 ms) performance cost is negligible. 2968 2969 Linux switched a decade ago from specific to arbitrary virtual-processor signal-delivery for applications with multiple kernel threads. 2970 In the new semantics, a virtual-processor directed signal may be delivered to any virtual processor created by the application that does not have the signal blocked. 2704 Because preemption frequency is usually long (1 millisecond) performance cost is negligible. 2705 2706 Linux switched a decade ago from specific to arbitrary process signal-delivery for applications with multiple kernel threads. 2707 \begin{cquote} 2708 A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. 2709 If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which it will deliver the signal. 2710 SIGNAL(7) - Linux Programmer's Manual 2711 \end{cquote} 2971 2712 Hence, the timer-expiry signal, which is generated \emph{externally} by the Linux kernel to an application, is delivered to any of its Linux subprocesses (kernel threads). 2972 2713 To ensure each virtual processor receives a preemption signal, a discrete-event simulation is run on a special virtual processor, and only it sets and receives timer events. … … 2976 2717 2977 2718 2978 \subsection{Debug kernel}2979 2980 There are two versions of the \CFA runtime kernel: debug and non debug.2981 The debugging version has many runtime checks and internal assertions, \eg stack nonwritableguard page, and checks for stack overflow whenever context switches occur among coroutines and threads, which catches most stack overflows.2982 After a program is debugged, the non debugging version can be used to significantly decrease space and increase performance.2719 \subsection{Debug Kernel} 2720 2721 There are two versions of the \CFA runtime kernel: debug and non-debug. 2722 The debugging version has many runtime checks and internal assertions, \eg stack (non-writable) guard page, and checks for stack overflow whenever context switches occur among coroutines and threads, which catches most stack overflows. 2723 After a program is debugged, the non-debugging version can be used to significantly decrease space and increase performance. 2983 2724 2984 2725 … … 2986 2727 \label{s:Performance} 2987 2728 2988 To test the performance of the \CFA runtime, a series of microbenchmarks are used to compare \CFA with pthreads, Java 11.0.6, Go 1.12.6, Rust 1.37.0, Python 3.7.6, Node.js 12.14.1, and \uC 7.0.0. 2989 For comparison, the package must be multiprocessor (M:N), which excludes libdil and libmil~\cite{libdill} (M:1)), and use a shared-memory programming model, \eg not message passing. 2990 The benchmark computer is an AMD Opteron\texttrademark\ 6380 NUMA 64-core, 8 socket, 2.5 GHz processor, running Ubuntu 16.04.6 LTS, and pthreads/\CFA/\uC are compiled with gcc 9.2.1. 2991 2992 All benchmarks are run using the following harness. 2993 (The Java harness is augmented to circumvent JIT issues.) 2994 \begin{cfa} 2995 #define BENCH( `run` ) uint64_t start = cputime_ns(); `run;` double result = (double)(cputime_ns() - start) / N; 2996 \end{cfa} 2997 where CPU time in nanoseconds is from the appropriate language clock. 2998 Each benchmark is performed @N@ times, where @N@ is selected so the benchmark runs in the range of 2--20 s for the specific programming language; 2999 each @N@ appears after the experiment name in the following tables. 3000 The total time is divided by @N@ to obtain the average time for a benchmark. 3001 Each benchmark experiment is run 13 times and the average appears in the table. 3002 For languages with a runtime JIT (Java, Node.js, Python), a single half-hour long experiment is run to check stability; 3003 all long-experiment results are statistically equivalent, \ie median/average/SD correlate with the short-experiment results, indicating the short experiments reached a steady state. 3004 All omitted tests for other languages are functionally identical to the \CFA tests and available online~\cite{CforallConcurrentBenchmarks}. 3005 3006 \subsection{Creation} 3007 3008 Creation is measured by creating and deleting a specific kind of control-flow object. 3009 Figure~\ref{f:creation} shows the code for \CFA with results in Table~\ref{t:creation}. 3010 Note, the call stacks of \CFA coroutines are lazily created on the first resume, therefore the cost of creation with and without a stack are presented. 2729 To verify the implementation of the \CFA runtime, a series of microbenchmarks are performed comparing \CFA with pthreads, Java OpenJDK-9, Go 1.12.6 and \uC 7.0.0. 2730 For comparison, the package must be multi-processor (M:N), which excludes libdill/libmil~\cite{libdill} (M:1)), and use a shared-memory programming model, \eg not message passing. 2731 The benchmark computer is an AMD Opteron\texttrademark\ 6380 NUMA 64-core, 8 socket, 2.5 GHz processor, running Ubuntu 16.04.6 LTS, and \CFA/\uC are compiled with gcc 6.5. 2732 2733 All benchmarks are run using the following harness. (The Java harness is augmented to circumvent JIT issues.) 2734 \begin{cfa} 2735 unsigned int N = 10_000_000; 2736 #define BENCH( `run` ) Time before = getTimeNsec(); `run;` Duration result = (getTimeNsec() - before) / N; 2737 \end{cfa} 2738 The method used to get time is @clock_gettime( CLOCK_REALTIME )@. 2739 Each benchmark is performed @N@ times, where @N@ varies depending on the benchmark; 2740 the total time is divided by @N@ to obtain the average time for a benchmark. 2741 Each benchmark experiment is run 31 times. 2742 All omitted tests for other languages are functionally identical to the \CFA tests and available online~\cite{CforallBenchMarks}. 2743 % tar --exclude=.deps --exclude=Makefile --exclude=Makefile.in --exclude=c.c --exclude=cxx.cpp --exclude=fetch_add.c -cvhf benchmark.tar benchmark 2744 2745 \paragraph{Object Creation} 2746 2747 Object creation is measured by creating/deleting the specific kind of concurrent object. 2748 Figure~\ref{f:creation} shows the code for \CFA, with results in Table~\ref{tab:creation}. 2749 The only note here is that the call stacks of \CFA coroutines are lazily created, therefore without priming the coroutine to force stack creation, the creation cost is artificially low. 3011 2750 3012 2751 \begin{multicols}{2} 3013 \begin{cfa}[xleftmargin=0pt] 3014 `coroutine` MyCoroutine {}; 3015 void ?{}( MyCoroutine & this ) { 3016 #ifdef EAGER 3017 resume( this ); 3018 #endif 3019 } 3020 void main( MyCoroutine & ) {} 2752 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2753 \begin{cfa} 2754 @thread@ MyThread {}; 2755 void @main@( MyThread & ) {} 3021 2756 int main() { 3022 BENCH( for ( N ) { `MyCoroutine c;`} )3023 sout | result ;3024 } 3025 \end{cfa} 3026 \captionof{figure}{\CFA creation benchmark}2757 BENCH( for ( N ) { @MyThread m;@ } ) 2758 sout | result`ns; 2759 } 2760 \end{cfa} 2761 \captionof{figure}{\CFA object-creation benchmark} 3027 2762 \label{f:creation} 3028 2763 … … 3030 2765 3031 2766 \vspace*{-16pt} 3032 \captionof{table}{ Creation comparison (nanoseconds)}3033 \label{t :creation}2767 \captionof{table}{Object creation comparison (nanoseconds)} 2768 \label{tab:creation} 3034 2769 3035 2770 \begin{tabular}[t]{@{}r*{3}{D{.}{.}{5.2}}@{}} 3036 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3037 \CFA generator (1B) & 0.6 & 0.6 & 0.0 \\ 3038 \CFA coroutine lazy (100M) & 13.4 & 13.1 & 0.5 \\ 3039 \CFA coroutine eager (10M) & 144.7 & 143.9 & 1.5 \\ 3040 \CFA thread (10M) & 466.4 & 468.0 & 11.3 \\ 3041 \uC coroutine (10M) & 155.6 & 155.7 & 1.7 \\ 3042 \uC thread (10M) & 523.4 & 523.9 & 7.7 \\ 3043 Python generator (10M) & 123.2 & 124.3 & 4.1 \\ 3044 Node.js generator (10M) & 33.4 & 33.5 & 0.3 \\ 3045 Goroutine thread (10M) & 751.0 & 750.5 & 3.1 \\ 3046 Rust tokio thread (10M) & 1860.0 & 1881.1 & 37.6 \\ 3047 Rust thread (250K) & 53801.0 & 53896.8 & 274.9 \\ 3048 Java thread (250K) & 119256.0 & 119679.2 & 2244.0 \\ 3049 % Java thread (1 000 000) & 123100.0 & 123052.5 & 751.6 \\ 3050 Pthreads thread (250K) & 31465.5 & 31419.5 & 140.4 2771 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2772 \CFA Coroutine Lazy & 13.2 & 13.1 & 0.44 \\ 2773 \CFA Coroutine Eager & 531.3 & 536.0 & 26.54 \\ 2774 \CFA Thread & 2074.9 & 2066.5 & 170.76 \\ 2775 \uC Coroutine & 89.6 & 90.5 & 1.83 \\ 2776 \uC Thread & 528.2 & 528.5 & 4.94 \\ 2777 Goroutine & 4068.0 & 4113.1 & 414.55 \\ 2778 Java Thread & 103848.5 & 104295.4 & 2637.57 \\ 2779 Pthreads & 33112.6 & 33127.1 & 165.90 3051 2780 \end{tabular} 3052 2781 \end{multicols} 3053 2782 3054 \vspace*{-10pt} 3055 \subsection{Internal scheduling} 3056 3057 Internal scheduling is measured using a cycle of two threads signaling and waiting. 3058 Figure~\ref{f:schedint} shows the code for \CFA, with results in Table~\ref{t:schedint}. 3059 Note, the \CFA incremental cost for bulk acquire is a fixed cost for small numbers of mutex objects. 3060 User-level threading has one kernel thread, eliminating contention between the threads (direct handoff of the kernel thread). 3061 Kernel-level threading has two kernel threads allowing some contention. 2783 2784 \paragraph{Context-Switching} 2785 2786 In procedural programming, the cost of a function call is important as modularization (refactoring) increases. 2787 (In many cases, a compiler inlines function calls to eliminate this cost.) 2788 Similarly, when modularization extends to coroutines/tasks, the time for a context switch becomes a relevant factor. 2789 The coroutine test is from resumer to suspender and from suspender to resumer, which is two context switches. 2790 The thread test is using yield to enter and return from the runtime kernel, which is two context switches. 2791 The difference in performance between coroutine and thread context-switch is the cost of scheduling for threads, whereas coroutines are self-scheduling. 2792 Figure~\ref{f:ctx-switch} only shows the \CFA code for coroutines/threads (other systems are similar) with all results in Table~\ref{tab:ctx-switch}. 3062 2793 3063 2794 \begin{multicols}{2} 3064 \setlength{\tabcolsep}{3pt} 3065 \begin{cfa}[xleftmargin=0pt] 2795 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2796 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2797 @coroutine@ C {} c; 2798 void main( C & ) { for ( ;; ) { @suspend;@ } } 2799 int main() { // coroutine test 2800 BENCH( for ( N ) { @resume( c );@ } ) 2801 sout | result`ns; 2802 } 2803 int main() { // task test 2804 BENCH( for ( N ) { @yield();@ } ) 2805 sout | result`ns; 2806 } 2807 \end{cfa} 2808 \captionof{figure}{\CFA context-switch benchmark} 2809 \label{f:ctx-switch} 2810 2811 \columnbreak 2812 2813 \vspace*{-16pt} 2814 \captionof{table}{Context switch comparison (nanoseconds)} 2815 \label{tab:ctx-switch} 2816 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 2817 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2818 C function & 1.8 & 1.8 & 0.01 \\ 2819 \CFA generator & 2.4 & 2.2 & 0.25 \\ 2820 \CFA Coroutine & 36.2 & 36.2 & 0.25 \\ 2821 \CFA Thread & 93.2 & 93.5 & 2.09 \\ 2822 \uC Coroutine & 52.0 & 52.1 & 0.51 \\ 2823 \uC Thread & 96.2 & 96.3 & 0.58 \\ 2824 Goroutine & 141.0 & 141.3 & 3.39 \\ 2825 Java Thread & 374.0 & 375.8 & 10.38 \\ 2826 Pthreads Thread & 361.0 & 365.3 & 13.19 2827 \end{tabular} 2828 \end{multicols} 2829 2830 2831 \paragraph{Mutual-Exclusion} 2832 2833 Uncontented mutual exclusion, which frequently occurs, is measured by entering/leaving a critical section. 2834 For monitors, entering and leaving a monitor function is measured. 2835 To put the results in context, the cost of entering a non-inline function and the cost of acquiring and releasing a @pthread_mutex@ lock is also measured. 2836 Figure~\ref{f:mutex} shows the code for \CFA with all results in Table~\ref{tab:mutex}. 2837 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 2838 2839 \begin{multicols}{2} 2840 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2841 \begin{cfa} 2842 @monitor@ M {} m1/*, m2, m3, m4*/; 2843 void __attribute__((noinline)) 2844 do_call( M & @mutex m/*, m2, m3, m4*/@ ) {} 2845 int main() { 2846 BENCH( 2847 for( N ) do_call( m1/*, m2, m3, m4*/ ); 2848 ) 2849 sout | result`ns; 2850 } 2851 \end{cfa} 2852 \captionof{figure}{\CFA acquire/release mutex benchmark} 2853 \label{f:mutex} 2854 2855 \columnbreak 2856 2857 \vspace*{-16pt} 2858 \captionof{table}{Mutex comparison (nanoseconds)} 2859 \label{tab:mutex} 2860 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 2861 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2862 test and test-and-test lock & 19.1 & 18.9 & 0.40 \\ 2863 \CFA @mutex@ function, 1 arg. & 45.9 & 46.6 & 1.45 \\ 2864 \CFA @mutex@ function, 2 arg. & 105.0 & 104.7 & 3.08 \\ 2865 \CFA @mutex@ function, 4 arg. & 165.0 & 167.6 & 5.65 \\ 2866 \uC @monitor@ member rtn. & 54.0 & 53.7 & 0.82 \\ 2867 Java synchronized method & 31.0 & 31.1 & 0.50 \\ 2868 Pthreads Mutex Lock & 33.6 & 32.6 & 1.14 2869 \end{tabular} 2870 \end{multicols} 2871 2872 2873 \paragraph{External Scheduling} 2874 2875 External scheduling is measured using a cycle of two threads calling and accepting the call using the @waitfor@ statement. 2876 Figure~\ref{f:ext-sched} shows the code for \CFA, with results in Table~\ref{tab:ext-sched}. 2877 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 2878 2879 \begin{multicols}{2} 2880 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2881 \vspace*{-16pt} 2882 \begin{cfa} 3066 2883 volatile int go = 0; 3067 `condition c;` 3068 `monitor` M {} m1/*, m2, m3, m4*/; 3069 void call( M & `mutex p1/*, p2, p3, p4*/` ) { 3070 `signal( c );` 3071 } 3072 void wait( M & `mutex p1/*, p2, p3, p4*/` ) { 2884 @monitor@ M {} m; 2885 thread T {}; 2886 void __attribute__((noinline)) 2887 do_call( M & @mutex@ ) {} 2888 void main( T & ) { 2889 while ( go == 0 ) { yield(); } 2890 while ( go == 1 ) { do_call( m ); } 2891 } 2892 int __attribute__((noinline)) 2893 do_wait( M & @mutex@ m ) { 3073 2894 go = 1; // continue other thread 3074 for ( N ) { `wait( c );` } ); 3075 } 3076 thread T {}; 3077 void main( T & ) { 3078 while ( go == 0 ) { yield(); } // waiter must start first 3079 BENCH( for ( N ) { call( m1/*, m2, m3, m4*/ ); } ) 3080 sout | result; 2895 BENCH( for ( N ) { @waitfor( do_call, m );@ } ) 2896 go = 0; // stop other thread 2897 sout | result`ns; 3081 2898 } 3082 2899 int main() { 3083 2900 T t; 3084 wait( m1/*, m2, m3, m4*/ ); 3085 } 3086 \end{cfa} 3087 \vspace*{-8pt} 2901 do_wait( m ); 2902 } 2903 \end{cfa} 2904 \captionof{figure}{\CFA external-scheduling benchmark} 2905 \label{f:ext-sched} 2906 2907 \columnbreak 2908 2909 \vspace*{-16pt} 2910 \captionof{table}{External-scheduling comparison (nanoseconds)} 2911 \label{tab:ext-sched} 2912 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 2913 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2914 \CFA @waitfor@, 1 @monitor@ & 376.4 & 376.8 & 7.63 \\ 2915 \CFA @waitfor@, 2 @monitor@ & 491.4 & 492.0 & 13.31 \\ 2916 \CFA @waitfor@, 4 @monitor@ & 681.0 & 681.7 & 19.10 \\ 2917 \uC @_Accept@ & 331.1 & 331.4 & 2.66 2918 \end{tabular} 2919 \end{multicols} 2920 2921 2922 \paragraph{Internal Scheduling} 2923 2924 Internal scheduling is measured using a cycle of two threads signalling and waiting. 2925 Figure~\ref{f:int-sched} shows the code for \CFA, with results in Table~\ref{tab:int-sched}. 2926 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 2927 Java scheduling is significantly greater because the benchmark explicitly creates multiple thread in order to prevent the JIT from making the program sequential, \ie removing all locking. 2928 2929 \begin{multicols}{2} 2930 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2931 \begin{cfa} 2932 volatile int go = 0; 2933 @monitor@ M { @condition c;@ } m; 2934 void __attribute__((noinline)) 2935 do_call( M & @mutex@ a1 ) { @signal( c );@ } 2936 thread T {}; 2937 void main( T & this ) { 2938 while ( go == 0 ) { yield(); } 2939 while ( go == 1 ) { do_call( m ); } 2940 } 2941 int __attribute__((noinline)) 2942 do_wait( M & mutex m ) with(m) { 2943 go = 1; // continue other thread 2944 BENCH( for ( N ) { @wait( c );@ } ); 2945 go = 0; // stop other thread 2946 sout | result`ns; 2947 } 2948 int main() { 2949 T t; 2950 do_wait( m ); 2951 } 2952 \end{cfa} 3088 2953 \captionof{figure}{\CFA Internal-scheduling benchmark} 3089 \label{f: schedint}2954 \label{f:int-sched} 3090 2955 3091 2956 \columnbreak … … 3093 2958 \vspace*{-16pt} 3094 2959 \captionof{table}{Internal-scheduling comparison (nanoseconds)} 3095 \label{t :schedint}2960 \label{tab:int-sched} 3096 2961 \bigskip 3097 2962 3098 2963 \begin{tabular}{@{}r*{3}{D{.}{.}{5.2}}@{}} 3099 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3100 \CFA @signal@, 1 monitor (10M) & 364.4 & 364.2 & 4.4 \\ 3101 \CFA @signal@, 2 monitor (10M) & 484.4 & 483.9 & 8.8 \\ 3102 \CFA @signal@, 4 monitor (10M) & 709.1 & 707.7 & 15.0 \\ 3103 \uC @signal@ monitor (10M) & 328.3 & 327.4 & 2.4 \\ 3104 Rust cond. variable (1M) & 7514.0 & 7437.4 & 397.2 \\ 3105 Java @notify@ monitor (1M) & 8717.0 & 8774.1 & 471.8 \\ 3106 % Java @notify@ monitor (100 000 000) & 8634.0 & 8683.5 & 330.5 \\ 3107 Pthreads cond. variable (1M) & 5553.7 & 5576.1 & 345.6 2964 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2965 \CFA @signal@, 1 @monitor@ & 372.6 & 374.3 & 14.17 \\ 2966 \CFA @signal@, 2 @monitor@ & 492.7 & 494.1 & 12.99 \\ 2967 \CFA @signal@, 4 @monitor@ & 749.4 & 750.4 & 24.74 \\ 2968 \uC @signal@ & 320.5 & 321.0 & 3.36 \\ 2969 Java @notify@ & 10160.5 & 10169.4 & 267.71 \\ 2970 Pthreads Cond. Variable & 4949.6 & 5065.2 & 363 3108 2971 \end{tabular} 3109 2972 \end{multicols} 3110 2973 3111 2974 3112 \subsection{External scheduling} 3113 3114 External scheduling is measured using a cycle of two threads calling and accepting the call using the @waitfor@ statement. 3115 Figure~\ref{f:schedext} shows the code for \CFA with results in Table~\ref{t:schedext}. 3116 Note, the \CFA incremental cost for bulk acquire is a fixed cost for small numbers of mutex objects. 3117 3118 \begin{multicols}{2} 3119 \setlength{\tabcolsep}{5pt} 3120 \vspace*{-16pt} 3121 \begin{cfa}[xleftmargin=0pt] 3122 `monitor` M {} m1/*, m2, m3, m4*/; 3123 void call( M & `mutex p1/*, p2, p3, p4*/` ) {} 3124 void wait( M & `mutex p1/*, p2, p3, p4*/` ) { 3125 for ( N ) { `waitfor( call : p1/*, p2, p3, p4*/ );` } 3126 } 3127 thread T {}; 3128 void main( T & ) { 3129 BENCH( for ( N ) { call( m1/*, m2, m3, m4*/ ); } ) 3130 sout | result; 3131 } 3132 int main() { 3133 T t; 3134 wait( m1/*, m2, m3, m4*/ ); 3135 } 3136 \end{cfa} 3137 \captionof{figure}{\CFA external-scheduling benchmark} 3138 \label{f:schedext} 3139 3140 \columnbreak 3141 3142 \vspace*{-18pt} 3143 \captionof{table}{External-scheduling comparison (nanoseconds)} 3144 \label{t:schedext} 3145 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 3146 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3147 \CFA @waitfor@, 1 monitor (10M) & 367.1 & 365.3 & 5.0 \\ 3148 \CFA @waitfor@, 2 monitor (10M) & 463.0 & 464.6 & 7.1 \\ 3149 \CFA @waitfor@, 4 monitor (10M) & 689.6 & 696.2 & 21.5 \\ 3150 \uC \lstinline[language=uC++]|_Accept| monitor (10M) & 328.2 & 329.1 & 3.4 \\ 3151 Go \lstinline[language=Golang]|select| channel (10M) & 365.0 & 365.5 & 1.2 3152 \end{tabular} 3153 \end{multicols} 3154 3155 \subsection{Mutual-Exclusion} 3156 3157 Uncontented mutual exclusion, which frequently occurs, is measured by entering and leaving a critical section. 3158 For monitors, entering and leaving a mutex function are measured, otherwise the language-appropriate mutex-lock is measured. 3159 For comparison, a spinning (vs.\ blocking) test-and-test-set lock is presented. 3160 Figure~\ref{f:mutex} shows the code for \CFA with results in Table~\ref{t:mutex}. 3161 Note the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 3162 3163 \begin{multicols}{2} 3164 \setlength{\tabcolsep}{3pt} 3165 \begin{cfa}[xleftmargin=0pt] 3166 `monitor` M {} m1/*, m2, m3, m4*/; 3167 call( M & `mutex p1/*, p2, p3, p4*/` ) {} 3168 int main() { 3169 BENCH( for( N ) call( m1/*, m2, m3, m4*/ ); ) 3170 sout | result; 3171 } 3172 \end{cfa} 3173 \captionof{figure}{\CFA acquire/release mutex benchmark} 3174 \label{f:mutex} 3175 3176 \columnbreak 3177 3178 \vspace*{-16pt} 3179 \captionof{table}{Mutex comparison (nanoseconds)} 3180 \label{t:mutex} 3181 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 3182 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3183 test-and-test-set lock (50M) & 19.1 & 18.9 & 0.4 \\ 3184 \CFA @mutex@ function, 1 arg. (50M) & 48.3 & 47.8 & 0.9 \\ 3185 \CFA @mutex@ function, 2 arg. (50M) & 86.7 & 87.6 & 1.9 \\ 3186 \CFA @mutex@ function, 4 arg. (50M) & 173.4 & 169.4 & 5.9 \\ 3187 \uC @monitor@ member rtn. (50M) & 54.8 & 54.8 & 0.1 \\ 3188 Goroutine mutex lock (50M) & 34.0 & 34.0 & 0.0 \\ 3189 Rust mutex lock (50M) & 33.0 & 33.2 & 0.8 \\ 3190 Java synchronized method (50M) & 31.0 & 30.9 & 0.5 \\ 3191 % Java synchronized method (10 000 000 000) & 31.0 & 30.2 & 0.9 \\ 3192 Pthreads mutex Lock (50M) & 31.0 & 31.1 & 0.4 3193 \end{tabular} 3194 \end{multicols} 3195 3196 \subsection{Context switching} 3197 3198 In procedural programming, the cost of a function call is important as modularization (refactoring) increases. 3199 (In many cases, a compiler inlines function calls to increase the size and number of basic blocks for optimizing.) 3200 Similarly, when modularization extends to coroutines and threads, the time for a context switch becomes a relevant factor. 3201 The coroutine test is from resumer to suspender and from suspender to resumer, which is two context switches. 3202 %For async-await systems, the test is scheduling and fulfilling @N@ empty promises, where all promises are allocated before versus interleaved with fulfillment to avoid garbage collection. 3203 For async-await systems, the test measures the cost of the @await@ expression entering the event engine by awaiting @N@ promises, where each created promise is resolved by an immediate event in the engine (using Node.js @setImmediate@). 3204 The thread test is using yield to enter and return from the runtime kernel, which is two context switches. 3205 The difference in performance between coroutine and thread context-switch is the cost of scheduling for threads, whereas coroutines are self-scheduling. 3206 Figure~\ref{f:ctx-switch} shows the \CFA code for a coroutine and thread with results in Table~\ref{t:ctx-switch}. 3207 3208 % From: Gregor Richards <gregor.richards@uwaterloo.ca> 3209 % To: "Peter A. Buhr" <pabuhr@plg2.cs.uwaterloo.ca> 3210 % Date: Fri, 24 Jan 2020 13:49:18 -0500 3211 % 3212 % I can also verify that the previous version, which just tied a bunch of promises together, *does not* go back to the 3213 % event loop at all in the current version of Node. Presumably they're taking advantage of the fact that the ordering of 3214 % events is intentionally undefined to just jump right to the next 'then' in the chain, bypassing event queueing 3215 % entirely. That's perfectly correct behavior insofar as its difference from the specified behavior isn't observable, but 3216 % it isn't typical or representative of much anything useful, because most programs wouldn't have whole chains of eager 3217 % promises. Also, it's not representative of *anything* you can do with async/await, as there's no way to encode such an 3218 % eager chain that way. 3219 3220 \begin{multicols}{2} 3221 \begin{cfa}[xleftmargin=0pt] 3222 `coroutine` C {}; 3223 void main( C & ) { for () { `suspend;` } } 3224 int main() { // coroutine test 3225 C c; 3226 BENCH( for ( N ) { `resume( c );` } ) 3227 sout | result; 3228 } 3229 int main() { // thread test 3230 BENCH( for ( N ) { `yield();` } ) 3231 sout | result; 3232 } 3233 \end{cfa} 3234 \captionof{figure}{\CFA context-switch benchmark} 3235 \label{f:ctx-switch} 3236 3237 \columnbreak 3238 3239 \vspace*{-16pt} 3240 \captionof{table}{Context switch comparison (nanoseconds)} 3241 \label{t:ctx-switch} 3242 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 3243 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3244 C function (10B) & 1.8 & 1.8 & 0.0 \\ 3245 \CFA generator (5B) & 1.8 & 2.0 & 0.3 \\ 3246 \CFA coroutine (100M) & 32.5 & 32.9 & 0.8 \\ 3247 \CFA thread (100M) & 93.8 & 93.6 & 2.2 \\ 3248 \uC coroutine (100M) & 50.3 & 50.3 & 0.2 \\ 3249 \uC thread (100M) & 97.3 & 97.4 & 1.0 \\ 3250 Python generator (100M) & 40.9 & 41.3 & 1.5 \\ 3251 Node.js await (5M) & 1852.2 & 1854.7 & 16.4 \\ 3252 Node.js generator (100M) & 33.3 & 33.4 & 0.3 \\ 3253 Goroutine thread (100M) & 143.0 & 143.3 & 1.1 \\ 3254 Rust async await (100M) & 32.0 & 32.0 & 0.0 \\ 3255 Rust tokio thread (100M) & 143.0 & 143.0 & 1.7 \\ 3256 Rust thread (25M) & 332.0 & 331.4 & 2.4 \\ 3257 Java thread (100M) & 405.0 & 415.0 & 17.6 \\ 3258 % Java thread ( 100 000 000) & 413.0 & 414.2 & 6.2 \\ 3259 % Java thread (5 000 000 000) & 415.0 & 415.2 & 6.1 \\ 3260 Pthreads thread (25M) & 334.3 & 335.2 & 3.9 3261 \end{tabular} 3262 \end{multicols} 3263 3264 3265 \subsection{Discussion} 3266 3267 Languages using 1:1 threading based on pthreads can at best meet or exceed, due to language overhead, the pthread results. 3268 Note, pthreads has a fast zero-contention mutex lock checked in user space. 3269 Languages with M:N threading have better performance than 1:1 because there is no operating-system interactions (context-switching or locking). 3270 As well, for locking experiments, M:N threading has less contention if only one kernel thread is used. 3271 Languages with stackful coroutines have higher cost than stackless coroutines because of stack allocation and context switching; 3272 however, stackful \uC and \CFA coroutines have approximately the same performance as stackless Python and Node.js generators. 3273 The \CFA stackless generator is approximately 25 times faster for suspend/resume and 200 times faster for creation than stackless Python and Node.js generators. 3274 The Node.js context-switch is costly when asynchronous await must enter the event engine because a promise is not fulfilled. 3275 Finally, the benchmark results correlate across programming languages with and without JIT, indicating the JIT has completed any runtime optimizations. 3276 3277 3278 \section{Conclusions and Future Work} 2975 \section{Conclusion} 3279 2976 3280 2977 Advanced control-flow will always be difficult, especially when there is temporal ordering and nondeterminism. 3281 2978 However, many systems exacerbate the difficulty through their presentation mechanisms. 3282 This paper shows it is possible to understand high-level control-flow using three properties: statefulness, thread, mutual-exclusion/synchronization. 3283 Combining these properties creates a number of high-level, efficient, and maintainable control-flow types: generator, coroutine, thread, each of which can be a monitor. 3284 Eliminated from \CFA are barging and spurious wakeup, which are nonintuitive and lead to errors, and having to work with a bewildering set of low-level locks and acquisition techniques. 3285 \CFA high-level race-free monitors and threads, when used with mutex access function, provide the core mechanisms for mutual exclusion and synchronization, without having to resort to magic qualifiers like @volatile@ or @atomic@. 2979 This paper shows it is possible to present a hierarchy of control-flow features, generator, coroutine, thread, and monitor, providing an integrated set of high-level, efficient, and maintainable control-flow features. 2980 Eliminated from \CFA are spurious wakeup and barging, which are nonintuitive and lead to errors, and having to work with a bewildering set of low-level locks and acquisition techniques. 2981 \CFA high-level race-free monitors and tasks provide the core mechanisms for mutual exclusion and synchronization, without having to resort to magic qualifiers like @volatile@/@atomic@. 3286 2982 Extending these mechanisms to handle high-level deadlock-free bulk acquire across both mutual exclusion and synchronization is a unique contribution. 3287 2983 The \CFA runtime provides concurrency based on a preemptive M:N user-level threading-system, executing in clusters, which encapsulate scheduling of work on multiple kernel threads providing parallelism. 3288 2984 The M:N model is judged to be efficient and provide greater flexibility than a 1:1 threading model. 3289 2985 These concepts and the \CFA runtime-system are written in the \CFA language, extensively leveraging the \CFA type-system, which demonstrates the expressiveness of the \CFA language. 3290 Performance comparisons with other concurrent systems and languages show the \CFA approach is competitive across all basic operations, which translates directly into good performance in well-written applications with advanced control-flow. 3291 C programmers should feel comfortable using these mechanisms for developing complex control-flow in applications, with the ability to obtain maximum available performance by selecting mechanisms at the appropriate level of need using only calling communication. 2986 Performance comparisons with other concurrent systems/languages show the \CFA approach is competitive across all low-level operations, which translates directly into good performance in well-written concurrent applications. 2987 C programmers should feel comfortable using these mechanisms for developing complex control-flow in applications, with the ability to obtain maximum available performance by selecting mechanisms at the appropriate level of need. 2988 2989 2990 \section{Future Work} 3292 2991 3293 2992 While control flow in \CFA has a strong start, development is still underway to complete a number of missing features. 3294 2993 3295 \medskip 3296 \textbf{Flexible scheduling:} 2994 \paragraph{Flexible Scheduling} 2995 \label{futur:sched} 2996 3297 2997 An important part of concurrency is scheduling. 3298 Different scheduling algorithms can affect performance , both in terms of average and variation.2998 Different scheduling algorithms can affect performance (both in terms of average and variation). 3299 2999 However, no single scheduler is optimal for all workloads and therefore there is value in being able to change the scheduler for given programs. 3300 3000 One solution is to offer various tuning options, allowing the scheduler to be adjusted to the requirements of the workload. 3301 3001 However, to be truly flexible, a pluggable scheduler is necessary. 3302 Currently, the \CFA pluggable scheduler is too simple to handle complex scheduling, \eg quality of service and real time, where the scheduler must interact with mutex objects to deal with issues like priority inversion~\cite{Buhr00b}. 3303 3304 \smallskip 3305 \textbf{Non-Blocking I/O:} 3306 Many modern workloads are not bound by computation but IO operations, common cases being web servers and XaaS~\cite{XaaS} (anything as a service). 3002 Currently, the \CFA pluggable scheduler is too simple to handle complex scheduling, \eg quality of service and real-time, where the scheduler must interact with mutex objects to deal with issues like priority inversion~\cite{Buhr00b}. 3003 3004 \paragraph{Non-Blocking I/O} 3005 \label{futur:nbio} 3006 3007 Many modern workloads are not bound by computation but IO operations, a common case being web servers and XaaS~\cite{XaaS} (anything as a service). 3307 3008 These types of workloads require significant engineering to amortizing costs of blocking IO-operations. 3308 At its core, non blocking I/O is an operating-system level feature queuing IO operations, \eg network operations, and registering for notifications instead of waiting for requests to complete.3009 At its core, non-blocking I/O is an operating-system level feature queuing IO operations, \eg network operations, and registering for notifications instead of waiting for requests to complete. 3309 3010 Current trends use asynchronous programming like callbacks, futures, and/or promises, \eg Node.js~\cite{NodeJs} for JavaScript, Spring MVC~\cite{SpringMVC} for Java, and Django~\cite{Django} for Python. 3310 However, these solutions lead to code that is hard to create, read, and maintain. 3311 A better approach is to tie nonblocking I/O into the concurrency system to provide ease of use with low overhead, \eg thread-per-connection web-services. 3312 A nonblocking I/O library is currently under development for \CFA. 3313 3314 \smallskip 3315 \textbf{Other concurrency tools:} 3011 However, these solutions lead to code that is hard to create, read and maintain. 3012 A better approach is to tie non-blocking I/O into the concurrency system to provide ease of use with low overhead, \eg thread-per-connection web-services. 3013 A non-blocking I/O library is currently under development for \CFA. 3014 3015 \paragraph{Other Concurrency Tools} 3016 \label{futur:tools} 3017 3316 3018 While monitors offer flexible and powerful concurrency for \CFA, other concurrency tools are also necessary for a complete multi-paradigm concurrency package. 3317 3019 Examples of such tools can include futures and promises~\cite{promises}, executors and actors. … … 3319 3021 As well, new \CFA extensions should make it possible to create a uniform interface for virtually all mutual exclusion, including monitors and low-level locks. 3320 3022 3321 \smallskip 3322 \textbf{Implicit threading:} 3323 Basic \emph{embarrassingly parallel} applications can benefit greatly from implicit concurrency, where sequential programs are converted to concurrent, with some help from pragmas to guide the conversion. 3023 \paragraph{Implicit Threading} 3024 \label{futur:implcit} 3025 3026 Basic concurrent (embarrassingly parallel) applications can benefit greatly from implicit concurrency, where sequential programs are converted to concurrent, possibly with some help from pragmas to guide the conversion. 3324 3027 This type of concurrency can be achieved both at the language level and at the library level. 3325 3028 The canonical example of implicit concurrency is concurrent nested @for@ loops, which are amenable to divide and conquer algorithms~\cite{uC++book}. 3326 The \CFA language features should make it possible to develop a reasonable number of implicit concurrency mechanism sto solve basic HPC data-concurrency problems.3029 The \CFA language features should make it possible to develop a reasonable number of implicit concurrency mechanism to solve basic HPC data-concurrency problems. 3327 3030 However, implicit concurrency is a restrictive solution with significant limitations, so it can never replace explicit concurrent programming. 3328 3031 … … 3330 3033 \section{Acknowledgements} 3331 3034 3332 The authors recognize the design assistance of Aaron Moss, Rob Schluntz, Andrew Beach, and Michael Brooks; David Dice for commenting and helping with the Java benchmarks; and Gregor Richards for helping with the Node.js benchmarks.3333 This research is funded by the NSERC/Waterloo-Huawei (\url{http://www.huawei.com}) Joint Innovation Lab. %, and Peter Buhr is partially funded by the Natural Sciences and Engineering Research Council of Canada.3035 The authors would like to recognize the design assistance of Aaron Moss, Rob Schluntz, Andrew Beach and Michael Brooks on the features described in this paper. 3036 Funding for this project has been provided by Huawei Ltd.\ (\url{http://www.huawei.com}). %, and Peter Buhr is partially funded by the Natural Sciences and Engineering Research Council of Canada. 3334 3037 3335 3038 {% 3336 \fontsize{9bp}{1 1.5bp}\selectfont%3039 \fontsize{9bp}{12bp}\selectfont% 3337 3040 \bibliography{pl,local} 3338 3041 }% -
doc/papers/concurrency/annex/local.bib
reef8dfb rbdfc032 29 29 booktitle = {Supercomputing, 2005. Proceedings of the ACM/IEEE SC 2005 Conference}, 30 30 publisher = {IEEE}, 31 location = {Seattle, Washington, U.S.A.},32 month = nov,33 31 year = {2005}, 34 32 pages = {35-35}, 33 month = nov, 35 34 } 36 35 … … 59 58 60 59 @manual{Cpp-Transactions, 61 keywords = {C++, Transactional Memory},62 title = {Tech. Spec. for C++ Extensions for Transactional Memory {ISO/IEC} {TS} 19841:2015},63 organization= {International Standard Organization},64 address = {Geneva, Switzerland},65 year = 2015,66 note = {\href{https://www.iso.org/standard/66343.html}{https://\-www.iso.org/\-standard/\-66343.html}},60 keywords = {C++, Transactional Memory}, 61 title = {Technical Specification for C++ Extensions for Transactional Memory}, 62 organization= {International Standard ISO/IEC TS 19841:2015 }, 63 publisher = {American National Standards Institute}, 64 address = {http://www.iso.org}, 65 year = 2015, 67 66 } 68 67 … … 110 109 @manual{affinityLinux, 111 110 key = {TBB}, 112 title = "{Linux man page - sched\_setaffinity(2)}", 113 note = {\href{https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html}{https://\-man7.org/\-linux/man-pages/\-man2/sched\_setaffinity.2.html}}, 111 title = "{Linux man page - sched\_setaffinity(2)}" 114 112 } 115 113 116 114 @manual{affinityWindows, 117 title = "{Windows documentation - SetThreadAffinityMask function}", 118 note = {\href{https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadaffinitymask}{https://\-docs.microsoft.com/\-en-us/\-windows/\-win32/api/\-winbase/\-nf-winbase-setthreadaffinitymask}} 115 title = "{Windows (vs.85) - SetThreadAffinityMask function}" 119 116 } 120 117 -
doc/papers/concurrency/examples/Fib.py
reef8dfb rbdfc032 4 4 while True: 5 5 fn = fn1 + fn2; fn2 = fn1; fn1 = fn; yield fn 6 7 6 8 7 9 f1 = Fib() … … 12 14 # Local Variables: # 13 15 # tab-width: 4 # 14 # compile-command: "python3. 7Fib.py" #16 # compile-command: "python3.5 Fib.py" # 15 17 # End: # -
doc/papers/concurrency/examples/Fib2.c
reef8dfb rbdfc032 1 1 #include <stdio.h> 2 2 3 void mary() { 4 printf( "MARY\n" ); 5 } 6 3 7 #define FIB_INIT { 0 } 4 typedef struct { int restart; int fn1, fn2; } Fib;8 typedef struct { int next; int fn1, fn2; } Fib; 5 9 int fib( Fib * f ) { 6 static void * states[] = { &&s0, &&s1, &&s2 }; 7 goto *states[f->restart]; 8 s0: 10 static void * states[] = { &&s1, &&s2, &&s3 }; 11 goto *states[f->next]; 12 s1: 13 mary(); 9 14 f->fn1 = 0; 10 f-> restart = 1;15 f->next = 1; 11 16 return f->fn1; 12 s1: 17 s2: 18 mary(); 13 19 f->fn2 = f->fn1; 14 20 f->fn1 = 1; 15 f-> restart = 2;21 f->next = 2; 16 22 return f->fn1; 17 s2:; 23 s3:; 24 mary(); 18 25 int fn = f->fn1 + f->fn2; 19 26 f->fn2 = f->fn1; -
doc/papers/concurrency/examples/Fib2.py
reef8dfb rbdfc032 1 1 def Fib(): 2 fn1, fn = 1, 02 fn1, fn = 0, 1 3 3 while True: 4 yield fn 4 yield fn1 5 5 fn1, fn = fn, fn1 + fn 6 6 … … 12 12 # Local Variables: # 13 13 # tab-width: 4 # 14 # compile-command: "python3. 7Fib2.py" #14 # compile-command: "python3.5 Fib2.py" # 15 15 # End: # -
doc/papers/concurrency/examples/Fib3.c
reef8dfb rbdfc032 2 2 3 3 typedef struct { 4 int restart, fn1, fn; 4 int fn1, fn; 5 void * next; 5 6 } Fib; 6 #define FibCtor { 0, 1, 0}7 #define FibCtor { 1, 0, NULL } 7 8 8 9 Fib * comain( Fib * f ) { 9 static void * states[] = {&&s0, &&s1}; 10 goto *states[f->restart]; 11 s0: f->restart = 1; 10 if ( __builtin_expect(f->next != 0, 1) ) goto *f->next; 11 f->next = &&s1; 12 12 for ( ;; ) { 13 13 return f; -
doc/papers/concurrency/examples/FibRefactor.py
reef8dfb rbdfc032 22 22 # Local Variables: # 23 23 # tab-width: 4 # 24 # compile-command: "python3. 7FibRefactor.py" #24 # compile-command: "python3.5 FibRefactor.py" # 25 25 # End: # -
doc/papers/concurrency/examples/Format.c
reef8dfb rbdfc032 2 2 3 3 typedef struct { 4 int restart, g, b;4 void * next; 5 5 char ch; 6 int g, b; 6 7 } Fmt; 7 8 8 9 void comain( Fmt * f ) { 9 static void * states[] = {&&s0, &&s1}; 10 goto *states[f->restart]; 11 s0: f->restart = 1; 10 if ( __builtin_expect(f->next != 0, 1) ) goto *f->next; 11 f->next = &&s1; 12 12 for ( ;; ) { 13 13 for ( f->g = 0; f->g < 5; f->g += 1 ) { // groups 14 14 for ( f->b = 0; f->b < 4; f->b += 1 ) { // blocks 15 do { 16 return; s1: ; 17 } while ( f->ch == '\n' ); // ignore 15 return; 16 s1:; while ( f->ch == '\n' ) return; // ignore 18 17 printf( "%c", f->ch ); // print character 19 18 } … … 25 24 26 25 int main() { 27 Fmt fmt = { 0};26 Fmt fmt = { NULL }; 28 27 comain( &fmt ); // prime 29 28 for ( ;; ) { -
doc/papers/concurrency/examples/Format.cc
reef8dfb rbdfc032 6 6 for ( g = 0; g < 5; g += 1 ) { // groups of 5 blocks 7 7 for ( b = 0; b < 4; b += 1 ) { // blocks of 4 characters 8 for ( ;; ) { // for newline characters8 // for ( ;; ) { // for newline characters 9 9 suspend(); 10 if ( ch != '\n' ) break; // ignore newline11 }10 // if ( ch != '\n' ) break; // ignore newline 11 // } 12 12 // cout << ch; // print character 13 13 } … … 31 31 // Local Variables: // 32 32 // tab-width: 4 // 33 // compile-command: "u++-work -O2 -nodebu g Format.cc" //33 // compile-command: "u++-work -O2 -nodebubg Format.cc" // 34 34 // End: // -
doc/papers/concurrency/examples/Format.cfa
reef8dfb rbdfc032 11 11 for ( g = 0; g < 5; g += 1 ) { // groups of 5 blocks 12 12 for ( b = 0; b < 4; b += 1 ) { // blocks of 4 characters 13 do {13 // do { 14 14 suspend(); 15 } while ( ch == '\n' || ch == '\t' );15 // } while ( ch == '\n' || ch == '\t' ); 16 16 sout | ch; // print character 17 17 } -
doc/papers/concurrency/examples/Format.data
reef8dfb rbdfc032 1 abcdefghijklmnop 2 qrstuvwxyzx 3 xxxxxxxxxxxxx 1 abcdefghijklmnopqrstuvwxyzxxxxxxxxxxxxxx -
doc/papers/concurrency/examples/Format.py
reef8dfb rbdfc032 4 4 for g in range( 5 ): # groups of 5 blocks 5 5 for b in range( 4 ): # blocks of 4 characters 6 while True: 7 ch = (yield) # receive from send 8 if '\n' not in ch: 9 break 10 print( ch, end='' ) # receive from send 6 print( (yield), end='' ) # receive from send 11 7 print( ' ', end='' ) # block separator 12 8 print() # group separator … … 15 11 print() 16 12 17 input = "abcdefghijklmnop\nqrstuvwx\nyzxxxxxxxxxxxxxx\n"18 19 13 fmt = Format() 20 14 next( fmt ) # prime generator 21 for i in input:22 fmt.send( i); # send to yield15 for i in range( 41 ): 16 fmt.send( 'a' ); # send to yield 23 17 24 18 # Local Variables: # 25 19 # tab-width: 4 # 26 # compile-command: "python3. 7Format.py" #20 # compile-command: "python3.5 Format.py" # 27 21 # End: # -
doc/papers/concurrency/examples/Format1.c
reef8dfb rbdfc032 2 2 3 3 typedef struct { 4 int restart, g, b;4 void * next; 5 5 char ch; 6 int g, b; 6 7 } Fmt; 7 8 8 9 void format( Fmt * f ) { 9 static void * states[] = {&&s0, &&s1}; 10 goto *states[f->restart]; 11 s0: f->restart = 1; 10 if ( __builtin_expect(f->next != 0, 1) ) goto *f->next; 11 f->next = &&s1; 12 12 for ( ;; ) { 13 13 for ( f->g = 0; f->g < 5; f->g += 1 ) { // groups 14 14 for ( f->b = 0; f->b < 4; f->b += 1 ) { // blocks 15 15 return; 16 s1: if ( f->ch == '\0' ) goto fini; // EOF ? 16 s1: ; 17 if ( f->ch == '\0' ) goto fini; // EOF ? 17 18 while ( f->ch == '\n' ) return; // ignore 18 //printf( "%c", f->ch ); // print character19 printf( "%c", f->ch ); // print character 19 20 } 20 //printf( " " ); // block separator21 printf( " " ); // block separator 21 22 } 22 //printf( "\n" ); // group separator23 printf( "\n" ); // group separator 23 24 } 24 fini: ;25 //if ( f->g != 0 || f->b != 0 ) printf( "\n" );25 fini: 26 if ( f->g != 0 || f->b != 0 ) printf( "\n" ); 26 27 } 27 28 28 29 int main() { 29 Fmt fmt = { 0};30 Fmt fmt = { NULL }; 30 31 format( &fmt ); // prime 31 fmt.ch = 'a'; 32 for ( long int i = 0; i < 1000000000; i += 1 ) { 33 // scanf( "%c", &fmt.ch ); // direct read into communication variable 34 // if ( feof( stdin ) ) break; 32 for ( ;; ) { 33 scanf( "%c", &fmt.ch ); // direct read into communication variable 34 if ( feof( stdin ) ) break; 35 35 format( &fmt ); 36 36 } 37 fmt.ch = '\0'; // sentential (EOF)37 fmt.ch = '\0'; 38 38 format( &fmt ); 39 39 } -
doc/papers/concurrency/examples/PingPong.c
reef8dfb rbdfc032 2 2 3 3 typedef struct PingPong { 4 int restart; // style 14 const char * name; 5 5 int N, i; 6 const char * name;7 6 struct PingPong * partner; 8 void * next; // style 27 void * next; 9 8 } PingPong; 10 #define PPCtor( name, N ) { 0, N, 0, name, NULL, NULL } 11 9 #define PPCtor( name, N ) { name, N, 0, NULL, NULL } 12 10 void comain( PingPong * pp ) __attribute__(( noinline )); 13 11 void comain( PingPong * pp ) { 12 if ( __builtin_expect(pp->next != 0, 1) ) goto *pp->next; 14 13 #if 0 15 if ( __builtin_expect(pp->next != 0, 1) ) goto *pp->next; 14 pp->next = &&here; 15 asm( "mov %0,%%rdi" : "=m" (pp) ); 16 asm( "mov %rdi,%rax" ); 17 #ifndef OPT 18 #ifdef PRINT 19 asm( "add $16, %rsp" ); 20 #endif // PRINT 21 asm( "popq %rbp" ); 22 #endif // ! OPT 23 24 #ifdef OPT 25 #ifdef PRINT 26 asm( "popq %rbx" ); 27 #endif // PRINT 28 #endif // OPT 29 asm( "jmp comain" ); 30 here: ; 31 #endif // 0 32 16 33 pp->next = &&cycle; 17 34 for ( ; pp->i < pp->N; pp->i += 1 ) { … … 36 53 cycle: ; 37 54 } // for 38 #endif // 039 40 #if 141 static void * states[] = {&&s0, &&s1};42 goto *states[pp->restart];43 s0: pp->restart = 1;44 for ( ; pp->i < pp->N; pp->i += 1 ) {45 #ifdef PRINT46 printf( "%s %d\n", pp->name, pp->i );47 #endif // PRINT48 asm( "mov %0,%%rdi" : "=m" (pp->partner) );49 asm( "mov %rdi,%rax" );50 #ifndef OPT51 #ifdef PRINT52 asm( "add $16, %rsp" );53 #endif // PRINT54 asm( "popq %rbp" );55 #endif // ! OPT56 57 #ifdef OPT58 #ifdef PRINT59 asm( "popq %rbx" );60 #endif // PRINT61 #endif // OPT62 asm( "jmp comain" );63 s1: ;64 } // for65 #endif // 066 55 } 67 56 … … 81 70 // Local Variables: // 82 71 // tab-width: 4 // 83 // compile-command: "gcc- 9-g -DPRINT PingPong.c" //72 // compile-command: "gcc-8 -g -DPRINT PingPong.c" // 84 73 // End: // -
doc/papers/concurrency/examples/Pingpong.py
reef8dfb rbdfc032 1 1 def PingPong( name, N ): 2 partner = yield# get partner3 yield # resume scheduler2 partner = (yield) # get partner 3 yield # resume scheduler 4 4 for i in range( N ): 5 5 print( name ) 6 yield partner # execute next6 yield partner # execute next 7 7 print( "end", name ) 8 8 9 9 def Scheduler(): 10 n = yield # starting coroutine 11 try: 12 while True: 13 n = next( n ) # schedule coroutine 14 except StopIteration: 15 pass 10 n = (yield) # starting coroutine 11 while True: 12 n = next( n ) # schedule coroutine 16 13 17 14 pi = PingPong( "ping", 5 ) 18 15 po = PingPong( "pong", 5 ) 19 next( pi ) # prime20 pi.send( po ) # send partner21 next( po ) # prime22 po.send( pi ) # send partner16 next( pi ) # prime 17 pi.send( po ) # send partner 18 next( po ) # prime 19 po.send( pi ) # send partner 23 20 24 21 s = Scheduler(); 25 next( s ) # prime22 next( s ) # prime 26 23 try: 27 24 s.send( pi ) # start cycle 28 except StopIteration: # scheduler stopped29 p ass25 except StopIteration: 26 print( "scheduler stop" ) 30 27 print( "stop" ) 31 28 32 29 # Local Variables: # 33 30 # tab-width: 4 # 34 # compile-command: "python3. 7Pingpong.py" #31 # compile-command: "python3.5 Pingpong.py" # 35 32 # End: # -
doc/papers/concurrency/examples/ProdCons.py
reef8dfb rbdfc032 1 1 def Prod( N ): 2 cons = yield# get cons3 yield # resume scheduler2 cons = (yield) # get cons 3 yield # resume scheduler 4 4 for i in range( N ): 5 5 print( "prod" ) 6 yield cons # execute next6 yield cons # execute next 7 7 print( "end", "prod" ) 8 8 9 9 def Cons( N ): 10 prod = yield# get prod11 yield # resume scheduler10 prod = (yield) # get prod 11 yield # resume scheduler 12 12 for i in range( N ): 13 13 print( "cons" ) 14 yield prod # execute next14 yield prod # execute next 15 15 print( "end", "cons" ) 16 16 17 17 def Scheduler(): 18 n = yield # starting coroutine 19 try: 20 while True: 21 n = next( n ) # schedule coroutine 22 except StopIteration: 23 pass 18 n = (yield) # starting coroutine 19 while True: 20 n = next( n ) # schedule coroutine 24 21 25 22 prod = Prod( 5 ) 26 23 cons = Cons( 5 ) 27 next( prod ) # prime28 prod.send( cons ) # send cons29 next( cons ) # prime30 cons.send( prod ) # send prod24 next( prod ) # prime 25 prod.send( cons ) # send cons 26 next( cons ) # prime 27 cons.send( prod ) # send prod 31 28 32 29 s = Scheduler(); 33 next( s ) # prime30 next( s ) # prime 34 31 try: 35 32 s.send( prod ) # start cycle 36 except StopIteration: # scheduler stopped37 p ass33 except StopIteration: 34 print( "scheduler stop" ) 38 35 print( "stop" ) 39 36 40 37 # Local Variables: # 41 38 # tab-width: 4 # 42 # compile-command: "python3. 7ProdCons.py" #39 # compile-command: "python3.5 ProdCons.py" # 43 40 # End: # -
doc/papers/concurrency/examples/Refactor.py
reef8dfb rbdfc032 26 26 # Local Variables: # 27 27 # tab-width: 4 # 28 # compile-command: "python3. 7Refactor.py" #28 # compile-command: "python3.5 Refactor.py" # 29 29 # End: # -
doc/papers/concurrency/figures/FullCoroutinePhases.fig
reef8dfb rbdfc032 8 8 -2 9 9 1200 2 10 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 5175.000 2437.500 4875 1875 5175 1800 5475 187510 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 4575.000 2437.500 4275 1875 4575 1800 4875 1875 11 11 1 1 1.00 45.00 90.00 12 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 5175.000 1537.500 5475 2100 5175 2175 4875 210012 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 4575.000 1537.500 4875 2100 4575 2175 4275 2100 13 13 1 1 1.00 45.00 90.00 14 5 1 0 1 0 7 50 -1 -1 0.000 0 1 1 0 4 807.500 1642.500 4725 1425 4575 1650 4800 187514 5 1 0 1 0 7 50 -1 -1 0.000 0 1 1 0 4207.500 1642.500 4125 1425 3975 1650 4200 1875 15 15 1 1 1.00 45.00 90.00 16 6 1575 1575 2700 202517 16 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 18 17 1 1 1.00 45.00 90.00 … … 21 20 1 1 1.00 45.00 90.00 22 21 2175 1575 2400 1800 22 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 23 1 1 1.00 45.00 90.00 24 3300 1575 3300 1800 25 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 26 1 1 1.00 45.00 90.00 27 3300 2025 3300 2250 28 4 1 0 100 0 0 10 0.0000 2 105 555 2100 1200 creation\001 23 29 4 1 0 100 0 4 10 0.0000 2 165 300 1725 1950 ping\001 24 30 4 1 0 100 0 4 10 0.0000 2 135 360 2475 1950 pong\001 25 -6 26 6 3075 1575 4200 2025 27 6 3075 1575 4200 2025 28 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 29 1 1 1.00 45.00 90.00 30 3525 1575 3300 1800 31 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 32 1 1 1.00 45.00 90.00 33 3675 1575 3900 1800 34 4 1 0 100 0 4 10 0.0000 2 165 300 3225 1950 ping\001 35 4 1 0 100 0 4 10 0.0000 2 135 360 3975 1950 pong\001 36 -6 37 -6 31 4 1 0 100 0 4 10 0.0000 2 165 300 3300 1950 ping\001 32 4 1 0 100 0 4 10 0.0000 2 135 360 3300 2400 pong\001 33 4 1 0 100 0 0 10 0.0000 2 105 675 4575 1200 execution\001 34 4 1 0 100 0 4 10 0.0000 2 165 300 4275 2025 ping\001 35 4 1 0 100 0 4 10 0.0000 2 135 360 4875 2025 pong\001 36 4 1 0 100 0 0 10 0.0000 2 90 420 3300 1200 starter\001 38 37 4 1 0 100 0 4 10 0.0000 2 165 705 2100 1500 pgm main\001 39 4 1 0 100 0 4 10 0.0000 2 165 705 3600 1500 pgm main\001 40 4 1 0 100 0 4 10 0.0000 2 165 300 4875 2025 ping\001 41 4 1 0 100 0 4 10 0.0000 2 135 360 5475 2025 pong\001 42 4 1 0 100 0 4 10 0.0000 2 165 705 5100 1500 pgm main\001 43 4 1 0 100 0 2 10 0.0000 2 105 540 2100 1275 creator\001 44 4 1 0 100 0 2 10 0.0000 2 105 495 3600 1275 starter\001 45 4 1 0 100 0 2 10 0.0000 2 105 690 5175 1275 execution\001 38 4 1 0 100 0 4 10 0.0000 2 165 705 3300 1500 pgm main\001 39 4 1 0 100 0 4 10 0.0000 2 165 705 4500 1500 pgm main\001 -
doc/papers/concurrency/figures/RunTimeStructure.fig
reef8dfb rbdfc032 8 8 -2 9 9 1200 2 10 6 3 255 2475 3555 262511 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3 330 2550 30 30 3330 2550 3360 258012 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3435 2550 30 30 3435 2550 3465 258010 6 3855 2775 4155 2925 11 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3930 2850 30 30 3930 2850 3960 2880 12 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4035 2850 30 30 4035 2850 4065 2880 13 13 -6 14 6 4 155 3225 4455 337515 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4 230 3300 30 30 4230 3300 4260 333016 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4 335 3300 30 30 4335 3300 4365 333014 6 4755 3525 5055 3675 15 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4830 3600 30 30 4830 3600 4860 3630 16 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4935 3600 30 30 4935 3600 4965 3630 17 17 -6 18 6 4 050 2475 4350 262519 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4 125 2550 15 15 4125 2550 4140 256520 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4 200 2550 15 15 4200 2550 4215 256521 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4 275 2550 15 15 4275 2550 4290 256518 6 4650 2775 4950 2925 19 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4725 2850 15 15 4725 2850 4740 2865 20 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4800 2850 15 15 4800 2850 4815 2865 21 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4875 2850 15 15 4875 2850 4890 2865 22 22 -6 23 6 2625 2100 2925 225024 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2700 2175 15 15 2700 2175 2715 219025 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2775 2175 15 15 2775 2175 2790 219026 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2850 2175 15 15 2850 2175 2865 219023 6 3225 2400 3525 2550 24 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3300 2475 15 15 3300 2475 3315 2490 25 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3375 2475 15 15 3375 2475 3390 2490 26 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3450 2475 15 15 3450 2475 3465 2490 27 27 -6 28 6 4875 3150 5025 345029 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3225 15 15 4950 3225 4935 324030 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3300 15 15 4950 3300 4935 331531 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3375 15 15 4950 3375 4935 339028 6 5475 3450 5625 3750 29 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3525 15 15 5550 3525 5535 3540 30 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3600 15 15 5550 3600 5535 3615 31 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3675 15 15 5550 3675 5535 3690 32 32 -6 33 6 3675 3225 3975 337534 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3750 3300 15 15 3750 3300 3765 331535 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3825 3300 15 15 3825 3300 3840 331536 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3900 3300 15 15 3900 3300 3915 331533 6 4275 3525 4575 3675 34 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4350 3600 15 15 4350 3600 4365 3615 35 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4425 3600 15 15 4425 3600 4440 3615 36 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4500 3600 15 15 4500 3600 4515 3615 37 37 -6 38 6 2625 3825 4050 4125 39 6 3750 3900 4050 4050 40 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3825 3975 15 15 3825 3975 3840 3990 41 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3900 3975 15 15 3900 3975 3915 3990 42 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3975 3975 15 15 3975 3975 3990 3990 38 6 2175 4650 7050 4950 39 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 2250 4830 30 30 2250 4830 2280 4860 40 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4200 4800 150 75 4200 4800 4350 4875 41 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3275 4800 100 100 3275 4800 3375 4800 42 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 43 5400 4950 5400 4725 5175 4725 5175 4950 5400 4950 44 2 2 1 1 -1 -1 0 0 -1 3.000 0 0 0 0 0 5 45 6525 4950 6300 4950 6300 4725 6525 4725 6525 4950 46 4 0 -1 0 0 0 10 0.0000 2 105 450 6600 4875 cluster\001 47 4 0 -1 0 0 0 10 0.0000 2 105 660 5475 4875 processor\001 48 4 0 -1 0 0 0 10 0.0000 2 105 555 4425 4875 monitor\001 49 4 0 -1 0 0 0 10 0.0000 2 120 270 3450 4875 task\001 50 4 0 -1 0 0 0 10 0.0000 2 105 660 2325 4875 coroutine\001 43 51 -6 44 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 2850 3975 225 150 2850 3975 3075 4125 45 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3450 3975 225 150 3450 3975 3675 4125 52 6 3450 1275 3750 1425 53 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3525 1350 15 15 3525 1350 3540 1365 54 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3600 1350 15 15 3600 1350 3615 1365 55 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3675 1350 15 15 3675 1350 3690 1365 46 56 -6 47 6 6075 3825 6900 4125 48 6 6600 3900 6900 4050 49 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 6675 3975 15 15 6675 3975 6690 3990 50 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 6750 3975 15 15 6750 3975 6765 3990 51 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 6825 3975 15 15 6825 3975 6840 3990 57 6 5550 1275 5850 1425 58 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5625 1350 15 15 5625 1350 5640 1365 59 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5700 1350 15 15 5700 1350 5715 1365 60 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5775 1350 15 15 5775 1350 5790 1365 52 61 -6 53 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 6300 3975 225 150 6300 3975 6525 4125 54 -6 55 6 6075 3225 7425 3675 62 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 2625 150 150 5550 2625 5700 2625 63 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 3225 150 150 5550 3225 5700 3225 64 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 3975 150 150 5550 3975 5700 3975 65 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3525 2850 150 150 3525 2850 3675 2850 66 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4200 2475 150 150 4200 2475 4350 2475 67 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4425 2850 150 150 4425 2850 4575 2850 68 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4650 2475 150 150 4650 2475 4800 2475 69 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3525 3600 150 150 3525 3600 3675 3600 70 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3975 3600 150 150 3975 3600 4125 3600 71 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3525 3600 30 30 3525 3600 3555 3630 72 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3750 2475 150 150 3750 2475 3900 2625 73 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4875 3600 150 150 4875 3600 5025 3750 74 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3975 2850 150 150 3975 2850 4125 2850 75 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 7200 2775 150 150 7200 2775 7350 2775 76 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4650 1350 225 150 4650 1350 4875 1500 77 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5250 1350 225 150 5250 1350 5475 1500 78 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 1350 225 150 4050 1350 4275 1500 79 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 80 2400 4200 2400 3750 1950 3750 1950 4200 2400 4200 81 2 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5 82 6300 4500 6300 1800 3000 1800 3000 4500 6300 4500 83 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 84 5775 2850 5775 2400 5325 2400 5325 2850 5775 2850 85 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 86 5775 4200 5775 3750 5325 3750 5325 4200 5775 4200 56 87 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 57 88 1 1 1.00 45.00 90.00 58 6075 3450 6375 345089 5175 3975 5325 3975 59 90 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 60 91 1 1 1.00 45.00 90.00 61 6525 3450 6750 3450 62 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 63 7200 3675 7200 3225 6750 3225 6750 3675 7200 3675 92 5175 3225 5325 3225 64 93 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 65 94 1 1 1.00 45.00 90.00 66 7200 3450 7425 3450 67 -6 68 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4950 2325 150 150 4950 2325 5100 2325 69 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4950 2925 150 150 4950 2925 5100 2925 70 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4950 3675 150 150 4950 3675 5100 3675 71 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 2925 2550 150 150 2925 2550 3075 2550 72 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3600 2175 150 150 3600 2175 3750 2175 73 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3825 2550 150 150 3825 2550 3975 2550 74 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 2175 150 150 4050 2175 4200 2175 75 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3375 3300 150 150 3375 3300 3525 3300 76 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 2925 3300 30 30 2925 3300 2955 3330 77 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3150 2175 150 150 3150 2175 3300 2325 78 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4275 3300 150 150 4275 3300 4425 3450 79 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3375 2550 150 150 3375 2550 3525 2550 80 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 6600 2475 150 150 6600 2475 6750 2475 81 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 1650 4530 30 30 1650 4530 1680 4560 82 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 6600 2475 30 30 6600 2475 6630 2505 83 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 2925 3300 150 150 2925 3300 3075 3300 84 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3275 4500 100 100 3275 4500 3375 4500 85 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 4500 150 75 4050 4500 4200 4575 86 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 87 1800 3900 1800 3450 1350 3450 1350 3900 1800 3900 88 2 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5 89 5700 4200 5700 1500 2400 1500 2400 4200 5700 4200 90 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 91 5175 2550 5175 2100 4725 2100 4725 2550 5175 2550 92 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 93 5175 3900 5175 3450 4725 3450 4725 3900 5175 3900 95 5175 2625 5325 2625 94 96 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 95 97 1 1 1.00 45.00 90.00 96 4575 3675 4725 367598 5775 3975 5925 3975 97 99 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 98 100 1 1 1.00 45.00 90.00 99 4575 2925 4725 2925101 5775 3225 5925 3225 100 102 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 101 103 1 1 1.00 45.00 90.00 102 4575 2325 4725 2325 104 5775 2625 5925 2625 105 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 106 5175 3975 5175 2625 103 107 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 104 108 1 1 1.00 45.00 90.00 105 5 175 3675 5325 3675109 5925 3975 5925 2025 106 110 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 107 111 1 1 1.00 45.00 90.00 108 5 175 2925 5325 2925112 5925 3750 6225 3750 109 113 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 110 114 1 1 1.00 45.00 90.00 111 5175 2325 5325 2325 115 3450 2625 3225 2625 116 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 3 117 1 1 1.00 45.00 90.00 118 5925 2025 4200 2025 4200 2250 112 119 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 113 4575 3675 4575 2325120 3225 2625 3225 3600 114 121 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 115 122 1 1 1.00 45.00 90.00 116 5325 3675 5325 1725123 3075 3600 3375 3600 117 124 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 118 125 1 1 1.00 45.00 90.00 119 5325 3450 5625 3450126 3675 3600 3825 3600 120 127 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 121 128 1 1 1.00 45.00 90.00 122 2850 2325 2625 2325 123 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 3 124 1 1 1.00 45.00 90.00 125 5325 1725 3600 1725 3600 1950 126 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 127 2625 2325 2625 3300 129 4125 3600 4275 3600 128 130 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 129 131 1 1 1.00 45.00 90.00 130 2475 3300 2775 3300132 4575 3600 4725 3600 131 133 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 132 134 1 1 1.00 45.00 90.00 133 3075 3300 3225 3300 135 5025 3600 5175 3600 136 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 137 5775 3450 5775 3000 5325 3000 5325 3450 5775 3450 138 2 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5 139 8100 4500 8100 1800 6600 1800 6600 4500 8100 4500 134 140 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 135 141 1 1 1.00 45.00 90.00 136 3525 3300 3675 3300142 6675 3975 6975 3975 137 143 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 138 144 1 1 1.00 45.00 90.00 139 3975 3300 4125 3300 145 7050 2775 6825 2775 146 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 147 6825 2775 6825 3975 140 148 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 141 149 1 1 1.00 45.00 90.00 142 4425 3300 4575 3300150 7125 3975 7350 3975 143 151 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 144 5175 3150 5175 2700 4725 2700 4725 3150 5175 3150 145 2 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5 146 7500 4200 7500 1500 6000 1500 6000 4200 7500 4200 152 7800 4200 7800 3750 7350 3750 7350 4200 7800 4200 147 153 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 148 154 1 1 1.00 45.00 90.00 149 6450 2475 6225 2475 150 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 151 6225 2475 6225 3450 155 7800 3975 8025 3975 152 156 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 4 153 157 1 1 1.00 45.00 90.00 154 7275 3450 7275 2025 6600 2025 6600 2250 155 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 156 5250 4650 5250 4425 5025 4425 5025 4650 5250 4650 157 2 2 1 1 -1 -1 0 0 -1 3.000 0 0 0 0 0 5 158 6375 4650 6150 4650 6150 4425 6375 4425 6375 4650 159 4 1 -1 0 0 0 10 0.0000 2 105 720 4950 4125 Processors\001 160 4 1 -1 0 0 0 10 0.0000 2 120 1005 3600 2925 Blocked Tasks\001 161 4 1 -1 0 0 0 10 0.0000 2 150 870 3600 3675 Ready Tasks\001 162 4 1 -1 0 0 0 10 0.0000 2 135 1095 6750 1425 Other Cluster(s)\001 163 4 1 -1 0 0 0 10 0.0000 2 105 840 4050 1425 User Cluster\001 164 4 1 -1 0 0 0 10 0.0000 2 150 615 1575 3375 Manager\001 165 4 1 -1 0 0 0 10 0.0000 2 105 990 1575 3225 Discrete-event\001 166 4 1 -1 0 0 0 10 0.0000 2 135 795 1575 4050 preemption\001 167 4 0 -1 0 0 0 10 0.0000 2 150 1365 1725 4575 generator/coroutine\001 168 4 0 -1 0 0 0 10 0.0000 2 120 270 3450 4575 task\001 169 4 0 -1 0 0 0 10 0.0000 2 105 450 6450 4575 cluster\001 170 4 0 -1 0 0 0 10 0.0000 2 105 660 5325 4575 processor\001 171 4 0 -1 0 0 0 10 0.0000 2 105 555 4275 4575 monitor\001 158 7875 3975 7875 2325 7200 2325 7200 2550 159 4 1 -1 0 0 0 10 0.0000 2 105 720 5550 4425 Processors\001 160 4 1 -1 0 0 0 10 0.0000 2 120 1005 4200 3225 Blocked Tasks\001 161 4 1 -1 0 0 0 10 0.0000 2 150 870 4200 3975 Ready Tasks\001 162 4 1 -1 0 0 0 10 0.0000 2 135 1095 7350 1725 Other Cluster(s)\001 163 4 1 -1 0 0 0 10 0.0000 2 105 840 4650 1725 User Cluster\001 164 4 1 -1 0 0 0 10 0.0000 2 150 615 2175 3675 Manager\001 165 4 1 -1 0 0 0 10 0.0000 2 105 990 2175 3525 Discrete-event\001 166 4 1 -1 0 0 0 10 0.0000 2 135 795 2175 4350 preemption\001 -
doc/papers/concurrency/mail
reef8dfb rbdfc032 10 10 Dear Dr Buhr, 11 11 12 Your manuscript entitled "Concurrency in C forall" has been received by Software:12 Your manuscript entitled "Concurrency in Cā" has been received by Software: 13 13 Practice and Experience. It will be given full consideration for publication in 14 14 the journal. … … 41 41 Dear Dr Buhr, 42 42 43 Many thanks for submitting SPE-18-0205 entitled "Concurrency in C forall" to Software: Practice and Experience.43 Many thanks for submitting SPE-18-0205 entitled "Concurrency in Cā" to Software: Practice and Experience. 44 44 45 45 In view of the comments of the referees found at the bottom of this letter, I cannot accept your paper for publication in Software: Practice and Experience. I hope that you find the referees' very detailed comments helpful. -
doc/papers/concurrency/mail2
reef8dfb rbdfc032 22 22 Software: Practice and Experience Editorial Office 23 23 24 25 26 Date: Tue, 12 Nov 2019 22:25:17 +000027 From: Richard Jones <onbehalfof@manuscriptcentral.com>28 Reply-To: R.E.Jones@kent.ac.uk29 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca30 Subject: Software: Practice and Experience - Decision on Manuscript ID31 SPE-19-021932 33 12-Nov-201934 35 Dear Dr Buhr,36 37 Many thanks for submitting SPE-19-0219 entitled "Advanced Control-flow and Concurrency in Cforall" to Software: Practice and Experience. The paper has now been reviewed and the comments of the referees are included at the bottom of this letter.38 39 The decision on this paper is that it requires substantial further work is required. The referees have a number of substantial concerns. All the reviewers found the submission very hard to read; two of the reviewers state that it needs very substantial restructuring. These concerns must be addressed before your submission can be considered further.40 41 A revised version of your manuscript that takes into account the comments of the referees will be reconsidered for publication.42 43 Please note that submitting a revision of your manuscript does not guarantee eventual acceptance, and that your revision will be subject to re-review by the referees before a decision is rendered.44 45 You have 90 days from the date of this email to submit your revision. If you are unable to complete the revision within this time, please contact me to request an extension.46 47 You can upload your revised manuscript and submit it through your Author Center. Log into https://mc.manuscriptcentral.com/spe and enter your Author Center, where you will find your manuscript title listed under "Manuscripts with Decisions".48 49 When submitting your revised manuscript, you will be able to respond to the comments made by the referee(s) in the space provided. You can use this space to document any changes you make to the original manuscript.50 51 If you feel that your paper could benefit from English language polishing, you may wish to consider having your paper professionally edited for English language by a service such as Wiley's at http://wileyeditingservices.com. Please note that while this service will greatly improve the readability of your paper, it does not guarantee acceptance of your paper by the journal.52 53 Once again, thank you for submitting your manuscript to Software: Practice and Experience and I look forward to receiving your revision.54 55 56 Sincerely,57 58 Prof. Richard Jones59 Software: Practice and Experience60 R.E.Jones@kent.ac.uk61 62 63 Referee(s)' Comments to Author:64 65 Reviewing: 166 67 Comments to the Author68 This article presents the design and rationale behind the various69 threading and synchronization mechanisms of C-forall, a new low-level70 programming language. This paper is very similar to a companion paper71 which I have also received: as the papers are similar, so will these72 reviews be --- in particular any general comments from the other73 review apply to this paper also.74 75 As far as I can tell, the article contains three main ideas: an76 asynchronous execution / threading model; a model for monitors to77 provide mutual exclusion; and an implementation. The first two ideas78 are drawn together in Table 1: unfortunately this is on page 25 of 3079 pages of text. Implementation choices and descriptions are scattered80 throughout the paper - and the sectioning of the paper seems almost81 arbitrary.82 83 The article is about its contributions. Simply adding feature X to84 language Y isn't by itself a contribution, (when feature X isn't85 already a contribution). The contribution can be in the design: the86 motivation, the space of potential design options, the particular87 design chosen and the rationale for that choice, or the resulting88 performance. For example: why support two kinds of generators as well89 as user-level threads? Why support both low and high level90 synchronization constructs? Similarly I would have found the article91 easier to follow if it was written top down, presenting the design92 principles, present the space of language features, justify chosen93 language features (and rationale) and those excluded, and then present94 implementation, and performance.95 96 Then the writing of the article is often hard to follow, to say the97 least. Two examples: section 3 "stateful functions" - I've some idea98 what that is (a function with Algol's "own" or C's "static" variables?99 but in fact the paper has a rather more specific idea than that. The100 top of page 3 throws a whole lot of defintions at the reader101 "generator" "coroutine" "stackful" "stackless" "symmetric"102 "asymmetric" without every stopping to define each one --- but then in103 footnote "C" takes the time to explain what C's "main" function is? I104 cannot imagine a reader of this paper who doesn't know what "main" is105 in C; especially if they understand the other concepts already106 presented in the paper. The start of section 3 then does the same107 thing: putting up a whole lot of definitions, making distinctions and108 comparisons, even talking about some runtime details, but the critical109 definition of a monitor doesn't appear until three pages later, at the110 start of section 5 on p15, lines 29-34 are a good, clear, description111 of what a monitor actually is. That needs to come first, rather than112 being buried again after two sections of comparisons, discussions,113 implementations, and options that are ungrounded because they haven't114 told the reader what they are actually talking about. First tell the115 reader what something is, then how they might use it (as programmers:116 what are the rules and restrictions) and only then start comparison117 with other things, other approaches, other languages, or118 implementations.119 120 The description of the implementation is similarly lost in the trees121 without ever really seeing the wood. Figure 19 is crucial here, but122 it's pretty much at the end of the paper, and comments about123 implementations are threaded throughout the paper without the context124 (fig 19) to understand what's going on. The protocol for performance125 testing may just about suffice for C (although is N constantly ten126 million, or does it vary for each benchmark) but such evaluation isn't127 appropriate for garbage-collected or JITTed languages like Java or Go.128 129 other comments working through the paper - these are mostly low level130 and are certainly not comprehensive.131 132 p1 only a subset of C-forall extensions?133 134 p1 "has features often associated with object-oriented programming135 languages, such as constructors, destructors, virtuals and simple136 inheritance." There's no need to quibble about this. Once a language137 has inheritance, it's hard to claim it's not object-oriented.138 139 140 p2 barging? signals-as-hints?141 142 p3 start your discussion of generations with a simple example of a143 C-forall generator. Fig 1(b) might do: but put it inline instead of144 the python example - and explain the key rules and restrictions on the145 construct. Then don't even start to compare with coroutines until146 you've presented, described and explained your coroutines...147 p3 I'd probably leave out the various "C" versions unless there are148 key points to make you can't make in C-forall. All the alternatives149 are just confusing.150 151 152 p4 but what's that "with" in Fig 1(B)153 154 p5 start with the high level features of C-forall generators...155 156 p5 why is the paper explaining networking protocols?157 158 p7 lines 1-9 (transforming generator to coroutine - why would I do any159 of this? Why would I want one instead of the other (do not use "stack"160 in your answer!)161 162 p10 last para "A coroutine must retain its last resumer to suspend163 back because the resumer is on a different stack. These reverse164 pointers allow suspend to cycle backwards, " I've no idea what is165 going on here? why should I care? Shouldn't I just be using threads166 instead? why not?167 168 p16 for the same reasons - what reasons?169 170 p17 if the multiple-monitor entry procedure really is novel, write a171 paper about that, and only about that.172 173 p23 "Loose Object Definitions" - no idea what that means. in that174 section: you can't leave out JS-style dynamic properties. Even in175 OOLs that (one way or another) allow separate definitions of methods176 (like Objective-C, Swift, Ruby, C#) at any time a runtime class has a177 fixed definition. Quite why the detail about bit mask implementation178 is here anyway, I've no idea.179 180 p25 this cluster isn't a CLU cluster then?181 182 * conclusion should conclude the paper, not the related.183 184 185 Reviewing: 2186 187 Comments to the Author188 This paper describes the concurrency features of an extension of C (whose name I will write as "C\/" here, for convenience), including much design-level discussion of the coroutine- and monitor-based features and some microbenchmarks exploring the current implementation's performance. The key message of the latter is that the system's concurrency abstractions are much lighter-weight than the threading found in mainstream C or Java implementations.189 190 There is much description of the system and its details, but nothing about (non-artificial) uses of it. Although the microbenchmark data is encouraging, arguably not enough practical experience with the system has been reported here to say much about either its usability advantages or its performance.191 192 As such, the main contribution of the paper seem to be to document the existence of the described system and to provide a detailed design rationale and (partial) tutorial. I believe that could be of interest to some readers, so an acceptable manuscript is lurking in here somewhere.193 194 Unfortunately, at present the writing style is somewhere between unclear and infuriating. It omits to define terms; it uses needlessly many terms for what are apparently (but not clearly) the same things; it interrupts itself rather than deliver the natural consequent of whatever it has just said; and so on. Section 5 is particularly bad in these regards -- see my detailed comments below. Fairly major additional efforts will be needed to turn the present text into a digestible design-and-tutorial document. I suspect that a shorter paper could do this job better than the present manuscript, which is overwrought in parts.195 196 p2: lines 4--9 are a little sloppy. It is not the languages but their popular implementations which "adopt" the 1:1 kernel threading model.197 198 line 10: "medium work" -- "medium-sized work"?199 200 line 18: "is all sequential to the compiler" -- not true in modern compilers, and in 2004 H-J Boehm wrote a tech report describing exactly why ("Threads cannot be implemented as a library", HP Labs).201 202 line 20: "knows the optimization boundaries" -- I found this vague. What's an example?203 204 line 31: this paragraph has made a lot of claims. Perhaps forward-reference to the parts of the paper that discuss each one.205 206 line 33: "so the reader can judge if" -- this reads rather passive-aggressively. Perhaps better: "... to support our argument that..."207 208 line 41: "a dynamic partitioning mechanism" -- I couldn't tell what this meant209 210 p3. Presenting concept of a "stateful function" as a new language feature seems odd. In C, functions often have local state thanks to static local variables (or globals, indeed). Of course, that has several limitations. Can you perhaps present your contributions by enumerating these limitations? See also my suggestion below about a possible framing centred on a strawman.211 212 line 2: "an old idea that is new again" -- this is too oblique213 214 lines 2--15: I found this to be a word/concept soup. Stacks, closures, generators, stackless stackful, coroutine, symmetric, asymmetric, resume/suspend versus resume/resume... there needs to be a more gradual and structured way to introduce all this, and ideally one that minimises redundancy. Maybe present it as a series of "definitions" each with its own heading, e.g. "A closure is stackless if its local state has statically known fixed size"; "A generator simply means a stackless closure." And so on. Perhaps also strongly introduce the word "activate" as a direct contrast with resume and suspend. These are just a flavour of the sort of changes that might make this paragraph into something readable.215 216 Continuing the thought: I found it confusing that by these definitinos, a stackful closure is not a stack, even though logically the stack *is* a kind of closure (it is a representation of the current thread's continuation).217 218 lines 24--27: without explaining what the boost functor types mean, I don't think the point here comes across.219 220 line 34: "semantically coupled" -- I wasn't surew hat this meant221 222 p4: the point of Figure 1 (C) was not immediately clear. It seem to be showing how one might "compile down" Figure 1 (B). Or is that Figure 1 (A)?223 224 It's right that the incidental language features of the system are not front-and-centre, but I'd appreciate some brief glossing of non-C languages features as they appear. Examples are the square bracket notation, the pipe notation and the constructor syntax. These explanations could go in the caption of the figure which first uses them, perhaps. Overall I found the figure captions to be terse, and a missed opportunity to explain clearly what was going on.225 226 p5 line 23: "This restriction is removed..." -- give us some up-front summary of your contributions and the elements of the language design that will be talked about, so that this isn't an aside. This will reduce the "twisty passages" feeling that characterises much of the paper.227 228 line 40: "a killer asymmetric generator" -- this is stylistically odd, and the sentence about failures doesn't convincigly argue that C\/ will help with them. Have you any experience writing device drivers using C\/? Or any argument that the kinds of failures can be traced to the "stack-ripping" style that one is forced to use without coroutines? Also, a typo on line 41: "device drives". And saying "Windows/Linux" is sloppy... what does the cited paper actually say?229 230 p6 lines 13--23: this paragraph is difficult to understand. It seems to be talking about a control-flow pattern roughly equivalent to tail recursion. What is the high-level point, other than that this is possible?231 232 line 34: "which they call coroutines" -- a better way to make this point is presumably that the C++20 proposal only provides a specialised kind of coroutine, namely generators, despite its use of the more general word.233 234 line 47: "... due to dynamic stack allocation, execution..." -- this sentence doesn't scan. I suggest adding "and for" in the relevant places where currently there are only commas.235 236 p8 / Figure 5 (B) -- the GNU C extension of unary "&&" needs to be explained. The whole figure needs a better explanation, in fact.237 238 p9, lines 1--10: I wasn't sure this stepping-through really added much value. What are the truly important points to note about this code?239 240 p10: similarly, lines 3--27 again are somewhere between tedious and confusing. I'm sure the motivation and details of "starter semantics" can both be stated much more pithily.241 242 line 32: "a self-resume does not overwrite the last resumer" -- is this a hack or a defensible principled decision?243 244 p11: "a common source of errors" -- among beginners or among production code? Presumably the former.245 246 line 23: "with builtin and library" -- not sure what this means247 248 lines 31--36: these can be much briefer. The only important point here seems to be that coroutines cannot be copied.249 250 p12: line 1: what is a "task"? Does it matter?251 252 line 7: calling it "heap stack" seems to be a recipe for confusion. "Stack-and-heap" might be better, and contrast with "stack-and-VLS" perhaps. When "VLS" is glossed, suggest actually expanding its initials: say "length" not "size".253 254 line 21: are you saying "cooperative threading" is the same as "non-preemptive scheduling", or that one is a special case (kind) of the other? Both are defensible, but be clear.255 256 line 27: "mutual exclusion and synchronization" -- the former is a kind of the latter, so I suggest "and other forms of synchronization".257 258 line 30: "can either be a stackless or stackful" -- stray "a", but also, this seems to be switching from generic/background terminology to C\/-specific terminology.259 260 An expositional idea occurs: start the paper with a strawman naive/limited realisation of coroutines -- say, Simon Tatham's popular "Coroutines in C" web page -- and identify point by point what the limitations are and how C\/ overcomes them. Currently the presentation is often flat (lacking motivating contrasts) and backwards (stating solutions before problems). The foregoing approach might fix both of these.261 262 page 13: line 23: it seems a distraction to mention the Python feature here.263 264 p14 line 5: it seems odd to describe these as "stateless" just because they lack shared mutable state. It means the code itself is even more stateful. Maybe the "stack ripping" argument could usefully be given here.265 266 line 16: "too restrictive" -- would be good to have a reference to justify this, or at least give a sense of what the state-of-the-art performance in transactional memory systems is (both software and hardware)267 268 line 22: "simulate monitors" -- what about just *implementing* monitors? isn't that what these systems do? or is the point more about refining them somehow into something more specialised?269 270 p15: sections 4.1 and 4.2 seem adrift and misplaced. Split them into basic parts (which go earlier) and more advanced parts (e.g. barging, which can be explained later).271 272 line 31: "acquire/release" -- misses an opportunity to contrast the monitor's "enter/exit" abstraction with the less structured acquire/release of locks.273 274 p16 line 12: the "implicit" versus "explicit" point is unclear. Is it perhaps about the contract between an opt-in *discipline* and a language-enforced *guarantee*?275 276 line 28: no need to spend ages dithering about which one is default and which one is the explicit qualifier. Tell us what you decided, briefly justify it, and move on.277 278 p17: Figure 11: since the main point seems to be to highlight bulk acquire, include a comment which identifies the line where this is happening.279 280 line 2: "impossible to statically..." -- or dynamically. Doing it dynamically would be perfectly acceptable (locking is a dynamic operation after all)281 282 "guarantees acquisition order is consistent" -- assuming it's done in a single bulk acquire.283 284 p18: section 5.3: the text here is a mess. The explanations of "internal" versus "external" scheduling are unclear, and "signals as hints" is not explained. "... can cause thread starvation" -- means including a while loop, or not doing so? "There are three signalling mechanisms.." but the text does not follow that by telling us what they are. My own scribbled attempt at unpicking the internal/external thing: "threads already in the monitor, albeit waiting, have priority over those trying to enter".285 286 p19: line 3: "empty condition" -- explain that condition variables don't store anything. So being "empty" means that the queue of waiting threads (threads waiting to be signalled that the condition has become true) is empty.287 288 line 6: "... can be transformed into external scheduling..." -- OK, but give some motivation.289 290 p20: line 6: "mechnaism"291 292 lines 16--20: this is dense and can probably only be made clear with an example293 294 p21 line 21: clarify that nested monitor deadlock was describe earlier (in 5.2). (Is the repetition necessary?)295 296 line 27: "locks, and by extension monitors" -- this is true but the "by extension" argument is faulty. It is perfectly possible to use locks as a primitive and build a compositional mechanism out of them, e.g. transactions.297 298 p22 line 2: should say "restructured"299 300 line 33: "Implementing a fast subset check..." -- make clear that the following section explains how to do this. Restructuring the sections themselves could do this, or noting in the text.301 302 p23: line 3: "dynamic member adding, eg, JavaScript" -- needs to say "as permitted in JavaScript", and "dynamically adding members" is stylistically better303 304 p23: line 18: "urgent stack" -- back-reference to where this was explained before305 306 p24 line 7: I did not understand what was more "direct" about "direct communication". Also, what is a "passive monitor" -- just a monitor, given that monitors are passive by design?307 308 line 14 / section 5.9: this table was useful and it (or something like it) could be used much earlier on to set the structure of the rest of the paper. The explanation at present is too brief, e.g. I did not really understand the point about cases 7 and 8.309 310 p25 line 2: instead of casually dropping in a terse explanation for the newly intrdouced term "virtual processor", introduce it properly. Presumably the point is to give a less ambiguous meaning to "thread" by reserving it only for C\/'s green threads.311 312 Table 1: what does "No / Yes" mean?313 314 p26 line 15: "transforms user threads into fibres" -- a reference is needed to explain what "fibres" means... guessing it's in the sense of Adya et al.315 316 line 20: "Microsoft runtime" -- means Windows?317 318 lines 21--26: don't say "interrupt" to mean "signal", especially not without clear introduction. You can use "POSIX signal" to disambiguate from condition variables' "signal".319 320 p27 line 3: "frequency is usually long" -- that's a "time period" or "interval", not a frequency321 322 line 5: the lengthy quotation is not really necessary; just paraphrase the first sentence and move on.323 324 line 20: "to verify the implementation" -- I don't think that means what is intended325 326 Tables in section 7 -- too many significant figures. How many overall runs are described? What is N in each case?327 328 p29 line 2: "to eliminate this cost" -- arguably confusing since nowadays on commodity CPUs most of the benefits of inlining are not to do with call overheads, but from later optimizations enabled as a consequence of the inlining329 330 line 41: "a hierarchy" -- are they a hierarchy? If so, this could be explained earlier. Also, to say these make up "an integrated set... of control-flow features" verges on the tautologous.331 332 p30 line 15: "a common case being web servers and XaaS" -- that's two cases333 334 335 Reviewing: 3336 337 Comments to the Author338 # Cforall review339 340 Overall, I quite enjoyed reading the paper. Cforall has some very interesting ideas. I did have some suggestions that I think would be helpful before final publication. I also left notes on various parts of the paper that I find confusing when reading, in hopes that it may be useful to you.341 342 ## Summary343 344 * Expand on the motivations for including both generator and coroutines, vs trying to build one atop the other345 * Expand on the motivations for having Why both symmetric and asymettric coroutines?346 * Comparison to async-await model adopted by other languages347 * C#, JS348 * Rust and its async/await model349 * Consider performance comparisons against node.js and Rust frameworks350 * Discuss performance of monitors vs finer-grained memory models and atomic operations found in other languages351 * Why both internal/external scheduling for synchronization?352 353 ## Generator/coroutines354 355 In general, this section was clear, but I thought it would be useful to provide a somewhat deeper look into why Cforall opted for the particular combination of features that it offers. I see three main differences from other languages:356 357 * Generators are not exposed as a "function" that returns a generator object, but rather as a kind of struct, with communication happening via mutable state instead of "return values". That is, the generator must be manually resumed and (if I understood) it is expected to store values that can then later be read (perhaps via methods), instead of having a `yield <Expr>` statement that yields up a value explicitly.358 * Both "symmetric" and "asymmetric" generators are supported, instead of only asymmetric.359 * Coroutines (multi-frame generators) are an explicit mechanism.360 361 In most other languages, coroutines are rather built by layering single-frame generators atop one another (e.g., using a mechanism like async-await), and symmetric coroutines are basically not supported. I'd like to see a bit more justification for Cforall including all the above mechanisms -- it seemed like symmetric coroutines were a useful building block for some of the user-space threading and custom scheduler mechanisms that were briefly mentioned later in the paper.362 363 In the discussion of coroutines, I would have expected a bit more of a comparison to the async-await mechanism offered in other languages. Certainly the semantics of async-await in JavaScript implies significantly more overhead (because each async fn is a distinct heap object). [Rust's approach avoids this overhead][zc], however, and might be worthy of a comparison (see the Performance section).364 365 ## Locks and threading366 367 ### Comparison to atomics overlooks performance368 369 There are several sections in the paper that compare against atomics -- for example, on page 15, the paper shows a simple monitor that encapsulates an integer and compares that to C++ atomics. Later, the paper compares the simplicity of monitors against the `volatile` quantifier from Java. The conclusion in section 8 also revisits this point.370 371 While I agree that monitors are simpler, they are obviously also significantly different from a performance perspective -- the paper doesn't seem to address this at all. It's plausible that (e.g.) the `Aint` monitor type described in the paper can be compiled and mapped to the specialized instructions offered by hardware, but I didn't see any mention of how this would be done. There is also no mention of the more nuanced memory ordering relations offered by C++11 and how one might achieve similar performance characteristics in Cforall (perhaps the answer is that one simply doesn't need to; I think that's defensible, but worth stating explicitly).372 373 ### Justification for external scheduling feels lacking374 375 Cforall includes both internal and external scheduling; I found the explanation for the external scheduling mechanism to be lacking in justification. Why include both mechanisms when most languages seem to make do with only internal scheduling? It would be useful to show some scenarios where external scheduling is truly more powerful.376 377 I would have liked to see some more discussion of external scheduling and how it interacts with software engineering best practices. It seems somewhat similar to AOP in certain regards. It seems to add a bit of "extra semantics" to monitor methods, in that any method may now also become a kind of synchronization point. The "open-ended" nature of this feels like it could easily lead to subtle bugs, particularly when code refactoring occurs (which may e.g. split an existing method into two). This seems particularly true if external scheduling can occur across compilation units -- the paper suggested that this is true, but I wasn't entirely clear.378 379 I would have also appreciated a few more details on how external scheduling is implemented. It seems to me that there must be some sort of "hooks" on mutex methods so that they can detect whether some other function is waiting on them and awaken those blocked threads. I'm not sure how such hooks are inserted, particularly across compilation units. The material in Section 5.6 didn't quite clarify the matter for me. For example, it left me somewhat confused about whether the `f` and `g` functions declared were meant to be local to a translation unit, or shared with other unit.380 381 ### Presentation of monitors is somewhat confusing382 383 I found myself confused fairly often in the section on monitors. I'm just going to leave some notes here on places that I got confused in how that it could be useful to you as feedback on writing that might want to be clarified.384 385 To start, I did not realize that the `mutex_opt` notation was a keyword, I thought it was a type annotation. I think this could be called out more explicitly.386 387 Later, in section 5.2, the paper discusses `nomutex` annotations, which initially threw me, as they had not been introduced (now I realize that this paragraph is there to justify why there is no such keyword). The paragraph might be rearranged to make that clearer, perhaps by leading with the choice that Cforall made.388 389 On page 17, the paper states that "acquiring multiple monitors is safe from deadlock", but this could be stated a bit more precisely: acquiring multiple monitors in a bulk-acquire is safe from deadlock (deadlock can still result from nested acquires).390 391 On page 18, the paper states that wait states do not have to be enclosed in loops, as there is no concern of barging. This seems true but there are also other reasons to use loops (e.g., if there are multiple reasons to notify on the same condition). Thus the statement initially surprised me, as barging is only one of many reasons that I typically employ loops around waits.392 393 I did not understand the diagram in Figure 12 for some time. Initially, I thought that it was generic to all monitors, and I could not understand the state space. It was only later that I realized it was specific to your example. Updating the caption from "Monitor scheduling to "Monitor scheduling in the example from Fig 13" might have helped me quite a bit.394 395 I spent quite some time reading the boy/girl dating example (\*) and I admit I found it somewhat confusing. For example, I couldn't tell whether there were supposed to be many "girl" threads executing at once, or if there was only supposed to be one girl and one boy thread executing in a loop. Are the girl/boy threads supposed to invoke the girl/boy methods or vice versa? Surely there is some easier way to set this up? I believe that when reading the paper I convinced myself of how it was supposed to be working, but I'm writing this review some days later, and I find myself confused all over again and not able to easily figure it out.396 397 (\*) as an aside, I would consider modifying the example to some other form of matching, like customers and support personnel.398 399 ## Related work400 401 The paper offered a number of comparisons to Go, C#, Scala, and so forth, but seems to have overlooked another recent language, Rust. In many ways, Rust seems to be closest in philosophy to Cforall, so it seems like an odd omission. I already mentioned above that Rust is in the process of shipping [async-await syntax][aa], which is definitely an alternative to the generator/coroutine approach in Cforall (though one with clear pros/cons).402 403 ## Performance404 405 In the performance section in particular, you might consider comparing against some of the Rust web servers and threading systems. For example, actix is top of the [single query TechEmpower Framework benchmarks], and tokio is near the top of the [plainthreading benchmarks][pt] (hyper, the top, is more of an HTTP framework, though it is also written in Rust). It would seem worth trying to compare their "context switching" costs as well -- I believe both actix and tokio have a notion of threads that could be readily compared.406 407 Another addition that might be worth considering is to compare against node.js promises, although I think the comparison to process creation is not as clean.408 409 That said, I think that the performance comparison is not a big focus of the paper, so it may not be necessary to add anything to it.410 411 ## Authorship of this review412 413 I'm going to sign this review. This review was authored by Nicholas D. Matsakis. In the intrerest of full disclosure, I'm heavily involved in the Rust project, although I dont' think that influenced this review in particular. Feel free to reach out to me for clarifying questions.414 415 ## Links416 417 [aa]: https://blog.rust-lang.org/2019/09/30/Async-await-hits-beta.html418 [zc]: https://aturon.github.io/blog/2016/08/11/futures/419 [sq]: https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=db420 [pt]: https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=plaintext421 422 423 424 Subject: Re: manuscript SPE-19-0219425 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>426 From: Richard Jones <R.E.Jones@kent.ac.uk>427 Date: Tue, 12 Nov 2019 22:43:55 +0000428 429 Dear Dr Buhr430 431 Your should have received a decision letter on this today. I am sorry that this432 has taken so long. Unfortunately SP&E receives a lot of submissions and getting433 reviewers is a perennial problem.434 435 Regards436 Richard437 438 Peter A. Buhr wrote on 11/11/2019 13:10:439 > 26-Jun-2019440 > Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall"441 > has been received by Software: Practice and Experience. It will be given442 > full consideration for publication in the journal.443 >444 > Hi, it has been over 4 months since submission of our manuscript SPE-19-0219445 > with no response.446 >447 > Currently, I am refereeing a paper for IEEE that already cites our prior SP&E448 > paper and the Master's thesis forming the bases of the SP&E paper under449 > review. Hence our work is apropos and we want to get it disseminates as soon as450 > possible.451 >452 > [3] A. Moss, R. Schluntz, and P. A. Buhr, "Cforall: Adding modern programming453 > language features to C," Software - Practice and Experience, vol. 48,454 > no. 12, pp. 2111-2146, 2018.455 >456 > [4] T. Delisle, "Concurrency in C for all," Master's thesis, University of457 > Waterloo, 2018. [Online]. Available:458 > https://uwspace.uwaterloo.ca/bitstream/handle/10012/12888459 460 461 462 Date: Mon, 13 Jan 2020 05:33:15 +0000463 From: Richard Jones <onbehalfof@manuscriptcentral.com>464 Reply-To: R.E.Jones@kent.ac.uk465 To: pabuhr@uwaterloo.ca466 Subject: Revision reminder - SPE-19-0219467 468 13-Jan-2020469 Dear Dr Buhr470 SPE-19-0219471 472 This is a reminder that your opportunity to revise and re-submit your473 manuscript will expire 28 days from now. If you require more time please474 contact me directly and I may grant an extension to this deadline, otherwise475 the option to submit a revision online, will not be available.476 477 I look forward to receiving your revision.478 479 Sincerely,480 481 Prof. Richard Jones482 Editor, Software: Practice and Experience483 https://mc.manuscriptcentral.com/spe484 485 486 487 Date: Wed, 5 Feb 2020 04:22:18 +0000488 From: Aaron Thomas <onbehalfof@manuscriptcentral.com>489 Reply-To: speoffice@wiley.com490 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca491 Subject: SPE-19-0219.R1 successfully submitted492 493 04-Feb-2020494 495 Dear Dr Buhr,496 497 Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has498 been successfully submitted online and is presently being given full499 consideration for publication in Software: Practice and Experience.500 501 Your manuscript number is SPE-19-0219.R1. Please mention this number in all502 future correspondence regarding this submission.503 504 You can view the status of your manuscript at any time by checking your Author505 Center after logging into https://mc.manuscriptcentral.com/spe. If you have506 difficulty using this site, please click the 'Get Help Now' link at the top507 right corner of the site.508 509 Thank you for submitting your manuscript to Software: Practice and Experience.510 511 Sincerely,512 Software: Practice and Experience Editorial Office513 514 515 516 Date: Sat, 18 Apr 2020 10:42:13 +0000517 From: Richard Jones <onbehalfof@manuscriptcentral.com>518 Reply-To: R.E.Jones@kent.ac.uk519 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca520 Subject: Software: Practice and Experience - Decision on Manuscript ID521 SPE-19-0219.R1522 523 18-Apr-2020524 525 Dear Dr Buhr,526 527 Many thanks for submitting SPE-19-0219.R1 entitled "Advanced Control-flow and Concurrency in Cforall" to Software: Practice and Experience. The paper has now been reviewed and the comments of the referees are included at the bottom of this letter.528 529 I believe that we are making progress here towards a paper that can be published in Software: Practice and Experience. However the referees still have significant concerns about the paper. The journal's focus is on practice and experience, and one of the the reviewers' concerns remains that your submission should focus the narrative more on the perspective of the programmer than the language designer. I agree that this would strengthen your submission, and I ask you to address this as well as the referees' other comments.530 531 A revised version of your manuscript that takes into account the comments of the referee(s) will be reconsidered for publication.532 533 Please note that submitting a revision of your manuscript does not guarantee eventual acceptance, and that your revision may be subject to re-review by the referees before a decision is rendered.534 535 You have 90 days from the date of this email to submit your revision. If you are unable to complete the revision within this time, please contact me to request a short extension.536 537 You can upload your revised manuscript and submit it through your Author Center. Log into https://mc.manuscriptcentral.com/spe and enter your Author Center, where you will find your manuscript title listed under "Manuscripts with Decisions".538 539 When submitting your revised manuscript, you will be able to respond to the comments made by the referee(s) in the space provided. You can use this space to document any changes you make to the original manuscript.540 541 If you would like help with English language editing, or other article preparation support, Wiley Editing Services offers expert help with English Language Editing, as well as translation, manuscript formatting, and figure formatting at www.wileyauthors.com/eeo/preparation. You can also check out our resources for Preparing Your Article for general guidance about writing and preparing your manuscript at www.wileyauthors.com/eeo/prepresources.542 543 Once again, thank you for submitting your manuscript to Software: Practice and Experience and I look forward to receiving your revision.544 545 Sincerely,546 Richard547 548 Prof. Richard Jones549 Software: Practice and Experience550 R.E.Jones@kent.ac.uk551 552 553 Referee(s)' Comments to Author:554 555 Reviewing: 1556 557 Comments to the Author558 (A relatively short second review)559 560 I thank the authors for their revisions and comprehensive response to561 reviewers' comments --- many of my comments have been successfully562 addressed by the revisions. Here I'll structure my comments around563 the main salient points in that response which I consider would564 benefit from further explanation.565 566 > Table 1 is moved to the start and explained in detail.567 568 I consider this change makes a significant improvement to the paper,569 laying out the landscape of language features at the start, and thus570 addresses my main concerns about the paper.571 572 I still have a couple of issues --- perhaps the largest is that it's573 still not clear at this point in the paper what some of these options574 are, or crucially how they would be used. I don't know if it's575 possbile to give high-level examples or use cases to be clear about576 these up front - or if that would duplicate too much information from577 later in the paper - either way expanding out the discussion - even if578 just two a couple of sentences for each row - would help me more. The579 point is not just to define these categories but to ensure the580 readers' understanding of these definitons agrees with that used in581 the paper.582 583 in a little more detail:584 585 * 1st para section 2 begs the question: why not support each586 dimension independently, and let the programmer or library designer587 combiine features?588 589 * "execution state" seems a relatively low-level description here.590 I don't think of e.g. the lambda calculus that way. Perhaps it's as591 good a term as any.592 593 * Why must there "be language mechanisms to create, block/unblock,594 and join with a thread"? There aren't in Smalltalk (although there595 are in the runtime). Especially given in Cforall those mechanisms596 are *implicit* on thread creation and destruction?597 598 * "Case 1 is a function that borrows storage for its state (stack599 frame/activation) and a thread from its invoker"600 601 this much makes perfect sense to me, but I don't understand how a602 non-stateful, non-theaded function can then retain603 604 "this state across callees, ie, function local-variables are605 retained on the stack across calls."606 607 how can it retain function-local values *across calls* when it608 doesn't have any functional-local state?609 610 I'm not sure if I see two separate cases here - rougly equivalent611 to C functions without static storage, and then C functions *with*612 static storage. I assumed that was the distinction between cases 1613 & 3; but perhpas the actual distinction is that 3 has a614 suspend/resume point, and so the "state" in figure 1 is this615 component of execution state (viz figs 1 & 2), not the state616 representing the cross-call variables?617 618 > but such evaluation isn't appropriate for garbage-collected or JITTed619 languages like Java or Go.620 621 For JITTed languages in particular, reporting peak performance needs622 to "warm up" the JIT with a number of iterators before beginning623 measurement. Actually for JIT's its even worse: see Edd Barrett et al624 OOPSLA 2017.625 626 627 628 minor issues:629 630 * footnote A - I've looked at various other papers & the website to631 try to understand how "object-oriented" Cforall is - I'm still not632 sure. This footnote says Cforall has "virtuals" - presumably633 virtual functions, i.e. dynamic dispatch - and inheritance: that634 really is OO as far as I (and most OO people) are concerned. For635 example Haskell doesn't have inheritance, so it's not OO; while636 CLOS (the Common Lisp *Object* System) or things like Cecil and637 Dylan are considered OO even though they have "multiple function638 parameters as receivers", lack "lexical binding between a structure639 and set of functions", and don't have explicit receiver invocation640 syntax. Python has receiver syntax, but unlike Java or Smalltalk641 or C++, method declarations still need to have an explicit "self"642 receiver parameter. Seems to me that Go, for example, is643 more-or-less OO with interfaces, methods, and dynamic dispatch (yes644 also and an explicit receiver syntax but that's not645 determiniative); while Rust lacks dynamic dispatch built-in. C is646 not OO as a language, but as you say given it supports function647 pointers with structures, it does support an OO programm style.648 649 This is why I again recommend just not buying into this fight: not650 making any claims about whether Cforall is OO or is not - because651 as I see it, the rest of the paper doesn't depend on whether652 Cforall is OO or not. That said: this is just a recommendation,653 and I won't quibble over this any further.654 655 * is a "monitor function" the same as a "mutex function"?656 if so the paper should pick one term; if not, make the distinction clear.657 658 659 * "As stated on line 1 because state declarations from the generator660 type can be moved out of the coroutine type into the coroutine main"661 662 OK sure, but again: *why* would a programmer want to do that?663 (Other than, I guess, to show the difference between coroutines &664 generators?) Perhaps another way to put this is that the first665 para of 3.2 gives the disadvantages of coroutines vs-a-vs666 generators, briefly describes the extended semantics, but never667 actualy says why a programmer may want those extended semantics,668 or how they would benefit. I don't mean to belabour the point,669 but (generalist?) readers like me would generally benefit from670 those kinds of discussions about each feature throughout the671 paper: why might a programmer want to use them?672 673 674 > p17 if the multiple-monitor entry procedure really is novel, write a paper675 > about that, and only about that.676 677 > We do not believe this is a practical suggestion.678 679 * I'm honestly not trying to be snide here: I'm not an expert on680 monitor or concurrent implementations. Brinch Hansen's original681 monitors were single acquire; this draft does not cite any other682 previous work that I could see. I'm not suggesting that the brief683 mention of this mechanism necessarily be removed from this paper,684 but if this is novel (and a clear advance over a classical OO685 monitor a-la Java which only acquires the distinguished reciever)686 then that would be worth another paper in itself.687 688 > * conclusion should conclude the paper, not the related.689 > We do not understand this comment.if ithis690 691 My typo: the paper's conclusion should come at the end, after the692 future work section.693 694 695 696 697 To encourage accountability, I'm signing my reviews in 2020.698 For the record, I am James Noble, kjx@ecs.vuw.ac.nz.699 700 701 Reviewing: 2702 703 Comments to the Author704 I thank the authors for their detailed response. To respond to a couple of points raised in response to my review (number 2):705 706 - on the Boehm paper and whether code is "all sequential to the compiler": I now understand the authors' position better and suspect we are in violent agreement, except for whether it's appropriate to use the rather breezy phrase "all sequential to the compiler". It would be straightforward to clarify that code not using the atomics features is optimized *as if* it were sequential, i.e. on the assumption of a lack of data races.707 708 - on the distinction between "mutual exclusion" and "synchronization": the added citation does help, in that it makes a coherent case for the definition the authors prefer. However, the text could usefully clarify that this is a matter of definition not of fact, given especially that in my assessment the authors' preferred definition is not the most common one. (Although the mention of Hoare's apparent use of this definition is one data point, countervailing ones are found in many contemporaneous or later papers, e.g. Habermann's 1972 "Synchronization of Communicating Processes" (CACM 15(3)), Reed & Kanodia's 1979 "Synchronization with eventcounts and sequencers" (CACM (22(2)) and so on.)709 710 I am glad to see that the authors have taken on board most of the straightforward improvements I suggested.711 712 However, a recurring problem of unclear writing still remains through many parts of the paper, including much of sections 2, 3 and 6. To highlight a couple of problem patches (by no means exhaustive):713 714 - section 2 (an expanded version of what was previously section 5.9) lacks examples and is generally obscure and allusory ("the most advanced feature" -- name it! "in triplets" -- there is only one triplet!; what are "execution locations"? "initialize" and "de-initialize" what? "borrowed from the invoker" is a concept in need of explaining or at least a fully explained example -- in what sense does a plain function borrow" its stack frame? "computation only" as opposed to what? in 2.2, in what way is a "request" fundamental to "synchronization"? and the "implicitly" versus "explicitly" point needs stating as elsewhere, with a concrete example e.g. Java built-in mutexes versus java.util.concurrent).715 716 - section 6: 6.2 omits the most important facts in preference for otherwise inscrutable detail: "identify the kind of parameter" (first say *that there are* kinds of parameter, and what "kinds" means!); "mutex parameters are documentation" is misleading (they are also semantically significant!) and fails to say *what* they mean; the most important thing is surely that 'mutex' is a language feature for performing lock/unlock operations at function entry/exit. So say it! The meanings of examples f3 and f4 remain unclear. Meanwhile in 6.3, "urgent" is not introduced (we are supposed to infer its meaning from Figure 12, but that Figure is incomprehensible to me), and we are told of "external scheduling"'s long history in Ada but not clearly what it actually means; 6.4's description of "waitfor" tells us it is different from an if-else chain but tries to use two *different* inputs to tell us that the behavior is different; tell us an instance where *the same* values of C1 and C2 give different behavior (I even wrote out a truth table and still don't see the semantic difference)717 718 The authors frequently use bracketed phrases, and sometimes slashes "/", in ways that are confusing and/or detrimental to readability. Page 13 line 2's "forward (backward)" is one particularly egregious example. In general I would recommend the the authors try to limit their use of parentheses and slashes as a means of forcing a clearer wording to emerge. Also, the use of "eg." is often cursory and does not explain the examples given, which are frequently a one- or two-word phrase of unclear referent.719 720 Considering the revision more broadly, none of the more extensive or creative rewrites I suggested in my previous review have been attempted, nor any equivalent efforts to improve its readability. The hoisting of the former section 5.9 is a good idea, but the newly added material accompanying it (around Table 1) suffers fresh deficiencies in clarity. Overall the paper is longer than before, even though (as my previous review stated), I believe a shorter paper is required in order to serve the likely purpose of publication. (Indeed, the authors' letter implies that a key goal of publication is to build community and gain external users.)721 722 Given this trajectory, I no longer see a path to an acceptable revision of the present submission. Instead I suggest the authors consider splitting the paper in two: one half about coroutines and stack management, the other about mutexes, monitors and the runtime. (A briefer presentation of the runtime may be helpful in the first paper also, and a brief recap of the generator and coroutine support is obviously needed in the second too.) Both of these new papers would need to be written with a strong emphasis on clarity, paying great care to issues of structure, wording, choices of example, and restraint (saying what's important, not everything that could be said). I am confident the authors could benefit from getting early feedback from others at their institution. For the performance experiments, of course these do not split evenly -- most (but not all) belong in the second of these two hypothetical papers. But the first of them would still have plenty of meat to it; for me, a clear and thorough study of the design space around coroutines is the most interesting and tantalizing prospect.723 724 I do not buy the authors' defense of the limited practical experience or "non-micro" benchmarking presented. Yes, gaining external users is hard and I am sympathetic on that point. But building something at least *somewhat* substantial with your own system should be within reach, and without it the "practice and experience" aspects of the work have not been explored. Clearly C\/ is the product of a lot of work over an extended period, so it is a surprise that no such experience is readily available for inclusion.725 726 Some smaller points:727 728 It does not seem right to state that a stack is essential to Von Neumann architectures -- since the earliest Von Neumann machines (and indeed early Fortran) did not use one.729 730 To elaborate on something another reviewer commented on: it is a surprise to find a "Future work" section *after* the "Conclusion" section. A "Conclusions and future work" section often works well.731 732 733 Reviewing: 3734 735 Comments to the Author736 This is the second round of reviewing.737 738 As in the first review, I found that the paper (and Cforall) contains739 a lot of really interesting ideas, but it remains really difficult to740 have a good sense of which idea I should use and when. This applies in741 different ways to different features from the language:742 743 * coroutines/generators/threads: here there is744 some discussion, but it can be improved.745 * interal/external scheduling: I didn't find any direct comparison746 between these features, except by way of example.747 748 I requested similar things in my previous review and I see that749 content was added in response to those requests. Unfortunately, I'm750 not sure that I can say it improved the paper's overall read. I think751 in some sense the additions were "too much" -- I would have preferred752 something more like a table or a few paragraphs highlighting the key753 reasons one would pick one construct or the other.754 755 In general, I do wonder if the paper is just trying to do too much.756 The discussion of clusters and pre-emption in particular feels quite757 rushed.758 759 ## Summary760 761 I make a number of suggestions below but the two most important762 I think are:763 764 * Recommend to shorten the comparison on coroutine/generator/threads765 in Section 2 to a paragraph with a few examples, or possibly a table766 explaining the trade-offs between the constructs767 * Recommend to clarify the relationship between internal/external768 scheduling -- is one more general but more error-prone or low-level?769 770 ## Coroutines/generators/threads771 772 There is obviously a lot of overlap between these features, and in773 particular between coroutines and generators. As noted in the previous774 review, many languages have chosen to offer *only* generators, and to775 build coroutines by stacks of generators invoking one another.776 777 I believe the newly introduced Section 2 of the paper is trying to778 motivate why each of these constructs exist, but I did not find it779 effective. It was dense and difficult to understand. I think the780 problem is that Section 2 seems to be trying to derive "from first781 principles" why each construct exists, but I think that a more "top782 down" approach would be easier to understand.783 784 In fact, the end of Section 2.1 (on page 5) contains a particular785 paragraph that embodies this "top down" approach. It starts,786 "programmers can now answer three basic questions", and thus gives787 some practical advice for which construct you should use and when. I788 think giving some examples of specific applications that this789 paragraph, combined with some examples of cases where each construct790 was needed, would be a better approach.791 792 I don't think this compariosn needs to be very long. It seems clear793 enough that one would794 795 * prefer generators for simple computations that yield up many values,796 * prefer coroutines for more complex processes that have significant797 internal structure,798 * prefer threads for cases where parallel execution is desired or799 needed.800 801 I did appreciate the comparison in Section 2.3 between async-await in802 JS/Java and generators/coroutines. I agree with its premise that those803 mechanisms are a poor replacement for generators (and, indeed, JS has804 a distinct generator mechanism, for example, in part for this reason).805 I believe I may have asked for this in a previous review, but having806 read it, I wonder if it is really necessary, since those mechanisms807 are so different in purpose.808 809 ## Internal vs external scheduling810 811 I find the motivation for supporting both internal and external812 scheduling to be fairly implicit. After several reads through the813 section, I came to the conclusion that internal scheduling is more814 expressive than external scheduling, but sometimes less convenient or815 clear. Is this correct? If not, it'd be useful to clarify where816 external scheduling is more expressive.817 818 The same is true, I think, of the `signal_block` function, which I819 have not encountered before; it seems like its behavior can be modeled820 with multiple condition variables, but that's clearly more complex.821 822 One question I had about `signal_block`: what happens if one signals823 but no other thread is waiting? Does it block until some other thread824 waits? Or is that user error?825 826 I would find it very interesting to try and capture some of the827 properties that make internal vs external scheduling the better828 choice.829 830 For example, it seems to me that external scheduling works well if831 there are only a few "key" operations, but that internal scheduling832 might be better otherwise, simply because it would be useful to have833 the ability to name a signal that can be referenced by many834 methods. Consider the bounded buffer from Figure 13: if it had835 multiple methods for removing elements, and not just `remove`, then836 the `waitfor(remove)` call in `insert` might not be sufficient.837 838 ## Comparison of external scheduling to messaging839 840 I did enjoy the section comparing external scheduling to Go's841 messaging mechanism, which I believe is a new addition.842 843 I believe that one difference between the Go program and the Cforall844 equivalent is that the Goroutine has an associated queue, so that845 multiple messages could be enqueued, whereas the Cforall equivalent is846 effectively a "bounded buffer" of length 1. Is that correct? I think847 this should be stated explicitly. (Presumably, one could modify the848 Cforall program to include an explicit vector of queued messages if849 desired, but you would also be reimplementing the channel850 abstraction.)851 852 Also, in Figure 20, I believe that there is a missing `mutex` keyword.853 The fiugre states:854 855 ```856 void main(GoRtn & gortn) with(gortn) {857 ```858 859 but I think it should probably be as follows:860 861 ```862 void main(GoRtn & mutex gortn) with(gortn) {863 ```864 865 Unless there is some implicit `mutex` associated with being a main866 function for a `monitor thread`.867 868 ## Atomic operations and race freedom869 870 I was glad to see that the paper acknowledged that Cforall still had871 low-level atomic operations, even if their use is discouraged in favor872 of higher-level alternatives.873 874 However, I still feel that the conclusion overstates the value of the875 contribution here when it says that "Cforall high-level race-free876 monitors and threads provide the core mechanisms for mutual exclusion877 and synchronization, without the need for volatile and atomics". I878 feel confident that Java programmers, for example, would be advised to879 stick with synchronized methods whenever possible, and it seems to me880 that they offer similar advantages -- but they sometimes wind up using881 volatiles for performance reasons.882 883 I was also confused by the term "race-free" in that sentence. In884 particular, I don't think that Cforall has any mechanisms for885 preventing *data races*, and it clearly doesn't prevent "race886 conditions" (which would bar all sorts of useful programs). I suppose887 that "race free" here might be referring to the improvements such as888 removing barging behavior.889 890 ## Performance comparisons891 892 In my previous review, I requested comparisons against Rust and893 node.js, and I see that the new version of the paper includes both,894 which is a good addition.895 896 One note on the Rust results: I believe that the results are comparing897 against the threads found in Rust's standard library, which are898 essentially a shallow wrapper around pthreads, and hence the899 performance is quite close to pthread performance (as one would900 expect). It would perhaps be more interesting to see a comparison901 built using [tokio] or [async-std], two of the more prominent902 user-space threading libraries that build on Rust's async-await903 feature (which operates quite differently than Javascript's904 async-await, in that it doesn't cause every aync function call to905 schedule a distinct task).906 907 [tokio]: https://tokio.rs/908 [async-std]: https://async.rs/909 910 That said, I am satisfied with the performance results as they are in911 the current revision.912 913 ## Minor notes and typos914 915 Several figures used the `with` keyword. I deduced that `with(foo)`916 permits one to write `bar` instead of `foo.bar`. It seems worth917 introducing. Apologies if this is stated in the paper, if so I missed918 it.919 920 On page 20, section 6.3, "external scheduling and vice versus" should be921 "external scheduling and vice versa".922 923 On page 5, section 2.3, the paper states "we content" but it should be924 "we contend".925 926 Reviewing: Editor927 928 A few small comments in addition to those of the referees.929 930 Page 1. I don't believe that it s fair to imply that Scala is "research vehicle" as it is used by major players, Twitter being the most prominent example.931 932 Page 15. Must Cforall threads start after construction (e.g. see your example on page 15, line 21)? I can think of examples where it is not desirable that threads start immediately after construction, e.g. a game with N players, each of whom is expensive to create, but all of whom should be started at the same time.933 934 Page 18, line 17: is using935 936 937 938 Date: Tue, 16 Jun 2020 13:45:03 +0000939 From: Aaron Thomas <onbehalfof@manuscriptcentral.com>940 Reply-To: speoffice@wiley.com941 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca942 Subject: SPE-19-0219.R2 successfully submitted943 944 16-Jun-2020945 946 Dear Dr Buhr,947 948 Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has been successfully submitted online and is presently being given full consideration for publication in Software: Practice and Experience.949 950 Your manuscript number is SPE-19-0219.R2. Please mention this number in all future correspondence regarding this submission.951 952 You can view the status of your manuscript at any time by checking your Author Center after logging into https://mc.manuscriptcentral.com/spe. If you have difficulty using this site, please click the 'Get Help Now' link at the top right corner of the site.953 954 955 Thank you for submitting your manuscript to Software: Practice and Experience.956 957 Sincerely,958 959 Software: Practice and Experience Editorial Office960 961 962 963 Date: Wed, 2 Sep 2020 20:55:34 +0000964 From: Richard Jones <onbehalfof@manuscriptcentral.com>965 Reply-To: R.E.Jones@kent.ac.uk966 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca967 Subject: Software: Practice and Experience - Decision on Manuscript ID968 SPE-19-0219.R2969 970 02-Sep-2020971 972 Dear Dr Buhr,973 974 Many thanks for submitting SPE-19-0219.R2 entitled "Advanced Control-flow and Concurrency in Cforall" to Software: Practice and Experience. The paper has now been reviewed and the comments of the referees are included at the bottom of this letter. I apologise for the length of time it has taken to get these.975 976 Both reviewers consider this paper to be close to acceptance. However, before I can accept this paper, I would like you address the comments of Reviewer 2, particularly with regard to the description of the adaptation Java harness to deal with warmup. I would expect to see a convincing argument that the computation has reached a steady state. I would also like you to provide the values for N for each benchmark run. This should be very straightforward for you to do. There are a couple of papers on steady state that you may wish to consult (though I am certainly not pushing my own work).977 978 1) Barrett, Edd; Bolz-Tereick, Carl Friedrich; Killick, Rebecca; Mount, Sarah and Tratt, Laurence. Virtual Machine Warmup Blows Hot and Cold. OOPSLA 2017. https://doi.org/10.1145/3133876979 Virtual Machines (VMs) with Just-In-Time (JIT) compilers are traditionally thought to execute programs in two phases: the initial warmup phase determines which parts of a program would most benefit from dynamic compilation, before JIT compiling those parts into machine code; subsequently the program is said to be at a steady state of peak performance. Measurement methodologies almost always discard data collected during the warmup phase such that reported measurements focus entirely on peak performance. We introduce a fully automated statistical approach, based on changepoint analysis, which allows us to determine if a program has reached a steady state and, if so, whether that represents peak performance or not. Using this, we show that even when run in the most controlled of circumstances, small, deterministic, widely studied microbenchmarks often fail to reach a steady state of peak performance on a variety of common VMs. Repeating our experiment on 3 different machines, we found that at most 43.5% of pairs consistently reach a steady state of peak performance.980 981 2) Kalibera, Tomas and Jones, Richard. Rigorous Benchmarking in Reasonable Time. ISMM 2013. https://doi.org/10.1145/2555670.2464160982 Experimental evaluation is key to systems research. Because modern systems are complex and non-deterministic, good experimental methodology demands that researchers account for uncertainty. To obtain valid results, they are expected to run many iterations of benchmarks, invoke virtual machines (VMs) several times, or even rebuild VM or benchmark binaries more than once. All this repetition costs time to complete experiments. Currently, many evaluations give up on sufficient repetition or rigorous statistical methods, or even run benchmarks only in training sizes. The results reported often lack proper variation estimates and, when a small difference between two systems is reported, some are simply unreliable.In contrast, we provide a statistically rigorous methodology for repetition and summarising results that makes efficient use of experimentation time. Time efficiency comes from two key observations. First, a given benchmark on a given platform is typically prone to much less non-determinism than the common worst-case of published corner-case studies. Second, repetition is most needed where most uncertainty arises (whether between builds, between executions or between iterations). We capture experimentation cost with a novel mathematical model, which we use to identify the number of repetitions at each level of an experiment necessary and sufficient to obtain a given level of precision.We present our methodology as a cookbook that guides researchers on the number of repetitions they should run to obtain reliable results. We also show how to present results with an effect size confidence interval. As an example, we show how to use our methodology to conduct throughput experiments with the DaCapo and SPEC CPU benchmarks on three recent platforms.983 984 You have 42 days from the date of this email to submit your revision. If you are unable to complete the revision within this time, please contact me to request a short extension.985 986 You can upload your revised manuscript and submit it through your Author Center. Log into https://mc.manuscriptcentral.com/spe and enter your Author Center, where you will find your manuscript title listed under "Manuscripts with Decisions".987 988 When submitting your revised manuscript, you will be able to respond to the comments made by the referee(s) in the space provided. You can use this space to document any changes you make to the original manuscript.989 990 If you would like help with English language editing, or other article preparation support, Wiley Editing Services offers expert help with English Language Editing, as well as translation, manuscript formatting, and figure formatting at www.wileyauthors.com/eeo/preparation. You can also check out our resources for Preparing Your Article for general guidance about writing and preparing your manuscript at www.wileyauthors.com/eeo/prepresources.991 992 Once again, thank you for submitting your manuscript to Software: Practice and Experience. I look forward to receiving your revision.993 994 Sincerely,995 Richard996 997 Prof. Richard Jones998 Editor, Software: Practice and Experience999 R.E.Jones@kent.ac.uk1000 1001 Referee(s)' Comments to Author:1002 1003 Reviewing: 11004 1005 Comments to the Author1006 Overall, I felt that this draft was an improvement on previous drafts and I don't have further changes to request.1007 1008 I appreciated the new language to clarify the relationship of external and internal scheduling, for example, as well as the new measurements of Rust tokio. Also, while I still believe that the choice between thread/generator/coroutine and so forth could be made crisper and clearer, the current draft of Section 2 did seem adequate to me in terms of specifying the considerations that users would have to take into account to make the choice.1009 1010 1011 Reviewing: 21012 1013 Comments to the Author1014 First: let me apologise for the delay on this review. I'll blame the global pandemic combined with my institution's senior management's counterproductive decisions for taking up most of my time and all of my energy.1015 1016 At this point, reading the responses, I think we've been around the course enough times that further iteration is unlikely to really improve the paper any further, so I'm happy to recommend acceptance. My main comments are that there were some good points in the responses to *all* the reviews and I strongly encourage the authors to incorporate those discursive responses into the final paper so they may benefit readers as well as reviewers. I agree with the recommendations of reviewer #2 that the paper could usefully be split in to two, which I think I made to a previous revision, but I'm happy to leave that decision to the Editor.1017 1018 Finally, the paper needs to describe how the Java harness was adapted to deal with warmup; why the computation has warmed up and reached a steady state - similarly for js and Python. The tables should also give the "N" chosen for each benchmark run.1019 1020 minor points1021 * don't start sentences with "However"1022 * most downloaded isn't an "Award"1023 1024 1025 1026 Date: Thu, 1 Oct 2020 05:34:29 +00001027 From: Richard Jones <onbehalfof@manuscriptcentral.com>1028 Reply-To: R.E.Jones@kent.ac.uk1029 To: pabuhr@uwaterloo.ca1030 Subject: Revision reminder - SPE-19-0219.R21031 1032 01-Oct-20201033 1034 Dear Dr Buhr1035 1036 SPE-19-0219.R21037 1038 This is a reminder that your opportunity to revise and re-submit your manuscript will expire 14 days from now. If you require more time please contact me directly and I may grant an extension to this deadline, otherwise the option to submit a revision online, will not be available.1039 1040 If your article is of potential interest to the general public, (which means it must be timely, groundbreaking, interesting and impact on everyday society) then please e-mail ejp@wiley.co.uk explaining the public interest side of the research. Wiley will then investigate the potential for undertaking a global press campaign on the article.1041 1042 I look forward to receiving your revision.1043 1044 Sincerely,1045 1046 Prof. Richard Jones1047 Editor, Software: Practice and Experience1048 1049 https://mc.manuscriptcentral.com/spe1050 1051 1052 1053 Date: Tue, 6 Oct 2020 15:29:41 +00001054 From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com>1055 Reply-To: speoffice@wiley.com1056 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca1057 Subject: SPE-19-0219.R3 successfully submitted1058 1059 06-Oct-20201060 1061 Dear Dr Buhr,1062 1063 Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has been successfully submitted online and is presently being given full consideration for publication in Software: Practice and Experience.1064 1065 Your manuscript number is SPE-19-0219.R3. Please mention this number in all future correspondence regarding this submission.1066 1067 You can view the status of your manuscript at any time by checking your Author Center after logging into https://mc.manuscriptcentral.com/spe. If you have difficulty using this site, please click the 'Get Help Now' link at the top right corner of the site.1068 1069 1070 Thank you for submitting your manuscript to Software: Practice and Experience.1071 1072 Sincerely,1073 1074 Software: Practice and Experience Editorial Office1075 1076 1077 1078 Date: Thu, 15 Oct 2020 13:48:52 +00001079 From: Richard Jones <onbehalfof@manuscriptcentral.com>1080 Reply-To: R.E.Jones@kent.ac.uk1081 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca1082 Subject: Software: Practice and Experience - Decision on Manuscript ID1083 SPE-19-0219.R31084 1085 15-Oct-20201086 1087 Dear Dr Buhr,1088 1089 It is a pleasure to accept your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" in its current form for publication in Software: Practice and Experience.1090 1091 Please note although the manuscript is accepted the files will now be checked to ensure that everything is ready for publication, and you may be contacted if final versions of files for publication are required.1092 1093 Your article cannot be published until the publisher has received the appropriate signed license agreement. Within the next few days the corresponding author will receive an email from Wiley's Author Services system which will ask them to log in and will present them with the appropriate license for completion.1094 1095 Thank you for your fine contribution.1096 1097 Sincerely,1098 Richard1099 1100 Prof. Richard Jones1101 Editor, Software: Practice and Experience1102 R.E.Jones@kent.ac.uk1103 1104 P.S. - You can help your research get the attention it deserves! Check out Wiley's free Promotion Guide for best-practice recommendations for promoting your work at www.wileyauthors.com/eeo/guide. And learn more about Wiley Editing Services which offers professional video, design, and writing services to create shareable video abstracts, infographics, conference posters, lay summaries, and research news stories for your research at www.wileyauthors.com/eeo/promotion.1105 1106 This journal accepts artwork submissions for Cover Images. This is an optional service you can use to help increase article exposure and showcase your research. For more information, including artwork guidelines, pricing, and submission details, please visit the Journal Cover Image page at www.wileyauthors.com/eeo/covers. If you want help creating an image, Wiley Editing Services offers a professional cover image design service that creates eye-catching images, ready to be showcased on the journal cover at www.wileyauthors.com/eeo/design.1107 1108 1109 1110 Date: Fri, 16 Oct 2020 12:44:42 +00001111 From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com>1112 Reply-To: speoffice@wiley.com1113 To: pabuhr@uwaterloo.ca1114 Subject: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]1115 1116 16-Oct-20201117 1118 Dear Dr. Buhr,1119 1120 Manuscript id: SPE-19-0219.R31121 Manuscript title: Advanced Control-flow and Concurrency in Cforall1122 1123 Although your manuscript has been accepted for publication it is now being returned to your author center for you to review and make any final adjustments or corrections prior to production and publication.1124 1125 Any special instructions will be listed below:1126 1) Funding Information added in ScholorOne but missing in main document, Kindly add the Funding information in main document.1127 2) Please provide the clean version of the manuscript without any highlights or tracked changes.1128 3) Kindly check and make sure citations for all figures and Tables are present in the main document1129 1130 Please now log back into your Scholar One Author Center and click on the "Manuscripts Accepted for First Look" queue. In order to update the submission, click on the "submit updated manuscript" link in the "Actions" column and follow the steps as you would during a manuscript submission process.1131 1132 On the File Upload screen please upload the FINAL versions of all the files, including print quality image files. For information about image quality requirements, please refer to the guidelines at https://authorservices.wiley.com/asset/photos/electronic_artwork_guidelines.pdf.1133 1134 Instructions for uploading replacement files:1135 1. On the "File Upload" step, click on the "edit" button for the file you wish to replace.1136 2. In the "Upload a later version" section, browse to locate the replacement final version.1137 3. Add any comments concerning the replacement (e.g. "high res image").1138 4. Select whether the new file is a minor or major version (we suggest you select minor version)1139 5. Click upload.1140 6. Click 'Submit' when all the files have been uploaded and you will receive an automated email to say that submission is successful.1141 1142 Please submit your updates within the next 7 days to ensure there are no unnecessary delays in production.1143 1144 Sincerely,1145 Software: Practice and Experience Editorial Office1146 1147 1148 1149 From: SPE Office <speoffice@wiley.com>1150 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>1151 Subject: Re: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]1152 Date: Mon, 19 Oct 2020 17:04:24 +00001153 1154 Dear Dr. Buhr,1155 1156 Thank you very much for contacting the Editorial Office.1157 1158 I would like to let you know that the files has been found in order and moved to production.1159 1160 Plesae let me know for further assistance in this regard.1161 1162 Best Regards1163 1164 Mayank Roy Chowdhury1165 Editorial Assistant1166 Software practice and Experience1167 ________________________________1168 From: Peter A. Buhr <pabuhr@uwaterloo.ca>1169 Sent: Sunday, October 18, 2020 2:00 PM1170 To: SPE Office <speoffice@wiley.com>1171 Cc: Thierry Delisle <tdelisle@uwaterloo.ca>1172 Subject: Re: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]1173 1174 This is an external email.1175 1176 Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com> writes:1177 1178 Instructions for uploading replacement files:1179 1. On the "File Upload" step, click on the "edit" button for the file you wish to replace.1180 2. In the "Upload a later version" section, browse to locate the replacement final version.1181 3. Add any comments concerning the replacement (e.g. "high res image").1182 4. Select whether the new file is a minor or major version (we suggest you select minor version)1183 5. Click upload.1184 6. Click 'Submit' when all the files have been uploaded and you will receive an automated email to say that submission is successful.1185 1186 There was no "edit" button on the "File Upload" page, so I just upload the1187 final version of the PDF and source files using the mechanism on the "File1188 Upload" page and submitted that.1189 1190 1191 1192 Date: Tue, 20 Oct 2020 13:28:37 +05301193 To: "Dr. Peter Buhr" <pabuhr@uwaterloo.ca>1194 From: jpcms@spi-global.com1195 Subject: Information: Production Editor Contact Software:Practice and Experience | Advanced Control-flow and Concurrency in C A1196 1197 Dear Dr. Peter Buhr,1198 1199 We are in the process of preparing "Advanced Control-flow and Concurrency in C A" for publication. Your production editor, Joel Pacaanas, will support you and your article throughout the process.1200 1201 Please get in touch with your Production Editor at SPEproofs@wiley.com;EllaMae.Navor@spi-global.com if you have any questions.1202 1203 Sincerely,1204 Booking-in Team,1205 On behalf of Wiley1206 1207 Article ID: SPE_29251208 Article DOI: 10.1002/SPE.29251209 1210 1211 1212 Date: Tue, 20 Oct 2020 10:33:04 +00001213 From: <cs-author@wiley.com>1214 To: <pabuhr@uwaterloo.ca>1215 Subject: In Production: Your article accepted in Software: Practice and Experience1216 1217 Dear Peter Buhr,1218 1219 Article ID: SPE29251220 Article DOI: 10.1002/spe.29251221 Internal Article ID: 169222131222 Article: Advanced Control-flow and Concurrency in C A1223 Journal: Software: Practice and Experience1224 1225 Congratulations on the acceptance of your article for publication in Software: Practice and Experience.1226 1227 Your article has been received and the production process is now underway. We look forward to working with you and publishing your article. Using Wiley Author Services, you can track your article's progress.1228 1229 Please click below to login - if you are using a different email address than this one, you will need to manually assign this article to your Dashboard (see https://hub.wiley.com/docs/support/assigning-a-missing-article-to-my-dashboard-DOC-11871?utm_source=new%20user%20invitation&utm_medium=email How do I assign a missing article to My Dashboard?):1230 1231 https://authorservices.wiley.com/index.html#login?campaign=email_invitation-new1232 1233 If applicable, a list of available actions will appear below - check out your Author Services Dashboard for all actions related to your articles.1234 1235 Sign your license agreement (REQUIRED) -- you will receive an email when this task is ready on your dashboard. Track your article's progress to publicationAccess your published articleInvite colleagues to view your published article1236 If you need any assistance, please click http://www.wileyauthors.com/help?utm_source=new%20user%20invitation&utm_medium=email here to view our Help section.1237 1238 Sincerely,1239 Wiley Author Services1240 1241 P.S. - Some journals accept artwork submissions for Cover Images. This is an optional service you can use to help increase article exposure and showcase your research. Pricing and placement options vary by journal. For more information, including artwork guidelines, pricing, and submission details, please visit the https://authorservices.wiley.com/author-resources/Journal-Authors/Promotion/journal-cover-image.html?utm_source=as&utm_medium=email&utm_term=invitation_msg&utm_content=covers&utm_campaign=2019feb?campaign=email_invitation-new" target=_blank">Journal Cover Image page. If you want help creating an image, Wiley Editing Services offers a professional https://wileyeditingservices.com/en/article-promotion/cover-image-design.html?utm_source=as&utm_medium=email&utm_term=ie&utm_content=cid&utm_campaign=prodops" target=_blank">Cover Image Design service that creates eye-catching images, ready to be showcased on the journal cover.1242 1243 1244 1245 Date: Thu, 22 Oct 2020 20:21:49 +00001246 From: <cs-author@wiley.com>1247 To: <pabuhr@uwaterloo.ca>1248 Subject: You have actions to complete in Author Services1249 1250 Dear Peter Buhr,1251 1252 Article ID: SPE29251253 Article DOI: 10.1002/spe.29251254 Internal Article ID: 169222131255 Article: Advanced Control-flow and Concurrency in C A1256 Journal: Software: Practice and Experience1257 1258 For the above article, you have the following open tasks:1259 1260 Sign your license agreement in order to publish your article. Simply click the Sign License button on your https://authorservices.wiley.com?campaign=email_license-notice1">Wiley Author Services Dashboard.1261 1262 Need any help? Please visit our https://authorsupport.wiley.com/s/">Author Support Center.1263 1264 Sincerely,1265 Wiley Author Services1266 1267 1268 1269 Date: Thu, 22 Oct 2020 23:13:07 +00001270 From: <cs-author@wiley.com>1271 To: <pabuhr@uwaterloo.ca>1272 Subject: License was successfully submitted! Thank you!1273 1274 Dear Peter Buhr,1275 1276 Article ID: SPE29251277 Article DOI: 10.1002/spe.29251278 Internal Article ID: 169222131279 Article: Advanced Control-flow and Concurrency in C A1280 Journal: Software: Practice and Experience1281 1282 You've successfully completed license signing for your article - thank you! You can view your signed agreement at any time by visiting your https://authorservices.wiley.com?campaign=email_license-confirm">Wiley Author Services Dashboard.1283 1284 Sincerely,1285 1286 Wiley Author Services1287 1288 1289 1290 From: "Pacaanas, Joel -" <jpacaanas@wiley.com>1291 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>1292 CC: Thierry Delisle <tdelisle@uwaterloo.ca>1293 Subject: RE: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review1294 Date: Thu, 5 Nov 2020 02:03:27 +00001295 1296 Dear Dr Buhr,1297 1298 Thank you for letting me know. We will wait for your corrections then.1299 1300 Best regards,1301 Joel1302 1303 Joel Q. Pacaanas1304 Production Editor1305 On behalf of Wiley1306 Manila1307 We partner with global experts to further innovative research.1308 1309 E-mail: jpacaanas@wiley.com1310 Tel: +632 885586181311 Fax: +632 5325 07681312 1313 -----Original Message-----1314 From: Peter A. Buhr [mailto:pabuhr@uwaterloo.ca]1315 Sent: Thursday, November 5, 2020 5:57 AM1316 To: SPE Proofs <speproofs@wiley.com>1317 Cc: Thierry Delisle <tdelisle@uwaterloo.ca>1318 Subject: Re: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review1319 1320 This is an external email.1321 1322 We appreciate that the COVID-19 pandemic may create conditions for you that1323 make it difficult for you to review your proof within standard time1324 frames. If you have any problems keeping to this schedule, please reach out1325 to me at (SPEproofs@wiley.com) to discuss alternatives.1326 1327 Hi,1328 1329 We are in the middle of reading the proofs but it will take a little more1330 time. I can send the proofs back by Monday Nov 9, but probably earlier.1331 1332 1333 1334 From: "Pacaanas, Joel -" <jpacaanas@wiley.com>1335 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>1336 CC: "tdelisle@uwaterloo.ca" <tdelisle@uwaterloo.ca>1337 Subject: RE: Action: Proof of SPE_EV_SPE2925 for Software: Practice And Experience ready for review1338 Date: Fri, 20 Nov 2020 05:27:18 +00001339 1340 Dear Peter,1341 1342 We have now reset the proof back to original stage. Please refer to the below editable link.1343 1344 https://wiley.eproofing.in/Proof.aspx?token=ab7739d5678447fbbe5036f3bcba24450815000611345 1346 Since the proof was reset, your added corrections before has also been removed. Please add them back.1347 1348 Please return your corrections at your earliest convenience.1349 1350 Best regards,1351 Joel1352 1353 Joel Q. Pacaanas1354 Production Editor1355 On behalf of Wiley1356 Manila1357 We partner with global experts to further innovative research.1358 1359 E-mail: jpacaanas@wiley.com1360 Tel: +632 885586181361 Fax: +632 5325 07681362 1363 1364 1365 From: "Wiley Online Proofing" <notifications@eproofing.in>1366 To: pabuhr@uwaterloo.ca1367 Cc: SPEproofs@wiley.com1368 Reply-To: eproofing@wiley.com1369 Date: 26 Nov 2020 18:57:27 +00001370 Subject: Corrections successfully submitted for SPE_EV_SPE2925, Advanced control-flow in Cforall.1371 1372 Corrections successfully submitted1373 1374 Dear Dr. Peter Buhr,1375 1376 Thank you for reviewing the proof of the Software: Practice And Experience article Advanced control-flow in Cforall.1377 1378 View Article https://wiley.eproofing.in/Proof.aspx?token=ab7739d5678447fbbe5036f3bcba24450815000611379 1380 This is a read-only version of your article with the corrections you have marked up.1381 1382 If you encounter any problems or have questions please contact me, Joel Pacaanas at (SPEproofs@wiley.com). For the quickest response include the journal name and your article ID (found in the subject line) in all correspondence.1383 1384 Best regards,1385 Joel Pacaanas -
doc/proposals/vtable.md
reef8dfb rbdfc032 237 237 default is provided or not, the second syntax can be used to pick a 238 238 parameter on instantiation. 239 240 ### Extension: Object Access241 This requires that the resolution scope (see below) is at the type level or242 has explicate points with names. These are the tables and table names used243 here.244 245 The system already knows where to find the virtual table and the object. If246 the tables have particular identities, or on the user side names, then it is247 meaningful to check if a binding virtual table is the same* as another. The248 main use of this is virtual table declarations also give the type they bind249 and if a binding table matches a known table then the underlyind object in the250 trait object must be of that type.251 252 * By identity, by value would work and in some senses be more flexiable. But253 it would be slower and refering to further away functions would be harder.254 255 This gives one of the main new features of the hierarchical use of virtual256 tables (see below); the ability to recover the underlying object. Or a pointer257 of the approprate type it which both reflects the implementation and gives a258 convenent way to encode the boolean/conditional aspect of the operation which259 is that a different virtual table might be in use.260 261 There are two general ways to reperent this; a cast or a field access. The262 cast is traditional and would definitely fit if a single pointer repersents263 a trait object with the virtual table as part of the object. However for a264 double pointer field access might be more approprate. By this system though265 it is not the type that is used as the identifier but the virtual table. If266 there is one table per type than it becomes equivilant again. Otherwise the267 table has to be used as the identifier and the type is just a result of that268 which seems important for syntax.269 239 270 240 Hierarchy … … 512 482 possibly like the one used to create the assertion. 513 483 514 ### Extension: Associated Types Use515 If the `associated_types.md` proposal is accepted the following trait could516 be added:517 518 trait is_virtual(dtype T) {519 dtype table;520 // An example assertion:521 const table & get_virtual_table(T &);522 }523 524 There may be more assertions but there has to be at least one way to find525 the (possibly default) virtual table. It is required to construct instances526 of the type.527 528 Without the assotiated type it would look like this:529 530 trait is_virtual(dtype T, dtype table) {531 const table & get_virtual_table(T &);532 }533 534 Which is just a little bit longer to use but becomes more problematic if the535 user has to explicately provide the table's name as it doesn't really have its536 own type name. If it does it is probably mangled.537 538 484 ### Virtual Tables as Types 539 485 Here we consider encoding plus the implementation of functions on it to be a … … 614 560 be used in only some of the declarations. 615 561 616 trait combiner fee = {summation_instance, sum};562 trait combiner fee = (summation_instance, sum); 617 563 trait combiner foe = summation_instance; 618 564 -
doc/refrat/refrat.tex
reef8dfb rbdfc032 11 11 %% Created On : Wed Apr 6 14:52:25 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Oct 5 09:02:53 202014 %% Update Count : 1 1013 %% Last Modified On : Wed Jan 31 17:30:23 2018 14 %% Update Count : 108 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 30 30 \usepackage{upquote} % switch curled `'" to straight 31 31 \usepackage{calc} 32 \usepackage{xspace} 32 33 \usepackage{varioref} % extended references 34 \usepackage{listings} % format program code 33 35 \usepackage[flushmargin]{footmisc} % support label/reference in footnote 34 36 \usepackage{latexsym} % \Box glyph 35 37 \usepackage{mathptmx} % better math font with "times" 36 38 \usepackage[usenames]{color} 37 \newcommand{\CFALatin}{} 39 \input{common} % common CFA document macros 40 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 41 \usepackage{breakurl} 42 \renewcommand{\UrlFont}{\small\sf} 43 44 \usepackage[pagewise]{lineno} 45 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 46 \usepackage[firstpage]{draftwatermark} 47 \SetWatermarkLightness{0.9} 48 49 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 50 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 51 % AFTER HYPERREF. 52 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 53 54 \setlength{\topmargin}{-0.45in} % move running title into header 55 \setlength{\headsep}{0.25in} 56 57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 58 59 \CFAStyle % use default CFA format-style 60 \lstnewenvironment{C++}[1][] % use C++ style 61 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®}#1}} 62 {} 63 38 64 % inline code Ā©...Ā© (copyright symbol) emacs: C-q M-) 39 65 % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-. … … 43 69 % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 44 70 % math escape $...$ (dollar symbol) 45 \input{common} % common CFA document macros46 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}47 \usepackage{breakurl}48 \renewcommand{\UrlFont}{\small\sf}49 50 \usepackage[pagewise]{lineno}51 \renewcommand{\linenumberfont}{\scriptsize\sffamily}52 \usepackage[firstpage]{draftwatermark}53 \SetWatermarkLightness{0.9}54 55 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore56 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR57 % AFTER HYPERREF.58 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}59 60 \setlength{\topmargin}{-0.45in} % move running title into header61 \setlength{\headsep}{0.25in}62 71 63 72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 64 73 65 \CFAStyle % use default CFA format-style66 \lstnewenvironment{C++}[1][] % use C++ style67 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}}68 {}69 70 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%71 72 74 % Names used in the document. 73 \newcommand{\Version}{\input{ build/version}}75 \newcommand{\Version}{\input{../../version}} 74 76 \newcommand{\Textbf}[2][red]{{\color{#1}{\textbf{#2}}}} 75 77 \newcommand{\Emph}[2][red]{{\color{#1}\textbf{\emph{#2}}}} -
doc/user/Makefile
reef8dfb rbdfc032 55 55 56 56 ${DOCUMENT} : ${BASE}.ps 57 ps2pdf -dPDFSETTINGS=/prepress$<57 ps2pdf $< 58 58 59 59 ${BASE}.ps : ${BASE}.dvi -
doc/user/user.tex
reef8dfb rbdfc032 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Oct 5 08:57:29 202014 %% Update Count : 3 99813 %% Last Modified On : Sat Jul 13 18:36:18 2019 14 %% Update Count : 3876 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 30 30 \usepackage{upquote} % switch curled `'" to straight 31 31 \usepackage{calc} 32 \usepackage{xspace} 32 33 \usepackage{varioref} % extended references 33 \usepackage[labelformat=simple,aboveskip=0pt,farskip=0pt]{subfig} 34 \renewcommand{\thesubfigure}{\alph{subfigure})} 34 \usepackage{listings} % format program code 35 35 \usepackage[flushmargin]{footmisc} % support label/reference in footnote 36 36 \usepackage{latexsym} % \Box glyph 37 37 \usepackage{mathptmx} % better math font with "times" 38 38 \usepackage[usenames]{color} 39 \newcommand{\CFALatin}{} 39 \input{common} % common CFA document macros 40 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 41 \usepackage{breakurl} 42 43 \usepackage[pagewise]{lineno} 44 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 45 \usepackage[firstpage]{draftwatermark} 46 \SetWatermarkLightness{0.9} 47 48 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 49 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 50 % AFTER HYPERREF. 51 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 52 53 \setlength{\topmargin}{-0.45in} % move running title into header 54 \setlength{\headsep}{0.25in} 55 56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 57 58 \CFAStyle % use default CFA format-style 59 \lstnewenvironment{C++}[1][] % use C++ style 60 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}} 61 {} 62 40 63 % inline code Ā©...Ā© (copyright symbol) emacs: C-q M-) 41 64 % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-. … … 45 68 % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 46 69 % math escape $...$ (dollar symbol) 47 \input{common} % common CFA document macros48 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref}49 \usepackage{breakurl}50 51 \renewcommand\footnoterule{\kern -3pt\rule{0.3\linewidth}{0.15pt}\kern 2pt}52 53 \usepackage[pagewise]{lineno}54 \renewcommand{\linenumberfont}{\scriptsize\sffamily}55 \usepackage[firstpage]{draftwatermark}56 \SetWatermarkLightness{0.9}57 58 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore59 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR60 % AFTER HYPERREF.61 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}62 63 \setlength{\topmargin}{-0.45in} % move running title into header64 \setlength{\headsep}{0.25in}65 66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%67 68 \CFAStyle % use default CFA format-style69 \lstnewenvironment{C++}[1][] % use C++ style70 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}}71 {}72 73 \newsavebox{\myboxA}74 \newsavebox{\myboxB}75 70 76 71 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 84 79 \newcommand{\G}[1]{{\Textbf[OliveGreen]{#1}}} 85 80 \newcommand{\KWC}{K-W C\xspace} 81 82 \newsavebox{\LstBox} 86 83 87 84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 214 211 Even with all its problems, C continues to be popular because it allows writing software at virtually any level in a computer system without restriction. 215 212 For system programming, where direct access to hardware, storage management, and real-time issues are a requirement, C is usually the only language of choice. 216 The TIOBE index~\cite{TIOBE} for February 2020 ranks the top six most \emph{popular} programming languages as \Index*{Java} 17.4\%, C 16.8\%, Python 9.3\%, \Index*[C++]{\CC{}} 6.2\%, \Csharp 5.9\%, Visual Basic 5.9\% = 61.5\%, where the next 50 languages are less than 2\% each, with a long tail.217 The top 4 rankings over the past 35years are:213 The TIOBE index~\cite{TIOBE} for July 2018 ranks the top five most \emph{popular} programming languages as \Index*{Java} 16\%, C 14\%, \Index*[C++]{\CC{}} 7.5\%, Python 6\%, Visual Basic 4\% = 47.5\%, where the next 50 languages are less than 4\% each, with a long tail. 214 The top 3 rankings over the past 30 years are: 218 215 \begin{center} 219 216 \setlength{\tabcolsep}{10pt} 220 \begin{tabular}{@{}rcccccccc@{}} 221 & 2020 & 2015 & 2010 & 2005 & 2000 & 1995 & 1990 & 1985 \\ \hline 222 Java & 1 & 2 & 1 & 2 & 3 & - & - & - \\ 223 \R{C} & \R{2} & \R{1} & \R{2} & \R{1} & \R{1} & \R{2} & \R{1} & \R{1} \\ 224 Python & 3 & 7 & 6 & 6 & 22 & 21 & - & - \\ 225 \CC & 4 & 4 & 4 & 3 & 2 & 1 & 2 & 12 \\ 217 \begin{tabular}{@{}rccccccc@{}} 218 & 2018 & 2013 & 2008 & 2003 & 1998 & 1993 & 1988 \\ \hline 219 Java & 1 & 2 & 1 & 1 & 16 & - & - \\ 220 \R{C} & \R{2} & \R{1} & \R{2} & \R{2} & \R{1} & \R{1} & \R{1} \\ 221 \CC & 3 & 4 & 3 & 3 & 2 & 2 & 5 \\ 226 222 \end{tabular} 227 223 \end{center} … … 256 252 257 253 The signature feature of \CFA is \emph{\Index{overload}able} \Index{parametric-polymorphic} functions~\cite{forceone:impl,Cormack90,Duggan96} with functions generalized using a Ā©forallĀ© clause (giving the language its name): 258 \begin{ cfa}254 \begin{lstlisting} 259 255 Ā®forall( otype T )Ā® T identity( T val ) { return val; } 260 256 int forty_two = identity( 42 ); §\C{// T is bound to int, forty\_two == 42}§ 261 \end{ cfa}257 \end{lstlisting} 262 258 % extending the C type system with parametric polymorphism and overloading, as opposed to the \Index*[C++]{\CC{}} approach of object-oriented extensions. 263 259 \CFA{}\hspace{1pt}'s polymorphism was originally formalized by \Index*{Glen Ditchfield}\index{Ditchfield, Glen}~\cite{Ditchfield92}, and first implemented by \Index*{Richard Bilson}\index{Bilson, Richard}~\cite{Bilson03}. … … 278 274 \begin{comment} 279 275 A simple example is leveraging the existing type-unsafe (Ā©void *Ā©) C Ā©bsearchĀ© to binary search a sorted floating array: 280 \begin{ cfa}276 \begin{lstlisting} 281 277 void * bsearch( const void * key, const void * base, size_t dim, size_t size, 282 278 int (* compar)( const void *, const void * )); … … 287 283 double key = 5.0, vals[10] = { /* 10 sorted floating values */ }; 288 284 double * val = (double *)bsearch( &key, vals, 10, sizeof(vals[0]), comp ); §\C{// search sorted array}§ 289 \end{ cfa}285 \end{lstlisting} 290 286 which can be augmented simply with a polymorphic, type-safe, \CFA-overloaded wrappers: 291 \begin{ cfa}287 \begin{lstlisting} 292 288 forall( otype T | { int ?<?( T, T ); } ) T * bsearch( T key, const T * arr, size_t size ) { 293 289 int comp( const void * t1, const void * t2 ) { /* as above with double changed to T */ } … … 300 296 double * val = bsearch( 5.0, vals, 10 ); §\C{// selection based on return type}§ 301 297 int posn = bsearch( 5.0, vals, 10 ); 302 \end{ cfa}298 \end{lstlisting} 303 299 The nested function Ā©compĀ© provides the hidden interface from typed \CFA to untyped (Ā©void *Ā©) C, plus the cast of the result. 304 300 Providing a hidden Ā©compĀ© function in \CC is awkward as lambdas do not use C calling-conventions and template declarations cannot appear at block scope. … … 308 304 \CFA has replacement libraries condensing hundreds of existing C functions into tens of \CFA overloaded functions, all without rewriting the actual computations. 309 305 For example, it is possible to write a type-safe \CFA wrapper Ā©mallocĀ© based on the C Ā©mallocĀ©: 310 \begin{ cfa}306 \begin{lstlisting} 311 307 forall( dtype T | sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); } 312 308 int * ip = malloc(); §\C{// select type and size from left-hand side}§ 313 309 double * dp = malloc(); 314 310 struct S {...} * sp = malloc(); 315 \end{ cfa}311 \end{lstlisting} 316 312 where the return type supplies the type/size of the allocation, which is impossible in most type systems. 317 313 \end{comment} … … 516 512 Keyword clashes are accommodated by syntactic transformations using the \CFA backquote escape-mechanism: 517 513 \begin{cfa} 518 int Ā®` `Ā®otype= 3; §\C{// make keyword an identifier}§519 double Ā®` `Ā®forall= 3.5;514 int Ā®`Ā®otypeĀ®`Ā® = 3; §\C{// make keyword an identifier}§ 515 double Ā®`Ā®forallĀ®`Ā® = 3.5; 520 516 \end{cfa} 521 517 … … 528 524 // include file uses the CFA keyword "with". 529 525 #if ! defined( with ) §\C{// nesting ?}§ 530 #define with Ā®` `Ā®with§\C{// make keyword an identifier}§526 #define with Ā®`Ā®withĀ®`Ā® §\C{// make keyword an identifier}§ 531 527 #define __CFA_BFD_H__ 532 528 #endif 533 §{\color{red}\#\textbf{include\_next} <bfdlink.h>}§ §\C{// must have internal check for multiple expansion}§ 529 530 Ā®#include_next <bfdlink.h> §\C{// must have internal check for multiple expansion}§ 531 Ā® 534 532 #if defined( with ) && defined( __CFA_BFD_H__ ) §\C{// reset only if set}§ 535 533 #undef with … … 578 576 \section{Exponentiation Operator} 579 577 580 C, \CC, and Java (and many other programming languages) have no exponentiation operator\index{exponentiation!operator}\index{operator!exponentiation}, \ie $x^y$, and instead use a routine, like \Indexc{pow (x,y)}, to perform the exponentiation operation.581 \CFA extends the basic operators with the exponentiation operator Ā©? Ā®\Ā®?Ā©\index{?\\?@Ā©?Ā®\Ā®?Ā©} and Ā©?\=?Ā©\index{?\\=?@©®\Ā®=?Ā©}, as in, Ā©x Ā®\Ā® yĀ© and Ā©x Ā®\Ā®= yĀ©, which means $x^y$ and $x \leftarrow x^y$.578 C, \CC, and Java (and many other programming languages) have no exponentiation operator\index{exponentiation!operator}\index{operator!exponentiation}, \ie $x^y$, and instead use a routine, like \Indexc{pow}, to perform the exponentiation operation. 579 \CFA extends the basic operators with the exponentiation operator Ā©?\?Ā©\index{?\\?@Ā©?\?Ā©} and Ā©?\=?Ā©\index{?\\=?@Ā©\=?Ā©}, as in, Ā©x \ yĀ© and Ā©x \= yĀ©, which means $x^y$ and $x \leftarrow x^y$. 582 580 The priority of the exponentiation operator is between the cast and multiplicative operators, so that Ā©w * (int)x \ (int)y * zĀ© is parenthesized as Ā©((w * (((int)x) \ ((int)y))) * z)Ā©. 583 581 584 There are exponentiation operators for integral and floating types, including the builtin \Index{complex} types.582 As for \Index{division}, there are exponentiation operators for integral and floating types, including the builtin \Index{complex} types. 585 583 Integral exponentiation\index{exponentiation!unsigned integral} is performed with repeated multiplication\footnote{The multiplication computation is $O(\log y)$.} (or shifting if the exponent is 2). 586 Overflow f or a large exponent or negative exponent returnszero.584 Overflow from large exponents or negative exponents return zero. 587 585 Floating exponentiation\index{exponentiation!floating} is performed using \Index{logarithm}s\index{exponentiation!logarithm}, so the exponent cannot be negative. 588 586 \begin{cfa} … … 591 589 1 1 256 -64 125 Ā®0Ā® 3273344365508751233 Ā®0Ā® Ā®0Ā® -0.015625 18.3791736799526 0.264715-1.1922i 592 590 \end{cfa} 593 Note, Ā©5 \ 32Ā© and Ā©5L \ 64Ā© overflow, and Ā©-4 \-3Ā© is a fraction but stored in an integer so all three computations generate an integral zero.591 Note, Ā©5 Ā®\Ā® 32Ā© and Ā©5L Ā®\Ā® 64Ā© overflow, and Ā©-4 Ā®\Ā® -3Ā© is a fraction but stored in an integer so all three computations generate an integral zero. 594 592 Parenthesis are necessary for complex constants or the expression is parsed as Ā©1.0f+Ā®(Ā®2.0fi \ 3.0fĀ®)Ā®+2.0fiĀ©. 595 593 The exponentiation operator is available for all the basic types, but for user-defined types, only the integral-computation version is available. … … 600 598 OT ?Ā®\Ā®?( OT ep, unsigned long int y ); 601 599 \end{cfa} 602 The user type Ā©TĀ© must define multiplication, one (Ā©1Ā©), andĀ©*Ā©.600 The user type Ā©TĀ© must define multiplication, one, Ā©1Ā©, and, Ā©*Ā©. 603 601 604 602 … … 628 626 629 627 630 %\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}} 631 \subsection{\texorpdfstring{\LstKeywordStyle{case} Clause}{case Clause}} 632 633 C restricts the Ā©caseĀ© clause of a Ā©switchĀ© statement to a single value. 634 For multiple Ā©caseĀ© clauses associated with the same statement, it is necessary to have multiple Ā©caseĀ© clauses rather than multiple values. 635 Requiring a Ā©caseĀ© clause for each value does not seem to be in the spirit of brevity normally associated with C. 636 Therefore, the Ā©caseĀ© clause is extended with a list of values, as in: 628 \subsection{Loop Control} 629 630 The Ā©forĀ©/Ā©whileĀ©/Ā©do-whileĀ© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}). 631 \begin{itemize} 632 \item 633 An empty conditional implies Ā©1Ā©. 634 \item 635 The up-to range Ā©~Ā©\index{~@Ā©~Ā©} means exclusive range [M,N). 636 \item 637 The up-to range Ā©~=Ā©\index{~=@Ā©~=Ā©} means inclusive range [M,N]. 638 \item 639 The down-to range Ā©-~Ā©\index{-~@Ā©-~Ā©} means exclusive range [N,M). 640 \item 641 The down-to range Ā©-~=Ā©\index{-~=@Ā©-~=Ā©} means inclusive range [N,M]. 642 \item 643 Ā©@Ā© means put nothing in this field. 644 \item 645 Ā©0Ā© is the implicit start value; 646 \item 647 Ā©1Ā© is the implicit increment value. 648 \item 649 The up-to range uses Ā©+=Ā© for increment; 650 \item 651 The down-to range uses Ā©-=Ā© for decrement. 652 \item 653 The loop index is polymorphic in the type of the start value or comparison value when start is implicitly Ā©0Ā©. 654 \end{itemize} 655 656 \begin{figure} 637 657 \begin{cquote} 638 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}} 639 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{C}} \\ 640 \begin{cfa} 641 switch ( i ) { 642 case Ā®1, 3, 5Ā®: 643 ... 644 case Ā®2, 4, 6Ā®: 645 ... 646 } 658 \begin{tabular}{@{}l|l@{}} 659 \multicolumn{1}{c|}{loop control} & \multicolumn{1}{c}{output} \\ 660 \hline 661 \begin{cfa} 662 sout | nlOff; 663 while Ā®()Ā® { sout | "empty"; break; } sout | nl; 664 do { sout | "empty"; break; } while Ā®()Ā®; sout | nl; 665 for Ā®()Ā® { sout | "empty"; break; } sout | nl; 666 for ( Ā®0Ā® ) { sout | "A"; } sout | "zero" | nl; 667 for ( Ā®1Ā® ) { sout | "A"; } sout | nl; 668 for ( Ā®10Ā® ) { sout | "A"; } sout | nl; 669 for ( Ā®1 ~= 10 ~ 2Ā® ) { sout | "B"; } sout | nl; 670 for ( Ā®10 -~= 1 ~ 2Ā® ) { sout | "C"; } sout | nl; 671 for ( Ā®0.5 ~ 5.5Ā® ) { sout | "D"; } sout | nl; 672 for ( Ā®5.5 -~ 0.5Ā® ) { sout | "E"; } sout | nl; 673 for ( Ā®i; 10Ā® ) { sout | i; } sout | nl; 674 for ( Ā®i; 1 ~= 10 ~ 2Ā® ) { sout | i; } sout | nl; 675 for ( Ā®i; 10 -~= 1 ~ 2Ā® ) { sout | i; } sout | nl; 676 for ( Ā®i; 0.5 ~ 5.5Ā® ) { sout | i; } sout | nl; 677 for ( Ā®i; 5.5 -~ 0.5Ā® ) { sout | i; } sout | nl; 678 for ( Ā®ui; 2u ~= 10u ~ 2uĀ® ) { sout | ui; } sout | nl; 679 for ( Ā®ui; 10u -~= 2u ~ 2uĀ® ) { sout | ui; } sout | nl; 680 enum { N = 10 }; 681 for ( Ā®NĀ® ) { sout | "N"; } sout | nl; 682 for ( Ā®i; NĀ® ) { sout | i; } sout | nl; 683 for ( Ā®i; N -~ 0Ā® ) { sout | i; } sout | nl; 684 const int start = 3, comp = 10, inc = 2; 685 for ( Ā®i; start ~ comp ~ inc + 1Ā® ) { sout | i; } sout | nl; 686 for ( Ā®i; 1 ~ @Ā® ) { if ( i > 10 ) break; 687 sout | i; } sout | nl; 688 for ( Ā®i; 10 -~ @Ā® ) { if ( i < 0 ) break; 689 sout | i; } sout | nl; 690 for ( Ā®i; 2 ~ @ ~ 2Ā® ) { if ( i > 10 ) break; 691 sout | i; } sout | nl; 692 for ( Ā®i; 2.1 ~ @ ~ @Ā® ) { if ( i > 10.5 ) break; 693 sout | i; i += 1.7; } sout | nl; 694 for ( Ā®i; 10 -~ @ ~ 2Ā® ) { if ( i < 0 ) break; 695 sout | i; } sout | nl; 696 for ( Ā®i; 12.1 ~ @ ~ @Ā® ) { if ( i < 2.5 ) break; 697 sout | i; i -= 1.7; } sout | nl; 698 for ( Ā®i; 5 : j; -5 ~ @Ā® ) { sout | i | j; } sout | nl; 699 for ( Ā®i; 5 : j; -5 -~ @Ā® ) { sout | i | j; } sout | nl; 700 for ( Ā®i; 5 : j; -5 ~ @ ~ 2Ā® ) { sout | i | j; } sout | nl; 701 for ( Ā®i; 5 : j; -5 -~ @ ~ 2Ā® ) { sout | i | j; } sout | nl; 702 for ( Ā®j; -5 ~ @ : i; 5Ā® ) { sout | i | j; } sout | nl; 703 for ( Ā®j; -5 -~ @ : i; 5Ā® ) { sout | i | j; } sout | nl; 704 for ( Ā®j; -5 ~ @ ~ 2 : i; 5Ā® ) { sout | i | j; } sout | nl; 705 for ( Ā®j; -5 -~ @ ~ 2 : i; 5Ā® ) { sout | i | j; } sout | nl; 706 for ( Ā®j; -5 -~ @ ~ 2 : i; 5 : k; 1.5 ~ @Ā® ) { 707 sout | i | j | k; } sout | nl; 708 for ( Ā®j; -5 -~ @ ~ 2 : k; 1.5 ~ @ : i; 5Ā® ) { 709 sout | i | j | k; } sout | nl; 710 for ( Ā®k; 1.5 ~ @ : j; -5 -~ @ ~ 2 : i; 5Ā® ) { 711 sout | i | j | k; } sout | nl; 647 712 \end{cfa} 648 713 & 649 714 \begin{cfa} 650 switch ( i ) { 651 case 1: case 3 : case 5: 652 ... 653 case 2: case 4 : case 6: 654 ... 655 } 656 \end{cfa} 657 & 658 \begin{cfa} 659 660 // odd values 661 662 // even values 663 664 715 716 empty 717 empty 718 empty 719 zero 720 A 721 A A A A A A A A A A 722 B B B B B 723 C C C C C 724 D D D D D 725 E E E E E 726 0 1 2 3 4 5 6 7 8 9 727 1 3 5 7 9 728 10 8 6 4 2 729 0.5 1.5 2.5 3.5 4.5 730 5.5 4.5 3.5 2.5 1.5 731 2 4 6 8 10 732 10 8 6 4 2 733 734 N N N N N N N N N N 735 0 1 2 3 4 5 6 7 8 9 736 10 9 8 7 6 5 4 3 2 1 737 738 3 6 9 739 740 1 2 3 4 5 6 7 8 9 10 741 742 10 9 8 7 6 5 4 3 2 1 0 743 744 2 4 6 8 10 745 746 2.1 3.8 5.5 7.2 8.9 747 748 10 8 6 4 2 0 749 750 12.1 10.4 8.7 7 5.3 3.6 751 0 -5 1 -4 2 -3 3 -2 4 -1 752 0 -5 1 -6 2 -7 3 -8 4 -9 753 0 -5 1 -3 2 -1 3 1 4 3 754 0 -5 1 -7 2 -9 3 -11 4 -13 755 0 -5 1 -4 2 -3 3 -2 4 -1 756 0 -5 1 -6 2 -7 3 -8 4 -9 757 0 -5 1 -3 2 -1 3 1 4 3 758 0 -5 1 -7 2 -9 3 -11 4 -13 759 760 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 761 762 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 763 764 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 665 765 \end{cfa} 666 766 \end{tabular} 667 767 \end{cquote} 668 In addition, subranges are allowed to specify case values.\footnote{ 669 gcc has the same mechanism but awkward syntax, \lstinline@2 ...42@, because a space is required after a number, otherwise the period is a decimal point.} 670 \begin{cfa} 671 switch ( i ) { 672 case Ā®1~5:Ā® §\C{// 1, 2, 3, 4, 5}§ 673 ... 674 case Ā®10~15:Ā® §\C{// 10, 11, 12, 13, 14, 15}§ 675 ... 676 } 677 \end{cfa} 678 Lists of subranges are also allowed. 679 \begin{cfa} 680 case Ā®1~5, 12~21, 35~42Ā®: 681 \end{cfa} 768 \caption{Loop Control Examples} 769 \label{f:LoopControlExamples} 770 \end{figure} 682 771 683 772 … … 888 977 889 978 890 \subsection{Non-terminating and Labelled \texorpdfstring{\LstKeywordStyle{fallthrough}}{Non-terminating and Labelled fallthrough}} 891 892 The Ā©fallthroughĀ© clause may be non-terminating within a Ā©caseĀ© clause or have a target label to common code from multiple case clauses. 893 \begin{center} 894 \begin{tabular}{@{}lll@{}} 895 \begin{cfa} 896 choose ( ... ) { 897 case 3: 898 if ( ... ) { 899 ... Ā®fallthru;Ā® // goto case 4 900 } else { 901 ... 902 } 903 // implicit break 904 case 4: 905 906 907 908 979 %\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}} 980 \subsection{\texorpdfstring{\LstKeywordStyle{case} Statement}{case Statement}} 981 982 C restricts the Ā©caseĀ© clause of a Ā©switchĀ© statement to a single value. 983 For multiple Ā©caseĀ© clauses associated with the same statement, it is necessary to have multiple Ā©caseĀ© clauses rather than multiple values. 984 Requiring a Ā©caseĀ© clause for each value does not seem to be in the spirit of brevity normally associated with C. 985 Therefore, the Ā©caseĀ© clause is extended with a list of values, as in: 986 \begin{cquote} 987 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}} 988 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{C}} \\ 989 \begin{cfa} 990 switch ( i ) { 991 case Ā®1, 3, 5Ā®: 992 ... 993 case Ā®2, 4, 6Ā®: 994 ... 995 } 909 996 \end{cfa} 910 997 & 911 998 \begin{cfa} 912 choose ( ... ) { 913 case 3: 914 ... Ā®fallthrough common;Ā® 915 case 4: 916 ... Ā®fallthrough common;Ā® 917 918 Ā®common:Ā® // below fallthrough 919 // at case-clause level 920 ... // common code for cases 3/4 921 // implicit break 922 case 4: 923 924 999 switch ( i ) { 1000 case 1: case 3 : case 5: 1001 ... 1002 case 2: case 4 : case 6: 1003 ... 1004 } 925 1005 \end{cfa} 926 1006 & 927 1007 \begin{cfa} 928 choose ( ... ) { 929 case 3: 930 choose ( ... ) { 931 case 4: 932 for ( ... ) { 933 // multi-level transfer 934 ... Ā®fallthru common;Ā® 935 } 936 ... 937 } 1008 1009 // odd values 1010 1011 // even values 1012 1013 1014 \end{cfa} 1015 \end{tabular} 1016 \end{cquote} 1017 In addition, subranges are allowed to specify case values.\footnote{ 1018 gcc has the same mechanism but awkward syntax, \lstinline@2 ...42@, because a space is required after a number, otherwise the period is a decimal point.} 1019 \begin{cfa} 1020 switch ( i ) { 1021 case Ā®1~5:Ā® §\C{// 1, 2, 3, 4, 5}§ 938 1022 ... 939 Ā®common:Ā® // below fallthrough 940 // at case-clause level 941 \end{cfa} 942 \end{tabular} 943 \end{center} 944 The target label must be below the Ā©fallthroughĀ© and may not be nested in a control structure, and 945 the target label must be at the same or higher level as the containing Ā©caseĀ© clause and located at 946 the same level as a Ā©caseĀ© clause; the target label may be case Ā©defaultĀ©, but only associated 947 with the current Ā©switchĀ©/Ā©chooseĀ© statement. 948 949 \begin{figure} 950 \begin{tabular}{@{}l|l@{}} 951 \multicolumn{1}{c|}{loop control} & \multicolumn{1}{c}{output} \\ 952 \hline 953 \begin{cfa}[xleftmargin=0pt] 954 while Ā®()Ā® { sout | "empty"; break; } 955 do { sout | "empty"; break; } while Ā®()Ā®; 956 for Ā®()Ā® { sout | "empty"; break; } 957 for ( Ā®0Ā® ) { sout | "A"; } sout | "zero"; 958 for ( Ā®1Ā® ) { sout | "A"; } 959 for ( Ā®10Ā® ) { sout | "A"; } 960 for ( Ā®= 10Ā® ) { sout | "A"; } 961 for ( Ā®1 ~= 10 ~ 2Ā® ) { sout | "B"; } 962 for ( Ā®10 -~= 1 ~ 2Ā® ) { sout | "C"; } 963 for ( Ā®0.5 ~ 5.5Ā® ) { sout | "D"; } 964 for ( Ā®5.5 -~ 0.5Ā® ) { sout | "E"; } 965 for ( Ā®i; 10Ā® ) { sout | i; } 966 for ( Ā®i; = 10Ā® ) { sout | i; } 967 for ( Ā®i; 1 ~= 10 ~ 2Ā® ) { sout | i; } 968 for ( Ā®i; 10 -~= 1 ~ 2Ā® ) { sout | i; } 969 for ( Ā®i; 0.5 ~ 5.5Ā® ) { sout | i; } 970 for ( Ā®i; 5.5 -~ 0.5Ā® ) { sout | i; } 971 for ( Ā®ui; 2u ~= 10u ~ 2uĀ® ) { sout | ui; } 972 for ( Ā®ui; 10u -~= 2u ~ 2uĀ® ) { sout | ui; } 973 enum { N = 10 }; 974 for ( Ā®NĀ® ) { sout | "N"; } 975 for ( Ā®i; NĀ® ) { sout | i; } 976 for ( Ā®i; N -~ 0Ā® ) { sout | i; } 977 const int start = 3, comp = 10, inc = 2; 978 for ( Ā®i; start ~ comp ~ inc + 1Ā® ) { sout | i; } 979 for ( i; 1 ~ Ā®@Ā® ) { if ( i > 10 ) break; sout | i; } 980 for ( i; 10 -~ Ā®@Ā® ) { if ( i < 0 ) break; sout | i; } 981 for ( i; 2 ~ Ā®@Ā® ~ 2 ) { if ( i > 10 ) break; sout | i; } 982 for ( i; 2.1 ~ Ā®@Ā® ~ Ā®@Ā® ) { if ( i > 10.5 ) break; sout | i; i += 1.7; } 983 for ( i; 10 -~ Ā®@Ā® ~ 2 ) { if ( i < 0 ) break; sout | i; } 984 for ( i; 12.1 ~ Ā®@Ā® ~ Ā®@Ā® ) { if ( i < 2.5 ) break; sout | i; i -= 1.7; } 985 for ( i; 5 Ā®:Ā® j; -5 ~ @ ) { sout | i | j; } 986 for ( i; 5 Ā®:Ā® j; -5 -~ @ ) { sout | i | j; } 987 for ( i; 5 Ā®:Ā® j; -5 ~ @ ~ 2 ) { sout | i | j; } 988 for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 ) { sout | i | j; } 989 for ( i; 5 Ā®:Ā® j; -5 ~ @ ) { sout | i | j; } 990 for ( i; 5 Ā®:Ā® j; -5 -~ @ ) { sout | i | j; } 991 for ( i; 5 Ā®:Ā® j; -5 ~ @ ~ 2 ) { sout | i | j; } 992 for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 ) { sout | i | j; } 993 for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 Ā®:Ā® k; 1.5 ~ @ ) { sout | i | j | k; } 994 for ( i; 5 Ā®:Ā® j; -5 -~ @ ~ 2 Ā®:Ā® k; 1.5 ~ @ ) { sout | i | j | k; } 995 for ( i; 5 Ā®:Ā® k; 1.5 ~ @ Ā®:Ā® j; -5 -~ @ ~ 2 ) { sout | i | j | k; } 996 \end{cfa} 997 & 998 \begin{cfa} 999 empty 1000 empty 1001 empty 1002 zero 1003 A 1004 A A A A A A A A A A 1005 A A A A A A A A A A A 1006 B B B B B 1007 C C C C C 1008 D D D D D 1009 E E E E E 1010 0 1 2 3 4 5 6 7 8 9 1011 0 1 2 3 4 5 6 7 8 9 10 1012 1 3 5 7 9 1013 10 8 6 4 2 1014 0.5 1.5 2.5 3.5 4.5 1015 5.5 4.5 3.5 2.5 1.5 1016 2 4 6 8 10 1017 10 8 6 4 2 1018 1019 N N N N N N N N N N 1020 0 1 2 3 4 5 6 7 8 9 1021 10 9 8 7 6 5 4 3 2 1 1022 1023 3 6 9 1024 1 2 3 4 5 6 7 8 9 10 1025 10 9 8 7 6 5 4 3 2 1 0 1026 2 4 6 8 10 1027 2.1 3.8 5.5 7.2 8.9 1028 10 8 6 4 2 0 1029 12.1 10.4 8.7 7. 5.3 3.6 1030 0 -5 1 -4 2 -3 3 -2 4 -1 1031 0 -5 1 -6 2 -7 3 -8 4 -9 1032 0 -5 1 -3 2 -1 3 1 4 3 1033 0 -5 1 -7 2 -9 3 -11 4 -13 1034 0 -5 1 -4 2 -3 3 -2 4 -1 1035 0 -5 1 -6 2 -7 3 -8 4 -9 1036 0 -5 1 -3 2 -1 3 1 4 3 1037 0 -5 1 -7 2 -9 3 -11 4 -13 1038 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 1039 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 1040 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 1041 \end{cfa} 1042 \end{tabular} 1043 \caption{Loop Control Examples} 1044 \label{f:LoopControlExamples} 1045 \end{figure} 1023 case Ā®10~15:Ā® §\C{// 10, 11, 12, 13, 14, 15}§ 1024 ... 1025 } 1026 \end{cfa} 1027 Lists of subranges are also allowed. 1028 \begin{cfa} 1029 case Ā®1~5, 12~21, 35~42Ā®: 1030 \end{cfa} 1031 1046 1032 1047 1033 % for () => for ( ;; ) … … 1054 1040 1055 1041 1056 \subsection{Loop Control}1057 1058 The Ā©forĀ©/Ā©whileĀ©/Ā©do-whileĀ© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}).1059 \begin{itemize}1060 \item1061 The loop index is polymorphic in the type of the comparison value N (when the start value is implicit) or the start value M.1062 \item1063 An empty conditional implies comparison value of Ā©1Ā© (true).1064 \item1065 A comparison N is implicit up-to exclusive range [0,N©®)®©.1066 \item1067 A comparison Ā©=Ā© N is implicit up-to inclusive range [0,N©®]®©.1068 \item1069 The up-to range M Ā©~Ā©\index{~@Ā©~Ā©} N means exclusive range [M,N©®)®©.1070 \item1071 The up-to range M Ā©~=Ā©\index{~=@Ā©~=Ā©} N means inclusive range [M,N©®]®©.1072 \item1073 The down-to range M Ā©-~Ā©\index{-~@Ā©-~Ā©} N means exclusive range [N,M©®)®©.1074 \item1075 The down-to range M Ā©-~=Ā©\index{-~=@Ā©-~=Ā©} N means inclusive range [N,M©®]®©.1076 \item1077 Ā©0Ā© is the implicit start value;1078 \item1079 Ā©1Ā© is the implicit increment value.1080 \item1081 The up-to range uses operator Ā©+=Ā© for increment;1082 \item1083 The down-to range uses operator Ā©-=Ā© for decrement.1084 \item1085 Ā©@Ā© means put nothing in this field.1086 \item1087 Ā©:Ā© means start another index.1088 \end{itemize}1089 1090 1091 1042 %\subsection{\texorpdfstring{Labelled \protect\lstinline@continue@ / \protect\lstinline@break@}{Labelled continue / break}} 1092 1043 \subsection{\texorpdfstring{Labelled \LstKeywordStyle{continue} / \LstKeywordStyle{break} Statement}{Labelled continue / break Statement}} … … 1098 1049 for Ā©breakĀ©, the target label can also be associated with a Ā©switchĀ©, Ā©ifĀ© or compound (Ā©{}Ā©) statement. 1099 1050 \VRef[Figure]{f:MultiLevelExit} shows Ā©continueĀ© and Ā©breakĀ© indicating the specific control structure, and the corresponding C program using only Ā©gotoĀ© and labels. 1100 The innermost loop has 8exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s.1051 The innermost loop has 7 exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s. 1101 1052 1102 1053 \begin{figure} 1103 \centering 1104 \begin{lrbox}{\myboxA} 1105 \begin{cfa}[tabsize=3] 1106 Ā®Compound:Ā® { 1107 Ā®Try:Ā® try { 1108 Ā®For:Ā® for ( ... ) { 1109 Ā®While:Ā® while ( ... ) { 1110 Ā®Do:Ā® do { 1111 Ā®If:Ā® if ( ... ) { 1112 Ā®Switch:Ā® switch ( ... ) { 1113 case 3: 1114 Ā®break CompoundĀ®; 1115 Ā®break TryĀ®; 1116 Ā®break ForĀ®; /* or */ Ā®continue ForĀ®; 1117 Ā®break WhileĀ®; /* or */ Ā®continue WhileĀ®; 1118 Ā®break DoĀ®; /* or */ Ā®continue DoĀ®; 1119 Ā®break IfĀ®; 1120 Ā®break SwitchĀ®; 1121 } // switch 1122 } else { 1123 ... Ā®break IfĀ®; ... // terminate if 1124 } // if 1125 } while ( ... ); // do 1126 } // while 1127 } // for 1128 } Ā®finallyĀ® { // always executed 1129 } // try 1054 \begin{tabular}{@{\hspace{\parindentlnth}}l@{\hspace{\parindentlnth}}l@{\hspace{\parindentlnth}}l@{}} 1055 \multicolumn{1}{@{\hspace{\parindentlnth}}c@{\hspace{\parindentlnth}}}{\textbf{\CFA}} & \multicolumn{1}{@{\hspace{\parindentlnth}}c}{\textbf{C}} \\ 1056 \begin{cfa} 1057 Ā®LC:Ā® { 1058 ... §declarations§ ... 1059 Ā®LS:Ā® switch ( ... ) { 1060 case 3: 1061 Ā®LIF:Ā® if ( ... ) { 1062 Ā®LF:Ā® for ( ... ) { 1063 Ā®LW:Ā® while ( ... ) { 1064 ... break Ā®LCĀ®; ... 1065 ... break Ā®LSĀ®; ... 1066 ... break Ā®LIFĀ®; ... 1067 ... continue Ā®LF;Ā® ... 1068 ... break Ā®LFĀ®; ... 1069 ... continue Ā®LWĀ®; ... 1070 ... break Ā®LWĀ®; ... 1071 } // while 1072 } // for 1073 } else { 1074 ... break Ā®LIFĀ®; ... 1075 } // if 1076 } // switch 1130 1077 } // compound 1131 1078 \end{cfa} 1132 \end{lrbox} 1133 1134 \begin{lrbox}{\myboxB} 1135 \begin{cfa}[tabsize=3] 1079 & 1080 \begin{cfa} 1136 1081 { 1137 1138 Ā®ForC:Ā® for ( ... ) { 1139 Ā®WhileC:Ā® while ( ... ) { 1140 Ā®DoC:Ā® do { 1141 if ( ... ) { 1142 switch ( ... ) { 1143 case 3: 1144 Ā®goto CompoundĀ®; 1145 Ā®goto TryĀ®; 1146 Ā®goto ForBĀ®; /* or */ Ā®goto ForCĀ®; 1147 Ā®goto WhileBĀ®; /* or */ Ā®goto WhileCĀ®; 1148 Ā®goto DoBĀ®; /* or */ Ā®goto DoCĀ®; 1149 Ā®goto IfĀ®; 1150 Ā®goto SwitchĀ®; 1151 } Ā®Switch:Ā® ; 1152 } else { 1153 ... Ā®goto IfĀ®; ... // terminate if 1154 } Ā®If:Ā®; 1155 } while ( ... ); Ā®DoB:Ā® ; 1156 } Ā®WhileB:Ā® ; 1157 } Ā®ForB:Ā® ; 1158 1159 1160 } Ā®Compound:Ā® ; 1161 \end{cfa} 1162 \end{lrbox} 1163 1164 \subfloat[\CFA]{\label{f:CFibonacci}\usebox\myboxA} 1165 \hspace{2pt} 1166 \vrule 1167 \hspace{2pt} 1168 \subfloat[C]{\label{f:CFAFibonacciGen}\usebox\myboxB} 1082 ... §declarations§ ... 1083 switch ( ... ) { 1084 case 3: 1085 if ( ... ) { 1086 for ( ... ) { 1087 while ( ... ) { 1088 ... goto Ā®LCĀ®; ... 1089 ... goto Ā®LSĀ®; ... 1090 ... goto Ā®LIFĀ®; ... 1091 ... goto Ā®LFCĀ®; ... 1092 ... goto Ā®LFBĀ®; ... 1093 ... goto Ā®LWCĀ®; ... 1094 ... goto Ā®LWBĀ®; ... 1095 Ā®LWCĀ®: ; } Ā®LWB:Ā® ; 1096 Ā®LFC:Ā® ; } Ā®LFB:Ā® ; 1097 } else { 1098 ... goto Ā®LIFĀ®; ... 1099 } Ā®L3:Ā® ; 1100 } Ā®LS:Ā® ; 1101 } Ā®LC:Ā® ; 1102 \end{cfa} 1103 & 1104 \begin{cfa} 1105 1106 1107 1108 1109 1110 1111 1112 // terminate compound 1113 // terminate switch 1114 // terminate if 1115 // continue loop 1116 // terminate loop 1117 // continue loop 1118 // terminate loop 1119 1120 1121 1122 // terminate if 1123 1124 1125 1126 \end{cfa} 1127 \end{tabular} 1169 1128 \caption{Multi-level Exit} 1170 1129 \label{f:MultiLevelExit} … … 1421 1380 try { 1422 1381 f(...); 1423 } catch( E e ; §boolean-predicate§ ) { §\C {// termination handler}§1382 } catch( E e ; §boolean-predicate§ ) { §\C[8cm]{// termination handler}§ 1424 1383 // recover and continue 1425 } catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler} §1384 } catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler}\CRT§ 1426 1385 // repair and return 1427 1386 } finally { … … 3486 3445 For implicit formatted input, the common case is reading a sequence of values separated by whitespace, where the type of an input constant must match with the type of the input variable. 3487 3446 \begin{cquote} 3488 \begin{lrbox}{\ myboxA}3447 \begin{lrbox}{\LstBox} 3489 3448 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 3490 3449 int x; double y char z; … … 3492 3451 \end{lrbox} 3493 3452 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{3em}}l@{}} 3494 \multicolumn{1}{@{}l@{}}{\usebox\ myboxA} \\3453 \multicolumn{1}{@{}l@{}}{\usebox\LstBox} \\ 3495 3454 \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}} & \multicolumn{1}{c}{\textbf{Python}} \\ 3496 3455 \begin{cfa}[aboveskip=0pt,belowskip=0pt] … … 6588 6547 hence, names in these include files are not mangled\index{mangling!name} (see~\VRef{s:Interoperability}). 6589 6548 All other C header files must be explicitly wrapped in Ā©extern "C"Ā© to prevent name mangling. 6590 This approach is different from \Index*[C++]{\CC{}} where the name-mangling issue is handled internally inC header-files through checks for preprocessor variable Ā©__cplusplusĀ©, which adds appropriate Ā©extern "C"Ā© qualifiers.6549 For \Index*[C++]{\CC{}}, the name-mangling issue is often handled internally in many C header-files through checks for preprocessor variable Ā©__cplusplusĀ©, which adds appropriate Ā©extern "C"Ā© qualifiers. 6591 6550 6592 6551 … … 6602 6561 The storage-management routines extend their C equivalents by overloading, alternate names, providing shallow type-safety, and removing the need to specify the allocation size for non-array types. 6603 6562 6604 C storage management provides the following capabilities:6563 Storage management provides the following capabilities: 6605 6564 \begin{description} 6606 \item[fill ed]6607 after allocation with a specified character or value.6565 \item[fill] 6566 after allocation the storage is filled with a specified character. 6608 6567 \item[resize] 6609 an existing allocation to decreased or increased itssize.6610 In either case, new storage may or may not be allocated and, if there is a new allocation, as much data from the existing allocation is copied into the new allocation.6568 an existing allocation is decreased or increased in size. 6569 In either case, new storage may or may not be allocated and, if there is a new allocation, as much data from the existing allocation is copied. 6611 6570 For an increase in storage size, new storage after the copied data may be filled. 6612 \item[align ]6613 an allocation on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes.6571 \item[alignment] 6572 an allocation starts on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes. 6614 6573 \item[array] 6615 6574 the allocation size is scaled to the specified number of array elements. 6616 6575 An array may be filled, resized, or aligned. 6617 6576 \end{description} 6618 \VRef[Table]{t:AllocationVersusCapabilities} shows allocation routines supporting different combinations of storage-management capabilities. 6619 \begin{table} 6620 \centering 6621 \begin{minipage}{0.75\textwidth} 6622 \begin{tabular}{@{}r|l|l|l|l|l@{}} 6577 The table shows allocation routines supporting different combinations of storage-management capabilities: 6578 \begin{center} 6579 \begin{tabular}{@{}r|r|l|l|l|l@{}} 6623 6580 \multicolumn{1}{c}{}& & \multicolumn{1}{c|}{fill} & resize & alignment & array \\ 6624 6581 \hline 6625 6582 C & Ā©mallocĀ© & no & no & no & no \\ 6626 6583 & Ā©callocĀ© & yes (0 only) & no & no & yes \\ 6627 & Ā©reallocĀ© & copy& yes & no & no \\6584 & Ā©reallocĀ© & no/copy & yes & no & no \\ 6628 6585 & Ā©memalignĀ© & no & no & yes & no \\ 6629 & Ā©aligned_allocĀ©\footnote{Same as Ā©memalignĀ© but size is an integral multiple of alignment, which is universally ignored.}6630 & no & no & yes & no \\6631 6586 & Ā©posix_memalignĀ© & no & no & yes & no \\ 6632 & Ā©vallocĀ© & no & no & yes (page size)& no \\6633 & Ā©pvallocĀ©\footnote{Same as Ā©vallocĀ© but rounds size to multiple of page size.}6634 & no & no & yes (page size)& no \\6635 6587 \hline 6636 \CFA & Ā©cmemalignĀ© & yes (0 only) & no & yes & yes \\ 6637 & Ā©reallocĀ© & copy & yes & yes & no \\ 6638 & Ā©allocĀ© & no & yes & no & yes \\ 6639 & Ā©alloc_setĀ© & yes & yes & no & yes \\ 6640 & Ā©alloc_alignĀ© & no & yes & yes & yes \\ 6641 & Ā©alloc_align_setĀ© & yes & yes & yes & yes \\ 6588 C11 & Ā©aligned_allocĀ© & no & no & yes & no \\ 6589 \hline 6590 \CFA & Ā©allocĀ© & no/copy/yes & no/yes & no & yes \\ 6591 & Ā©align_allocĀ© & no/yes & no & yes & yes \\ 6642 6592 \end{tabular} 6643 \end{minipage} 6644 \caption{Allocation Routines versus Storage-Management Capabilities} 6645 \label{t:AllocationVersusCapabilities} 6646 \end{table} 6647 6648 \CFA memory management extends the type safety of all allocations by using the type of the left-hand-side type to determine the allocation size and return a matching type for the new storage. 6649 Type-safe allocation is provided for all C allocation routines and new \CFA allocation routines, \eg in 6650 \begin{cfa} 6651 int * ip = (int *)malloc( sizeof(int) ); §\C{// C}§ 6652 int * ip = malloc(); §\C{// \CFA type-safe version of C malloc}§ 6653 int * ip = alloc(); §\C{// \CFA type-safe uniform alloc}§ 6654 \end{cfa} 6655 the latter two allocations determine the allocation size from the type of Ā©pĀ© (Ā©intĀ©) and cast the pointer to the allocated storage to Ā©int *Ā©. 6656 6657 \CFA memory management extends allocation safety by implicitly honouring all alignment requirements, \eg in 6658 \begin{cfa} 6659 struct S { int i; } __attribute__(( aligned( 128 ) )); // cache-line alignment 6660 S * sp = malloc(); §\C{// honour type alignment}§ 6661 \end{cfa} 6662 the storage allocation is implicitly aligned to 128 rather than the default 16. 6663 The alignment check is performed at compile time so there is no runtime cost. 6664 6665 \CFA memory management extends the resize capability with the notion of \newterm{sticky properties}. 6666 Hence, initial allocation capabilities are remembered and maintained when resize requires copying. 6667 For example, an initial alignment and fill capability are preserved during a resize copy so the copy has the same alignment and extended storage is filled. 6668 Without sticky properties it is dangerous to use Ā©reallocĀ©, resulting in an idiom of manually performing the reallocation to maintain correctness. 6669 \begin{cfa} 6670 6671 \end{cfa} 6672 6673 \CFA memory management extends allocation to support constructors for initialization of allocated storage, \eg in 6674 \begin{cfa} 6675 struct S { int i; }; §\C{// cache-line aglinment}§ 6676 void ?{}( S & s, int i ) { s.i = i; } 6677 // assume ?|? operator for printing an S 6678 6679 S & sp = *Ā®newĀ®( 3 ); §\C{// call constructor after allocation}§ 6680 sout | sp.i; 6681 Ā®deleteĀ®( &sp ); 6682 6683 S * spa = Ā®anewĀ®( 10, 5 ); §\C{// allocate array and initialize each array element}§ 6684 for ( i; 10 ) sout | spa[i] | nonl; 6685 sout | nl; 6686 Ā®adeleteĀ®( 10, spa ); 6687 \end{cfa} 6688 Allocation routines Ā©newĀ©/Ā©anewĀ© allocate a variable/array and initialize storage using the allocated type's constructor. 6689 Note, the matching deallocation routines Ā©deleteĀ©/Ā©adeleteĀ©. 6593 \end{center} 6594 It is impossible to resize with alignment because the underlying Ā©reallocĀ© allocates storage if more space is needed, and it does not honour alignment from the original allocation. 6690 6595 6691 6596 \leavevmode 6692 6597 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6598 // C unsafe allocation 6693 6599 extern "C" { 6694 // C unsafe allocation 6695 void * malloc( size_t size );§\indexc{malloc}§ 6696 void * calloc( size_t dim, size_t size );§\indexc{calloc}§ 6697 void * realloc( void * ptr, size_t size );§\indexc{realloc}§ 6698 void * memalign( size_t align, size_t size );§\indexc{memalign}§ 6699 void * aligned_alloc( size_t align, size_t size );§\indexc{aligned_alloc}§ 6700 int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§ 6701 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize );§\indexc{cmemalign}§ // CFA 6702 6703 // C unsafe initialization/copy 6704 void * memset( void * dest, int c, size_t size );§\indexc{memset}§ 6705 void * memcpy( void * dest, const void * src, size_t size );§\indexc{memcpy}§ 6706 } 6707 6708 void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap 6600 void * malloc( size_t size );§\indexc{memset}§ 6601 void * calloc( size_t dim, size_t size );§\indexc{calloc}§ 6602 void * realloc( void * ptr, size_t size );§\indexc{realloc}§ 6603 void * memalign( size_t align, size_t size );§\indexc{memalign}§ 6604 int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§ 6605 6606 // C unsafe initialization/copy 6607 void * memset( void * dest, int c, size_t size ); 6608 void * memcpy( void * dest, const void * src, size_t size ); 6609 } 6709 6610 6710 6611 forall( dtype T | sized(T) ) { 6711 // §\CFA§ safe equivalents, i.e., implicit size specification6612 // §\CFA§ safe equivalents, i.e., implicit size specification 6712 6613 T * malloc( void ); 6713 6614 T * calloc( size_t dim ); 6714 6615 T * realloc( T * ptr, size_t size ); 6715 6616 T * memalign( size_t align ); 6716 T * cmemalign( size_t align, size_t dim );6717 6617 T * aligned_alloc( size_t align ); 6718 6618 int posix_memalign( T ** ptr, size_t align ); 6719 6619 6720 // §\CFA§ safe general allocation, fill, resize, alignment, array 6721 T * alloc( void );§\indexc{alloc}§ §\C[3.5in]{// variable, T size}§ 6722 T * alloc( size_t dim ); §\C{// array[dim], T size elements}§ 6723 T * alloc( T ptr[], size_t dim ); §\C{// realloc array[dim], T size elements}§ 6724 6725 T * alloc_set( char fill );§\indexc{alloc_set}§ §\C{// variable, T size, fill bytes with value}§ 6726 T * alloc_set( T fill ); §\C{// variable, T size, fill with value}§ 6727 T * alloc_set( size_t dim, char fill ); §\C{// array[dim], T size elements, fill bytes with value}§ 6728 T * alloc_set( size_t dim, T fill ); §\C{// array[dim], T size elements, fill elements with value}§ 6729 T * alloc_set( size_t dim, const T fill[] ); §\C{// array[dim], T size elements, fill elements with array}§ 6730 T * alloc_set( T ptr[], size_t dim, char fill ); §\C{// realloc array[dim], T size elements, fill bytes with value}§ 6731 6732 T * alloc_align( size_t align ); §\C{// aligned variable, T size}§ 6733 T * alloc_align( size_t align, size_t dim ); §\C{// aligned array[dim], T size elements}§ 6734 T * alloc_align( T ptr[], size_t align ); §\C{// realloc new aligned array}§ 6735 T * alloc_align( T ptr[], size_t align, size_t dim ); §\C{// realloc new aligned array[dim]}§ 6736 6737 T * alloc_align_set( size_t align, char fill ); §\C{// aligned variable, T size, fill bytes with value}§ 6738 T * alloc_align_set( size_t align, T fill ); §\C{// aligned variable, T size, fill with value}§ 6739 T * alloc_align_set( size_t align, size_t dim, char fill ); §\C{// aligned array[dim], T size elements, fill bytes with value}§ 6740 T * alloc_align_set( size_t align, size_t dim, T fill ); §\C{// aligned array[dim], T size elements, fill elements with value}§ 6741 T * alloc_align_set( size_t align, size_t dim, const T fill[] ); §\C{// aligned array[dim], T size elements, fill elements with array}§ 6742 T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); §\C{// realloc new aligned array[dim], fill new bytes with value}§ 6743 6744 // §\CFA§ safe initialization/copy, i.e., implicit size specification 6745 T * memset( T * dest, char fill );§\indexc{memset}§ 6620 // §\CFA§ safe general allocation, fill, resize, array 6621 T * alloc( void );§\indexc{alloc}§ 6622 T * alloc( char fill ); 6623 T * alloc( size_t dim ); 6624 T * alloc( size_t dim, char fill ); 6625 T * alloc( T ptr[], size_t dim ); 6626 T * alloc( T ptr[], size_t dim, char fill ); 6627 6628 // §\CFA§ safe general allocation, align, fill, array 6629 T * align_alloc( size_t align ); 6630 T * align_alloc( size_t align, char fill ); 6631 T * align_alloc( size_t align, size_t dim ); 6632 T * align_alloc( size_t align, size_t dim, char fill ); 6633 6634 // §\CFA§ safe initialization/copy, i.e., implicit size specification 6635 T * memset( T * dest, char c );§\indexc{memset}§ 6746 6636 T * memcpy( T * dest, const T * src );§\indexc{memcpy}§ 6747 6637 6748 // §\CFA§ safe initialization/copy, i.e., implicit size specification, array types 6749 T * amemset( T dest[], char fill, size_t dim );6638 // §\CFA§ safe initialization/copy array 6639 T * amemset( T dest[], char c, size_t dim ); 6750 6640 T * amemcpy( T dest[], const T src[], size_t dim ); 6751 6641 } 6752 6642 6753 // §\CFA§ allocation/deallocation and constructor/destructor , non-array types6754 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p );§\indexc{new}§6755 forall( dtype T | sized(T) | { void ^?{}( T &); } ) void delete( T * ptr );§\indexc{delete}§6756 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T &); void delete( Params ); } )6643 // §\CFA§ allocation/deallocation and constructor/destructor 6644 forall( dtype T | sized(T), ttype Params | { void ?{}( T *, Params ); } ) T * new( Params p );§\indexc{new}§ 6645 forall( dtype T | { void ^?{}( T * ); } ) void delete( T * ptr );§\indexc{delete}§ 6646 forall( dtype T, ttype Params | { void ^?{}( T * ); void delete( Params ); } ) 6757 6647 void delete( T * ptr, Params rest ); 6758 6648 6759 // §\CFA§ allocation/deallocation and constructor/destructor, array types6760 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p );§\indexc{anew}§6761 forall( dtype T | sized(T) | { void ^?{}( T &); } ) void adelete( size_t dim, T arr[] );§\indexc{adelete}§6762 forall( dtype T | sized(T) | { void ^?{}( T &); }, ttype Params | { void adelete( Params ); } )6649 // §\CFA§ allocation/deallocation and constructor/destructor, array 6650 forall( dtype T | sized(T), ttype Params | { void ?{}( T *, Params ); } ) T * anew( size_t dim, Params p );§\indexc{anew}§ 6651 forall( dtype T | sized(T) | { void ^?{}( T * ); } ) void adelete( size_t dim, T arr[] );§\indexc{adelete}§ 6652 forall( dtype T | sized(T) | { void ^?{}( T * ); }, ttype Params | { void adelete( Params ); } ) 6763 6653 void adelete( size_t dim, T arr[], Params rest ); 6764 6654 \end{cfa} -
driver/Makefile.am
reef8dfb rbdfc032 28 28 @test -z "$(CFA_BINDIR)" || $(MKDIR_P) "$(CFA_BINDIR)" 29 29 @echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) cfa '$(CFA_BINDIR)/$(CFA_NAME)'"; \ 30 chmod u+w $(CFA_BINDIR);\31 30 $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) cfa $(CFA_BINDIR)/$(CFA_NAME) || exit $$? 32 31 -
driver/cc1.cc
reef8dfb rbdfc032 10 10 // Created On : Fri Aug 26 14:23:51 2005 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 17 14:27:08 202013 // Update Count : 41412 // Last Modified On : Sun Oct 20 08:14:33 2019 13 // Update Count : 385 14 14 // 15 15 … … 24 24 #include <unistd.h> // execvp, fork, unlink 25 25 #include <sys/wait.h> // wait 26 #include <fcntl.h> // creat26 #include <fcntl.h> 27 27 28 28 … … 38 38 static string o_file; 39 39 static string bprefix; 40 static string lang; // -x flag41 40 42 41 … … 59 58 60 59 61 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); // " __CFA_FLAG__=" suffix60 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); // "N__=" suffix 62 61 63 62 static void checkEnv1( const char * args[], int & nargs ) { // stage 1 … … 74 73 if ( prefix( val, "-compiler=" ) ) { 75 74 compiler_path = val.substr( 10 ); 76 } else if ( prefix( val, "-x=" ) ) {77 lang = val.substr( 3 );78 75 } // if 79 76 } // if … … 97 94 } else if ( val == "-CFA" ) { 98 95 CFA_flag = true; 99 } else if ( val == "-save-temps" || val == "--save-temps") {96 } else if ( val == "-save-temps" ) { 100 97 save_temps = true; 101 98 } else if ( prefix( val, "-o=" ) ) { // output file for -CFA … … 103 100 } else if ( prefix( val, "-B=" ) ) { // location of cfa-cpp 104 101 bprefix = val.substr( 3 ); 105 } else if ( prefix( val, "-x=" ) ) { // ignore106 102 } else { // normal flag for cfa-cpp 107 103 args[nargs++] = ( *new string( arg.substr( arg.find_first_of( "=" ) + 1 ) ) ).c_str(); … … 111 107 } // checkEnv2 112 108 113 #define CFA_SUFFIX ".ifa" 114 115 static char tmpname[] = P_tmpdir "/CFAXXXXXX" CFA_SUFFIX; 109 110 static char tmpname[] = P_tmpdir "/CFAXXXXXX.ifa"; 116 111 static int tmpfilefd = -1; 117 112 static bool startrm = false; … … 171 166 if ( arg == "-quiet" ) { 172 167 } else if ( arg == "-imultilib" || arg == "-imultiarch" ) { 173 i += 1; // and argument168 i += 1; // and the argument 174 169 } else if ( prefix( arg, "-A" ) ) { 175 170 } else if ( prefix( arg, "-D__GNU" ) ) { … … 178 173 //******** 179 174 } else if ( arg == "-D" && prefix( argv[i + 1], "__GNU" ) ) { 180 i += 1; // and argument175 i += 1; // and the argument 181 176 182 177 // strip flags controlling cpp step … … 185 180 cpp_flag = true; 186 181 } else if ( arg == "-D" && string( argv[i + 1] ) == "__CPP__" ) { 187 i += 1; // and argument182 i += 1; // and the argument 188 183 cpp_flag = true; 189 184 … … 195 190 cpp_out = argv[i]; 196 191 } else { 197 args[nargs++] = argv[i]; // pass flag along192 args[nargs++] = argv[i]; // pass the flag along 198 193 // CPP flags with an argument 199 194 if ( arg == "-D" || arg == "-U" || arg == "-I" || arg == "-MF" || arg == "-MT" || arg == "-MQ" || … … 201 196 arg == "-iwithprefix" || arg == "-iwithprefixbefore" || arg == "-isystem" || arg == "-isysroot" ) { 202 197 i += 1; 203 args[nargs++] = argv[i]; // pass argument along198 args[nargs++] = argv[i]; // pass the argument along 204 199 #ifdef __DEBUG_H__ 205 200 cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl; 206 201 #endif // __DEBUG_H__ 207 202 } else if ( arg == "-MD" || arg == "-MMD" ) { 208 // gcc frontend generates the dependency file-name after the -MD/-MMD flag, but it is necessary to209 // prefix that file name with -MF.210 203 args[nargs++] = "-MF"; // insert before file 211 204 i += 1; 212 args[nargs++] = argv[i]; // pass argument along205 args[nargs++] = argv[i]; // pass the argument along 213 206 #ifdef __DEBUG_H__ 214 207 cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl; … … 254 247 255 248 args[0] = compiler_path.c_str(); 256 if ( lang.size() == 0 ) { 257 suffix( cpp_in, args, nargs ); // check suffix 258 } else { 259 args[nargs++] = "-x"; 260 args[nargs++] = ( *new string( lang.c_str() ) ).c_str(); 261 } // if 249 suffix( cpp_in, args, nargs ); // check suffix 262 250 args[nargs++] = cpp_in; 263 251 if ( o_flag ) { // location for output … … 282 270 // Run the C preprocessor and save the output in the given file. 283 271 284 if ( fork() == 0 ) { // child process ?272 if ( fork() == 0 ) { // child process ? 285 273 // -o xxx.ii cannot be used to write the output file from cpp because no output file is created if cpp detects 286 274 // an error (e.g., cannot find include file). Whereas, output is always generated, even when there is an error, … … 292 280 293 281 args[0] = compiler_path.c_str(); 294 if ( lang.size() == 0 ) { 295 suffix( cpp_in, args, nargs ); // check suffix 296 } else { 297 args[nargs++] = "-x"; 298 args[nargs++] = ( *new string( lang.c_str() ) ).c_str(); 299 } // if 282 suffix( cpp_in, args, nargs ); // check suffix 300 283 args[nargs++] = cpp_in; // input to cpp 301 284 args[nargs] = nullptr; // terminate argument list … … 322 305 323 306 if ( WIFSIGNALED(code) ) { // child failed ? 324 rmtmpfile(); // remove tmpname325 307 cerr << "CC1 Translator error: stage 1, child failed " << WTERMSIG(code) << endl; 326 308 exit( EXIT_FAILURE ); 327 309 } // if 328 310 329 exit( WEXITSTATUS( code ) );// bad cpp result stops top-level gcc311 exit( WEXITSTATUS(code) ); // bad cpp result stops top-level gcc 330 312 } // Stage1 331 313 … … 375 357 } else if ( arg == "-fno-diagnostics-color" ) { 376 358 color_arg = Color_Auto; 377 } // if359 } 378 360 379 361 if ( arg == "-quiet" || arg == "-version" || arg == "-fpreprocessed" || 380 // Currently CFA does not suppose precompiled .h files.381 prefix( arg, "--output-pch" ) ) {362 // Currently CFA does not suppose precompiled .h files. 363 prefix( arg, "--output-pch" ) ) { 382 364 383 365 // strip inappropriate flags with an argument … … 392 374 393 375 } else { 394 args[nargs++] = argv[i]; // pass flag along376 args[nargs++] = argv[i]; // pass the flag along 395 377 if ( arg == "-o" ) { 396 378 i += 1; 397 379 cpp_out = argv[i]; 398 args[nargs++] = argv[i]; // pass argument along380 args[nargs++] = argv[i]; // pass the argument along 399 381 #ifdef __DEBUG_H__ 400 382 cerr << "arg:\"" << argv[i] << "\"" << endl; … … 443 425 } // if 444 426 445 cfa_cpp_out = cfa_cpp_out.substr( 0, dot ) + CFA_SUFFIX;427 cfa_cpp_out = cfa_cpp_out.substr( 0, dot ) + ".ifa"; 446 428 if ( creat( cfa_cpp_out.c_str(), 0666 ) == -1 ) { 447 429 perror( "CC1 Translator error: stage 2, creat" ); … … 464 446 // output. Otherwise, run the cfa-cpp preprocessor on the temporary file and save the result into the output file. 465 447 466 if ( fork() == 0 ) { // child runs CFA preprocessor448 if ( fork() == 0 ) { // child runs CFA 467 449 cargs[0] = ( *new string( bprefix + "cfa-cpp" ) ).c_str(); 468 450 cargs[ncargs++] = cpp_in; … … 522 504 #endif // __DEBUG_H__ 523 505 524 if ( fork() == 0 ) { // child runs gcc506 if ( fork() == 0 ) { // child runs CFA 525 507 args[0] = compiler_path.c_str(); 526 508 args[nargs++] = "-S"; // only compile and put assembler output in specified file -
driver/cfa.cc
reef8dfb rbdfc032 10 10 // Created On : Tue Aug 20 13:44:49 2002 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 17 14:27:28202013 // Update Count : 4 4012 // Last Modified On : Fri Jan 31 16:48:03 2020 13 // Update Count : 421 14 14 // 15 15 16 16 #include <iostream> 17 #include <cstdio> // perror 18 #include <cstdlib> // putenv, exit 19 #include <climits> // PATH_MAX 20 #include <string> // STL version 21 #include <algorithm> // find 22 23 #include <unistd.h> // execvp 17 #include <cstdio> // perror 18 #include <cstdlib> // putenv, exit 19 #include <climits> // PATH_MAX 20 #include <unistd.h> // execvp 21 #include <string> // STL version 22 #include <string.h> // strcmp 23 #include <algorithm> // find 24 24 25 #include <sys/types.h> 25 26 #include <sys/stat.h> … … 33 34 using std::to_string; 34 35 35 //#define __DEBUG_H__ 36 37 #define xstr(s) str(s) 38 #define str(s) #s 39 40 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); // "__CFA_FLAG__=" suffix 41 42 static void Putenv( char * argv[], string arg ) { 36 // #define __DEBUG_H__ 37 38 // "N__=" suffix 39 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); 40 41 void Putenv( char * argv[], string arg ) { 43 42 // environment variables must have unique names 44 43 static int flags = 0; … … 50 49 } // Putenv 51 50 52 static bool prefix( const string & arg, const string & pre ) { // check if string has prefix 51 // check if string has prefix 52 bool prefix( const string & arg, const string & pre ) { 53 53 return arg.substr( 0, pre.size() ) == pre; 54 54 } // prefix 55 55 56 staticinline bool ends_with(const string & str, const string & sfix) {56 inline bool ends_with(const string & str, const string & sfix) { 57 57 if (sfix.size() > str.size()) return false; 58 58 return std::equal(str.rbegin(), str.rbegin() + sfix.size(), sfix.rbegin(), sfix.rend()); … … 60 60 61 61 // check if string has suffix 62 staticbool suffix( const string & arg ) {62 bool suffix( const string & arg ) { 63 63 enum { NumSuffixes = 3 }; 64 64 static const string suffixes[NumSuffixes] = { "cfa", "hfa", "ifa" }; … … 70 70 } // suffix 71 71 72 72 73 static inline bool dirExists( const string & path ) { // check if directory exists 73 74 struct stat info; … … 78 79 static inline string dir(const string & path) { 79 80 return path.substr(0, path.find_last_of('/')); 80 } // dir81 } 81 82 82 83 // Different path modes … … 117 118 } 118 119 120 121 #define xstr(s) str(s) 122 #define str(s) #s 119 123 120 124 int main( int argc, char * argv[] ) { … … 154 158 PathMode path = FromProc(); 155 159 156 const char * args[argc + 100]; // cfa command line values, plus some space for additional flags160 const char *args[argc + 100]; // cfa command line values, plus some space for additional flags 157 161 int sargs = 1; // starting location for arguments in args list 158 162 int nargs = sargs; // number of arguments in args list; 0 => command name 159 163 160 const char * libs[argc + 20]; // non-user libraries must come separately, plus some added libraries and flags164 const char *libs[argc + 20]; // non-user libraries must come separately, plus some added libraries and flags 161 165 int nlibs = 0; 162 166 … … 176 180 177 181 if ( arg == "-Xlinker" || arg == "-o" ) { 178 args[nargs++] = argv[i]; // pass flagalong182 args[nargs++] = argv[i]; // pass argument along 179 183 i += 1; 180 184 if ( i == argc ) continue; // next argument available ? 181 185 args[nargs++] = argv[i]; // pass argument along 182 186 if ( arg == "-o" ) o_file = i; // remember file 187 } else if ( arg == "-XCFA" ) { // CFA pass through 188 i += 1; 189 if ( i == argc ) continue; // next argument available ? 190 Putenv( argv, argv[i] ); 183 191 184 192 // CFA specific arguments 185 193 186 } else if ( strncmp(arg.c_str(), "-XCFA", 5) == 0 ) { // CFA pass through187 if ( arg.size() == 5 ) {188 i += 1;189 if ( i == argc ) continue; // next argument available ?190 Putenv( argv, argv[i] );191 } else if ( arg[5] == ',' ) { // CFA specific arguments192 Putenv( argv, argv[i] + 6 );193 } else { // CFA specific arguments194 args[nargs++] = argv[i];195 } // if196 194 } else if ( arg == "-CFA" ) { 197 195 CFA_flag = true; // strip the -CFA flag … … 202 200 } else if ( arg == "-nodebug" ) { 203 201 debug = false; // strip the nodebug flag 202 } else if ( arg == "-nolib" ) { 203 nolib = true; // strip the nodebug flag 204 204 } else if ( arg == "-quiet" ) { 205 205 quiet = true; // strip the quiet flag 206 206 } else if ( arg == "-noquiet" ) { 207 207 quiet = false; // strip the noquiet flag 208 } else if ( arg == "-no-include-stdhdr" ) {209 noincstd_flag = true; // strip the no-include-stdhdr flag210 } else if ( arg == "-nolib" ) {211 nolib = true; // strip the nolib flag212 208 } else if ( arg == "-help" ) { 213 209 help = true; // strip the help flag 214 210 } else if ( arg == "-nohelp" ) { 215 211 help = false; // strip the nohelp flag 212 } else if ( arg == "-no-include-stdhdr" ) { 213 noincstd_flag = true; // strip the no-include-stdhdr flag 216 214 } else if ( arg == "-cfalib") { 217 215 compiling_libs = true; … … 227 225 } else if ( arg == "-v" ) { 228 226 verbose = true; // verbosity required 229 args[nargs++] = argv[i]; // pass flagalong227 args[nargs++] = argv[i]; // pass argument along 230 228 } else if ( arg == "-g" ) { 231 229 debugging = true; // symbolic debugging required 232 args[nargs++] = argv[i]; // pass flagalong233 } else if ( arg == "-save-temps" || arg == "--save-temps") {234 args[nargs++] = argv[i]; // pass flagalong230 args[nargs++] = argv[i]; // pass argument along 231 } else if ( arg == "-save-temps" ) { 232 args[nargs++] = argv[i]; // pass argument along 235 233 Putenv( argv, arg ); // save cfa-cpp output 236 234 } else if ( prefix( arg, "-x" ) ) { // file suffix ? 237 235 string lang; 238 args[nargs++] = argv[i]; // pass flagalong236 args[nargs++] = argv[i]; // pass argument along 239 237 if ( arg.length() == 2 ) { // separate argument ? 240 238 i += 1; … … 245 243 lang = arg.substr( 2 ); 246 244 } // if 247 if ( x_flag ) { 248 cerr << argv[0] << " warning, only one -x flag per compile, ignoring subsequent flag." << endl; 249 } else { 250 x_flag = true; 251 Putenv( argv, string( "-x=" ) + lang ); 252 } // if 245 x_flag = lang != "none"; 253 246 } else if ( prefix( arg, "-std=" ) || prefix( arg, "--std=" ) ) { 254 247 std_flag = true; // -std=XX provided 255 args[nargs++] = argv[i]; // pass flagalong248 args[nargs++] = argv[i]; // pass argument along 256 249 } else if ( arg == "-w" ) { 257 args[nargs++] = argv[i]; // pass flagalong250 args[nargs++] = argv[i]; // pass argument along 258 251 Putenv( argv, arg ); 259 252 } else if ( prefix( arg, "-W" ) ) { // check before next tests 260 253 if ( arg == "-Werror" || arg == "-Wall" ) { 261 args[nargs++] = argv[i]; // pass flagalong254 args[nargs++] = argv[i]; // pass argument along 262 255 Putenv( argv, argv[i] ); 263 256 } else { … … 273 266 bprefix = arg.substr(2); // strip the -B flag 274 267 } else if ( arg == "-c" || arg == "-S" || arg == "-E" || arg == "-M" || arg == "-MM" ) { 275 args[nargs++] = argv[i]; // pass flagalong268 args[nargs++] = argv[i]; // pass argument along 276 269 if ( arg == "-E" || arg == "-M" || arg == "-MM" ) { 277 270 cpp_flag = true; // cpp only 278 271 } // if 279 272 link = false; // no linkage required 280 } else if ( arg == "-D" || arg == "-U" || arg == "-I" || arg == "-MF" || arg == "-MT" || arg == "-MQ" ||281 arg == "-include" || arg == "-imacros" || arg == "-idirafter" || arg == "-iprefix" ||282 arg == "-iwithprefix" || arg == "-iwithprefixbefore" || arg == "-isystem" || arg == "-isysroot" ) {283 args[nargs++] = argv[i]; // pass flag along284 i += 1;285 args[nargs++] = argv[i]; // pass argument along286 273 } else if ( arg[1] == 'l' ) { 287 274 // if the user specifies a library, load it after user code … … 315 302 316 303 #ifdef __x86_64__ 317 args[nargs++] = "-mcx16"; // allow double-wide CA S304 args[nargs++] = "-mcx16"; // allow double-wide CAA 318 305 #endif // __x86_64__ 319 306 … … 335 322 string libbase; 336 323 switch(path) { 337 case Installed:324 case Installed: 338 325 args[nargs++] = "-I" CFA_INCDIR; 339 326 // do not use during build … … 345 332 libbase = CFA_LIBDIR; 346 333 break; 347 case BuildTree:348 case Distributed:334 case BuildTree: 335 case Distributed: 349 336 args[nargs++] = "-I" TOP_SRCDIR "libcfa/src"; 350 337 // do not use during build … … 380 367 string libdir = libbase + arch + "-" + config; 381 368 382 if ( path != Distributed) {369 if (path != Distributed) { 383 370 if ( ! nolib && ! dirExists( libdir ) ) { 384 371 cerr << argv[0] << " internal error, configuration " << config << " not installed." << endl; … … 398 385 } // if 399 386 400 string preludedir;401 387 switch(path) { 402 case Installed : preludedir = libdir; break; 403 case BuildTree : preludedir = libdir + "/prelude"; break; 404 case Distributed : preludedir = dir(argv[0]); break; 405 } // switch 406 407 Putenv( argv, "--prelude-dir=" + preludedir ); 408 args[nargs++] = "-include"; 409 args[nargs++] = (*new string(preludedir + "/defines.hfa")).c_str(); 388 case Installed : Putenv( argv, "--prelude-dir=" + libdir ); break; 389 case BuildTree : Putenv( argv, "--prelude-dir=" + libdir + "/prelude" ); break; 390 case Distributed : Putenv( argv, "--prelude-dir=" + dir(argv[0]) ); break; 391 } 410 392 411 393 for ( int i = 0; i < nlibs; i += 1 ) { // copy non-user libraries after all user libraries … … 429 411 args[nargs++] = "-lcfathread"; 430 412 args[nargs++] = "-Wl,--pop-state"; 431 args[nargs++] = "-Wl,--push-state,--no-as-needed";432 413 args[nargs++] = "-lcfa"; 433 args[nargs++] = "-Wl,--pop-state";434 414 args[nargs++] = "-pthread"; 435 #if defined( __x86_64__ ) || defined( __ARM_ARCH )436 args[nargs++] = "-latomic"; // allow double-wide CAS437 #endif // __x86_64__438 415 args[nargs++] = "-ldl"; 416 args[nargs++] = "-lrt"; 439 417 args[nargs++] = "-lm"; 440 418 } // if … … 474 452 if ( bprefix.length() == 0 ) { 475 453 switch(path) { 476 case Installed : bprefix = installlibdir; break;477 case BuildTree : bprefix = srcdriverdir ; break;478 case Distributed : bprefix = dir(argv[0]) ; break;479 } // switch480 } // if481 if ( bprefix[bprefix.length() - 1] != '/' ) bprefix += '/';482 Putenv( argv, string("-B=") + bprefix );454 case Installed : bprefix = installlibdir; break; 455 case BuildTree : bprefix = srcdriverdir ; break; 456 case Distributed : bprefix = dir(argv[0]) ; break; 457 } 458 if ( bprefix[bprefix.length() - 1] != '/' ) bprefix += '/'; 459 Putenv( argv, string("-B=") + bprefix ); 460 } // if 483 461 484 462 args[nargs++] = "-Xlinker"; // used by backtrace … … 502 480 args[nargs++] = "-Wno-cast-function-type"; 503 481 #endif // HAVE_CAST_FUNCTION_TYPE 504 if ( ! std_flag && ! x_flag ) {505 args[nargs++] = "-std=gnu11"; // default c11, if none specified482 if ( ! std_flag ) { // default c11, if none specified 483 args[nargs++] = "-std=gnu11"; 506 484 } // if 507 485 args[nargs++] = "-fgnu89-inline"; … … 553 531 // execute the command and return the result 554 532 555 execvp( args[0], (char * const *)args );// should not return533 execvp( args[0], (char *const *)args ); // should not return 556 534 perror( "CFA Translator error: execvp" ); 557 535 exit( EXIT_FAILURE ); -
libcfa/configure.ac
reef8dfb rbdfc032 3 3 4 4 AC_PREREQ([2.68]) 5 AC_INIT([cfa-cc],[1.0.0 ],[cforall@plg.uwaterloo.ca])5 AC_INIT([cfa-cc],[1.0.0.0],[cforall@plg.uwaterloo.ca]) 6 6 AC_CONFIG_AUX_DIR([automake]) 7 7 AC_CONFIG_MACRO_DIRS([automake]) 8 8 AM_SILENT_RULES([yes]) 9 9 10 m4_include([../ tools/build/cfa.m4])10 m4_include([../automake/cfa.m4]) 11 11 12 12 AM_INIT_AUTOMAKE([subdir-objects]) … … 30 30 [ --enable-distcc whether or not to enable distributed compilation], 31 31 enable_distcc=$enableval, enable_distcc=no) 32 33 AC_ARG_WITH(bwlimit,34 [ --with-bwlimit=RATE RATE the maximum rate at which rsync will be limited when using distributed builds],35 DIST_BWLIMIT=$withval, DIST_BWLIMIT=0)36 32 37 33 echo -n "checking for distributated build... " … … 59 55 AC_SUBST(CFADIR_HASH) 60 56 AC_SUBST(CFA_VERSION) 61 AC_SUBST(DIST_BWLIMIT)62 57 63 58 #============================================================================== … … 105 100 AM_CONDITIONAL([BUILDLIB], [test "x${CONFIG_BUILDLIB}" = "xyes"]) 106 101 107 AM_T='$(T)'108 AC_SUBST(AM_T)109 110 102 #============================================================================== 111 103 #Trasforming cc1 will break compilation … … 117 109 118 110 # Checks for programs. 119 LT_INIT ([disable-static])111 LT_INIT 120 112 121 113 AC_PROG_CXX … … 126 118 AC_PROG_MAKE_SET 127 119 128 129 130 #io_uring 5.4 and earlier uses defines131 #io_uring 5.5 uses enum values132 #io_uring 5.6 and later uses probes133 134 AH_TEMPLATE([CFA_HAVE_LINUX_IO_URING_H],[Defined if io_uring support is present when compiling libcfathread.])135 AH_TEMPLATE([CFA_HAVE_IORING_OP_NOP],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_NOP.])136 AH_TEMPLATE([CFA_HAVE_IORING_OP_READV],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_READV.])137 AH_TEMPLATE([CFA_HAVE_IORING_OP_WRITEV],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_WRITEV.])138 AH_TEMPLATE([CFA_HAVE_IORING_OP_FSYNC],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FSYNC.])139 AH_TEMPLATE([CFA_HAVE_IORING_OP_READ_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_READ_FIXED.])140 AH_TEMPLATE([CFA_HAVE_IORING_OP_WRITE_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_WRITE_FIXED.])141 AH_TEMPLATE([CFA_HAVE_IORING_OP_POLL_ADD],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_POLL_ADD.])142 AH_TEMPLATE([CFA_HAVE_IORING_OP_POLL_REMOVE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_POLL_REMOVE.])143 AH_TEMPLATE([CFA_HAVE_IORING_OP_SYNC_FILE_RANGE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SYNC_FILE_RANGE.])144 AH_TEMPLATE([CFA_HAVE_IORING_OP_SENDMSG],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SENDMSG.])145 AH_TEMPLATE([CFA_HAVE_IORING_OP_RECVMSG],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_RECVMSG.])146 AH_TEMPLATE([CFA_HAVE_IORING_OP_TIMEOUT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_TIMEOUT.])147 AH_TEMPLATE([CFA_HAVE_IORING_OP_TIMEOUT_REMOVE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_TIMEOUT_REMOVE.])148 AH_TEMPLATE([CFA_HAVE_IORING_OP_ACCEPT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_ACCEPT.])149 AH_TEMPLATE([CFA_HAVE_IORING_OP_ASYNC_CANCEL],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_ASYNC_CANCEL.])150 AH_TEMPLATE([CFA_HAVE_IORING_OP_LINK_TIMEOUT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_LINK_TIMEOUT.])151 AH_TEMPLATE([CFA_HAVE_IORING_OP_CONNECT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_CONNECT.])152 AH_TEMPLATE([CFA_HAVE_IORING_OP_FALLOCATE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FALLOCATE.])153 AH_TEMPLATE([CFA_HAVE_IORING_OP_OPENAT],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_OPENAT.])154 AH_TEMPLATE([CFA_HAVE_IORING_OP_CLOSE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_CLOSE.])155 AH_TEMPLATE([CFA_HAVE_IORING_OP_FILES_UPDATE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FILES_UPDATE.])156 AH_TEMPLATE([CFA_HAVE_IORING_OP_STATX],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_STATX.])157 AH_TEMPLATE([CFA_HAVE_IORING_OP_READ],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_READ.])158 AH_TEMPLATE([CFA_HAVE_IORING_OP_WRITE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_WRITE.])159 AH_TEMPLATE([CFA_HAVE_IORING_OP_FADVISE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_FADVISE.])160 AH_TEMPLATE([CFA_HAVE_IORING_OP_MADVISE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_MADVISE.])161 AH_TEMPLATE([CFA_HAVE_IORING_OP_SEND],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SEND.])162 AH_TEMPLATE([CFA_HAVE_IORING_OP_RECV],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_RECV.])163 AH_TEMPLATE([CFA_HAVE_IORING_OP_OPENAT2],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_OPENAT2.])164 AH_TEMPLATE([CFA_HAVE_IORING_OP_EPOLL_CTL],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_EPOLL_CTL.])165 AH_TEMPLATE([CFA_HAVE_IORING_OP_SPLICE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_SPLICE.])166 AH_TEMPLATE([CFA_HAVE_IORING_OP_PROVIDE_BUFFERS],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_PROVIDE_BUFFERS.])167 AH_TEMPLATE([CFA_HAVE_IORING_OP_REMOVE_BUFFER],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_REMOVE_BUFFER.])168 AH_TEMPLATE([CFA_HAVE_IORING_OP_TEE],[Defined if io_uring support is present when compiling libcfathread and supports the operation IORING_OP_TEE.])169 AH_TEMPLATE([CFA_HAVE_IOSQE_FIXED_FILE],[Defined if io_uring support is present when compiling libcfathread and supports the flag FIXED_FILE.])170 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_DRAIN],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_DRAIN.])171 AH_TEMPLATE([CFA_HAVE_IOSQE_ASYNC],[Defined if io_uring support is present when compiling libcfathread and supports the flag ASYNC.])172 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_LINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_LINK.])173 AH_TEMPLATE([CFA_HAVE_IOSQE_IO_HARDLINK],[Defined if io_uring support is present when compiling libcfathread and supports the flag IO_HARDLINK.])174 AH_TEMPLATE([CFA_HAVE_SPLICE_F_FD_IN_FIXED],[Defined if io_uring support is present when compiling libcfathread and supports the flag SPLICE_F_FD_IN_FIXED.])175 AH_TEMPLATE([CFA_HAVE_IORING_SETUP_ATTACH_WQ],[Defined if io_uring support is present when compiling libcfathread and supports the flag IORING_SETUP_ATTACH_WQ.])176 AH_TEMPLATE([CFA_HAVE_PREADV2],[Defined if preadv2 support is present when compiling libcfathread.])177 AH_TEMPLATE([CFA_HAVE_PWRITEV2],[Defined if pwritev2 support is present when compiling libcfathread.])178 AH_TEMPLATE([CFA_HAVE_PWRITEV2],[Defined if pwritev2 support is present when compiling libcfathread.])179 AH_TEMPLATE([CFA_HAVE_STATX],[Defined if statx support is present when compiling libcfathread.])180 AH_TEMPLATE([CFA_HAVE_OPENAT2],[Defined if openat2 support is present when compiling libcfathread.])181 AH_TEMPLATE([__CFA_NO_STATISTICS__],[Defined if libcfathread was compiled without support for statistics.])182 183 define(ioring_ops, [IORING_OP_NOP,IORING_OP_READV,IORING_OP_WRITEV,IORING_OP_FSYNC,IORING_OP_READ_FIXED,IORING_OP_WRITE_FIXED,IORING_OP_POLL_ADD,IORING_OP_POLL_REMOVE,IORING_OP_SYNC_FILE_RANGE,IORING_OP_SENDMSG,IORING_OP_RECVMSG,IORING_OP_TIMEOUT,IORING_OP_TIMEOUT_REMOVE,IORING_OP_ACCEPT,IORING_OP_ASYNC_CANCEL,IORING_OP_LINK_TIMEOUT,IORING_OP_CONNECT,IORING_OP_FALLOCATE,IORING_OP_OPENAT,IORING_OP_CLOSE,IORING_OP_FILES_UPDATE,IORING_OP_STATX,IORING_OP_READ,IORING_OP_WRITE,IORING_OP_FADVISE,IORING_OP_MADVISE,IORING_OP_SEND,IORING_OP_RECV,IORING_OP_OPENAT2,IORING_OP_EPOLL_CTL,IORING_OP_SPLICE,IORING_OP_PROVIDE_BUFFERS,IORING_OP_REMOVE_BUFFER,IORING_OP_TEE])184 define(ioring_flags, [IOSQE_FIXED_FILE,IOSQE_IO_DRAIN,IOSQE_ASYNC,IOSQE_IO_LINK,IOSQE_IO_HARDLINK,SPLICE_F_FD_IN_FIXED,IORING_SETUP_ATTACH_WQ])185 186 define(ioring_from_decls, [187 m4_foreach([op], [ioring_ops], [188 AC_CHECK_DECL(op, [AC_DEFINE([CFA_HAVE_]op)], [], [[#include <linux/io_uring.h>]])189 ])190 ])191 192 AC_CHECK_HEADERS([linux/io_uring.h], [193 AC_DEFINE(CFA_HAVE_LINUX_IO_URING_H)194 AC_CHECK_HEADER([liburing.h], [195 AC_CHECK_LIB([uring], [io_uring_get_probe], [196 m4_foreach([op], [ioring_ops], [197 AC_CHECK_DECL(op, [198 AC_RUN_IFELSE([199 AC_LANG_PROGRAM(200 [[#include <liburing.h>]],201 [[int main() {]]202 [[ struct io_uring_probe *probe = io_uring_get_probe();]]203 [[ if(io_uring_opcode_supported(probe, ]]op[[))]]204 [[ return 0;]]205 [[ else]]206 [[ return 1;]]207 [[}]]208 )209 ],[210 AC_DEFINE([CFA_HAVE_]op)211 ],[212 AC_MSG_FAILURE([Check support for] op [ with liburing failed])213 ])214 ], [], [[#include <linux/io_uring.h>]])215 ])216 ], [217 ioring_from_decls218 ])219 ], [220 ioring_from_decls221 ])222 223 # check support for various io_uring flags224 m4_foreach([op], [ioring_flags], [225 AC_CHECK_DECL(op, [AC_DEFINE([CFA_HAVE_]op)], [], [[#include <linux/io_uring.h>]])226 ])227 ])228 AC_CHECK_FUNC([preadv2], [AC_DEFINE([CFA_HAVE_PREADV2])])229 AC_CHECK_FUNC([pwritev2], [AC_DEFINE([CFA_HAVE_PWRITEV2])])230 231 120 AC_CONFIG_FILES([ 232 121 Makefile … … 234 123 prelude/Makefile 235 124 ]) 236 AC_CONFIG_FILES([src/concurrency/io/call.cfa], [python3 ${srcdir}/src/concurrency/io/call.cfa.in > src/concurrency/io/call.cfa])237 238 AC_CONFIG_HEADERS(prelude/defines.hfa)239 125 240 126 AC_OUTPUT() -
libcfa/prelude/Makefile.am
reef8dfb rbdfc032 11 11 ## Created On : Sun May 31 08:54:01 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Mon Feb 3 21:27:18 202014 ## Update Count : 20 813 ## Last Modified On : Wed Dec 14 15:00:35 2016 14 ## Update Count : 205 15 15 ############################################################################### 16 16 … … 21 21 # put into lib for now 22 22 cfalibdir = ${CFA_LIBDIR} 23 cfalib_DATA = gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c defines.hfa 24 25 EXTRA_DIST = bootloader.cf builtins.c builtins.def extras.c extras.regx extras.regx2 prelude-gen.cc prototypes.awk prototypes.c prototypes.sed sync-builtins.cf 23 cfalib_DATA = gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c 26 24 27 25 CC = @LOCAL_CFACC@ … … 38 36 extras.cf : ${srcdir}/extras.regx ${srcdir}/extras.c 39 37 ${AM_V_GEN}gcc ${AM_CFLAGS} -E ${srcdir}/extras.c | grep -f ${srcdir}/extras.regx > extras.cf 40 ${AM_V_GEN}gcc ${AM_CFLAGS} -E ${srcdir}/extras.c | grep -zo -f ${srcdir}/extras.regx2 | tr '\0' '\n' >> extras.cf41 38 42 39 # create forward declarations for gcc builtins … … 70 67 71 68 MOSTLYCLEANFILES = bootloader.c builtins.cf extras.cf gcc-builtins.c gcc-builtins.cf prelude.cfa 72 DISTCLEANFILES = $(DEPDIR)/builtins.Po73 69 MAINTAINERCLEANFILES = ${addprefix ${libdir}/,${cfalib_DATA}} ${addprefix ${libdir}/,${lib_LIBRARIES}} 74 70 75 71 if ENABLE_DISTCC 76 72 distribution: @LOCAL_CFACC@ @LOCAL_CC1@ @CFACPP@ gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c $(srcdir)/../../tools/build/push2dist.sh 77 ${AM_V_GEN}$(srcdir)/../../tools/build/push2dist.sh @CFADIR_HASH@ @DIST_BWLIMIT@73 ${AM_V_GEN}$(srcdir)/../../tools/build/push2dist.sh @CFADIR_HASH@ 78 74 @echo "Dummy file to track distribution to remote hosts" > ${@} 79 75 -
libcfa/prelude/bootloader.cf
reef8dfb rbdfc032 1 1 extern "C" { static inline int invoke_main(int argc, char* argv[], char* envp[]); } 2 int cfa_args_argc;3 char ** cfa_args_argv;4 char ** cfa_args_envp;5 2 6 3 int main(int argc, char* argv[], char* envp[]) { 7 cfa_args_argc = argc;8 cfa_args_argv = argv;9 cfa_args_envp = envp;10 4 return invoke_main(argc, argv, envp); 11 5 } -
libcfa/prelude/builtins.c
reef8dfb rbdfc032 9 9 // Author : Peter A. Buhr 10 10 // Created On : Fri Jul 21 16:21:03 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : T ue Oct 27 14:42:00 202013 // Update Count : 1 1111 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Nov 21 16:31:39 2019 13 // Update Count : 101 14 14 // 15 16 #define __cforall_builtins__17 15 18 16 // type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions … … 51 49 void abort( const char fmt[], ... ) __attribute__ (( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ )); 52 50 53 forall(dtype T)54 static inline T & identity(T & i) {55 return i;56 }57 58 // generator support59 struct $generator {60 inline int;61 };62 63 static inline void ?{}($generator & this) { ((int&)this) = 0; }64 static inline void ^?{}($generator &) {}65 66 trait is_generator(dtype T) {67 void main(T & this);68 $generator * get_generator(T & this);69 };70 71 forall(dtype T | is_generator(T))72 static inline T & resume(T & gen) {73 main(gen);74 return gen;75 }76 77 51 // implicit increment, decrement if += defined, and implicit not if != defined 78 52 … … 96 70 // universal typed pointer constant 97 71 static inline forall( dtype DT ) DT * intptr( uintptr_t addr ) { return (DT *)addr; } 98 static inline forall( ftype FT ) FT * intptr( uintptr_t addr ) { return (FT *)addr; }99 100 #if defined(__SIZEOF_INT128__)101 // constructor for 128-bit numbers (all constants are unsigned as +/- are operators)102 static inline void ?{}( unsigned int128 & this, unsigned long int h, unsigned long int l ) {103 this = (unsigned int128)h << 64 | (unsigned int128)l;104 } // ?{}105 #endif // __SIZEOF_INT128__106 72 107 73 // exponentiation operator implementation -
libcfa/prelude/extras.regx
reef8dfb rbdfc032 24 24 typedef.* char32_t; 25 25 typedef.* wchar_t; 26 extern.*\*malloc\(.*\).* 27 extern.* free\(.*\).* 28 extern.* exit\(.*\).* 29 extern.* atexit\(.*\).* 30 extern.* abort\(.*\).* 31 extern.* printf\(.*\).* -
libcfa/prelude/prototypes.awk
reef8dfb rbdfc032 10 10 # Created On : Sat May 16 07:57:37 2015 11 11 # Last Modified By : Peter A. Buhr 12 # Last Modified On : Sat Feb 8 09:46:58 202013 # Update Count : 3 612 # Last Modified On : Thu Jun 6 20:46:28 2019 13 # Update Count : 34 14 14 # 15 15 … … 17 17 18 18 BEGIN { 19 FS = "[( )]"19 FS = "[( )]" 20 20 # order so string search is longest string 21 21 i=-1 … … 84 84 85 85 /BT_FN/ { 86 for (i = 1; i <= NF; i += 1) {87 if( match($i, "BT_FN") != 0 ) {88 prototypes[$i] = $i89 }86 for (i = 1; i <= NF; i++) { 87 if( match($i, "BT_FN") != 0 ) { 88 prototypes[$i] = $i 89 } 90 90 } 91 }91 } 92 92 93 93 END { … … 103 103 104 104 for ( prototype in prototypes ) { 105 # printf( "//\"%s\"\n", prototype ) 106 if ( index( "BT_LAST", prototype ) == 1 ) { 107 continue 105 # printf( "//\"%s\"\n", prototype ) 106 if ( index( "BT_LAST", prototype ) == 1 ) { 107 continue 108 } # if 109 110 printf( "#define %s(NAME) FUNC_SIMPLE(", prototype ) 111 112 if ( sub( "BT_FN_", "", prototype ) == 0 ) { 113 printf( "\n********** BAD MACRO NAME \"%s\" **********\n", prototype ) 114 exit 0 115 } # if 116 117 # generate function return type as macro 118 for ( t = 0; t < N; t += 1 ) { # find longest match 119 type = types[t]; 120 if ( index( prototype, type ) == 1 ) { # found match 121 printf( "BT_%s, NAME", type ) 122 sub( type, "", prototype ) 123 break; 108 124 } # if 125 } # for 109 126 110 printf( "#define %s(NAME) FUNC_SIMPLE(", prototype ) 111 112 if ( sub( "BT_FN_", "", prototype ) == 0 ) { 113 printf( "\n********** BAD MACRO NAME \"%s\" **********\n", prototype ) 127 # generate function parameter types as macro 128 if ( index( prototype, "VAR" ) != 2 ) { # C-style empty parameters ? 129 for ( p = 0; length( prototype ) > 0; p += 1 ) { # until all parameters types are removed 130 sub( "_", "", prototype) # remove "_" 131 printf( ", ", type ) 132 temp = prototype 133 for ( t = 0; t < N; t += 1 ) { # find longest match 134 type = types[t]; 135 if ( index( prototype, type ) == 1 ) { # found match 136 printf( "BT_%s", type ) 137 sub( type, "", prototype ) 138 break; 139 } # if 140 } # for 141 if ( temp == prototype ) { # no match found for parameter in macro table 142 printf( "\n********** MISSING TYPE \"%s\" **********\n", prototype ) 114 143 exit 0 115 } # if 116 117 # generate function return type as macro 118 for ( t = 0; t < N; t += 1 ) { # find longest match 119 type = types[t]; 120 if ( index( prototype, type ) == 1 ) { # found match 121 printf( "BT_%s, NAME", type ) 122 sub( type, "", prototype ) 123 break; 124 } # if 144 } # if 125 145 } # for 126 127 # generate function parameter types as macro 128 if ( index( prototype, "VAR" ) != 2 ) { # C-style empty parameters ? 129 for ( p = 0; length( prototype ) > 0; p += 1 ) { # until all parameters types are removed 130 sub( "_", "", prototype) # remove "_" 131 printf( ", ", type ) 132 temp = prototype 133 for ( t = 0; t < N; t += 1 ) { # find longest match 134 type = types[t]; 135 if ( index( prototype, type ) == 1 ) { # found match 136 printf( "BT_%s", type ) 137 sub( type, "", prototype ) 138 break; 139 } # if 140 } # for 141 if ( temp == prototype ) { # no match found for parameter in macro table 142 printf( "\n********** MISSING TYPE \"%s\" **********\n", prototype ) 143 exit 0 144 } # if 145 } # for 146 } # if 147 printf( ")\n" ) 146 } # if 147 printf( ")\n" ) 148 148 } # for 149 149 -
libcfa/src/Makefile.am
reef8dfb rbdfc032 11 11 ## Created On : Sun May 31 08:54:01 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Wed Dec 9 22:46:14 202014 ## Update Count : 2 5013 ## Last Modified On : Mon Jul 15 22:43:27 2019 14 ## Update Count : 241 15 15 ############################################################################### 16 16 … … 19 19 ACLOCAL_AMFLAGS = -I automake 20 20 21 include $( top_srcdir)/../tools/build/cfa.make21 include $(srcdir)/../../src/cfa.make 22 22 23 23 libdir = ${CFA_LIBDIR} … … 31 31 # AM_CFAFLAGS for only cfa source 32 32 # use -no-include-stdhdr to prevent rebuild cycles 33 # The built sources must not depend on the installed inst_headers_src34 AM_CFAFLAGS = -quiet -cfalib -I$(srcdir)/stdhdr -I$(srcdir)/concurrency$(if $(findstring ${gdbwaittarget}, ${@}), -XCFA --gdb) @CONFIG_CFAFLAGS@35 AM_CFLAGS = -g -Wall -Wno-unused-function -fPIC - fexceptions -pthread @ARCH_FLAGS@ @CONFIG_CFLAGS@33 # The built sources must not depend on the installed headers 34 AM_CFAFLAGS = -quiet -cfalib -I$(srcdir)/stdhdr $(if $(findstring ${gdbwaittarget}, ${@}), -XCFA --gdb) @CONFIG_CFAFLAGS@ 35 AM_CFLAGS = -g -Wall -Wno-unused-function -fPIC -pthread @ARCH_FLAGS@ @CONFIG_CFLAGS@ 36 36 AM_CCASFLAGS = -g -Wall -Wno-unused-function @ARCH_FLAGS@ @CONFIG_CFLAGS@ 37 37 CFACC = @CFACC@ … … 39 39 #---------------------------------------------------------------------------------------------------------------- 40 40 if BUILDLIB 41 inst_headers_nosrc = \ 42 bitmanip.hfa \ 43 clock.hfa \ 44 exception.hfa \ 45 exception.h \ 46 gmp.hfa \ 47 math.hfa \ 48 time_t.hfa \ 49 bits/align.hfa \ 50 bits/containers.hfa \ 51 bits/debug.hfa \ 52 bits/defs.hfa \ 53 bits/locks.hfa \ 54 bits/collection.hfa \ 55 bits/stack.hfa \ 56 bits/queue.hfa \ 57 bits/sequence.hfa \ 58 concurrency/iofwd.hfa \ 59 containers/list.hfa \ 60 containers/stackLockFree.hfa \ 61 vec/vec.hfa \ 62 vec/vec2.hfa \ 63 vec/vec3.hfa \ 64 vec/vec4.hfa 41 headers_nosrc = math.hfa gmp.hfa time_t.hfa bits/align.hfa bits/containers.hfa bits/defs.hfa bits/debug.hfa bits/locks.hfa 42 headers = fstream.hfa iostream.hfa iterator.hfa limits.hfa rational.hfa time.hfa stdlib.hfa common.hfa \ 43 containers/maybe.hfa containers/pair.hfa containers/result.hfa containers/vector.hfa \ 44 vec/vec.hfa vec/vec2.hfa vec/vec3.hfa vec/vec4.hfa 65 45 66 inst_headers_src = \ 67 common.hfa \ 68 fstream.hfa \ 69 heap.hfa \ 70 iostream.hfa \ 71 iterator.hfa \ 72 limits.hfa \ 73 memory.hfa \ 74 parseargs.hfa \ 75 rational.hfa \ 76 stdlib.hfa \ 77 time.hfa \ 78 containers/maybe.hfa \ 79 containers/pair.hfa \ 80 containers/result.hfa \ 81 containers/vector.hfa 82 83 libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \ 84 assert.cfa \ 85 bits/algorithm.hfa \ 86 bits/debug.cfa \ 87 exception.c \ 88 interpose.cfa \ 89 lsda.h \ 90 startup.cfa \ 91 startup.hfa \ 92 virtual.c \ 93 virtual.h 46 libsrc = startup.cfa interpose.cfa bits/debug.cfa assert.cfa exception.c virtual.c heap.cfa ${headers:.hfa=.cfa} 94 47 95 48 # not all platforms support concurrency, add option do disable it 96 inst_thread_headers_nosrc = \ 97 bits/random.hfa \ 98 concurrency/clib/cfathread.h \ 99 concurrency/invoke.h \ 100 concurrency/future.hfa \ 101 concurrency/kernel/fwd.hfa 102 103 inst_thread_headers_src = \ 104 concurrency/coroutine.hfa \ 105 concurrency/exception.hfa \ 106 concurrency/kernel.hfa \ 107 concurrency/locks.hfa \ 108 concurrency/monitor.hfa \ 109 concurrency/mutex.hfa \ 110 concurrency/thread.hfa 111 112 thread_libsrc = ${inst_thread_headers_src} ${inst_thread_headers_src:.hfa=.cfa} \ 113 bits/signal.hfa \ 114 concurrency/alarm.cfa \ 115 concurrency/alarm.hfa \ 116 concurrency/clib/cfathread.cfa \ 117 concurrency/CtxSwitch-@ARCHITECTURE@.S \ 118 concurrency/invoke.c \ 119 concurrency/io.cfa \ 120 concurrency/io/setup.cfa \ 121 concurrency/io/types.hfa \ 122 concurrency/io/call.cfa \ 123 concurrency/iofwd.hfa \ 124 concurrency/kernel_private.hfa \ 125 concurrency/kernel/startup.cfa \ 126 concurrency/preemption.cfa \ 127 concurrency/preemption.hfa \ 128 concurrency/ready_queue.cfa \ 129 concurrency/ready_subqueue.hfa \ 130 concurrency/snzi.hfa \ 131 concurrency/stats.cfa \ 132 concurrency/stats.hfa \ 133 concurrency/stats.hfa 134 49 thread_headers_nosrc = concurrency/invoke.h 50 thread_headers = concurrency/coroutine.hfa concurrency/thread.hfa concurrency/kernel.hfa concurrency/monitor.hfa concurrency/mutex.hfa 51 thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/preemption.cfa ${thread_headers:.hfa=.cfa} 135 52 else 136 inst_headers_src=137 inst_thread_headers_src=138 inst_headers_nosrc =139 inst_thread_headers_nosrc =53 headers = 54 thread_headers = 55 headers_nosrc = 56 thread_headers_nosrc = 140 57 libsrc = 141 58 endif … … 180 97 181 98 prelude.o : prelude.cfa extras.cf gcc-builtins.cf builtins.cf @LOCAL_CFACC@ @CFACPP@ 182 ${AM_V_GEN}$(CFACOMPILE) -quiet -XCFA ,-l ${<} -c -o ${@}99 ${AM_V_GEN}$(CFACOMPILE) -quiet -XCFA -l ${<} -c -o ${@} 183 100 184 101 prelude.lo: prelude.cfa extras.cf gcc-builtins.cf builtins.cf @LOCAL_CFACC@ @CFACPP@ 185 102 ${AM_V_GEN}$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile \ 186 $(CFACOMPILE) -quiet -XCFA ,-l ${<} -c -o ${@}103 $(CFACOMPILE) -quiet -XCFA -l ${<} -c -o ${@} 187 104 188 105 #---------------------------------------------------------------------------------------------------------------- 189 libcfa_la_SOURCES = ${libsrc} 190 nodist_libcfa_la_SOURCES = prelude.cfa 106 libcfa_la_SOURCES = prelude.cfa ${libsrc} 191 107 libcfa_la_LDFLAGS = -version-info @CFA_VERSION@ 192 108 … … 197 113 198 114 cfa_includedir = $(CFA_INCDIR) 199 nobase_cfa_include_HEADERS = ${stdhdr} ${inst_headers_src} ${inst_headers_nosrc} ${inst_thread_headers_src} ${inst_thread_headers_nosrc} 200 EXTRA_DIST = stdhdr 115 nobase_cfa_include_HEADERS = ${stdhdr} ${headers} ${headers_nosrc} ${thread_headers} ${thread_headers_nosrc} 201 116 202 117 #---------------------------------------------------------------------------------------------------------------- … … 204 119 -rm -rf ${CFA_INCDIR} ${CFA_LIBDIR} 205 120 206 distclean-local:207 find ${builddir} -path '*.Plo' -delete208 209 121 210 122 # $(AM_V_CFA)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -
libcfa/src/assert.cfa
reef8dfb rbdfc032 10 10 // Created On : Mon Nov 28 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T ue Feb 4 13:00:18 202013 // Update Count : 612 // Last Modified On : Thu Nov 21 17:09:26 2019 13 // Update Count : 5 14 14 // 15 15 … … 26 26 27 27 // called by macro assert in assert.h 28 void __assert_fail( const char assertion[], const char file[], unsigned int line, const char function[]) {28 void __assert_fail( const char *assertion, const char *file, unsigned int line, const char *function ) { 29 29 __cfaabi_bits_print_safe( STDERR_FILENO, CFA_ASSERT_FMT ".\n", assertion, __progname, function, line, file ); 30 30 abort(); … … 32 32 33 33 // called by macro assertf 34 void __assert_fail_f( const char assertion[], const char file[], unsigned int line, const char function[], const char fmt[], ... ) {34 void __assert_fail_f( const char *assertion, const char *file, unsigned int line, const char *function, const char *fmt, ... ) { 35 35 __cfaabi_bits_acquire(); 36 36 __cfaabi_bits_print_nolock( STDERR_FILENO, CFA_ASSERT_FMT ": ", assertion, __progname, function, line, file ); -
libcfa/src/bits/containers.hfa
reef8dfb rbdfc032 17 17 #include "bits/align.hfa" 18 18 #include "bits/defs.hfa" 19 #include <stdio.h> 19 20 20 //----------------------------------------------------------------------------- 21 21 // Array … … 36 36 #define __small_array_t(T) __small_array(T) 37 37 #else 38 #define __small_array_t(T) __small_array38 #define __small_array_t(T) struct __small_array 39 39 #endif 40 40 … … 146 146 static inline forall( dtype T | is_node(T) ) { 147 147 void ?{}( __queue(T) & this ) with( this ) { 148 (this.head){ 1p }; 149 (this.tail){ &this.head }; 150 verify(*this.tail == 1p); 148 head{ 0p }; 149 tail{ &head }; 151 150 } 152 151 153 152 void append( __queue(T) & this, T * val ) with( this ) { 154 verify(this.tail != 0p); 155 verify(*this.tail == 1p); 156 *this.tail = val; 157 this.tail = &get_next( *val ); 158 *this.tail = 1p; 159 } 160 161 T * peek( __queue(T) & this ) { 162 verify(*this.tail == 1p); 163 T * front = this.head; 164 if( front != 1p ) { 165 verify(*this.tail == 1p); 166 return front; 167 } 168 verify(*this.tail == 1p); 169 return 0p; 153 verify(tail != 0p); 154 *tail = val; 155 tail = &get_next( *val ); 170 156 } 171 157 172 158 T * pop_head( __queue(T) & this ) { 173 verify(*this.tail == 1p); 174 T * _head = this.head; 175 if( _head != 1p ) { 176 this.head = get_next( *_head ); 177 if( get_next( *_head ) == 1p ) { 159 T * head = this.head; 160 if( head ) { 161 this.head = get_next( *head ); 162 if( !get_next( *head ) ) { 178 163 this.tail = &this.head; 179 164 } 180 get_next( *_head ) = 0p; 181 verify(*this.tail == 1p); 182 verify( get_next(*_head) == 0p ); 183 return _head; 184 } 185 verify(*this.tail == 1p); 186 return 0p; 165 get_next( *head ) = 0p; 166 } 167 return head; 187 168 } 188 169 … … 193 174 (*it) = get_next( *val ); 194 175 195 if( t his.tail == &get_next( *val ) ) {196 t his.tail = it;176 if( tail == &get_next( *val ) ) { 177 tail = it; 197 178 } 198 179 199 180 get_next( *val ) = 0p; 200 181 201 verify( ( this.head == 1p) == (&this.head == this.tail) );202 verify( *t his.tail == 1p );182 verify( (head == 0p) == (&head == tail) ); 183 verify( *tail == 0p ); 203 184 return val; 204 185 } 205 186 206 187 int ?!=?( const __queue(T) & this, __attribute__((unused)) zero_t zero ) { 207 return this.head != 1p;188 return this.head != 0; 208 189 } 209 190 } … … 239 220 forall(dtype T ) 240 221 static inline [void] ?{}( __dllist(T) & this, * [T * & next, T * & prev] ( T & ) __get ) { 241 (this.head){ 0p };222 this.head{ 0p }; 242 223 this.__get = __get; 243 224 } … … 248 229 void push_front( __dllist(T) & this, T & node ) with( this ) { 249 230 verify(__get); 250 if ( this.head ) {251 __get( node ).next = this.head;252 __get( node ).prev = __get( * this.head ).prev;231 if ( head ) { 232 __get( node ).next = head; 233 __get( node ).prev = __get( *head ).prev; 253 234 // inserted node must be consistent before it is seen 254 235 // prevent code movement across barrier 255 236 asm( "" : : : "memory" ); 256 __get( * this.head ).prev = &node;237 __get( *head ).prev = &node; 257 238 T & _prev = *__get( node ).prev; 258 239 __get( _prev ).next = &node; … … 264 245 // prevent code movement across barrier 265 246 asm( "" : : : "memory" ); 266 this.head = &node;247 head = &node; 267 248 } 268 249 269 250 void remove( __dllist(T) & this, T & node ) with( this ) { 270 251 verify(__get); 271 if ( &node == this.head ) {272 if ( __get( * this.head ).next == this.head ) {273 this.head = 0p;252 if ( &node == head ) { 253 if ( __get( *head ).next == head ) { 254 head = 0p; 274 255 } else { 275 this.head = __get( *this.head ).next;256 head = __get( *head ).next; 276 257 } 277 258 } … … 285 266 return this.head != 0; 286 267 } 287 288 void move_to_front( __dllist(T) & src, __dllist(T) & dst, T & node ) {289 remove (src, node);290 push_front(dst, node);291 }292 268 } 293 269 #undef next -
libcfa/src/bits/debug.cfa
reef8dfb rbdfc032 10 10 // Created On : Thu Mar 30 12:30:01 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 17 11:07:13 202013 // Update Count : 1 212 // Last Modified On : Thu Nov 21 17:16:30 2019 13 // Update Count : 10 14 14 // 15 15 16 extern "C" { 16 17 #include <stdio.h> 17 18 #include <stdlib.h> … … 20 21 #include <stdarg.h> 21 22 #include <unistd.h> 23 } 22 24 23 25 enum { buffer_size = 4096 }; … … 25 27 26 28 extern "C" { 27 void __cfaabi_bits_write( int fd, const char in_buffer[], int len ) { 29 30 void __cfaabi_bits_write( int fd, const char *in_buffer, int len ) { 28 31 // ensure all data is written 29 32 for ( int count = 0, retcode; count < len; count += retcode ) { -
libcfa/src/bits/debug.hfa
reef8dfb rbdfc032 9 9 // Author : Thierry Delisle 10 10 // Created On : Mon Nov 28 12:27:26 2016 11 // Last Modified By : Andrew Beach12 // Last Modified On : Mon Apr 27 10:15:00 202013 // Update Count : 1011 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Nov 21 17:06:58 2019 13 // Update Count : 8 14 14 // 15 15 16 16 #pragma once 17 18 #include <assert.h>19 17 20 18 #ifdef __CFA_DEBUG__ … … 23 21 #define __cfaabi_dbg_ctx __PRETTY_FUNCTION__ 24 22 #define __cfaabi_dbg_ctx2 , __PRETTY_FUNCTION__ 25 #define __cfaabi_dbg_ctx_param const char caller[] 26 #define __cfaabi_dbg_ctx_param2 , const char caller[] 27 #define __cfaabi_dbg_ctx_fwd caller 28 #define __cfaabi_dbg_ctx_fwd2 , caller 23 #define __cfaabi_dbg_ctx_param const char * caller 24 #define __cfaabi_dbg_ctx_param2 , const char * caller 29 25 #else 30 26 #define __cfaabi_dbg_debug_do(...) … … 34 30 #define __cfaabi_dbg_ctx_param 35 31 #define __cfaabi_dbg_ctx_param2 36 #define __cfaabi_dbg_ctx_fwd37 #define __cfaabi_dbg_ctx_fwd238 32 #endif 39 33 … … 42 36 #endif 43 37 #include <stdarg.h> 38 #include <stdio.h> 44 39 45 extern void __cfaabi_bits_write( int fd, const char buffer[], int len );40 extern void __cfaabi_bits_write( int fd, const char *buffer, int len ); 46 41 extern void __cfaabi_bits_acquire(); 47 42 extern void __cfaabi_bits_release(); … … 50 45 extern void __cfaabi_bits_print_vararg( int fd, const char fmt[], va_list arg ); 51 46 extern void __cfaabi_bits_print_buffer( int fd, char buffer[], int buffer_size, const char fmt[], ... ) __attribute__(( format(printf, 4, 5) )); 52 53 #if defined(__CFA_DEBUG_PRINT__) \54 || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__) \55 || defined(__CFA_DEBUG_PRINT_MONITOR__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__) \56 || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__) \57 || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)58 #include <stdio.h>59 #include <unistd.h>60 #endif61 47 #ifdef __cforall 62 48 } 63 49 #endif 64 50 65 // Deprecated: Use the versions with the new module names.66 51 #ifdef __CFA_DEBUG_PRINT__ 67 52 #define __cfaabi_dbg_write( buffer, len ) __cfaabi_bits_write( STDERR_FILENO, buffer, len ) 68 53 #define __cfaabi_dbg_acquire() __cfaabi_bits_acquire() 69 54 #define __cfaabi_dbg_release() __cfaabi_bits_release() 70 #define __cfaabi_dbg_print_safe(...) __cfaabi_bits_print_safe ( STDERR_FILENO, __VA_ARGS__)71 #define __cfaabi_dbg_print_nolock(...) __cfaabi_bits_print_nolock ( STDERR_FILENO, __VA_ARGS__)72 #define __cfaabi_dbg_print_buffer(...) __cfaabi_bits_print_buffer ( STDERR_FILENO, __VA_ARGS__)73 #define __cfaabi_dbg_print_buffer_decl(...) char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( STDERR_FILENO,__dbg_text, __dbg_len );74 #define __cfaabi_dbg_print_buffer_local(...) __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_dbg_write( STDERR_FILENO,__dbg_text, __dbg_len );55 #define __cfaabi_dbg_print_safe(...) __cfaabi_bits_print_safe (__VA_ARGS__) 56 #define __cfaabi_dbg_print_nolock(...) __cfaabi_bits_print_nolock (__VA_ARGS__) 57 #define __cfaabi_dbg_print_buffer(...) __cfaabi_bits_print_buffer (__VA_ARGS__) 58 #define __cfaabi_dbg_print_buffer_decl(...) char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len ); 59 #define __cfaabi_dbg_print_buffer_local(...) __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_dbg_write( __dbg_text, __dbg_len ); 75 60 #else 76 61 #define __cfaabi_dbg_write(...) ((void)0) … … 84 69 #endif 85 70 86 // Debug print functions and statements:87 // Most are wrappers around the bits printing function but are not always used.88 // If they are used depends if the group (first argument) is active or not. The group must be one89 // defined belowe. The other arguments depend on the wrapped function.90 #define __cfadbg_write(group, buffer, len) \91 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_write(STDERR_FILENO, buffer, len))92 #define __cfadbg_acquire(group) \93 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_acquire())94 #define __cfadbg_release(group) \95 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_release())96 #define __cfadbg_print_safe(group, ...) \97 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_safe(STDERR_FILENO, __VA_ARGS__))98 #define __cfadbg_print_nolock(group, ...) \99 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_nolock(STDERR_FILENO, __VA_ARGS__))100 #define __cfadbg_print_buffer(group, ...) \101 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_buffer(STDERR_FILENO, __VA_ARGS__))102 #define __cfadbg_print_buffer_decl(group, ...) \103 __CFADBG_PRINT_GROUP_##group(char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len ))104 #define __cfadbg_print_buffer_local(group, ...) \105 __CFADBG_PRINT_GROUP_##group(__dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write(STDERR_FILENO, __dbg_text, __dbg_len))106 107 // The debug print groups:108 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_IO__)109 # define __CFADBG_PRINT_GROUP_io(...) __VA_ARGS__110 #else111 # define __CFADBG_PRINT_GROUP_io(...) ((void)0)112 #endif113 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__)114 # define __CFADBG_PRINT_GROUP_io_core(...) __VA_ARGS__115 #else116 # define __CFADBG_PRINT_GROUP_io_core(...) ((void)0)117 #endif118 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_MONITOR__)119 # define __CFADBG_PRINT_GROUP_monitor(...) __VA_ARGS__120 #else121 # define __CFADBG_PRINT_GROUP_monitor(...) ((void)0)122 #endif123 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__)124 # define __CFADBG_PRINT_GROUP_preemption(...) __VA_ARGS__125 #else126 # define __CFADBG_PRINT_GROUP_preemption(...) ((void)0)127 #endif128 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__)129 # define __CFADBG_PRINT_GROUP_runtime_core(...) __VA_ARGS__130 #else131 # define __CFADBG_PRINT_GROUP_runtime_core(...) ((void)0)132 #endif133 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)134 # define __CFADBG_PRINT_GROUP_ready_queue(...) __VA_ARGS__135 #else136 # define __CFADBG_PRINT_GROUP_ready_queue(...) ((void)0)137 #endif138 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__)139 # define __CFADBG_PRINT_GROUP_exception(...) __VA_ARGS__140 #else141 # define __CFADBG_PRINT_GROUP_exception(...) ((void)0)142 #endif143 144 71 // Local Variables: // 145 72 // mode: c // -
libcfa/src/bits/defs.hfa
reef8dfb rbdfc032 10 10 // Created On : Thu Nov 9 13:24:10 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Oct 24 10:53:15202013 // Update Count : 2112 // Last Modified On : Tue Jan 28 22:38:27 2020 13 // Update Count : 9 14 14 // 15 15 16 16 #pragma once 17 17 18 #include <stdbool.h> 19 #include <stddef.h> 18 20 #include <stdint.h> 19 #include <assert.h>20 21 21 22 #define likely(x) __builtin_expect(!!(x), 1) … … 29 30 #define __cfa_anonymous_object(x) inline struct x 30 31 #else 31 #define __cfa_anonymous_object(x) structx __cfa_anonymous_object32 #define __cfa_anonymous_object(x) x __cfa_anonymous_object 32 33 #endif 33 34 … … 48 49 #endif 49 50 50 static inline long long int rdtscl(void) { 51 #if defined( __i386 ) || defined( __x86_64 ) 52 unsigned int lo, hi; 53 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 54 return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 55 #elif defined( __aarch64__ ) || defined( __arm__ ) 56 // https://github.com/google/benchmark/blob/v1.1.0/src/cycleclock.h#L116 57 long long int virtual_timer_value; 58 asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); 59 return virtual_timer_value; 60 #else 61 #error unsupported hardware architecture 62 #endif 51 static inline long long rdtscl(void) { 52 unsigned int lo, hi; 53 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 54 return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 63 55 } -
libcfa/src/bits/locks.hfa
reef8dfb rbdfc032 10 10 // Created On : Tue Oct 31 15:14:38 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Aug 12 14:18:07 202013 // Update Count : 1 312 // Last Modified On : Sat Aug 11 15:42:24 2018 13 // Update Count : 10 14 14 // 15 15 … … 27 27 28 28 // pause to prevent excess processor bus usage 29 #if defined( __i386 ) || defined( __x86_64 ) 29 #if defined( __sparc ) 30 #define Pause() __asm__ __volatile__ ( "rd %ccr,%g0" ) 31 #elif defined( __i386 ) || defined( __x86_64 ) 30 32 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 31 33 #elif defined( __ARM_ARCH ) 32 #define Pause() __asm__ __volatile__ ( " YIELD" : : : )34 #define Pause() __asm__ __volatile__ ( "nop" : : : ) 33 35 #else 34 36 #error unsupported architecture … … 52 54 53 55 #ifdef __CFA_DEBUG__ 54 void __cfaabi_dbg_record _lock(__spinlock_t & this, const char prev_name[]);56 void __cfaabi_dbg_record(__spinlock_t & this, const char * prev_name); 55 57 #else 56 #define __cfaabi_dbg_record _lock(x, y)58 #define __cfaabi_dbg_record(x, y) 57 59 #endif 58 60 } 61 62 extern void yield( unsigned int ); 59 63 60 64 static inline void ?{}( __spinlock_t & this ) { … … 64 68 // Lock the spinlock, return false if already acquired 65 69 static inline bool try_lock ( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) { 66 disable_interrupts();67 70 bool result = (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0); 68 71 if( result ) { 69 __cfaabi_dbg_record_lock( this, caller ); 70 } else { 71 enable_interrupts_noPoll(); 72 disable_interrupts(); 73 __cfaabi_dbg_record( this, caller ); 72 74 } 73 75 return result; … … 81 83 #endif 82 84 83 disable_interrupts();84 85 for ( unsigned int i = 1;; i += 1 ) { 85 86 if ( (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0) ) break; … … 97 98 #endif 98 99 } 99 __cfaabi_dbg_record_lock( this, caller ); 100 disable_interrupts(); 101 __cfaabi_dbg_record( this, caller ); 100 102 } 101 103 102 104 static inline void unlock( __spinlock_t & this ) { 105 enable_interrupts_noPoll(); 103 106 __atomic_clear( &this.lock, __ATOMIC_RELEASE ); 104 enable_interrupts_noPoll();105 107 } 106 108 … … 110 112 #endif 111 113 112 extern "C" {113 char * strerror(int);114 }115 #define CHECKED(x) { int err = x; if( err != 0 ) abort("KERNEL ERROR: Operation \"" #x "\" return error %d - %s\n", err, strerror(err)); }116 117 114 struct __bin_sem_t { 115 bool signaled; 118 116 pthread_mutex_t lock; 119 117 pthread_cond_t cond; 120 int val;121 118 }; 122 119 123 120 static inline void ?{}(__bin_sem_t & this) with( this ) { 124 // Create the mutex with error checking 125 pthread_mutexattr_t mattr; 126 pthread_mutexattr_init( &mattr ); 127 pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP); 128 pthread_mutex_init(&lock, &mattr); 129 130 pthread_cond_init (&cond, (const pthread_condattr_t *)0p); // workaround trac#208: cast should not be required 131 val = 0; 121 signaled = false; 122 pthread_mutex_init(&lock, NULL); 123 pthread_cond_init (&cond, NULL); 132 124 } 133 125 134 126 static inline void ^?{}(__bin_sem_t & this) with( this ) { 135 CHECKED( pthread_mutex_destroy(&lock));136 CHECKED( pthread_cond_destroy (&cond));127 pthread_mutex_destroy(&lock); 128 pthread_cond_destroy (&cond); 137 129 } 138 130 139 131 static inline void wait(__bin_sem_t & this) with( this ) { 140 132 verify(__cfaabi_dbg_in_kernel()); 141 CHECKED( pthread_mutex_lock(&lock));142 while(val < 1) {133 pthread_mutex_lock(&lock); 134 if(!signaled) { // this must be a loop, not if! 143 135 pthread_cond_wait(&cond, &lock); 144 136 } 145 val -= 1;146 CHECKED( pthread_mutex_unlock(&lock));137 signaled = false; 138 pthread_mutex_unlock(&lock); 147 139 } 148 140 149 static inline boolpost(__bin_sem_t & this) with( this ) {150 bool needs_signal = false;141 static inline void post(__bin_sem_t & this) with( this ) { 142 verify(__cfaabi_dbg_in_kernel()); 151 143 152 CHECKED( pthread_mutex_lock(&lock) ); 153 if(val < 1) { 154 val += 1; 155 pthread_cond_signal(&cond); 156 needs_signal = true; 157 } 158 CHECKED( pthread_mutex_unlock(&lock) ); 144 pthread_mutex_lock(&lock); 145 bool needs_signal = !signaled; 146 signaled = true; 147 pthread_mutex_unlock(&lock); 159 148 160 return needs_signal; 161 } 162 163 #undef CHECKED 164 165 struct $thread; 166 extern void park( void ); 167 extern void unpark( struct $thread * this ); 168 static inline struct $thread * active_thread (); 169 170 // Semaphore which only supports a single thread 171 struct single_sem { 172 struct $thread * volatile ptr; 173 }; 174 175 static inline { 176 void ?{}(single_sem & this) { 177 this.ptr = 0p; 178 } 179 180 void ^?{}(single_sem &) {} 181 182 bool wait(single_sem & this) { 183 for() { 184 struct $thread * expected = this.ptr; 185 if(expected == 1p) { 186 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 187 return false; 188 } 189 } 190 else { 191 /* paranoid */ verify( expected == 0p ); 192 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 193 park(); 194 return true; 195 } 196 } 197 198 } 199 } 200 201 bool post(single_sem & this) { 202 for() { 203 struct $thread * expected = this.ptr; 204 if(expected == 1p) return false; 205 if(expected == 0p) { 206 if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 207 return false; 208 } 209 } 210 else { 211 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 212 unpark( expected ); 213 return true; 214 } 215 } 216 } 217 } 218 } 219 220 // Synchronozation primitive which only supports a single thread and one post 221 // Similar to a binary semaphore with a 'one shot' semantic 222 // is expected to be discarded after each party call their side 223 struct oneshot { 224 // Internal state : 225 // 0p : is initial state (wait will block) 226 // 1p : fulfilled (wait won't block) 227 // any thread : a thread is currently waiting 228 struct $thread * volatile ptr; 229 }; 230 231 static inline { 232 void ?{}(oneshot & this) { 233 this.ptr = 0p; 234 } 235 236 void ^?{}(oneshot &) {} 237 238 // Wait for the post, return immidiately if it already happened. 239 // return true if the thread was parked 240 bool wait(oneshot & this) { 241 for() { 242 struct $thread * expected = this.ptr; 243 if(expected == 1p) return false; 244 /* paranoid */ verify( expected == 0p ); 245 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 246 park(); 247 /* paranoid */ verify( this.ptr == 1p ); 248 return true; 249 } 250 } 251 } 252 253 // Mark as fulfilled, wake thread if needed 254 // return true if a thread was unparked 255 bool post(oneshot & this) { 256 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 257 if( got == 0p ) return false; 258 unpark( got ); 259 return true; 260 } 261 } 262 263 // base types for future to build upon 264 // It is based on the 'oneshot' type to allow multiple futures 265 // to block on the same instance, permitting users to block a single 266 // thread on "any of" [a given set of] futures. 267 // does not support multiple threads waiting on the same future 268 struct future_t { 269 // Internal state : 270 // 0p : is initial state (wait will block) 271 // 1p : fulfilled (wait won't block) 272 // 2p : in progress () 273 // 3p : abandoned, server should delete 274 // any oneshot : a context has been setup to wait, a thread could wait on it 275 struct oneshot * volatile ptr; 276 }; 277 278 static inline { 279 void ?{}(future_t & this) { 280 this.ptr = 0p; 281 } 282 283 void ^?{}(future_t &) {} 284 285 void reset(future_t & this) { 286 // needs to be in 0p or 1p 287 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 288 } 289 290 // check if the future is available 291 bool available( future_t & this ) { 292 return this.ptr == 1p; 293 } 294 295 // Prepare the future to be waited on 296 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 297 bool setup( future_t & this, oneshot & wait_ctx ) { 298 /* paranoid */ verify( wait_ctx.ptr == 0p ); 299 // The future needs to set the wait context 300 for() { 301 struct oneshot * expected = this.ptr; 302 // Is the future already fulfilled? 303 if(expected == 1p) return false; // Yes, just return false (didn't block) 304 305 // The future is not fulfilled, try to setup the wait context 306 /* paranoid */ verify( expected == 0p ); 307 if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 308 return true; 309 } 310 } 311 } 312 313 // Stop waiting on a future 314 // When multiple futures are waited for together in "any of" pattern 315 // futures that weren't fulfilled before the thread woke up 316 // should retract the wait ctx 317 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 318 void retract( future_t & this, oneshot & wait_ctx ) { 319 // Remove the wait context 320 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 321 322 // got == 0p: future was never actually setup, just return 323 if( got == 0p ) return; 324 325 // got == wait_ctx: since fulfil does an atomic_swap, 326 // if we got back the original then no one else saw context 327 // It is safe to delete (which could happen after the return) 328 if( got == &wait_ctx ) return; 329 330 // got == 1p: the future is ready and the context was fully consumed 331 // the server won't use the pointer again 332 // It is safe to delete (which could happen after the return) 333 if( got == 1p ) return; 334 335 // got == 2p: the future is ready but the context hasn't fully been consumed 336 // spin until it is safe to move on 337 if( got == 2p ) { 338 while( this.ptr != 1p ) Pause(); 339 return; 340 } 341 342 // got == any thing else, something wen't wrong here, abort 343 abort("Future in unexpected state"); 344 } 345 346 // Mark the future as abandoned, meaning it will be deleted by the server 347 bool abandon( future_t & this ) { 348 /* paranoid */ verify( this.ptr != 3p ); 349 350 // Mark the future as abandonned 351 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST); 352 353 // If the future isn't already fulfilled, let the server delete it 354 if( got == 0p ) return false; 355 356 // got == 2p: the future is ready but the context hasn't fully been consumed 357 // spin until it is safe to move on 358 if( got == 2p ) { 359 while( this.ptr != 1p ) Pause(); 360 got = 1p; 361 } 362 363 // The future is completed delete it now 364 /* paranoid */ verify( this.ptr != 1p ); 365 free( &this ); 366 return true; 367 } 368 369 // from the server side, mark the future as fulfilled 370 // delete it if needed 371 bool fulfil( future_t & this ) { 372 for() { 373 struct oneshot * expected = this.ptr; 374 // was this abandoned? 375 #if defined(__GNUC__) && __GNUC__ >= 7 376 #pragma GCC diagnostic push 377 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 378 #endif 379 if( expected == 3p ) { free( &this ); return false; } 380 #if defined(__GNUC__) && __GNUC__ >= 7 381 #pragma GCC diagnostic pop 382 #endif 383 384 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen 385 /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 386 387 // If there is a wait context, we need to consume it and mark it as consumed after 388 // If there is no context then we can skip the in progress phase 389 struct oneshot * want = expected == 0p ? 1p : 2p; 390 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 391 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; } 392 bool ret = post( *expected ); 393 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 394 return ret; 395 } 396 } 397 398 } 399 400 // Wait for the future to be fulfilled 401 bool wait( future_t & this ) { 402 oneshot temp; 403 if( !setup(this, temp) ) return false; 404 405 // Wait context is setup, just wait on it 406 bool ret = wait( temp ); 407 408 // Wait for the future to tru 409 while( this.ptr == 2p ) Pause(); 410 // Make sure the state makes sense 411 // Should be fulfilled, could be in progress but it's out of date if so 412 // since if that is the case, the oneshot was fulfilled (unparking this thread) 413 // and the oneshot should not be needed any more 414 __attribute__((unused)) struct oneshot * was = this.ptr; 415 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was ); 416 417 // Mark the future as fulfilled, to be consistent 418 // with potential calls to avail 419 // this.ptr = 1p; 420 return ret; 421 } 149 if (needs_signal) 150 pthread_cond_signal(&cond); 422 151 } 423 152 #endif -
libcfa/src/bits/signal.hfa
reef8dfb rbdfc032 19 19 #include "bits/defs.hfa" 20 20 21 extern "C" { 21 22 #include <errno.h> 22 23 #define __USE_GNU … … 25 26 #include <stdlib.h> 26 27 #include <string.h> 28 } 27 29 28 30 // Short hands for signal context information … … 52 54 sig, handler, flags, errno, strerror( errno ) 53 55 ); 54 _ Exit( EXIT_FAILURE );56 _exit( EXIT_FAILURE ); 55 57 } // if 56 58 } -
libcfa/src/common.hfa
reef8dfb rbdfc032 10 10 // Created On : Wed Jul 11 17:54:36 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 15 08:51:29 202013 // Update Count : 1412 // Last Modified On : Thu Jul 12 08:02:18 2018 13 // Update Count : 5 14 14 // 15 15 … … 67 67 68 68 static inline { 69 char min( char t1, char t2 ) { return t1 < t2 ? t1 : t2; } // optimization70 intptr_t min( intptr_t t1, intptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization71 uintptr_t min( uintptr_t t1, uintptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization72 69 forall( otype T | { int ?<?( T, T ); } ) 73 70 T min( T t1, T t2 ) { return t1 < t2 ? t1 : t2; } 74 71 75 char max( char t1, char t2 ) { return t1 > t2 ? t1 : t2; } // optimization76 intptr_t max( intptr_t t1, intptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization77 uintptr_t max( uintptr_t t1, uintptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization78 72 forall( otype T | { int ?>?( T, T ); } ) 79 73 T max( T t1, T t2 ) { return t1 > t2 ? t1 : t2; } -
libcfa/src/concurrency/CtxSwitch-i386.S
reef8dfb rbdfc032 10 10 // Created On : Tue Dec 6 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 6 18:23:37 2020 13 // Update Count : 5 12 // Last Modified On : Fri Jul 21 22:29:25 2017 13 // Update Count : 1 14 // 15 // This library is free software; you can redistribute it and/or modify it 16 // under the terms of the GNU Lesser General Public License as published by the 17 // Free Software Foundation; either version 2.1 of the License, or (at your 18 // option) any later version. 19 // 20 // This library is distributed in the hope that it will be useful, but WITHOUT 21 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 22 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 23 // for more details. 24 // 25 // You should have received a copy of the GNU Lesser General Public License 26 // along with this library. 14 27 // 15 28 16 // The context switch routine requires the initial the stack of a thread to 17 // look like the thread has saved its context in the normal manner. 29 // This context switch routine depends on the fact that the stack of a new 30 // thread has been set up to look like the thread has saved its context in 31 // the normal manner. 32 // 33 // void CtxSwitch( machine_context *from, machine_context *to ); 18 34 19 // Offsets must synchronized with the __stack_context_t in invoke.h. 35 // Offsets in the context structure. This needs to be synchronized with the 36 // high level code a little better. 20 37 21 38 #define PTR_BYTE 4 22 39 #define SP_OFFSET ( 0 * PTR_BYTE ) 23 40 #define FP_OFFSET ( 1 * PTR_BYTE ) 41 #define PC_OFFSET ( 2 * PTR_BYTE ) 24 42 25 // Context switch between coroutines/tasks.26 // void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) ;27 // Arguments "from" in register 4(%esp), "to" in register 20(%esp)28 29 .file "CtxSwitch-i386.S"30 43 .text 31 44 .align 2 32 .glob al __cfactx_switch33 .type __cfactx_switch, @function34 __cfactx_switch:45 .globl CtxSwitch 46 .type CtxSwitch, @function 47 CtxSwitch: 35 48 36 49 // Copy the "from" context argument from the stack to register eax 37 // Return address is at 0(%esp), with parameters following .50 // Return address is at 0(%esp), with parameters following 38 51 39 52 movl 4(%esp),%eax … … 50 63 movl %ebp,FP_OFFSET(%eax) 51 64 52 // Copy the "to" context argument from the stack to register eax . Having53 // pushed 3 words (= 12 bytes) on the stack, the argument is now at54 // 8 + 12 = 20(%esp).65 // Copy the "to" context argument from the stack to register eax 66 // Having pushed three words (= 12 bytes) on the stack, the 67 // argument is now at 8 + 12 = 20(%esp) 55 68 56 69 movl 20(%esp),%eax … … 70 83 71 84 ret 72 .size __cfactx_switch, .-__cfactx_switch85 .size CtxSwitch, .-CtxSwitch 73 86 74 87 // Local Variables: // -
libcfa/src/concurrency/CtxSwitch-x86_64.S
reef8dfb rbdfc032 7 7 // CtxSwitch-x86_64.S -- 8 8 // 9 // Author : Peter A. Buhr10 // Created On : Mon Aug 10 08:10:26 20209 // Author : Thierry Delisle 10 // Created On : Mon Nov 28 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Oct 24 14:36:25 2020 13 // Update Count : 10 12 // Last Modified On : Fri Jul 21 22:28:11 2017 13 // Update Count : 1 14 // 15 // This library is free software; you can redistribute it and/or modify it 16 // under the terms of the GNU Lesser General Public License as published by the 17 // Free Software Foundation; either version 2.1 of the License, or (at your 18 // option) any later version. 19 // 20 // This library is distributed in the hope that it will be useful, but WITHOUT 21 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 22 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 23 // for more details. 24 // 25 // You should have received a copy of the GNU Lesser General Public License 26 // along with this library. 14 27 // 15 28 16 // The context switch routine requires the initial the stack of a thread to 17 // look like the thread has saved its context in the normal manner. 29 // This context switch routine depends on the fact that the stack of a new 30 // thread has been set up to look like the thread has saved its context in 31 // the normal manner. 32 // 33 // void CtxSwitch( machine_context *from, machine_context *to ); 18 34 19 // Offsets must synchronized with the __stack_context_t in invoke.h. 35 // Offsets in the context structure. This needs to be synchronized with the 36 // high level code a little better. 20 37 21 38 #define PTR_BYTE 8 … … 23 40 #define FP_OFFSET ( 1 * PTR_BYTE ) 24 41 25 // Context switch between coroutines/tasks. 26 // void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) ; 27 // Arguments "from" in register rdi, "to" in register rsi. 28 29 .file "CtxSwitch-x86_64.S" 42 //----------------------------------------------------------------------------- 43 // Regular context switch routine which enables switching from one context to anouther 30 44 .text 31 45 .align 2 32 .glob al __cfactx_switch33 .type __cfactx_switch, @function34 __cfactx_switch:46 .globl CtxSwitch 47 .type CtxSwitch, @function 48 CtxSwitch: 35 49 36 50 // Save volatile registers on the stack. … … 63 77 64 78 ret 65 .size __cfactx_switch, .-__cfactx_switch79 .size CtxSwitch, .-CtxSwitch 66 80 67 // Stub to create new stacks which can be context switched to 68 // void __cfactx_invoke_stub( void ); 69 81 //----------------------------------------------------------------------------- 82 // Stub used to create new stacks which are ready to be context switched to 70 83 .text 71 84 .align 2 72 .glob al __cfactx_invoke_stub73 .type __cfactx_invoke_stub, @function74 __cfactx_invoke_stub:75 movq %rbx, %rdi // move main and this to first two arguments85 .globl CtxInvokeStub 86 .type CtxInvokeStub, @function 87 CtxInvokeStub: 88 movq %rbx, %rdi 76 89 movq %r12, %rsi 77 jmp *%r13 // jmp to invoke78 .size __cfactx_invoke_stub, .-__cfactx_invoke_stub90 jmp *%r13 91 .size CtxInvokeStub, .-CtxInvokeStub 79 92 80 93 // Local Variables: // 81 // mode: asm//94 // mode: c // 82 95 // tab-width: 4 // 83 96 // End: // -
libcfa/src/concurrency/alarm.cfa
reef8dfb rbdfc032 10 10 // Created On : Fri Jun 2 11:31:25 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 17 16:11:35202013 // Update Count : 7512 // Last Modified On : Sun Jan 5 08:41:36 2020 13 // Update Count : 69 14 14 // 15 15 16 16 #define __cforall_thread__ 17 17 18 extern "C" { 18 19 #include <errno.h> 19 20 #include <stdio.h> 21 #include <string.h> 20 22 #include <unistd.h> 21 #include <string.h>22 23 #include <sys/time.h> 24 } 23 25 24 26 #include "alarm.hfa" 25 #include "kernel /fwd.hfa"27 #include "kernel_private.hfa" 26 28 #include "preemption.hfa" 27 29 … … 45 47 //============================================================================================= 46 48 47 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period) with( this ) {49 void ?{}( alarm_node_t & this, thread_desc * thrd, Time alarm, Duration period ) with( this ) { 48 50 this.thrd = thrd; 49 51 this.alarm = alarm; 50 52 this.period = period; 53 next = 0; 51 54 set = false; 52 type = User;55 kernel_alarm = false; 53 56 } 54 57 55 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ) with( this ) {58 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ) with( this ) { 56 59 this.proc = proc; 57 60 this.alarm = alarm; 58 61 this.period = period; 62 next = 0; 59 63 set = false; 60 type = Kernel; 61 } 62 void ?{}( alarm_node_t & this, Alarm_Callback callback, Time alarm, Duration period ) with( this ) { 63 this.alarm = alarm; 64 this.period = period; 65 this.callback = callback; 66 set = false; 67 type = Callback; 64 kernel_alarm = true; 68 65 } 69 66 … … 74 71 } 75 72 76 void insert( alarm_list_t * this, alarm_node_t * n ) { 77 alarm_node_t * it = & (*this)`first; 78 while( it && (n->alarm > it->alarm) ) { 79 it = & (*it)`next; 80 } 81 if ( it ) { 82 insert_before( *it, *n ); 83 } else { 84 insert_last(*this, *n); 73 #if !defined(NDEBUG) && (defined(__CFA_DEBUG__) || defined(__CFA_VERIFY__)) 74 bool validate( alarm_list_t * this ) { 75 alarm_node_t ** it = &this->head; 76 while( (*it) ) { 77 it = &(*it)->next; 85 78 } 86 79 87 verify( validate( *this ) ); 80 return it == this->tail; 81 } 82 #endif 83 84 static inline void insert_at( alarm_list_t * this, alarm_node_t * n, __alarm_it_t p ) { 85 verify( !n->next ); 86 if( p == this->tail ) { 87 this->tail = &n->next; 88 } 89 else { 90 n->next = *p; 91 } 92 *p = n; 93 94 verify( validate( this ) ); 95 } 96 97 void insert( alarm_list_t * this, alarm_node_t * n ) { 98 alarm_node_t ** it = &this->head; 99 while( (*it) && (n->alarm > (*it)->alarm) ) { 100 it = &(*it)->next; 101 } 102 103 insert_at( this, n, it ); 104 105 verify( validate( this ) ); 88 106 } 89 107 90 108 alarm_node_t * pop( alarm_list_t * this ) { 91 verify( validate( *this ) ); 92 alarm_node_t * head = & (*this)`first; 109 alarm_node_t * head = this->head; 93 110 if( head ) { 94 remove(*head); 111 this->head = head->next; 112 if( !head->next ) { 113 this->tail = &this->head; 114 } 115 head->next = 0p; 95 116 } 96 verify( validate( *this ) );117 verify( validate( this ) ); 97 118 return head; 98 119 } 99 120 121 static inline void remove_at( alarm_list_t * this, alarm_node_t * n, __alarm_it_t it ) { 122 verify( it ); 123 verify( (*it) == n ); 124 125 (*it) = n->next; 126 if( !n-> next ) { 127 this->tail = it; 128 } 129 n->next = 0p; 130 131 verify( validate( this ) ); 132 } 133 134 static inline void remove( alarm_list_t * this, alarm_node_t * n ) { 135 alarm_node_t ** it = &this->head; 136 while( (*it) && (*it) != n ) { 137 it = &(*it)->next; 138 } 139 140 verify( validate( this ) ); 141 142 if( *it ) { remove_at( this, n, it ); } 143 144 verify( validate( this ) ); 145 } 146 100 147 void register_self( alarm_node_t * this ) { 101 alarm_list_t & alarms =event_kernel->alarms;148 alarm_list_t * alarms = &event_kernel->alarms; 102 149 103 150 disable_interrupts(); … … 105 152 { 106 153 verify( validate( alarms ) ); 107 bool first = ! & alarms`first;154 bool first = !alarms->head; 108 155 109 insert( &alarms, this );156 insert( alarms, this ); 110 157 if( first ) { 111 __kernel_set_timer( alarms `first.alarm - __kernel_get_time() );158 __kernel_set_timer( alarms->head->alarm - __kernel_get_time() ); 112 159 } 113 160 } … … 121 168 lock( event_kernel->lock __cfaabi_dbg_ctx2 ); 122 169 { 123 verify( validate( event_kernel->alarms ) );124 remove( *this );170 verify( validate( &event_kernel->alarms ) ); 171 remove( &event_kernel->alarms, this ); 125 172 } 126 173 unlock( event_kernel->lock ); … … 129 176 } 130 177 131 //=============================================================================================132 // Utilities133 //=============================================================================================134 135 void sleep( Duration duration ) {136 alarm_node_t node = { active_thread(), __kernel_get_time() + duration, 0`s };137 138 register_self( &node );139 park();140 141 /* paranoid */ verify( !node.set );142 /* paranoid */ verify( & node`next == 0p );143 /* paranoid */ verify( & node`prev == 0p );144 }145 146 178 // Local Variables: // 147 179 // mode: c // -
libcfa/src/concurrency/alarm.hfa
reef8dfb rbdfc032 23 23 #include "time.hfa" 24 24 25 #include "containers/list.hfa" 26 27 struct $thread; 25 struct thread_desc; 28 26 struct processor; 29 27 … … 39 37 //============================================================================================= 40 38 41 enum alarm_type{ Kernel = 0, User = 1, Callback = 2 };42 43 struct alarm_node_t;44 45 typedef void (*Alarm_Callback)(alarm_node_t & );46 47 39 struct alarm_node_t { 48 40 Time alarm; // time when alarm goes off 49 41 Duration period; // if > 0 => period of alarm 50 51 DLISTED_MGD_IMPL_IN(alarm_node_t) 42 alarm_node_t * next; // intrusive link list field 52 43 53 44 union { 54 $thread * thrd; // thrd who created event 55 processor * proc; // proc who created event 56 Alarm_Callback callback; // callback to handle event 45 thread_desc * thrd; // thrd who created event 46 processor * proc; // proc who created event 57 47 }; 58 48 59 49 bool set :1; // whether or not the alarm has be registered 60 enum alarm_type type; // true if this is not a user defined alarm50 bool kernel_alarm :1; // true if this is not a user defined alarm 61 51 }; 62 DLISTED_MGD_IMPL_OUT(alarm_node_t)63 52 64 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period ); 53 typedef alarm_node_t ** __alarm_it_t; 54 55 void ?{}( alarm_node_t & this, thread_desc * thrd, Time alarm, Duration period ); 65 56 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ); 66 void ?{}( alarm_node_t & this, Alarm_Callback callback, Time alarm, Duration period );67 57 void ^?{}( alarm_node_t & this ); 68 58 69 typedef dlist(alarm_node_t, alarm_node_t) alarm_list_t; 59 struct alarm_list_t { 60 alarm_node_t * head; 61 __alarm_it_t tail; 62 }; 63 64 static inline void ?{}( alarm_list_t & this ) with( this ) { 65 head = 0; 66 tail = &head; 67 } 70 68 71 69 void insert( alarm_list_t * this, alarm_node_t * n ); -
libcfa/src/concurrency/coroutine.cfa
reef8dfb rbdfc032 10 10 // Created On : Mon Nov 28 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T ue Dec 15 12:06:04 202013 // Update Count : 2312 // Last Modified On : Thu Dec 5 14:37:29 2019 13 // Update Count : 15 14 14 // 15 15 … … 18 18 #include "coroutine.hfa" 19 19 20 extern "C" { 20 21 #include <stddef.h> 21 22 #include <malloc.h> … … 23 24 #include <string.h> 24 25 #include <unistd.h> 25 #include <sys/mman.h> // mprotect 26 // use this define to make unwind.h play nice, definetely a hack 27 #define HIDE_EXPORTS 26 28 #include <unwind.h> 29 #undef HIDE_EXPORTS 30 #include <sys/mman.h> 31 } 27 32 28 33 #include "kernel_private.hfa" 29 #include "exception.hfa"30 #include "math.hfa"31 32 #define CFA_COROUTINE_USE_MMAP 033 34 34 35 #define __CFA_INVOKE_PRIVATE__ … … 36 37 37 38 extern "C" { 38 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct $coroutine*) __attribute__ ((__noreturn__));39 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc *) __attribute__ ((__noreturn__)); 39 40 static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) __attribute__ ((__noreturn__)); 40 41 static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) { … … 46 47 47 48 //----------------------------------------------------------------------------- 48 FORALL_DATA_INSTANCE(CoroutineCancelled, (dtype coroutine_t), (coroutine_t))49 50 forall(dtype T)51 void mark_exception(CoroutineCancelled(T) *) {}52 53 forall(dtype T)54 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) {55 dst->virtual_table = src->virtual_table;56 dst->the_coroutine = src->the_coroutine;57 dst->the_exception = src->the_exception;58 }59 60 forall(dtype T)61 const char * msg(CoroutineCancelled(T) *) {62 return "CoroutineCancelled(...)";63 }64 65 // This code should not be inlined. It is the error path on resume.66 forall(dtype T | is_coroutine(T))67 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ) {68 verify( desc->cancellation );69 desc->state = Cancelled;70 exception_t * except = __cfaehm_cancellation_exception( desc->cancellation );71 72 // TODO: Remove explitate vtable set once trac#186 is fixed.73 CoroutineCancelled(T) except;74 except.virtual_table = &get_exception_vtable(&except);75 except.the_coroutine = &cor;76 except.the_exception = except;77 throwResume except;78 79 except->virtual_table->free( except );80 free( desc->cancellation );81 desc->cancellation = 0p;82 }83 84 //-----------------------------------------------------------------------------85 49 // Global state variables 86 50 87 51 // minimum feasible stack size in bytes 88 static const size_t MinStackSize = 1000; 52 #define MinStackSize 1000 89 53 extern size_t __page_size; // architecture pagesize HACK, should go in proper runtime singleton 90 extern int __map_prot;91 54 92 55 void __stack_prepare( __stack_info_t * this, size_t create_size ); 93 void __stack_clean ( __stack_info_t * this );94 56 95 57 //----------------------------------------------------------------------------- … … 112 74 bool userStack = ((intptr_t)this.storage & 0x1) != 0; 113 75 if ( ! userStack && this.storage ) { 114 __stack_clean( &this ); 115 } 116 } 117 118 void ?{}( $coroutine & this, const char name[], void * storage, size_t storageSize ) with( this ) { 76 __attribute__((may_alias)) intptr_t * istorage = (intptr_t *)&this.storage; 77 *istorage &= (intptr_t)-1; 78 79 void * storage = this.storage->limit; 80 __cfaabi_dbg_debug_do( 81 storage = (char*)(storage) - __page_size; 82 if ( mprotect( storage, __page_size, PROT_READ | PROT_WRITE ) == -1 ) { 83 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) ); 84 } 85 ); 86 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage); 87 free( storage ); 88 } 89 } 90 91 void ?{}( coroutine_desc & this, const char * name, void * storage, size_t storageSize ) with( this ) { 119 92 (this.context){0p, 0p}; 120 93 (this.stack){storage, storageSize}; … … 126 99 } 127 100 128 void ^?{}( $coroutine& this) {101 void ^?{}(coroutine_desc& this) { 129 102 if(this.state != Halted && this.state != Start && this.state != Primed) { 130 $coroutine * src = active_coroutine();131 $coroutine* dst = &this;103 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 104 coroutine_desc * dst = &this; 132 105 133 106 struct _Unwind_Exception storage; … … 142 115 } 143 116 144 $ctx_switch( src, dst );117 CoroutineCtxSwitch( src, dst ); 145 118 } 146 119 } … … 150 123 forall(dtype T | is_coroutine(T)) 151 124 void prime(T& cor) { 152 $coroutine* this = get_coroutine(cor);125 coroutine_desc* this = get_coroutine(cor); 153 126 assert(this->state == Start); 154 127 … … 161 134 assert(__page_size != 0l); 162 135 size_t size = libCeiling( storageSize, 16 ) + stack_data_size; 163 size = ceiling(size, __page_size);164 136 165 137 // If we are running debug, we also need to allocate a guardpage to catch stack overflows. 166 138 void * storage; 167 #if CFA_COROUTINE_USE_MMAP 168 storage = mmap(0p, size + __page_size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 169 if(storage == ((void*)-1)) { 170 abort( "coroutine stack creation : internal error, mmap failure, error(%d) %s.", errno, strerror( errno ) ); 139 __cfaabi_dbg_debug_do( 140 storage = memalign( __page_size, size + __page_size ); 141 ); 142 __cfaabi_dbg_no_debug_do( 143 storage = (void*)malloc(size); 144 ); 145 146 __cfaabi_dbg_print_safe("Kernel : Created stack %p of size %zu\n", storage, size); 147 __cfaabi_dbg_debug_do( 148 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) { 149 abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) ); 171 150 } 172 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {173 abort( "coroutine stack creation : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) );174 } // if175 151 storage = (void *)(((intptr_t)storage) + __page_size); 176 #else 177 __cfaabi_dbg_debug_do( 178 storage = memalign( __page_size, size + __page_size ); 179 ); 180 __cfaabi_dbg_no_debug_do( 181 storage = (void*)malloc(size); 182 ); 183 184 __cfaabi_dbg_debug_do( 185 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) { 186 abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) ); 187 } 188 storage = (void *)(((intptr_t)storage) + __page_size); 189 ); 190 #endif 191 __cfaabi_dbg_print_safe("Kernel : Created stack %p of size %zu\n", storage, size); 152 ); 192 153 193 154 verify( ((intptr_t)storage & (libAlign() - 1)) == 0ul ); 194 155 return [storage, size]; 195 }196 197 void __stack_clean ( __stack_info_t * this ) {198 size_t size = ((intptr_t)this->storage->base) - ((intptr_t)this->storage->limit) + sizeof(__stack_t);199 void * storage = this->storage->limit;200 201 #if CFA_COROUTINE_USE_MMAP202 storage = (void *)(((intptr_t)storage) - __page_size);203 if(munmap(storage, size + __page_size) == -1) {204 abort( "coroutine stack destruction : internal error, munmap failure, error(%d) %s.", errno, strerror( errno ) );205 }206 #else207 __cfaabi_dbg_debug_do(208 storage = (char*)(storage) - __page_size;209 if ( mprotect( storage, __page_size, __map_prot ) == -1 ) {210 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );211 }212 );213 214 free( storage );215 #endif216 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage);217 156 } 218 157 … … 236 175 size = libFloor(create_size - stack_data_size - diff, libAlign()); 237 176 } // if 238 assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of % zd bytes for a stack.", size, MinStackSize );239 240 this->storage = (__stack_t *)((intptr_t)storage + size - sizeof(__stack_t));177 assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize ); 178 179 this->storage = (__stack_t *)((intptr_t)storage + size); 241 180 this->storage->limit = storage; 242 this->storage->base = (void*)((intptr_t)storage + size - sizeof(__stack_t)); 243 this->storage->exception_context.top_resume = 0p; 244 this->storage->exception_context.current_exception = 0p; 181 this->storage->base = (void*)((intptr_t)storage + size); 245 182 __attribute__((may_alias)) intptr_t * istorage = (intptr_t*)&this->storage; 246 183 *istorage |= userStack ? 0x1 : 0x0; … … 250 187 // is not inline (We can't inline Cforall in C) 251 188 extern "C" { 252 void __ cfactx_cor_leave( struct $coroutine* src ) {253 $coroutine* starter = src->cancellation != 0 ? src->last : src->starter;189 void __leave_coroutine( struct coroutine_desc * src ) { 190 coroutine_desc * starter = src->cancellation != 0 ? src->last : src->starter; 254 191 255 192 src->state = Halted; … … 264 201 src->name, src, starter->name, starter ); 265 202 266 $ctx_switch( src, starter );267 } 268 269 struct $coroutine * __cfactx_cor_finish(void) {270 struct $coroutine * cor = active_coroutine();203 CoroutineCtxSwitch( src, starter ); 204 } 205 206 struct coroutine_desc * __finish_coroutine(void) { 207 struct coroutine_desc * cor = kernelTLS.this_thread->curr_cor; 271 208 272 209 if(cor->state == Primed) { 273 __cfactx_suspend();210 suspend(); 274 211 } 275 212 -
libcfa/src/concurrency/coroutine.hfa
reef8dfb rbdfc032 10 10 // Created On : Mon Nov 28 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 12:29:26 202013 // Update Count : 1 112 // Last Modified On : Tue Dec 3 22:47:58 2019 13 // Update Count : 10 14 14 // 15 15 … … 18 18 #include <assert.h> 19 19 #include "invoke.h" 20 #include "../exception.hfa"21 22 //-----------------------------------------------------------------------------23 // Exception thrown from resume when a coroutine stack is cancelled.24 FORALL_DATA_EXCEPTION(CoroutineCancelled, (dtype coroutine_t), (coroutine_t)) (25 coroutine_t * the_coroutine;26 exception_t * the_exception;27 );28 29 forall(dtype T)30 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src);31 32 forall(dtype T)33 const char * msg(CoroutineCancelled(T) *);34 20 35 21 //----------------------------------------------------------------------------- … … 37 23 // Anything that implements this trait can be resumed. 38 24 // Anything that is resumed is a coroutine. 39 trait is_coroutine(dtype T | IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) {40 void main(T & this);41 $coroutine* get_coroutine(T & this);25 trait is_coroutine(dtype T) { 26 void main(T & this); 27 coroutine_desc * get_coroutine(T & this); 42 28 }; 43 29 44 #define DECL_COROUTINE(X) static inline $coroutine* get_coroutine(X& this) { return &this.__cor; } void main(X& this)30 #define DECL_COROUTINE(X) static inline coroutine_desc* get_coroutine(X& this) { return &this.__cor; } void main(X& this) 45 31 46 32 //----------------------------------------------------------------------------- … … 49 35 // void ^?{}( coStack_t & this ); 50 36 51 void ?{}( $coroutine & this, const char name[], void * storage, size_t storageSize );52 void ^?{}( $coroutine& this );37 void ?{}( coroutine_desc & this, const char * name, void * storage, size_t storageSize ); 38 void ^?{}( coroutine_desc & this ); 53 39 54 static inline void ?{}( $coroutine& this) { this{ "Anonymous Coroutine", 0p, 0 }; }55 static inline void ?{}( $coroutine& this, size_t stackSize) { this{ "Anonymous Coroutine", 0p, stackSize }; }56 static inline void ?{}( $coroutine& this, void * storage, size_t storageSize ) { this{ "Anonymous Coroutine", storage, storageSize }; }57 static inline void ?{}( $coroutine & this, const char name[]) { this{ name, 0p, 0 }; }58 static inline void ?{}( $coroutine & this, const char name[], size_t stackSize ) { this{ name, 0p, stackSize }; }40 static inline void ?{}( coroutine_desc & this) { this{ "Anonymous Coroutine", 0p, 0 }; } 41 static inline void ?{}( coroutine_desc & this, size_t stackSize) { this{ "Anonymous Coroutine", 0p, stackSize }; } 42 static inline void ?{}( coroutine_desc & this, void * storage, size_t storageSize ) { this{ "Anonymous Coroutine", storage, storageSize }; } 43 static inline void ?{}( coroutine_desc & this, const char * name) { this{ name, 0p, 0 }; } 44 static inline void ?{}( coroutine_desc & this, const char * name, size_t stackSize ) { this{ name, 0p, stackSize }; } 59 45 60 46 //----------------------------------------------------------------------------- 61 47 // Public coroutine API 48 static inline void suspend(void); 49 50 forall(dtype T | is_coroutine(T)) 51 static inline T & resume(T & cor); 52 62 53 forall(dtype T | is_coroutine(T)) 63 54 void prime(T & cor); 64 55 65 static inline struct $coroutine * active_coroutine() { return active_thread()->curr_cor; }56 static inline struct coroutine_desc * active_coroutine() { return TL_GET( this_thread )->curr_cor; } 66 57 67 58 //----------------------------------------------------------------------------- … … 70 61 // Start coroutine routines 71 62 extern "C" { 72 void __cfactx_invoke_coroutine(void (*main)(void *), void * this);63 void CtxInvokeCoroutine(void (*main)(void *), void * this); 73 64 74 65 forall(dtype T) 75 void __cfactx_start(void (*main)(T &), struct $coroutine* cor, T & this, void (*invoke)(void (*main)(void *), void *));66 void CtxStart(void (*main)(T &), struct coroutine_desc * cor, T & this, void (*invoke)(void (*main)(void *), void *)); 76 67 77 extern void _ _cfactx_coroutine_unwind(struct _Unwind_Exception * storage, struct $coroutine*) __attribute__ ((__noreturn__));68 extern void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc *) __attribute__ ((__noreturn__)); 78 69 79 extern void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) asm ("__cfactx_switch");70 extern void CtxSwitch( struct __stack_context_t * from, struct __stack_context_t * to ) asm ("CtxSwitch"); 80 71 } 81 72 82 73 // Private wrappers for context switch and stack creation 83 74 // Wrapper for co 84 static inline void $ctx_switch( $coroutine * src, $coroutine * dst ) __attribute__((nonnull (1, 2))) {75 static inline void CoroutineCtxSwitch(coroutine_desc* src, coroutine_desc* dst) { 85 76 // set state of current coroutine to inactive 86 src->state = src->state == Halted ? Halted : Blocked;77 src->state = src->state == Halted ? Halted : Inactive; 87 78 88 79 // set new coroutine that task is executing 89 active_thread()->curr_cor = dst;80 TL_GET( this_thread )->curr_cor = dst; 90 81 91 82 // context switch to specified coroutine 92 83 verify( dst->context.SP ); 93 __cfactx_switch( &src->context, &dst->context );94 // when __cfactx_switch returns we are back in the src coroutine84 CtxSwitch( &src->context, &dst->context ); 85 // when CtxSwitch returns we are back in the src coroutine 95 86 96 87 // set state of new coroutine to active … … 98 89 99 90 if( unlikely(src->cancellation != 0p) ) { 100 _ _cfactx_coroutine_unwind(src->cancellation, src);91 _CtxCoroutine_Unwind(src->cancellation, src); 101 92 } 102 93 } 103 94 104 extern void __stack_prepare( __stack_info_t * this, size_t size /* ignored if storage already allocated */); 105 extern void __stack_clean ( __stack_info_t * this ); 106 95 extern void __stack_prepare ( __stack_info_t * this, size_t size /* ignored if storage already allocated */); 107 96 108 97 // Suspend implementation inlined for performance 109 extern "C" { 110 static inline void __cfactx_suspend(void) { 111 // optimization : read TLS once and reuse it 112 // Safety note: this is preemption safe since if 113 // preemption occurs after this line, the pointer 114 // will also migrate which means this value will 115 // stay in syn with the TLS 116 $coroutine * src = active_coroutine(); 98 static inline void suspend(void) { 99 // optimization : read TLS once and reuse it 100 // Safety note: this is preemption safe since if 101 // preemption occurs after this line, the pointer 102 // will also migrate which means this value will 103 // stay in syn with the TLS 104 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 117 105 118 assertf( src->last != 0,119 "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n"120 "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",121 src->name, src );122 assertf( src->last->state != Halted,123 "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n"124 "Possible cause is terminated coroutine's main routine has already returned.",125 src->name, src, src->last->name, src->last );106 assertf( src->last != 0, 107 "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n" 108 "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.", 109 src->name, src ); 110 assertf( src->last->state != Halted, 111 "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n" 112 "Possible cause is terminated coroutine's main routine has already returned.", 113 src->name, src, src->last->name, src->last ); 126 114 127 $ctx_switch( src, src->last ); 128 } 115 CoroutineCtxSwitch( src, src->last ); 129 116 } 130 131 forall(dtype T | is_coroutine(T))132 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc );133 117 134 118 // Resume implementation inlined for performance … … 140 124 // will also migrate which means this value will 141 125 // stay in syn with the TLS 142 $coroutine * src = active_coroutine();143 $coroutine* dst = get_coroutine(cor);126 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 127 coroutine_desc * dst = get_coroutine(cor); 144 128 145 129 if( unlikely(dst->context.SP == 0p) ) { 130 TL_GET( this_thread )->curr_cor = dst; 146 131 __stack_prepare(&dst->stack, 65000); 147 __cfactx_start(main, dst, cor, __cfactx_invoke_coroutine); 132 CtxStart(main, dst, cor, CtxInvokeCoroutine); 133 TL_GET( this_thread )->curr_cor = src; 148 134 } 149 135 … … 161 147 162 148 // always done for performance testing 163 $ctx_switch( src, dst ); 164 if ( unlikely(dst->cancellation) ) { 165 __cfaehm_cancelled_coroutine( cor, dst ); 166 } 149 CoroutineCtxSwitch( src, dst ); 167 150 168 151 return cor; 169 152 } 170 153 171 static inline void resume( $coroutine * dst ) __attribute__((nonnull (1))) {154 static inline void resume(coroutine_desc * dst) { 172 155 // optimization : read TLS once and reuse it 173 156 // Safety note: this is preemption safe since if … … 175 158 // will also migrate which means this value will 176 159 // stay in syn with the TLS 177 $coroutine * src = active_coroutine();160 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 178 161 179 162 // not resuming self ? … … 189 172 190 173 // always done for performance testing 191 $ctx_switch( src, dst );174 CoroutineCtxSwitch( src, dst ); 192 175 } 193 176 -
libcfa/src/concurrency/invoke.c
reef8dfb rbdfc032 10 10 // Created On : Tue Jan 17 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Oct 24 14:35:28 202013 // Update Count : 3212 // Last Modified On : Fri Feb 9 16:37:42 2018 13 // Update Count : 5 14 14 // 15 15 … … 29 29 // Called from the kernel when starting a coroutine or task so must switch back to user mode. 30 30 31 extern struct $coroutine * __cfactx_cor_finish(void); 32 extern void __cfactx_cor_leave ( struct $coroutine * ); 33 extern void __cfactx_thrd_leave(); 34 31 extern void __leave_coroutine ( struct coroutine_desc * ); 32 extern struct coroutine_desc * __finish_coroutine(void); 33 extern void __leave_thread_monitor(); 35 34 extern void disable_interrupts() OPTIONAL_THREAD; 36 35 extern void enable_interrupts( __cfaabi_dbg_ctx_param ); 37 36 38 void __cfactx_invoke_coroutine(37 void CtxInvokeCoroutine( 39 38 void (*main)(void *), 40 39 void *this 41 40 ) { 42 41 // Finish setting up the coroutine by setting its state 43 struct $coroutine * cor = __cfactx_cor_finish();42 struct coroutine_desc * cor = __finish_coroutine(); 44 43 45 44 // Call the main of the coroutine … … 47 46 48 47 //Final suspend, should never return 49 __ cfactx_cor_leave( cor );48 __leave_coroutine( cor ); 50 49 __cabi_abort( "Resumed dead coroutine" ); 51 50 } 52 51 53 static _Unwind_Reason_Code _ _cfactx_coroutine_unwindstop(52 static _Unwind_Reason_Code _CtxCoroutine_UnwindStop( 54 53 __attribute((__unused__)) int version, 55 54 _Unwind_Action actions, … … 62 61 // We finished unwinding the coroutine, 63 62 // leave it 64 __ cfactx_cor_leave( param );63 __leave_coroutine( param ); 65 64 __cabi_abort( "Resumed dead coroutine" ); 66 65 } … … 70 69 } 71 70 72 void _ _cfactx_coroutine_unwind(struct _Unwind_Exception * storage, struct $coroutine* cor) __attribute__ ((__noreturn__));73 void _ _cfactx_coroutine_unwind(struct _Unwind_Exception * storage, struct $coroutine* cor) {74 _Unwind_Reason_Code ret = _Unwind_ForcedUnwind( storage, _ _cfactx_coroutine_unwindstop, cor );71 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc * cor) __attribute__ ((__noreturn__)); 72 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc * cor) { 73 _Unwind_Reason_Code ret = _Unwind_ForcedUnwind( storage, _CtxCoroutine_UnwindStop, cor ); 75 74 printf("UNWIND ERROR %d after force unwind\n", ret); 76 75 abort(); 77 76 } 78 77 79 void __cfactx_invoke_thread(78 void CtxInvokeThread( 80 79 void (*main)(void *), 81 80 void *this … … 94 93 // The order of these 4 operations is very important 95 94 //Final suspend, should never return 96 __ cfactx_thrd_leave();95 __leave_thread_monitor(); 97 96 __cabi_abort( "Resumed dead thread" ); 98 97 } 99 98 100 void __cfactx_start(99 void CtxStart( 101 100 void (*main)(void *), 102 struct $coroutine* cor,101 struct coroutine_desc * cor, 103 102 void *this, 104 103 void (*invoke)(void *) … … 109 108 110 109 struct FakeStack { 111 void *fixedRegisters[3]; // fixed registers ebx, edi, esi (popped on 1st uSwitch, values unimportant)112 void *rturn; // where to go on return from uSwitch113 void *dummyReturn; // fake return compiler would have pushed on call to uInvoke114 void *argument[3]; // for 16-byte ABI, 16-byte alignment starts here115 void *padding; // padding to force 16-byte alignment, as "base" is 16-byte aligned110 void *fixedRegisters[3]; // fixed registers ebx, edi, esi (popped on 1st uSwitch, values unimportant) 111 void *rturn; // where to go on return from uSwitch 112 void *dummyReturn; // fake return compiler would have pushed on call to uInvoke 113 void *argument[3]; // for 16-byte ABI, 16-byte alignment starts here 114 void *padding; // padding to force 16-byte alignment, as "base" is 16-byte aligned 116 115 }; 117 116 … … 122 121 123 122 fs->dummyReturn = NULL; 124 fs->argument[0] = main; // argument to invoke125 fs->argument[1] = this; // argument to invoke123 fs->argument[0] = main; // argument to invoke 124 fs->argument[1] = this; // argument to invoke 126 125 fs->rturn = invoke; 127 126 … … 129 128 130 129 struct FakeStack { 131 void *fixedRegisters[5]; // fixed registers rbx, r12, r13, r14, r15132 void *rturn; // where to go on return from uSwitch133 void *dummyReturn; // NULL return address to provide proper alignment130 void *fixedRegisters[5]; // fixed registers rbx, r12, r13, r14, r15 131 void *rturn; // where to go on return from uSwitch 132 void *dummyReturn; // NULL return address to provide proper alignment 134 133 }; 135 134 136 135 cor->context.SP = (char *)stack->base - sizeof( struct FakeStack ); 137 cor->context.FP = NULL; // terminate stack with NULL fp136 cor->context.FP = NULL; // terminate stack with NULL fp 138 137 139 138 struct FakeStack *fs = (struct FakeStack *)cor->context.SP; 140 139 141 140 fs->dummyReturn = NULL; 142 fs->rturn = __cfactx_invoke_stub;143 fs->fixedRegisters[0] = main; // argument to invoke144 fs->fixedRegisters[1] = this; // argument to invoke141 fs->rturn = CtxInvokeStub; 142 fs->fixedRegisters[0] = main; 143 fs->fixedRegisters[1] = this; 145 144 fs->fixedRegisters[2] = invoke; 146 145 147 #elif defined( __ARM_ARCH_32 ) 148 #error ARM needs to be upgrade to use two parameters like X86/X64 (A.K.A. : I broke this and do not know how to fix it) 149 // More details about the error: 150 // To avoid the thunk problem, I changed the invoke routine to pass the main explicitly 151 // instead of relying on an assertion. This effectively hoists any required thunk one level 152 // which was enough to get to global scope in most cases. 153 // This means that __cfactx_invoke_... now takes two parameters and the FakeStack needs 154 // to be adjusted as a consequence of that. 155 // I don't know how to do that for ARM, hence the #error 156 146 #elif defined( __ARM_ARCH ) 147 #error ARM needs to be upgrade to use to parameters like X86/X64 (A.K.A. : I broke this and do not know how to fix it) 157 148 struct FakeStack { 158 float fpRegs[16]; // floating point registers159 void * intRegs[9];// integer/pointer registers160 void * arg[2];// placeholder for this pointer149 float fpRegs[16]; // floating point registers 150 void *intRegs[9]; // integer/pointer registers 151 void *arg[2]; // placeholder for this pointer 161 152 }; 162 153 … … 166 157 struct FakeStack *fs = (struct FakeStack *)cor->context.SP; 167 158 168 fs->intRegs[8] = __cfactx_invoke_stub;159 fs->intRegs[8] = CtxInvokeStub; 169 160 fs->arg[0] = this; 170 161 fs->arg[1] = invoke; 171 162 172 #elif defined( __ARM_ARCH )173 struct FakeStack {174 void * intRegs[12]; // x19-x30 integer registers175 double fpRegs[8]; // v8-v15 floating point176 };177 178 cor->context.SP = (char *)stack->base - sizeof( struct FakeStack );179 cor->context.FP = NULL;180 181 struct FakeStack *fs = (struct FakeStack *)cor->context.SP;182 183 fs->intRegs[0] = main; // argument to invoke x19 => x0184 fs->intRegs[1] = this; // argument to invoke x20 => x1185 fs->intRegs[2] = invoke;186 fs->intRegs[11] = __cfactx_invoke_stub; // link register x30 => ret moves to pc187 163 #else 188 164 #error uknown hardware architecture -
libcfa/src/concurrency/invoke.h
reef8dfb rbdfc032 17 17 #include "bits/defs.hfa" 18 18 #include "bits/locks.hfa" 19 #include "kernel/fwd.hfa"20 19 21 20 #ifdef __cforall … … 27 26 #define _INVOKE_H_ 28 27 29 struct __cfaehm_try_resume_node; 30 struct __cfaehm_base_exception_t; 31 struct exception_context_t { 32 struct __cfaehm_try_resume_node * top_resume; 33 struct __cfaehm_base_exception_t * current_exception; 34 }; 28 #ifdef __ARM_ARCH 29 // function prototypes are only really used by these macros on ARM 30 void disable_global_interrupts(); 31 void enable_global_interrupts(); 32 33 #define TL_GET( member ) ( { __typeof__( kernelTLS.member ) target; \ 34 disable_global_interrupts(); \ 35 target = kernelTLS.member; \ 36 enable_global_interrupts(); \ 37 target; } ) 38 #define TL_SET( member, value ) disable_global_interrupts(); \ 39 kernelTLS.member = value; \ 40 enable_global_interrupts(); 41 #else 42 #define TL_GET( member ) kernelTLS.member 43 #define TL_SET( member, value ) kernelTLS.member = value; 44 #endif 45 46 #ifdef __cforall 47 extern "Cforall" { 48 extern __attribute__((aligned(128))) thread_local struct KernelThreadData { 49 struct thread_desc * volatile this_thread; 50 struct processor * volatile this_processor; 51 52 struct { 53 volatile unsigned short disable_count; 54 volatile bool enabled; 55 volatile bool in_progress; 56 } preemption_state; 57 58 uint32_t rand_seed; 59 } kernelTLS __attribute__ ((tls_model ( "initial-exec" ))); 60 } 61 #endif 35 62 36 63 struct __stack_context_t { … … 58 85 // base of stack 59 86 void * base; 60 61 // Information for exception handling.62 struct exception_context_t exception_context;63 87 }; 64 88 … … 68 92 }; 69 93 70 enum __Coroutine_State { Halted, Start, Primed, Blocked, Ready, Active, Cancelled, Halting};71 72 struct $coroutine{73 // context that is switch during a __cfactx_switch94 enum coroutine_state { Halted, Start, Inactive, Active, Primed }; 95 96 struct coroutine_desc { 97 // context that is switch during a CtxSwitch 74 98 struct __stack_context_t context; 75 99 … … 81 105 82 106 // current execution status for coroutine 83 enum __Coroutine_State state;107 enum coroutine_state state; 84 108 85 109 // first coroutine to resume this one 86 struct $coroutine* starter;110 struct coroutine_desc * starter; 87 111 88 112 // last coroutine to resume this one 89 struct $coroutine* last;113 struct coroutine_desc * last; 90 114 91 115 // If non-null stack must be unwound with this exception … … 93 117 94 118 }; 95 // Wrapper for gdb96 struct cfathread_coroutine_t { struct $coroutine debug; };97 98 static inline struct __stack_t * __get_stack( struct $coroutine * cor ) {99 return (struct __stack_t*)(((uintptr_t)cor->stack.storage) & ((uintptr_t)-2));100 }101 119 102 120 // struct which calls the monitor is accepting … … 109 127 }; 110 128 111 struct $monitor{129 struct monitor_desc { 112 130 // spinlock to protect internal data 113 131 struct __spinlock_t lock; 114 132 115 133 // current owner of the monitor 116 struct $thread* owner;134 struct thread_desc * owner; 117 135 118 136 // queue of threads that are blocked waiting for the monitor 119 __queue_t(struct $thread) entry_queue;137 __queue_t(struct thread_desc) entry_queue; 120 138 121 139 // stack of conditions to run next once we exit the monitor … … 131 149 struct __condition_node_t * dtor_node; 132 150 }; 133 // Wrapper for gdb134 struct cfathread_monitor_t { struct $monitor debug; };135 151 136 152 struct __monitor_group_t { 137 153 // currently held monitors 138 __cfa_anonymous_object( __small_array_t( $monitor*) );154 __cfa_anonymous_object( __small_array_t(monitor_desc*) ); 139 155 140 156 // last function that acquired monitors … … 142 158 }; 143 159 144 // Link lists fields 145 // instrusive link field for threads 146 struct __thread_desc_link { 147 struct $thread * next; 148 struct $thread * prev; 149 volatile unsigned long long ts; 150 int preferred; 151 }; 152 153 struct $thread { 160 struct thread_desc { 154 161 // Core threading fields 155 // context that is switch during a __cfactx_switch162 // context that is switch during a CtxSwitch 156 163 struct __stack_context_t context; 157 164 158 165 // current execution status for coroutine 159 // Possible values are: 160 // - TICKET_BLOCKED (-1) thread is blocked 161 // - TICKET_RUNNING ( 0) thread is running 162 // - TICKET_UNBLOCK ( 1) thread should ignore next block 163 volatile int ticket; 164 enum __Coroutine_State state:8; 165 enum __Preemption_Reason preempted:8; 166 enum coroutine_state state; 166 167 167 168 //SKULLDUGGERY errno is not save in the thread data structure because returnToKernel appears to be the only function to require saving and restoring it 169 170 // coroutine body used to store context 171 struct coroutine_desc self_cor; 172 173 // current active context 174 struct coroutine_desc * curr_cor; 175 176 // monitor body used for mutual exclusion 177 struct monitor_desc self_mon; 178 179 // pointer to monitor with sufficient lifetime for current monitors 180 struct monitor_desc * self_mon_p; 168 181 169 182 // pointer to the cluster on which the thread is running 170 183 struct cluster * curr_cluster; 171 184 185 // monitors currently held by this thread 186 struct __monitor_group_t monitors; 187 172 188 // Link lists fields 173 189 // instrusive link field for threads 174 struct __thread_desc_link link; 175 176 // coroutine body used to store context 177 struct $coroutine self_cor; 178 179 // current active context 180 struct $coroutine * curr_cor; 181 182 // monitor body used for mutual exclusion 183 struct $monitor self_mon; 184 185 // pointer to monitor with sufficient lifetime for current monitors 186 struct $monitor * self_mon_p; 187 188 // monitors currently held by this thread 189 struct __monitor_group_t monitors; 190 191 // used to put threads on user data structures 190 struct thread_desc * next; 191 192 192 struct { 193 struct $thread * next; 194 struct $thread * back; 195 } seqable; 196 197 struct { 198 struct $thread * next; 199 struct $thread * prev; 193 struct thread_desc * next; 194 struct thread_desc * prev; 200 195 } node; 201 202 #if defined( __CFA_WITH_VERIFY__ ) 203 void * canary; 204 #endif 205 }; 206 // Wrapper for gdb 207 struct cfathread_thread_t { struct $thread debug; }; 208 209 #ifdef __CFA_DEBUG__ 210 void __cfaabi_dbg_record_thrd($thread & this, bool park, const char prev_name[]); 211 #else 212 #define __cfaabi_dbg_record_thrd(x, y, z) 213 #endif 196 }; 214 197 215 198 #ifdef __cforall 216 199 extern "Cforall" { 217 218 static inline $thread *& get_next( $thread & this ) __attribute__((const)) { 219 return this.link.next; 220 } 221 222 static inline [$thread *&, $thread *& ] __get( $thread & this ) __attribute__((const)) { 200 static inline thread_desc *& get_next( thread_desc & this ) { 201 return this.next; 202 } 203 204 static inline [thread_desc *&, thread_desc *& ] __get( thread_desc & this ) { 223 205 return this.node.[next, prev]; 224 }225 226 static inline $thread *& Back( $thread * this ) __attribute__((const)) {227 return this->seqable.back;228 }229 230 static inline $thread *& Next( $thread * this ) __attribute__((const)) {231 return this->seqable.next;232 }233 234 static inline bool listed( $thread * this ) {235 return this->seqable.next != 0p;236 206 } 237 207 … … 242 212 } 243 213 244 static inline void ?{}(__monitor_group_t & this, struct $monitor** data, __lock_size_t size, fptr_t func) {214 static inline void ?{}(__monitor_group_t & this, struct monitor_desc ** data, __lock_size_t size, fptr_t func) { 245 215 (this.data){data}; 246 216 (this.size){size}; … … 248 218 } 249 219 250 static inline bool ?==?( const __monitor_group_t & lhs, const __monitor_group_t & rhs ) __attribute__((const)){220 static inline bool ?==?( const __monitor_group_t & lhs, const __monitor_group_t & rhs ) { 251 221 if( (lhs.data != 0) != (rhs.data != 0) ) return false; 252 222 if( lhs.size != rhs.size ) return false; … … 282 252 283 253 // assembler routines that performs the context switch 284 extern void __cfactx_invoke_stub( void );285 extern void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) asm ("__cfactx_switch");254 extern void CtxInvokeStub( void ); 255 extern void CtxSwitch( struct __stack_context_t * from, struct __stack_context_t * to ) asm ("CtxSwitch"); 286 256 // void CtxStore ( void * this ) asm ("CtxStore"); 287 257 // void CtxRet ( void * dst ) asm ("CtxRet"); -
libcfa/src/concurrency/kernel.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Jan 17 12:27:26 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 31 07:08:20 202013 // Update Count : 7112 // Last Modified On : Thu Jan 30 22:55:50 2020 13 // Update Count : 56 14 14 // 15 15 16 16 #define __cforall_thread__ 17 // #define __CFA_DEBUG_PRINT_RUNTIME_CORE__18 17 19 18 //C Includes 19 #include <stddef.h> 20 20 #include <errno.h> 21 #include <string.h> 22 extern "C" { 21 23 #include <stdio.h> 24 #include <fenv.h> 25 #include <sys/resource.h> 22 26 #include <signal.h> 23 27 #include <unistd.h> 28 #include <limits.h> // PTHREAD_STACK_MIN 29 #include <sys/mman.h> // mprotect 30 } 24 31 25 32 //CFA Includes 33 #include "time.hfa" 26 34 #include "kernel_private.hfa" 27 35 #include "preemption.hfa" 36 #include "startup.hfa" 28 37 29 38 //Private includes … … 31 40 #include "invoke.h" 32 41 33 34 42 //----------------------------------------------------------------------------- 35 43 // Some assembly required 36 44 #if defined( __i386 ) 45 #define CtxGet( ctx ) \ 46 __asm__ volatile ( \ 47 "movl %%esp,%0\n"\ 48 "movl %%ebp,%1\n"\ 49 : "=rm" (ctx.SP),\ 50 "=rm" (ctx.FP) \ 51 ) 52 37 53 // mxcr : SSE Status and Control bits (control bits are preserved across function calls) 38 54 // fcw : X87 FPU control word (preserved across function calls) … … 56 72 57 73 #elif defined( __x86_64 ) 74 #define CtxGet( ctx ) \ 75 __asm__ volatile ( \ 76 "movq %%rsp,%0\n"\ 77 "movq %%rbp,%1\n"\ 78 : "=rm" (ctx.SP),\ 79 "=rm" (ctx.FP) \ 80 ) 81 58 82 #define __x87_store \ 59 83 uint32_t __mxcr; \ … … 74 98 ) 75 99 76 #elif defined( __arm__ ) 77 #define __x87_store 78 #define __x87_load 79 80 #elif defined( __aarch64__ ) 81 #define __x87_store \ 82 uint32_t __fpcntl[2]; \ 83 __asm__ volatile ( \ 84 "mrs x9, FPCR\n" \ 85 "mrs x10, FPSR\n" \ 86 "stp x9, x10, %0\n" \ 87 : "=m" (__fpcntl) : : "x9", "x10" \ 88 ) 89 90 #define __x87_load \ 91 __asm__ volatile ( \ 92 "ldp x9, x10, %0\n" \ 93 "msr FPSR, x10\n" \ 94 "msr FPCR, x9\n" \ 95 : "=m" (__fpcntl) : : "x9", "x10" \ 96 ) 97 100 101 #elif defined( __ARM_ARCH ) 102 #define CtxGet( ctx ) __asm__ ( \ 103 "mov %0,%%sp\n" \ 104 "mov %1,%%r11\n" \ 105 : "=rm" (ctx.SP), "=rm" (ctx.FP) ) 98 106 #else 99 #error un supportedhardware architecture107 #error unknown hardware architecture 100 108 #endif 101 109 102 extern $thread * mainThread; 103 extern processor * mainProcessor; 104 105 //----------------------------------------------------------------------------- 106 // Kernel Scheduling logic 107 static $thread * __next_thread(cluster * this); 108 static $thread * __next_thread_slow(cluster * this); 109 static void __run_thread(processor * this, $thread * dst); 110 static void __wake_one(cluster * cltr); 111 112 static void push (__cluster_idles & idles, processor & proc); 113 static void remove(__cluster_idles & idles, processor & proc); 114 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles idles ); 115 110 //----------------------------------------------------------------------------- 111 //Start and stop routine for the kernel, declared first to make sure they run first 112 static void kernel_startup(void) __attribute__(( constructor( STARTUP_PRIORITY_KERNEL ) )); 113 static void kernel_shutdown(void) __attribute__(( destructor ( STARTUP_PRIORITY_KERNEL ) )); 114 115 //----------------------------------------------------------------------------- 116 // Kernel storage 117 KERNEL_STORAGE(cluster, mainCluster); 118 KERNEL_STORAGE(processor, mainProcessor); 119 KERNEL_STORAGE(thread_desc, mainThread); 120 KERNEL_STORAGE(__stack_t, mainThreadCtx); 121 122 cluster * mainCluster; 123 processor * mainProcessor; 124 thread_desc * mainThread; 125 126 extern "C" { 127 struct { __dllist_t(cluster) list; __spinlock_t lock; } __cfa_dbg_global_clusters; 128 } 129 130 size_t __page_size = 0; 131 132 //----------------------------------------------------------------------------- 133 // Global state 134 thread_local struct KernelThreadData kernelTLS __attribute__ ((tls_model ( "initial-exec" ))) = { 135 NULL, // cannot use 0p 136 NULL, 137 { 1, false, false }, 138 6u //this should be seeded better but due to a bug calling rdtsc doesn't work 139 }; 140 141 //----------------------------------------------------------------------------- 142 // Struct to steal stack 143 struct current_stack_info_t { 144 __stack_t * storage; // pointer to stack object 145 void * base; // base of stack 146 void * limit; // stack grows towards stack limit 147 void * context; // address of cfa_context_t 148 }; 149 150 void ?{}( current_stack_info_t & this ) { 151 __stack_context_t ctx; 152 CtxGet( ctx ); 153 this.base = ctx.FP; 154 155 rlimit r; 156 getrlimit( RLIMIT_STACK, &r); 157 size_t size = r.rlim_cur; 158 159 this.limit = (void *)(((intptr_t)this.base) - size); 160 this.context = &storage_mainThreadCtx; 161 } 162 163 //----------------------------------------------------------------------------- 164 // Main thread construction 165 166 void ?{}( coroutine_desc & this, current_stack_info_t * info) with( this ) { 167 stack.storage = info->storage; 168 with(*stack.storage) { 169 limit = info->limit; 170 base = info->base; 171 } 172 __attribute__((may_alias)) intptr_t * istorage = (intptr_t*) &stack.storage; 173 *istorage |= 0x1; 174 name = "Main Thread"; 175 state = Start; 176 starter = 0p; 177 last = 0p; 178 cancellation = 0p; 179 } 180 181 void ?{}( thread_desc & this, current_stack_info_t * info) with( this ) { 182 state = Start; 183 self_cor{ info }; 184 curr_cor = &self_cor; 185 curr_cluster = mainCluster; 186 self_mon.owner = &this; 187 self_mon.recursion = 1; 188 self_mon_p = &self_mon; 189 next = 0p; 190 191 node.next = 0p; 192 node.prev = 0p; 193 doregister(curr_cluster, this); 194 195 monitors{ &self_mon_p, 1, (fptr_t)0 }; 196 } 197 198 //----------------------------------------------------------------------------- 199 // Processor coroutine 200 void ?{}(processorCtx_t & this) { 201 202 } 203 204 // Construct the processor context of non-main processors 205 static void ?{}(processorCtx_t & this, processor * proc, current_stack_info_t * info) { 206 (this.__cor){ info }; 207 this.proc = proc; 208 } 209 210 static void start(processor * this); 211 void ?{}(processor & this, const char * name, cluster & cltr) with( this ) { 212 this.name = name; 213 this.cltr = &cltr; 214 terminated{ 0 }; 215 do_terminate = false; 216 preemption_alarm = 0p; 217 pending_preemption = false; 218 runner.proc = &this; 219 220 idleLock{}; 221 222 start( &this ); 223 } 224 225 void ^?{}(processor & this) with( this ){ 226 if( ! __atomic_load_n(&do_terminate, __ATOMIC_ACQUIRE) ) { 227 __cfaabi_dbg_print_safe("Kernel : core %p signaling termination\n", &this); 228 229 __atomic_store_n(&do_terminate, true, __ATOMIC_RELAXED); 230 wake( &this ); 231 232 P( terminated ); 233 verify( kernelTLS.this_processor != &this); 234 } 235 236 pthread_join( kernel_thread, 0p ); 237 free( this.stack ); 238 } 239 240 void ?{}(cluster & this, const char * name, Duration preemption_rate) with( this ) { 241 this.name = name; 242 this.preemption_rate = preemption_rate; 243 ready_queue{}; 244 ready_queue_lock{}; 245 246 procs{ __get }; 247 idles{ __get }; 248 threads{ __get }; 249 250 doregister(this); 251 } 252 253 void ^?{}(cluster & this) { 254 unregister(this); 255 } 116 256 117 257 //============================================================================================= 118 258 // Kernel Scheduling logic 119 259 //============================================================================================= 260 static void runThread(processor * this, thread_desc * dst); 261 static void finishRunning(processor * this); 262 static void halt(processor * this); 263 120 264 //Main of the processor contexts 121 265 void main(processorCtx_t & runner) { 122 266 // Because of a bug, we couldn't initialized the seed on construction 123 267 // Do it here 124 __cfaabi_tls.rand_seed ^= rdtscl(); 125 __cfaabi_tls.ready_rng.fwd_seed = 25214903917_l64u * (rdtscl() ^ (uintptr_t)&runner); 126 __tls_rand_advance_bck(); 268 kernelTLS.rand_seed ^= rdtscl(); 127 269 128 270 processor * this = runner.proc; 129 271 verify(this); 130 272 131 __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this); 132 #if !defined(__CFA_NO_STATISTICS__) 133 if( this->print_halts ) { 134 __cfaabi_bits_print_safe( STDOUT_FILENO, "Processor : %d - %s (%p)\n", this->id, this->name, (void*)this); 135 } 136 #endif 273 __cfaabi_dbg_print_safe("Kernel : core %p starting\n", this); 274 275 doregister(this->cltr, this); 137 276 138 277 { … … 140 279 preemption_scope scope = { this }; 141 280 142 __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this); 143 144 $thread * readyThread = 0p; 145 MAIN_LOOP: 146 for() { 147 // Try to get the next thread 148 readyThread = __next_thread( this->cltr ); 149 150 if( !readyThread ) { 151 readyThread = __next_thread_slow( this->cltr ); 281 __cfaabi_dbg_print_safe("Kernel : core %p started\n", this); 282 283 thread_desc * readyThread = 0p; 284 for( unsigned int spin_count = 0; ! __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST); spin_count++ ) { 285 readyThread = nextThread( this->cltr ); 286 287 if(readyThread) { 288 verify( ! kernelTLS.preemption_state.enabled ); 289 290 runThread(this, readyThread); 291 292 verify( ! kernelTLS.preemption_state.enabled ); 293 294 //Some actions need to be taken from the kernel 295 finishRunning(this); 296 297 spin_count = 0; 298 } else { 299 // spin(this, &spin_count); 300 halt(this); 152 301 } 153 154 HALT:155 if( !readyThread ) {156 // Don't block if we are done157 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;158 159 #if !defined(__CFA_NO_STATISTICS__)160 __tls_stats()->ready.sleep.halts++;161 #endif162 163 // Push self to idle stack164 push(this->cltr->idles, * this);165 166 // Confirm the ready-queue is empty167 readyThread = __next_thread_slow( this->cltr );168 if( readyThread ) {169 // A thread was found, cancel the halt170 remove(this->cltr->idles, * this);171 172 #if !defined(__CFA_NO_STATISTICS__)173 __tls_stats()->ready.sleep.cancels++;174 #endif175 176 // continue the mai loop177 break HALT;178 }179 180 #if !defined(__CFA_NO_STATISTICS__)181 if(this->print_halts) {182 __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->id, rdtscl());183 }184 #endif185 186 wait( this->idle );187 188 #if !defined(__CFA_NO_STATISTICS__)189 if(this->print_halts) {190 __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->id, rdtscl());191 }192 #endif193 194 // We were woken up, remove self from idle195 remove(this->cltr->idles, * this);196 197 // DON'T just proceed, start looking again198 continue MAIN_LOOP;199 }200 201 /* paranoid */ verify( readyThread );202 203 // We found a thread run it204 __run_thread(this, readyThread);205 206 // Are we done?207 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;208 302 } 209 303 210 __cfadbg_print_safe(runtime_core, "Kernel : core %p stopping\n", this); 211 } 304 __cfaabi_dbg_print_safe("Kernel : core %p stopping\n", this); 305 } 306 307 unregister(this->cltr, this); 212 308 213 309 V( this->terminated ); 214 310 215 if(this == mainProcessor) { 216 // HACK : the coroutine context switch expects this_thread to be set 217 // and it make sense for it to be set in all other cases except here 218 // fake it 219 __cfaabi_tls.this_thread = mainThread; 220 } 221 222 __cfadbg_print_safe(runtime_core, "Kernel : core %p terminated\n", this); 311 __cfaabi_dbg_print_safe("Kernel : core %p terminated\n", this); 223 312 } 224 313 … … 229 318 // runThread runs a thread by context switching 230 319 // from the processor coroutine to the target thread 231 static void __run_thread(processor * this, $thread * thrd_dst) { 232 /* paranoid */ verify( ! __preemption_enabled() ); 233 /* paranoid */ verifyf( thrd_dst->state == Ready || thrd_dst->preempted != __NO_PREEMPTION, "state : %d, preempted %d\n", thrd_dst->state, thrd_dst->preempted); 234 /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next ); 235 __builtin_prefetch( thrd_dst->context.SP ); 236 237 $coroutine * proc_cor = get_coroutine(this->runner); 238 239 // set state of processor coroutine to inactive 240 verify(proc_cor->state == Active); 241 proc_cor->state = Blocked; 242 243 // Actually run the thread 244 RUNNING: while(true) { 245 thrd_dst->preempted = __NO_PREEMPTION; 246 thrd_dst->state = Active; 247 248 // Update global state 249 kernelTLS().this_thread = thrd_dst; 250 251 /* paranoid */ verify( ! __preemption_enabled() ); 252 /* paranoid */ verify( kernelTLS().this_thread == thrd_dst ); 253 /* paranoid */ verify( thrd_dst->curr_cluster == this->cltr ); 254 /* paranoid */ verify( thrd_dst->context.SP ); 255 /* paranoid */ verify( thrd_dst->state != Halted ); 256 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst ); // add escape condition if we are setting up the processor 257 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst ); // add escape condition if we are setting up the processor 258 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_dst->canary ); 259 260 261 262 // set context switch to the thread that the processor is executing 263 __cfactx_switch( &proc_cor->context, &thrd_dst->context ); 264 // when __cfactx_switch returns we are back in the processor coroutine 265 266 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_dst->canary ); 267 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit), "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst ); 268 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ), "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst ); 269 /* paranoid */ verify( thrd_dst->context.SP ); 270 /* paranoid */ verify( thrd_dst->curr_cluster == this->cltr ); 271 /* paranoid */ verify( kernelTLS().this_thread == thrd_dst ); 272 /* paranoid */ verify( ! __preemption_enabled() ); 273 274 // Reset global state 275 kernelTLS().this_thread = 0p; 276 277 // We just finished running a thread, there are a few things that could have happened. 278 // 1 - Regular case : the thread has blocked and now one has scheduled it yet. 279 // 2 - Racy case : the thread has blocked but someone has already tried to schedule it. 280 // 4 - Preempted 281 // In case 1, we may have won a race so we can't write to the state again. 282 // In case 2, we lost the race so we now own the thread. 283 284 if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) { 285 // The thread was preempted, reschedule it and reset the flag 286 __schedule_thread( thrd_dst ); 287 break RUNNING; 320 static void runThread(processor * this, thread_desc * thrd_dst) { 321 coroutine_desc * proc_cor = get_coroutine(this->runner); 322 323 // Reset the terminating actions here 324 this->finish.action_code = No_Action; 325 326 // Update global state 327 kernelTLS.this_thread = thrd_dst; 328 329 // set state of processor coroutine to inactive and the thread to active 330 proc_cor->state = proc_cor->state == Halted ? Halted : Inactive; 331 thrd_dst->state = Active; 332 333 // set context switch to the thread that the processor is executing 334 verify( thrd_dst->context.SP ); 335 CtxSwitch( &proc_cor->context, &thrd_dst->context ); 336 // when CtxSwitch returns we are back in the processor coroutine 337 338 // set state of processor coroutine to active and the thread to inactive 339 thrd_dst->state = thrd_dst->state == Halted ? Halted : Inactive; 340 proc_cor->state = Active; 341 } 342 343 // KERNEL_ONLY 344 static void returnToKernel() { 345 coroutine_desc * proc_cor = get_coroutine(kernelTLS.this_processor->runner); 346 thread_desc * thrd_src = kernelTLS.this_thread; 347 348 // set state of current coroutine to inactive 349 thrd_src->state = thrd_src->state == Halted ? Halted : Inactive; 350 proc_cor->state = Active; 351 int local_errno = *__volatile_errno(); 352 #if defined( __i386 ) || defined( __x86_64 ) 353 __x87_store; 354 #endif 355 356 // set new coroutine that the processor is executing 357 // and context switch to it 358 verify( proc_cor->context.SP ); 359 CtxSwitch( &thrd_src->context, &proc_cor->context ); 360 361 // set state of new coroutine to active 362 proc_cor->state = proc_cor->state == Halted ? Halted : Inactive; 363 thrd_src->state = Active; 364 365 #if defined( __i386 ) || defined( __x86_64 ) 366 __x87_load; 367 #endif 368 *__volatile_errno() = local_errno; 369 } 370 371 // KERNEL_ONLY 372 // Once a thread has finished running, some of 373 // its final actions must be executed from the kernel 374 static void finishRunning(processor * this) with( this->finish ) { 375 verify( ! kernelTLS.preemption_state.enabled ); 376 choose( action_code ) { 377 case No_Action: 378 break; 379 case Release: 380 unlock( *lock ); 381 case Schedule: 382 ScheduleThread( thrd ); 383 case Release_Schedule: 384 unlock( *lock ); 385 ScheduleThread( thrd ); 386 case Release_Multi: 387 for(int i = 0; i < lock_count; i++) { 388 unlock( *locks[i] ); 288 389 } 289 290 if(unlikely(thrd_dst->state == Halting)) { 291 // The thread has halted, it should never be scheduled/run again 292 // finish the thread 293 __thread_finish( thrd_dst ); 294 break RUNNING; 390 case Release_Multi_Schedule: 391 for(int i = 0; i < lock_count; i++) { 392 unlock( *locks[i] ); 295 393 } 296 297 /* paranoid */ verify( thrd_dst->state == Active ); 298 thrd_dst->state = Blocked; 299 300 // set state of processor coroutine to active and the thread to inactive 301 int old_ticket = __atomic_fetch_sub(&thrd_dst->ticket, 1, __ATOMIC_SEQ_CST); 302 switch(old_ticket) { 303 case TICKET_RUNNING: 304 // This is case 1, the regular case, nothing more is needed 305 break RUNNING; 306 case TICKET_UNBLOCK: 307 // This is case 2, the racy case, someone tried to run this thread before it finished blocking 308 // In this case, just run it again. 309 continue RUNNING; 310 default: 311 // This makes no sense, something is wrong abort 312 abort(); 394 for(int i = 0; i < thrd_count; i++) { 395 ScheduleThread( thrds[i] ); 313 396 } 314 } 315 316 // Just before returning to the processor, set the processor coroutine to active 317 proc_cor->state = Active; 318 319 /* paranoid */ verify( ! __preemption_enabled() ); 397 case Callback: 398 callback(); 399 default: 400 abort("KERNEL ERROR: Unexpected action to run after thread"); 401 } 320 402 } 321 403 322 404 // KERNEL_ONLY 323 void returnToKernel() { 324 /* paranoid */ verify( ! __preemption_enabled() ); 325 $coroutine * proc_cor = get_coroutine(kernelTLS().this_processor->runner); 326 $thread * thrd_src = kernelTLS().this_thread; 327 328 #if !defined(__CFA_NO_STATISTICS__) 329 struct processor * last_proc = kernelTLS().this_processor; 330 #endif 331 332 // Run the thread on this processor 333 { 334 int local_errno = *__volatile_errno(); 335 #if defined( __i386 ) || defined( __x86_64 ) 336 __x87_store; 337 #endif 338 /* paranoid */ verify( proc_cor->context.SP ); 339 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_src->canary ); 340 __cfactx_switch( &thrd_src->context, &proc_cor->context ); 341 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_src->canary ); 342 #if defined( __i386 ) || defined( __x86_64 ) 343 __x87_load; 344 #endif 345 *__volatile_errno() = local_errno; 346 } 347 348 #if !defined(__CFA_NO_STATISTICS__) 349 if(last_proc != kernelTLS().this_processor) { 350 __tls_stats()->ready.threads.migration++; 405 // Context invoker for processors 406 // This is the entry point for processors (kernel threads) 407 // It effectively constructs a coroutine by stealing the pthread stack 408 static void * CtxInvokeProcessor(void * arg) { 409 processor * proc = (processor *) arg; 410 kernelTLS.this_processor = proc; 411 kernelTLS.this_thread = 0p; 412 kernelTLS.preemption_state.[enabled, disable_count] = [false, 1]; 413 // SKULLDUGGERY: We want to create a context for the processor coroutine 414 // which is needed for the 2-step context switch. However, there is no reason 415 // to waste the perfectly valid stack create by pthread. 416 current_stack_info_t info; 417 __stack_t ctx; 418 info.storage = &ctx; 419 (proc->runner){ proc, &info }; 420 421 __cfaabi_dbg_print_safe("Coroutine : created stack %p\n", get_coroutine(proc->runner)->stack.storage); 422 423 //Set global state 424 kernelTLS.this_thread = 0p; 425 426 //We now have a proper context from which to schedule threads 427 __cfaabi_dbg_print_safe("Kernel : core %p created (%p, %p)\n", proc, &proc->runner, &ctx); 428 429 // SKULLDUGGERY: Since the coroutine doesn't have its own stack, we can't 430 // resume it to start it like it normally would, it will just context switch 431 // back to here. Instead directly call the main since we already are on the 432 // appropriate stack. 433 get_coroutine(proc->runner)->state = Active; 434 main( proc->runner ); 435 get_coroutine(proc->runner)->state = Halted; 436 437 // Main routine of the core returned, the core is now fully terminated 438 __cfaabi_dbg_print_safe("Kernel : core %p main ended (%p)\n", proc, &proc->runner); 439 440 return 0p; 441 } 442 443 static void Abort( int ret, const char * func ) { 444 if ( ret ) { // pthread routines return errno values 445 abort( "%s : internal error, error(%d) %s.", func, ret, strerror( ret ) ); 446 } // if 447 } // Abort 448 449 void * create_pthread( pthread_t * pthread, void * (*start)(void *), void * arg ) { 450 pthread_attr_t attr; 451 452 Abort( pthread_attr_init( &attr ), "pthread_attr_init" ); // initialize attribute 453 454 size_t stacksize; 455 // default stack size, normally defined by shell limit 456 Abort( pthread_attr_getstacksize( &attr, &stacksize ), "pthread_attr_getstacksize" ); 457 assert( stacksize >= PTHREAD_STACK_MIN ); 458 459 void * stack; 460 __cfaabi_dbg_debug_do( 461 stack = memalign( __page_size, stacksize + __page_size ); 462 // pthread has no mechanism to create the guard page in user supplied stack. 463 if ( mprotect( stack, __page_size, PROT_NONE ) == -1 ) { 464 abort( "mprotect : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) ); 465 } // if 466 ); 467 __cfaabi_dbg_no_debug_do( 468 stack = malloc( stacksize ); 469 ); 470 471 Abort( pthread_attr_setstack( &attr, stack, stacksize ), "pthread_attr_setstack" ); 472 473 Abort( pthread_create( pthread, &attr, start, arg ), "pthread_create" ); 474 return stack; 475 } 476 477 static void start(processor * this) { 478 __cfaabi_dbg_print_safe("Kernel : Starting core %p\n", this); 479 480 this->stack = create_pthread( &this->kernel_thread, CtxInvokeProcessor, (void *)this ); 481 482 __cfaabi_dbg_print_safe("Kernel : core %p started\n", this); 483 } 484 485 // KERNEL_ONLY 486 void kernel_first_resume( processor * this ) { 487 thread_desc * src = mainThread; 488 coroutine_desc * dst = get_coroutine(this->runner); 489 490 verify( ! kernelTLS.preemption_state.enabled ); 491 492 kernelTLS.this_thread->curr_cor = dst; 493 __stack_prepare( &dst->stack, 65000 ); 494 CtxStart(main, dst, this->runner, CtxInvokeCoroutine); 495 496 verify( ! kernelTLS.preemption_state.enabled ); 497 498 dst->last = &src->self_cor; 499 dst->starter = dst->starter ? dst->starter : &src->self_cor; 500 501 // set state of current coroutine to inactive 502 src->state = src->state == Halted ? Halted : Inactive; 503 504 // context switch to specified coroutine 505 verify( dst->context.SP ); 506 CtxSwitch( &src->context, &dst->context ); 507 // when CtxSwitch returns we are back in the src coroutine 508 509 mainThread->curr_cor = &mainThread->self_cor; 510 511 // set state of new coroutine to active 512 src->state = Active; 513 514 verify( ! kernelTLS.preemption_state.enabled ); 515 } 516 517 // KERNEL_ONLY 518 void kernel_last_resume( processor * this ) { 519 coroutine_desc * src = &mainThread->self_cor; 520 coroutine_desc * dst = get_coroutine(this->runner); 521 522 verify( ! kernelTLS.preemption_state.enabled ); 523 verify( dst->starter == src ); 524 verify( dst->context.SP ); 525 526 // context switch to the processor 527 CtxSwitch( &src->context, &dst->context ); 528 } 529 530 //----------------------------------------------------------------------------- 531 // Scheduler routines 532 533 // KERNEL ONLY 534 void ScheduleThread( thread_desc * thrd ) { 535 verify( thrd ); 536 verify( thrd->state != Halted ); 537 538 verify( ! kernelTLS.preemption_state.enabled ); 539 540 verifyf( thrd->next == 0p, "Expected null got %p", thrd->next ); 541 542 with( *thrd->curr_cluster ) { 543 lock ( ready_queue_lock __cfaabi_dbg_ctx2 ); 544 bool was_empty = !(ready_queue != 0); 545 append( ready_queue, thrd ); 546 unlock( ready_queue_lock ); 547 548 if(was_empty) { 549 lock (proc_list_lock __cfaabi_dbg_ctx2); 550 if(idles) { 551 wake_fast(idles.head); 552 } 553 unlock (proc_list_lock); 351 554 } 352 #endif353 354 /* paranoid */ verify( ! __preemption_enabled() );355 /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) < ((uintptr_t)__get_stack(thrd_src->curr_cor)->base ), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too small.\n", thrd_src ); 356 /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) > ((uintptr_t)__get_stack(thrd_src->curr_cor)->limit), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too large.\n", thrd_src );357 } 358 359 //----------------------------------------------------------------------------- 360 // Scheduler routines 555 else if( struct processor * idle = idles.head ) { 556 wake_fast(idle); 557 } 558 559 } 560 561 verify( ! kernelTLS.preemption_state.enabled ); 562 } 563 361 564 // KERNEL ONLY 362 void __schedule_thread( $thread * thrd ) { 363 /* paranoid */ verify( ! __preemption_enabled() ); 364 /* paranoid */ verify( kernelTLS().this_proc_id ); 365 /* paranoid */ verify( thrd ); 366 /* paranoid */ verify( thrd->state != Halted ); 367 /* paranoid */ verify( thrd->curr_cluster ); 368 /* paranoid */ #if defined( __CFA_WITH_VERIFY__ ) 369 /* paranoid */ if( thrd->state == Blocked || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION, 370 "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted ); 371 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active, 372 "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted ); 373 /* paranoid */ #endif 374 /* paranoid */ verifyf( thrd->link.next == 0p, "Expected null got %p", thrd->link.next ); 375 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary ); 376 377 378 if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready; 379 380 ready_schedule_lock(); 381 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 382 struct cluster * cl = thrd->curr_cluster; 383 384 // push the thread to the cluster ready-queue 385 push( cl, thrd ); 386 387 // variable thrd is no longer safe to use 388 389 // wake the cluster using the save variable. 390 __wake_one( cl ); 391 ready_schedule_unlock(); 392 393 /* paranoid */ verify( ! __preemption_enabled() ); 565 thread_desc * nextThread(cluster * this) with( *this ) { 566 verify( ! kernelTLS.preemption_state.enabled ); 567 lock( ready_queue_lock __cfaabi_dbg_ctx2 ); 568 thread_desc * head = pop_head( ready_queue ); 569 unlock( ready_queue_lock ); 570 verify( ! kernelTLS.preemption_state.enabled ); 571 return head; 572 } 573 574 void BlockInternal() { 575 disable_interrupts(); 576 verify( ! kernelTLS.preemption_state.enabled ); 577 returnToKernel(); 578 verify( ! kernelTLS.preemption_state.enabled ); 579 enable_interrupts( __cfaabi_dbg_ctx ); 580 } 581 582 void BlockInternal( __spinlock_t * lock ) { 583 disable_interrupts(); 584 with( *kernelTLS.this_processor ) { 585 finish.action_code = Release; 586 finish.lock = lock; 587 } 588 589 verify( ! kernelTLS.preemption_state.enabled ); 590 returnToKernel(); 591 verify( ! kernelTLS.preemption_state.enabled ); 592 593 enable_interrupts( __cfaabi_dbg_ctx ); 594 } 595 596 void BlockInternal( thread_desc * thrd ) { 597 disable_interrupts(); 598 with( * kernelTLS.this_processor ) { 599 finish.action_code = Schedule; 600 finish.thrd = thrd; 601 } 602 603 verify( ! kernelTLS.preemption_state.enabled ); 604 returnToKernel(); 605 verify( ! kernelTLS.preemption_state.enabled ); 606 607 enable_interrupts( __cfaabi_dbg_ctx ); 608 } 609 610 void BlockInternal( __spinlock_t * lock, thread_desc * thrd ) { 611 assert(thrd); 612 disable_interrupts(); 613 with( * kernelTLS.this_processor ) { 614 finish.action_code = Release_Schedule; 615 finish.lock = lock; 616 finish.thrd = thrd; 617 } 618 619 verify( ! kernelTLS.preemption_state.enabled ); 620 returnToKernel(); 621 verify( ! kernelTLS.preemption_state.enabled ); 622 623 enable_interrupts( __cfaabi_dbg_ctx ); 624 } 625 626 void BlockInternal(__spinlock_t * locks [], unsigned short count) { 627 disable_interrupts(); 628 with( * kernelTLS.this_processor ) { 629 finish.action_code = Release_Multi; 630 finish.locks = locks; 631 finish.lock_count = count; 632 } 633 634 verify( ! kernelTLS.preemption_state.enabled ); 635 returnToKernel(); 636 verify( ! kernelTLS.preemption_state.enabled ); 637 638 enable_interrupts( __cfaabi_dbg_ctx ); 639 } 640 641 void BlockInternal(__spinlock_t * locks [], unsigned short lock_count, thread_desc * thrds [], unsigned short thrd_count) { 642 disable_interrupts(); 643 with( *kernelTLS.this_processor ) { 644 finish.action_code = Release_Multi_Schedule; 645 finish.locks = locks; 646 finish.lock_count = lock_count; 647 finish.thrds = thrds; 648 finish.thrd_count = thrd_count; 649 } 650 651 verify( ! kernelTLS.preemption_state.enabled ); 652 returnToKernel(); 653 verify( ! kernelTLS.preemption_state.enabled ); 654 655 enable_interrupts( __cfaabi_dbg_ctx ); 656 } 657 658 void BlockInternal(__finish_callback_fptr_t callback) { 659 disable_interrupts(); 660 with( *kernelTLS.this_processor ) { 661 finish.action_code = Callback; 662 finish.callback = callback; 663 } 664 665 verify( ! kernelTLS.preemption_state.enabled ); 666 returnToKernel(); 667 verify( ! kernelTLS.preemption_state.enabled ); 668 669 enable_interrupts( __cfaabi_dbg_ctx ); 394 670 } 395 671 396 672 // KERNEL ONLY 397 static inline $thread * __next_thread(cluster * this) with( *this ) { 398 /* paranoid */ verify( ! __preemption_enabled() ); 399 /* paranoid */ verify( kernelTLS().this_proc_id ); 400 401 ready_schedule_lock(); 402 $thread * thrd = pop( this ); 403 ready_schedule_unlock(); 404 405 /* paranoid */ verify( kernelTLS().this_proc_id ); 406 /* paranoid */ verify( ! __preemption_enabled() ); 407 return thrd; 408 } 409 410 // KERNEL ONLY 411 static inline $thread * __next_thread_slow(cluster * this) with( *this ) { 412 /* paranoid */ verify( ! __preemption_enabled() ); 413 /* paranoid */ verify( kernelTLS().this_proc_id ); 414 415 ready_schedule_lock(); 416 $thread * thrd = pop_slow( this ); 417 ready_schedule_unlock(); 418 419 /* paranoid */ verify( kernelTLS().this_proc_id ); 420 /* paranoid */ verify( ! __preemption_enabled() ); 421 return thrd; 422 } 423 424 void unpark( $thread * thrd ) { 425 if( !thrd ) return; 426 427 int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST); 428 switch(old_ticket) { 429 case TICKET_RUNNING: 430 // Wake won the race, the thread will reschedule/rerun itself 431 break; 432 case TICKET_BLOCKED: 433 /* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION ); 434 /* paranoid */ verify( thrd->state == Blocked ); 435 436 { 437 /* paranoid */ verify( publicTLS_get(this_proc_id) ); 438 bool full = publicTLS_get(this_proc_id)->full_proc; 439 if(full) disable_interrupts(); 440 441 /* paranoid */ verify( ! __preemption_enabled() ); 442 443 // Wake lost the race, 444 __schedule_thread( thrd ); 445 446 /* paranoid */ verify( ! __preemption_enabled() ); 447 448 if(full) enable_interrupts( __cfaabi_dbg_ctx ); 449 /* paranoid */ verify( publicTLS_get(this_proc_id) ); 450 } 451 452 break; 453 default: 454 // This makes no sense, something is wrong abort 455 abort("Thread %p (%s) has mismatch park/unpark\n", thrd, thrd->self_cor.name); 456 } 457 } 458 459 void park( void ) { 460 /* paranoid */ verify( __preemption_enabled() ); 673 void LeaveThread(__spinlock_t * lock, thread_desc * thrd) { 674 verify( ! kernelTLS.preemption_state.enabled ); 675 with( * kernelTLS.this_processor ) { 676 finish.action_code = thrd ? Release_Schedule : Release; 677 finish.lock = lock; 678 finish.thrd = thrd; 679 } 680 681 returnToKernel(); 682 } 683 684 //============================================================================================= 685 // Kernel Setup logic 686 //============================================================================================= 687 //----------------------------------------------------------------------------- 688 // Kernel boot procedures 689 static void kernel_startup(void) { 690 verify( ! kernelTLS.preemption_state.enabled ); 691 __cfaabi_dbg_print_safe("Kernel : Starting\n"); 692 693 __page_size = sysconf( _SC_PAGESIZE ); 694 695 __cfa_dbg_global_clusters.list{ __get }; 696 __cfa_dbg_global_clusters.lock{}; 697 698 // Initialize the main cluster 699 mainCluster = (cluster *)&storage_mainCluster; 700 (*mainCluster){"Main Cluster"}; 701 702 __cfaabi_dbg_print_safe("Kernel : Main cluster ready\n"); 703 704 // Start by initializing the main thread 705 // SKULLDUGGERY: the mainThread steals the process main thread 706 // which will then be scheduled by the mainProcessor normally 707 mainThread = (thread_desc *)&storage_mainThread; 708 current_stack_info_t info; 709 info.storage = (__stack_t*)&storage_mainThreadCtx; 710 (*mainThread){ &info }; 711 712 __cfaabi_dbg_print_safe("Kernel : Main thread ready\n"); 713 714 715 716 // Construct the processor context of the main processor 717 void ?{}(processorCtx_t & this, processor * proc) { 718 (this.__cor){ "Processor" }; 719 this.__cor.starter = 0p; 720 this.proc = proc; 721 } 722 723 void ?{}(processor & this) with( this ) { 724 name = "Main Processor"; 725 cltr = mainCluster; 726 terminated{ 0 }; 727 do_terminate = false; 728 preemption_alarm = 0p; 729 pending_preemption = false; 730 kernel_thread = pthread_self(); 731 732 runner{ &this }; 733 __cfaabi_dbg_print_safe("Kernel : constructed main processor context %p\n", &runner); 734 } 735 736 // Initialize the main processor and the main processor ctx 737 // (the coroutine that contains the processing control flow) 738 mainProcessor = (processor *)&storage_mainProcessor; 739 (*mainProcessor){}; 740 741 //initialize the global state variables 742 kernelTLS.this_processor = mainProcessor; 743 kernelTLS.this_thread = mainThread; 744 745 // Enable preemption 746 kernel_start_preemption(); 747 748 // Add the main thread to the ready queue 749 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread 750 ScheduleThread(mainThread); 751 752 // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX 753 // context. Hence, the main thread does not begin through CtxInvokeThread, like all other threads. The trick here is that 754 // mainThread is on the ready queue when this call is made. 755 kernel_first_resume( kernelTLS.this_processor ); 756 757 758 759 // THE SYSTEM IS NOW COMPLETELY RUNNING 760 __cfaabi_dbg_print_safe("Kernel : Started\n--------------------------------------------------\n\n"); 761 762 verify( ! kernelTLS.preemption_state.enabled ); 763 enable_interrupts( __cfaabi_dbg_ctx ); 764 verify( TL_GET( preemption_state.enabled ) ); 765 } 766 767 static void kernel_shutdown(void) { 768 __cfaabi_dbg_print_safe("\n--------------------------------------------------\nKernel : Shutting down\n"); 769 770 verify( TL_GET( preemption_state.enabled ) ); 461 771 disable_interrupts(); 462 /* paranoid */ verify( ! __preemption_enabled() ); 463 /* paranoid */ verify( kernelTLS().this_thread->preempted == __NO_PREEMPTION ); 464 465 returnToKernel(); 466 467 /* paranoid */ verify( ! __preemption_enabled() ); 468 enable_interrupts( __cfaabi_dbg_ctx ); 469 /* paranoid */ verify( __preemption_enabled() ); 470 471 } 472 473 extern "C" { 474 // Leave the thread monitor 475 // last routine called by a thread. 476 // Should never return 477 void __cfactx_thrd_leave() { 478 $thread * thrd = active_thread(); 479 $monitor * this = &thrd->self_mon; 480 481 // Lock the monitor now 482 lock( this->lock __cfaabi_dbg_ctx2 ); 483 484 disable_interrupts(); 485 486 /* paranoid */ verify( ! __preemption_enabled() ); 487 /* paranoid */ verify( thrd->state == Active ); 488 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary ); 489 /* paranoid */ verify( kernelTLS().this_thread == thrd ); 490 /* paranoid */ verify( thrd->context.SP ); 491 /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) > ((uintptr_t)__get_stack(thrd->curr_cor)->limit), "ERROR : $thread %p has been corrupted.\n StackPointer too large.\n", thrd ); 492 /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) < ((uintptr_t)__get_stack(thrd->curr_cor)->base ), "ERROR : $thread %p has been corrupted.\n StackPointer too small.\n", thrd ); 493 494 thrd->state = Halting; 495 if( TICKET_RUNNING != thrd->ticket ) { abort( "Thread terminated with pending unpark" ); } 496 if( thrd != this->owner ) { abort( "Thread internal monitor has incorrect owner" ); } 497 if( this->recursion != 1) { abort( "Thread internal monitor has unbalanced recursion" ); } 498 499 // Leave the thread 500 returnToKernel(); 501 502 // Control flow should never reach here! 503 abort(); 504 } 505 } 506 507 // KERNEL ONLY 508 bool force_yield( __Preemption_Reason reason ) { 509 /* paranoid */ verify( __preemption_enabled() ); 510 disable_interrupts(); 511 /* paranoid */ verify( ! __preemption_enabled() ); 512 513 $thread * thrd = kernelTLS().this_thread; 514 /* paranoid */ verify(thrd->state == Active); 515 516 // SKULLDUGGERY: It is possible that we are preempting this thread just before 517 // it was going to park itself. If that is the case and it is already using the 518 // intrusive fields then we can't use them to preempt the thread 519 // If that is the case, abandon the preemption. 520 bool preempted = false; 521 if(thrd->link.next == 0p) { 522 preempted = true; 523 thrd->preempted = reason; 524 returnToKernel(); 525 } 526 527 /* paranoid */ verify( ! __preemption_enabled() ); 528 enable_interrupts_noPoll(); 529 /* paranoid */ verify( __preemption_enabled() ); 530 531 return preempted; 772 verify( ! kernelTLS.preemption_state.enabled ); 773 774 // SKULLDUGGERY: Notify the mainProcessor it needs to terminates. 775 // When its coroutine terminates, it return control to the mainThread 776 // which is currently here 777 __atomic_store_n(&mainProcessor->do_terminate, true, __ATOMIC_RELEASE); 778 kernel_last_resume( kernelTLS.this_processor ); 779 mainThread->self_cor.state = Halted; 780 781 // THE SYSTEM IS NOW COMPLETELY STOPPED 782 783 // Disable preemption 784 kernel_stop_preemption(); 785 786 // Destroy the main processor and its context in reverse order of construction 787 // These were manually constructed so we need manually destroy them 788 ^(mainProcessor->runner){}; 789 ^(mainProcessor){}; 790 791 // Final step, destroy the main thread since it is no longer needed 792 // Since we provided a stack to this taxk it will not destroy anything 793 ^(mainThread){}; 794 795 ^(__cfa_dbg_global_clusters.list){}; 796 ^(__cfa_dbg_global_clusters.lock){}; 797 798 __cfaabi_dbg_print_safe("Kernel : Shutdown complete\n"); 532 799 } 533 800 534 801 //============================================================================================= 535 // Kernel Idle Sleep802 // Kernel Quiescing 536 803 //============================================================================================= 537 // Wake a thread from the front if there are any 538 static void __wake_one(cluster * this) { 539 /* paranoid */ verify( ! __preemption_enabled() ); 540 /* paranoid */ verify( ready_schedule_islocked() ); 541 542 // Check if there is a sleeping processor 543 processor * p; 544 unsigned idle; 545 unsigned total; 546 [idle, total, p] = query(this->idles); 547 548 // If no one is sleeping, we are done 549 if( idle == 0 ) return; 550 551 // We found a processor, wake it up 552 post( p->idle ); 553 554 #if !defined(__CFA_NO_STATISTICS__) 555 __tls_stats()->ready.sleep.wakes++; 556 #endif 557 558 /* paranoid */ verify( ready_schedule_islocked() ); 559 /* paranoid */ verify( ! __preemption_enabled() ); 560 561 return; 562 } 563 564 // Unconditionnaly wake a thread 565 void __wake_proc(processor * this) { 566 __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this); 567 568 disable_interrupts(); 569 /* paranoid */ verify( ! __preemption_enabled() ); 570 post( this->idle ); 571 enable_interrupts( __cfaabi_dbg_ctx ); 572 } 573 574 static void push (__cluster_idles & this, processor & proc) { 575 /* paranoid */ verify( ! __preemption_enabled() ); 576 lock( this ); 577 this.idle++; 578 /* paranoid */ verify( this.idle <= this.total ); 579 580 insert_first(this.list, proc); 581 unlock( this ); 582 /* paranoid */ verify( ! __preemption_enabled() ); 583 } 584 585 static void remove(__cluster_idles & this, processor & proc) { 586 /* paranoid */ verify( ! __preemption_enabled() ); 587 lock( this ); 588 this.idle--; 589 /* paranoid */ verify( this.idle >= 0 ); 590 591 remove(proc); 592 unlock( this ); 593 /* paranoid */ verify( ! __preemption_enabled() ); 594 } 595 596 static [unsigned idle, unsigned total, * processor] query( & __cluster_idles this ) { 597 for() { 598 uint64_t l = __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST); 599 if( 1 == (l % 2) ) { Pause(); continue; } 600 unsigned idle = this.idle; 601 unsigned total = this.total; 602 processor * proc = &this.list`first; 603 // Compiler fence is unnecessary, but gcc-8 and older incorrectly reorder code without it 604 asm volatile("": : :"memory"); 605 if(l != __atomic_load_n(&this.lock, __ATOMIC_SEQ_CST)) { Pause(); continue; } 606 return [idle, total, proc]; 804 static void halt(processor * this) with( *this ) { 805 // verify( ! __atomic_load_n(&do_terminate, __ATOMIC_SEQ_CST) ); 806 807 with( *cltr ) { 808 lock (proc_list_lock __cfaabi_dbg_ctx2); 809 remove (procs, *this); 810 push_front(idles, *this); 811 unlock (proc_list_lock); 812 } 813 814 __cfaabi_dbg_print_safe("Kernel : Processor %p ready to sleep\n", this); 815 816 wait( idleLock ); 817 818 __cfaabi_dbg_print_safe("Kernel : Processor %p woke up and ready to run\n", this); 819 820 with( *cltr ) { 821 lock (proc_list_lock __cfaabi_dbg_ctx2); 822 remove (idles, *this); 823 push_front(procs, *this); 824 unlock (proc_list_lock); 607 825 } 608 826 } … … 618 836 // the globalAbort flag is true. 619 837 lock( kernel_abort_lock __cfaabi_dbg_ctx2 ); 620 621 // disable interrupts, it no longer makes sense to try to interrupt this processor622 disable_interrupts();623 838 624 839 // first task to abort ? … … 638 853 } 639 854 640 return __cfaabi_tls.this_thread;855 return kernelTLS.this_thread; 641 856 } 642 857 643 858 void kernel_abort_msg( void * kernel_data, char * abort_text, int abort_text_size ) { 644 $thread * thrd = ( $thread * )kernel_data;859 thread_desc * thrd = kernel_data; 645 860 646 861 if(thrd) { … … 663 878 664 879 int kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) { 665 return get_coroutine(kernelTLS ().this_thread) == get_coroutine(mainThread) ? 4 : 2;880 return get_coroutine(kernelTLS.this_thread) == get_coroutine(mainThread) ? 4 : 2; 666 881 } 667 882 … … 690 905 void ^?{}(semaphore & this) {} 691 906 692 boolP(semaphore & this) with( this ){907 void P(semaphore & this) with( this ){ 693 908 lock( lock __cfaabi_dbg_ctx2 ); 694 909 count -= 1; 695 910 if ( count < 0 ) { 696 911 // queue current task 697 append( waiting, active_thread());912 append( waiting, kernelTLS.this_thread ); 698 913 699 914 // atomically release spin lock and block 700 unlock( lock ); 701 park(); 702 return true; 915 BlockInternal( &lock ); 703 916 } 704 917 else { 705 918 unlock( lock ); 706 return false; 707 } 708 } 709 710 bool V(semaphore & this) with( this ) { 711 $thread * thrd = 0p; 919 } 920 } 921 922 void V(semaphore & this) with( this ) { 923 thread_desc * thrd = 0p; 712 924 lock( lock __cfaabi_dbg_ctx2 ); 713 925 count += 1; … … 720 932 721 933 // make new owner 722 unpark( thrd ); 723 724 return thrd != 0p; 725 } 726 727 bool V(semaphore & this, unsigned diff) with( this ) { 728 $thread * thrd = 0p; 729 lock( lock __cfaabi_dbg_ctx2 ); 730 int release = max(-count, (int)diff); 731 count += diff; 732 for(release) { 733 unpark( pop_head( waiting ) ); 734 } 735 736 unlock( lock ); 737 738 return thrd != 0p; 934 WakeThread( thrd ); 935 } 936 937 //----------------------------------------------------------------------------- 938 // Global Queues 939 void doregister( cluster & cltr ) { 940 lock ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2); 941 push_front( __cfa_dbg_global_clusters.list, cltr ); 942 unlock ( __cfa_dbg_global_clusters.lock ); 943 } 944 945 void unregister( cluster & cltr ) { 946 lock ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2); 947 remove( __cfa_dbg_global_clusters.list, cltr ); 948 unlock( __cfa_dbg_global_clusters.lock ); 949 } 950 951 void doregister( cluster * cltr, thread_desc & thrd ) { 952 lock (cltr->thread_list_lock __cfaabi_dbg_ctx2); 953 cltr->nthreads += 1; 954 push_front(cltr->threads, thrd); 955 unlock (cltr->thread_list_lock); 956 } 957 958 void unregister( cluster * cltr, thread_desc & thrd ) { 959 lock (cltr->thread_list_lock __cfaabi_dbg_ctx2); 960 remove(cltr->threads, thrd ); 961 cltr->nthreads -= 1; 962 unlock(cltr->thread_list_lock); 963 } 964 965 void doregister( cluster * cltr, processor * proc ) { 966 lock (cltr->proc_list_lock __cfaabi_dbg_ctx2); 967 cltr->nprocessors += 1; 968 push_front(cltr->procs, *proc); 969 unlock (cltr->proc_list_lock); 970 } 971 972 void unregister( cluster * cltr, processor * proc ) { 973 lock (cltr->proc_list_lock __cfaabi_dbg_ctx2); 974 remove(cltr->procs, *proc ); 975 cltr->nprocessors -= 1; 976 unlock(cltr->proc_list_lock); 739 977 } 740 978 … … 743 981 __cfaabi_dbg_debug_do( 744 982 extern "C" { 745 void __cfaabi_dbg_record _lock(__spinlock_t & this, const char prev_name[]) {983 void __cfaabi_dbg_record(__spinlock_t & this, const char * prev_name) { 746 984 this.prev_name = prev_name; 747 this.prev_thrd = kernelTLS ().this_thread;985 this.prev_thrd = kernelTLS.this_thread; 748 986 } 749 987 } … … 752 990 //----------------------------------------------------------------------------- 753 991 // Debug 754 bool threading_enabled(void) __attribute__((const)){992 bool threading_enabled(void) { 755 993 return true; 756 994 } 757 758 //-----------------------------------------------------------------------------759 // Statistics760 #if !defined(__CFA_NO_STATISTICS__)761 void print_halts( processor & this ) {762 this.print_halts = true;763 }764 765 void print_stats_now( cluster & this, int flags ) {766 __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this );767 }768 769 extern int __print_alarm_stats;770 void print_alarm_stats() {771 __print_alarm_stats = -1;772 }773 #endif774 995 // Local Variables: // 775 996 // mode: c // -
libcfa/src/concurrency/kernel.hfa
reef8dfb rbdfc032 10 10 // Created On : Tue Jan 17 12:27:26 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 12:29:26 202013 // Update Count : 2212 // Last Modified On : Wed Dec 4 07:54:51 2019 13 // Update Count : 18 14 14 // 15 15 16 16 #pragma once 17 18 #include <stdbool.h> 17 19 18 20 #include "invoke.h" … … 20 22 #include "coroutine.hfa" 21 23 22 #include "containers/list.hfa"23 24 24 extern "C" { 25 #include <bits/pthreadtypes.h>26 #include <linux/types.h>25 #include <pthread.h> 26 #include <semaphore.h> 27 27 } 28 28 … … 32 32 __spinlock_t lock; 33 33 int count; 34 __queue_t( $thread) waiting;34 __queue_t(thread_desc) waiting; 35 35 }; 36 36 37 37 void ?{}(semaphore & this, int count = 1); 38 38 void ^?{}(semaphore & this); 39 bool P (semaphore & this); 40 bool V (semaphore & this); 41 bool V (semaphore & this, unsigned count); 39 void P (semaphore & this); 40 void V (semaphore & this); 42 41 43 42 … … 46 45 extern struct cluster * mainCluster; 47 46 48 // Processor id, required for scheduling threads 49 struct __processor_id_t { 50 unsigned id:24; 51 bool full_proc:1; 52 53 #if !defined(__CFA_NO_STATISTICS__) 54 struct __stats_t * stats; 55 #endif 56 }; 57 47 enum FinishOpCode { No_Action, Release, Schedule, Release_Schedule, Release_Multi, Release_Multi_Schedule, Callback }; 48 49 typedef void (*__finish_callback_fptr_t)(void); 50 51 //TODO use union, many of these fields are mutually exclusive (i.e. MULTI vs NOMULTI) 52 struct FinishAction { 53 FinishOpCode action_code; 54 /* 55 // Union of possible actions 56 union { 57 // Option 1 : locks and threads 58 struct { 59 // 1 thread or N thread 60 union { 61 thread_desc * thrd; 62 struct { 63 thread_desc ** thrds; 64 unsigned short thrd_count; 65 }; 66 }; 67 // 1 lock or N lock 68 union { 69 __spinlock_t * lock; 70 struct { 71 __spinlock_t ** locks; 72 unsigned short lock_count; 73 }; 74 }; 75 }; 76 // Option 2 : action pointer 77 __finish_callback_fptr_t callback; 78 }; 79 /*/ 80 thread_desc * thrd; 81 thread_desc ** thrds; 82 unsigned short thrd_count; 83 __spinlock_t * lock; 84 __spinlock_t ** locks; 85 unsigned short lock_count; 86 __finish_callback_fptr_t callback; 87 //*/ 88 }; 89 static inline void ?{}(FinishAction & this) { 90 this.action_code = No_Action; 91 this.thrd = 0p; 92 this.lock = 0p; 93 } 94 static inline void ^?{}(FinishAction &) {} 95 96 // Processor 58 97 coroutine processorCtx_t { 59 98 struct processor * proc; … … 61 100 62 101 // Wrapper around kernel threads 63 struct __attribute__((aligned(128)))processor {102 struct processor { 64 103 // Main state 65 inline __processor_id_t; 104 // Coroutine ctx who does keeps the state of the processor 105 struct processorCtx_t runner; 66 106 67 107 // Cluster from which to get threads 68 108 struct cluster * cltr; 69 109 70 // Set to true to notify the processor should terminate71 volatile bool do_terminate;72 73 // Coroutine ctx who does keeps the state of the processor74 struct processorCtx_t runner;75 76 110 // Name of the processor 77 111 const char * name; … … 79 113 // Handle to pthreads 80 114 pthread_t kernel_thread; 115 116 // RunThread data 117 // Action to do after a thread is ran 118 struct FinishAction finish; 81 119 82 120 // Preemption data … … 87 125 bool pending_preemption; 88 126 89 // Idle lock (kernel semaphore) 90 __bin_sem_t idle; 91 92 // Termination synchronisation (user semaphore) 127 // Idle lock 128 __bin_sem_t idleLock; 129 130 // Termination 131 // Set to true to notify the processor should terminate 132 volatile bool do_terminate; 133 134 // Termination synchronisation 93 135 semaphore terminated; 94 136 … … 97 139 98 140 // Link lists fields 99 DLISTED_MGD_IMPL_IN(processor) 100 101 #if !defined(__CFA_NO_STATISTICS__) 102 int print_stats; 103 bool print_halts; 104 #endif 141 struct __dbg_node_proc { 142 struct processor * next; 143 struct processor * prev; 144 } node; 105 145 106 146 #ifdef __CFA_DEBUG__ … … 110 150 }; 111 151 112 void ?{}(processor & this, const char name[], struct cluster & cltr);152 void ?{}(processor & this, const char * name, struct cluster & cltr); 113 153 void ^?{}(processor & this); 114 154 115 155 static inline void ?{}(processor & this) { this{ "Anonymous Processor", *mainCluster}; } 116 156 static inline void ?{}(processor & this, struct cluster & cltr) { this{ "Anonymous Processor", cltr}; } 117 static inline void ?{}(processor & this, const char name[]) { this{name, *mainCluster }; } 118 119 DLISTED_MGD_IMPL_OUT(processor) 120 121 //----------------------------------------------------------------------------- 122 // I/O 123 struct __io_data; 124 125 // IO poller user-thread 126 // Not using the "thread" keyword because we want to control 127 // more carefully when to start/stop it 128 struct $io_ctx_thread { 129 struct __io_data * ring; 130 single_sem sem; 131 volatile bool done; 132 $thread self; 133 }; 134 135 136 struct io_context { 137 $io_ctx_thread thrd; 138 }; 139 140 struct io_context_params { 141 int num_entries; 142 int num_ready; 143 int submit_aff; 144 bool eager_submits:1; 145 bool poller_submits:1; 146 bool poll_submit:1; 147 bool poll_complete:1; 148 }; 149 150 void ?{}(io_context_params & this); 151 152 void ?{}(io_context & this, struct cluster & cl); 153 void ?{}(io_context & this, struct cluster & cl, const io_context_params & params); 154 void ^?{}(io_context & this); 155 156 struct io_cancellation { 157 __u64 target; 158 }; 159 160 static inline void ?{}(io_cancellation & this) { this.target = -1u; } 161 static inline void ^?{}(io_cancellation &) {} 162 bool cancel(io_cancellation & this); 163 164 //----------------------------------------------------------------------------- 165 // Cluster Tools 166 167 // Intrusives lanes which are used by the relaxed ready queue 168 struct __attribute__((aligned(128))) __intrusive_lane_t; 169 void ?{}(__intrusive_lane_t & this); 170 void ^?{}(__intrusive_lane_t & this); 171 172 // Counter used for wether or not the lanes are all empty 173 struct __attribute__((aligned(128))) __snzi_node_t; 174 struct __snzi_t { 175 unsigned mask; 176 int root; 177 __snzi_node_t * nodes; 178 }; 179 180 void ?{}( __snzi_t & this, unsigned depth ); 181 void ^?{}( __snzi_t & this ); 182 183 //TODO adjust cache size to ARCHITECTURE 184 // Structure holding the relaxed ready queue 185 struct __ready_queue_t { 186 // Data tracking how many/which lanes are used 187 // Aligned to 128 for cache locality 188 __snzi_t snzi; 189 190 // Data tracking the actual lanes 191 // On a seperate cacheline from the used struct since 192 // used can change on each push/pop but this data 193 // only changes on shrink/grow 194 struct { 195 // Arary of lanes 196 __intrusive_lane_t * volatile data; 197 198 // Number of lanes (empty or not) 199 volatile size_t count; 200 } lanes; 201 }; 202 203 void ?{}(__ready_queue_t & this); 204 void ^?{}(__ready_queue_t & this); 205 206 // Idle Sleep 207 struct __cluster_idles { 208 // Spin lock protecting the queue 209 volatile uint64_t lock; 210 211 // Total number of processors 212 unsigned total; 213 214 // Total number of idle processors 215 unsigned idle; 216 217 // List of idle processors 218 dlist(processor, processor) list; 219 }; 157 static inline void ?{}(processor & this, const char * name) { this{name, *mainCluster }; } 158 159 static inline [processor *&, processor *& ] __get( processor & this ) { 160 return this.node.[next, prev]; 161 } 220 162 221 163 //----------------------------------------------------------------------------- 222 164 // Cluster 223 struct __attribute__((aligned(128))) cluster { 165 struct cluster { 166 // Ready queue locks 167 __spinlock_t ready_queue_lock; 168 224 169 // Ready queue for threads 225 __ ready_queue_tready_queue;170 __queue_t(thread_desc) ready_queue; 226 171 227 172 // Name of the cluster … … 231 176 Duration preemption_rate; 232 177 233 // List of idle processors 234 __cluster_idles idles; 178 // List of processors 179 __spinlock_t proc_list_lock; 180 __dllist_t(struct processor) procs; 181 __dllist_t(struct processor) idles; 182 unsigned int nprocessors; 235 183 236 184 // List of threads 237 185 __spinlock_t thread_list_lock; 238 __dllist_t(struct $thread) threads;186 __dllist_t(struct thread_desc) threads; 239 187 unsigned int nthreads; 240 188 … … 244 192 cluster * prev; 245 193 } node; 246 247 struct {248 io_context * ctxs;249 unsigned cnt;250 } io;251 252 #if !defined(__CFA_NO_STATISTICS__)253 struct __stats_t * stats;254 int print_stats;255 #endif256 194 }; 257 195 extern Duration default_preemption(); 258 196 259 void ?{} (cluster & this, const char name[], Duration preemption_rate, unsigned num_io, const io_context_params & io_params);197 void ?{} (cluster & this, const char * name, Duration preemption_rate); 260 198 void ^?{}(cluster & this); 261 199 262 static inline void ?{} (cluster & this) { io_context_params default_params; this{"Anonymous Cluster", default_preemption(), 1, default_params}; } 263 static inline void ?{} (cluster & this, Duration preemption_rate) { io_context_params default_params; this{"Anonymous Cluster", preemption_rate, 1, default_params}; } 264 static inline void ?{} (cluster & this, const char name[]) { io_context_params default_params; this{name, default_preemption(), 1, default_params}; } 265 static inline void ?{} (cluster & this, unsigned num_io) { io_context_params default_params; this{"Anonymous Cluster", default_preemption(), num_io, default_params}; } 266 static inline void ?{} (cluster & this, Duration preemption_rate, unsigned num_io) { io_context_params default_params; this{"Anonymous Cluster", preemption_rate, num_io, default_params}; } 267 static inline void ?{} (cluster & this, const char name[], unsigned num_io) { io_context_params default_params; this{name, default_preemption(), num_io, default_params}; } 268 static inline void ?{} (cluster & this, const io_context_params & io_params) { this{"Anonymous Cluster", default_preemption(), 1, io_params}; } 269 static inline void ?{} (cluster & this, Duration preemption_rate, const io_context_params & io_params) { this{"Anonymous Cluster", preemption_rate, 1, io_params}; } 270 static inline void ?{} (cluster & this, const char name[], const io_context_params & io_params) { this{name, default_preemption(), 1, io_params}; } 271 static inline void ?{} (cluster & this, unsigned num_io, const io_context_params & io_params) { this{"Anonymous Cluster", default_preemption(), num_io, io_params}; } 272 static inline void ?{} (cluster & this, Duration preemption_rate, unsigned num_io, const io_context_params & io_params) { this{"Anonymous Cluster", preemption_rate, num_io, io_params}; } 273 static inline void ?{} (cluster & this, const char name[], unsigned num_io, const io_context_params & io_params) { this{name, default_preemption(), num_io, io_params}; } 274 275 static inline [cluster *&, cluster *& ] __get( cluster & this ) __attribute__((const)) { return this.node.[next, prev]; } 276 277 static inline struct processor * active_processor() { return publicTLS_get( this_processor ); } // UNSAFE 278 static inline struct cluster * active_cluster () { return publicTLS_get( this_processor )->cltr; } 279 280 #if !defined(__CFA_NO_STATISTICS__) 281 void print_stats_now( cluster & this, int flags ); 282 283 static inline void print_stats_at_exit( cluster & this, int flags ) { 284 this.print_stats |= flags; 285 } 286 287 static inline void print_stats_at_exit( processor & this, int flags ) { 288 this.print_stats |= flags; 289 } 290 291 void print_halts( processor & this ); 292 #endif 200 static inline void ?{} (cluster & this) { this{"Anonymous Cluster", default_preemption()}; } 201 static inline void ?{} (cluster & this, Duration preemption_rate) { this{"Anonymous Cluster", preemption_rate}; } 202 static inline void ?{} (cluster & this, const char * name) { this{name, default_preemption()}; } 203 204 static inline [cluster *&, cluster *& ] __get( cluster & this ) { 205 return this.node.[next, prev]; 206 } 207 208 static inline struct processor * active_processor() { return TL_GET( this_processor ); } // UNSAFE 209 static inline struct cluster * active_cluster () { return TL_GET( this_processor )->cltr; } 293 210 294 211 // Local Variables: // -
libcfa/src/concurrency/kernel_private.hfa
reef8dfb rbdfc032 10 10 // Created On : Mon Feb 13 12:27:26 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Aug 12 08:21:33 202013 // Update Count : 912 // Last Modified On : Sat Nov 30 19:25:02 2019 13 // Update Count : 8 14 14 // 15 15 … … 20 20 21 21 #include "alarm.hfa" 22 #include "stats.hfa" 22 23 23 24 24 //----------------------------------------------------------------------------- 25 25 // Scheduler 26 27 struct __attribute__((aligned(128))) __scheduler_lock_id_t;28 26 29 27 extern "C" { … … 33 31 } 34 32 35 void __schedule_thread( $thread * ) 36 #if defined(NDEBUG) || (!defined(__CFA_DEBUG__) && !defined(__CFA_VERIFY__)) 37 __attribute__((nonnull (1))) 38 #endif 39 ; 33 void ScheduleThread( thread_desc * ); 34 static inline void WakeThread( thread_desc * thrd ) { 35 if( !thrd ) return; 40 36 41 extern bool __preemption_enabled();37 verify(thrd->state == Inactive); 42 38 43 //release/wake-up the following resources 44 void __thread_finish( $thread * thrd ); 39 disable_interrupts(); 40 ScheduleThread( thrd ); 41 enable_interrupts( __cfaabi_dbg_ctx ); 42 } 43 thread_desc * nextThread(cluster * this); 44 45 //Block current thread and release/wake-up the following resources 46 void BlockInternal(void); 47 void BlockInternal(__spinlock_t * lock); 48 void BlockInternal(thread_desc * thrd); 49 void BlockInternal(__spinlock_t * lock, thread_desc * thrd); 50 void BlockInternal(__spinlock_t * locks [], unsigned short count); 51 void BlockInternal(__spinlock_t * locks [], unsigned short count, thread_desc * thrds [], unsigned short thrd_count); 52 void BlockInternal(__finish_callback_fptr_t callback); 53 void LeaveThread(__spinlock_t * lock, thread_desc * thrd); 45 54 46 55 //----------------------------------------------------------------------------- … … 48 57 void main(processorCtx_t *); 49 58 50 void * __create_pthread( pthread_t *, void * (*)(void *), void * ); 51 void __destroy_pthread( pthread_t pthread, void * stack, void ** retval ); 59 void * create_pthread( pthread_t *, void * (*)(void *), void * ); 52 60 61 static inline void wake_fast(processor * this) { 62 __cfaabi_dbg_print_safe("Kernel : Waking up processor %p\n", this); 63 post( this->idleLock ); 64 } 53 65 66 static inline void wake(processor * this) { 67 disable_interrupts(); 68 wake_fast(this); 69 enable_interrupts( __cfaabi_dbg_ctx ); 70 } 54 71 55 extern cluster * mainCluster; 72 struct event_kernel_t { 73 alarm_list_t alarms; 74 __spinlock_t lock; 75 }; 76 77 extern event_kernel_t * event_kernel; 78 79 struct __cfa_kernel_preemption_state_t { 80 bool enabled; 81 bool in_progress; 82 unsigned short disable_count; 83 }; 84 85 extern volatile thread_local __cfa_kernel_preemption_state_t preemption_state __attribute__ ((tls_model ( "initial-exec" ))); 56 86 57 87 //----------------------------------------------------------------------------- 58 88 // Threads 59 89 extern "C" { 60 void __cfactx_invoke_thread(void (*main)(void *), void * this);90 void CtxInvokeThread(void (*main)(void *), void * this); 61 91 } 62 92 93 extern void ThreadCtxSwitch(coroutine_desc * src, coroutine_desc * dst); 94 63 95 __cfaabi_dbg_debug_do( 64 extern void __cfaabi_dbg_thread_register ( $thread* thrd );65 extern void __cfaabi_dbg_thread_unregister( $thread* thrd );96 extern void __cfaabi_dbg_thread_register ( thread_desc * thrd ); 97 extern void __cfaabi_dbg_thread_unregister( thread_desc * thrd ); 66 98 ) 67 68 #define TICKET_BLOCKED (-1) // thread is blocked69 #define TICKET_RUNNING ( 0) // thread is running70 #define TICKET_UNBLOCK ( 1) // thread should ignore next block71 99 72 100 //----------------------------------------------------------------------------- 73 101 // Utils 74 void doregister( struct cluster * cltr, struct $thread & thrd ); 75 void unregister( struct cluster * cltr, struct $thread & thrd ); 102 #define KERNEL_STORAGE(T,X) static char storage_##X[sizeof(T)] 76 103 77 //----------------------------------------------------------------------------- 78 // I/O 79 void ^?{}(io_context & this, bool ); 80 81 //======================================================================= 82 // Cluster lock API 83 //======================================================================= 84 // Cells use by the reader writer lock 85 // while not generic it only relies on a opaque pointer 86 struct __attribute__((aligned(128))) __scheduler_lock_id_t { 87 // Spin lock used as the underlying lock 88 volatile bool lock; 89 90 // Handle pointing to the proc owning this cell 91 // Used for allocating cells and debugging 92 __processor_id_t * volatile handle; 93 94 #ifdef __CFA_WITH_VERIFY__ 95 // Debug, check if this is owned for reading 96 bool owned; 97 #endif 98 }; 99 100 static_assert( sizeof(struct __scheduler_lock_id_t) <= __alignof(struct __scheduler_lock_id_t)); 101 102 // Lock-Free registering/unregistering of threads 103 // Register a processor to a given cluster and get its unique id in return 104 unsigned doregister( struct __processor_id_t * proc ); 105 106 // Unregister a processor from a given cluster using its id, getting back the original pointer 107 void unregister( struct __processor_id_t * proc ); 108 109 //----------------------------------------------------------------------- 110 // Cluster idle lock/unlock 111 static inline void lock(__cluster_idles & this) { 112 for() { 113 uint64_t l = this.lock; 114 if( 115 (0 == (l % 2)) 116 && __atomic_compare_exchange_n(&this.lock, &l, l + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 117 ) return; 118 Pause(); 119 } 104 static inline uint32_t tls_rand() { 105 kernelTLS.rand_seed ^= kernelTLS.rand_seed << 6; 106 kernelTLS.rand_seed ^= kernelTLS.rand_seed >> 21; 107 kernelTLS.rand_seed ^= kernelTLS.rand_seed << 7; 108 return kernelTLS.rand_seed; 120 109 } 121 110 122 static inline void unlock(__cluster_idles & this) {123 /* paranoid */ verify( 1 == (this.lock % 2) );124 __atomic_fetch_add( &this.lock, 1, __ATOMIC_SEQ_CST );125 }126 111 127 //======================================================================= 128 // Reader-writer lock implementation 129 // Concurrent with doregister/unregister, 130 // i.e., threads can be added at any point during or between the entry/exit 112 void doregister( struct cluster & cltr ); 113 void unregister( struct cluster & cltr ); 131 114 132 //----------------------------------------------------------------------- 133 // simple spinlock underlying the RWLock 134 // Blocking acquire 135 static inline void __atomic_acquire(volatile bool * ll) { 136 while( __builtin_expect(__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST), false) ) { 137 while(__atomic_load_n(ll, (int)__ATOMIC_RELAXED)) 138 Pause(); 139 } 140 /* paranoid */ verify(*ll); 141 } 115 void doregister( struct cluster * cltr, struct thread_desc & thrd ); 116 void unregister( struct cluster * cltr, struct thread_desc & thrd ); 142 117 143 // Non-Blocking acquire 144 static inline bool __atomic_try_acquire(volatile bool * ll) { 145 return !__atomic_exchange_n(ll, (bool)true, __ATOMIC_SEQ_CST); 146 } 147 148 // Release 149 static inline void __atomic_unlock(volatile bool * ll) { 150 /* paranoid */ verify(*ll); 151 __atomic_store_n(ll, (bool)false, __ATOMIC_RELEASE); 152 } 153 154 //----------------------------------------------------------------------- 155 // Reader-Writer lock protecting the ready-queues 156 // while this lock is mostly generic some aspects 157 // have been hard-coded to for the ready-queue for 158 // simplicity and performance 159 struct __scheduler_RWLock_t { 160 // total cachelines allocated 161 unsigned int max; 162 163 // cachelines currently in use 164 volatile unsigned int alloc; 165 166 // cachelines ready to itereate over 167 // (!= to alloc when thread is in second half of doregister) 168 volatile unsigned int ready; 169 170 // writer lock 171 volatile bool lock; 172 173 // data pointer 174 __scheduler_lock_id_t * data; 175 }; 176 177 void ?{}(__scheduler_RWLock_t & this); 178 void ^?{}(__scheduler_RWLock_t & this); 179 180 extern __scheduler_RWLock_t * __scheduler_lock; 181 182 //----------------------------------------------------------------------- 183 // Reader side : acquire when using the ready queue to schedule but not 184 // creating/destroying queues 185 static inline void ready_schedule_lock(void) with(*__scheduler_lock) { 186 /* paranoid */ verify( ! __preemption_enabled() ); 187 /* paranoid */ verify( kernelTLS().this_proc_id ); 188 189 unsigned iproc = kernelTLS().this_proc_id->id; 190 /*paranoid*/ verify(data[iproc].handle == kernelTLS().this_proc_id); 191 /*paranoid*/ verify(iproc < ready); 192 193 // Step 1 : make sure no writer are in the middle of the critical section 194 while(__atomic_load_n(&lock, (int)__ATOMIC_RELAXED)) 195 Pause(); 196 197 // Fence needed because we don't want to start trying to acquire the lock 198 // before we read a false. 199 // Not needed on x86 200 // std::atomic_thread_fence(std::memory_order_seq_cst); 201 202 // Step 2 : acquire our local lock 203 __atomic_acquire( &data[iproc].lock ); 204 /*paranoid*/ verify(data[iproc].lock); 205 206 #ifdef __CFA_WITH_VERIFY__ 207 // Debug, check if this is owned for reading 208 data[iproc].owned = true; 209 #endif 210 } 211 212 static inline void ready_schedule_unlock(void) with(*__scheduler_lock) { 213 /* paranoid */ verify( ! __preemption_enabled() ); 214 /* paranoid */ verify( kernelTLS().this_proc_id ); 215 216 unsigned iproc = kernelTLS().this_proc_id->id; 217 /*paranoid*/ verify(data[iproc].handle == kernelTLS().this_proc_id); 218 /*paranoid*/ verify(iproc < ready); 219 /*paranoid*/ verify(data[iproc].lock); 220 /*paranoid*/ verify(data[iproc].owned); 221 #ifdef __CFA_WITH_VERIFY__ 222 // Debug, check if this is owned for reading 223 data[iproc].owned = false; 224 #endif 225 __atomic_unlock(&data[iproc].lock); 226 } 227 228 #ifdef __CFA_WITH_VERIFY__ 229 static inline bool ready_schedule_islocked(void) { 230 /* paranoid */ verify( ! __preemption_enabled() ); 231 /*paranoid*/ verify( kernelTLS().this_proc_id ); 232 __processor_id_t * proc = kernelTLS().this_proc_id; 233 return __scheduler_lock->data[proc->id].owned; 234 } 235 236 static inline bool ready_mutate_islocked() { 237 return __scheduler_lock->lock; 238 } 239 #endif 240 241 //----------------------------------------------------------------------- 242 // Writer side : acquire when changing the ready queue, e.g. adding more 243 // queues or removing them. 244 uint_fast32_t ready_mutate_lock( void ); 245 246 void ready_mutate_unlock( uint_fast32_t /* value returned by lock */ ); 247 248 //======================================================================= 249 // Ready-Queue API 250 //----------------------------------------------------------------------- 251 // pop thread from the ready queue of a cluster 252 // returns 0p if empty 253 __attribute__((hot)) bool query(struct cluster * cltr); 254 255 //----------------------------------------------------------------------- 256 // push thread onto a ready queue for a cluster 257 // returns true if the list was previously empty, false otherwise 258 __attribute__((hot)) bool push(struct cluster * cltr, struct $thread * thrd); 259 260 //----------------------------------------------------------------------- 261 // pop thread from the ready queue of a cluster 262 // returns 0p if empty 263 // May return 0p spuriously 264 __attribute__((hot)) struct $thread * pop(struct cluster * cltr); 265 266 //----------------------------------------------------------------------- 267 // pop thread from the ready queue of a cluster 268 // returns 0p if empty 269 // guaranteed to find any threads added before this call 270 __attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr); 271 272 //----------------------------------------------------------------------- 273 // remove thread from the ready queue of a cluster 274 // returns bool if it wasn't found 275 bool remove_head(struct cluster * cltr, struct $thread * thrd); 276 277 //----------------------------------------------------------------------- 278 // Increase the width of the ready queue (number of lanes) by 4 279 void ready_queue_grow (struct cluster * cltr, int target); 280 281 //----------------------------------------------------------------------- 282 // Decrease the width of the ready queue (number of lanes) by 4 283 void ready_queue_shrink(struct cluster * cltr, int target); 284 118 void doregister( struct cluster * cltr, struct processor * proc ); 119 void unregister( struct cluster * cltr, struct processor * proc ); 285 120 286 121 // Local Variables: // -
libcfa/src/concurrency/monitor.cfa
reef8dfb rbdfc032 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // $monitor.c --7 // monitor_desc.c -- 8 8 // 9 9 // Author : Thierry Delisle … … 27 27 //----------------------------------------------------------------------------- 28 28 // Forward declarations 29 static inline void __set_owner ( $monitor * this, $thread* owner );30 static inline void __set_owner ( $monitor * storage [], __lock_size_t count, $thread* owner );31 static inline void set_mask ( $monitor* storage [], __lock_size_t count, const __waitfor_mask_t & mask );32 static inline void reset_mask( $monitor* this );33 34 static inline $thread * next_thread( $monitor* this );35 static inline bool is_accepted( $monitor* this, const __monitor_group_t & monitors );29 static inline void set_owner ( monitor_desc * this, thread_desc * owner ); 30 static inline void set_owner ( monitor_desc * storage [], __lock_size_t count, thread_desc * owner ); 31 static inline void set_mask ( monitor_desc * storage [], __lock_size_t count, const __waitfor_mask_t & mask ); 32 static inline void reset_mask( monitor_desc * this ); 33 34 static inline thread_desc * next_thread( monitor_desc * this ); 35 static inline bool is_accepted( monitor_desc * this, const __monitor_group_t & monitors ); 36 36 37 37 static inline void lock_all ( __spinlock_t * locks [], __lock_size_t count ); 38 static inline void lock_all ( $monitor* source [], __spinlock_t * /*out*/ locks [], __lock_size_t count );38 static inline void lock_all ( monitor_desc * source [], __spinlock_t * /*out*/ locks [], __lock_size_t count ); 39 39 static inline void unlock_all( __spinlock_t * locks [], __lock_size_t count ); 40 static inline void unlock_all( $monitor* locks [], __lock_size_t count );41 42 static inline void save ( $monitor* ctx [], __lock_size_t count, __spinlock_t * locks [], unsigned int /*out*/ recursions [], __waitfor_mask_t /*out*/ masks [] );43 static inline void restore( $monitor* ctx [], __lock_size_t count, __spinlock_t * locks [], unsigned int /*in */ recursions [], __waitfor_mask_t /*in */ masks [] );44 45 static inline void init ( __lock_size_t count, $monitor* monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] );46 static inline void init_push( __lock_size_t count, $monitor* monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] );47 48 static inline $thread* check_condition ( __condition_criterion_t * );40 static inline void unlock_all( monitor_desc * locks [], __lock_size_t count ); 41 42 static inline void save ( monitor_desc * ctx [], __lock_size_t count, __spinlock_t * locks [], unsigned int /*out*/ recursions [], __waitfor_mask_t /*out*/ masks [] ); 43 static inline void restore( monitor_desc * ctx [], __lock_size_t count, __spinlock_t * locks [], unsigned int /*in */ recursions [], __waitfor_mask_t /*in */ masks [] ); 44 45 static inline void init ( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] ); 46 static inline void init_push( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] ); 47 48 static inline thread_desc * check_condition ( __condition_criterion_t * ); 49 49 static inline void brand_condition ( condition & ); 50 static inline [ $thread *, int] search_entry_queue( const __waitfor_mask_t &, $monitor* monitors [], __lock_size_t count );50 static inline [thread_desc *, int] search_entry_queue( const __waitfor_mask_t &, monitor_desc * monitors [], __lock_size_t count ); 51 51 52 52 forall(dtype T | sized( T )) 53 53 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ); 54 54 static inline __lock_size_t count_max ( const __waitfor_mask_t & mask ); 55 static inline __lock_size_t aggregate ( $monitor* storage [], const __waitfor_mask_t & mask );55 static inline __lock_size_t aggregate ( monitor_desc * storage [], const __waitfor_mask_t & mask ); 56 56 57 57 //----------------------------------------------------------------------------- … … 68 68 69 69 #define monitor_ctx( mons, cnt ) /* Define that create the necessary struct for internal/external scheduling operations */ \ 70 $monitor** monitors = mons; /* Save the targeted monitors */ \70 monitor_desc ** monitors = mons; /* Save the targeted monitors */ \ 71 71 __lock_size_t count = cnt; /* Save the count to a local variable */ \ 72 72 unsigned int recursions[ count ]; /* Save the current recursion levels to restore them later */ \ … … 80 80 //----------------------------------------------------------------------------- 81 81 // Enter/Leave routines 82 // Enter single monitor 83 static void __enter( $monitor * this, const __monitor_group_t & group ) { 84 $thread * thrd = active_thread(); 85 86 // Lock the monitor spinlock 87 lock( this->lock __cfaabi_dbg_ctx2 ); 88 89 __cfaabi_dbg_print_safe( "Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner); 90 91 if( unlikely(0 != (0x1 & (uintptr_t)this->owner)) ) { 92 abort( "Attempt by thread \"%.256s\" (%p) to access joined monitor %p.", thrd->self_cor.name, thrd, this ); 93 } 94 else if( !this->owner ) { 95 // No one has the monitor, just take it 96 __set_owner( this, thrd ); 97 98 __cfaabi_dbg_print_safe( "Kernel : mon is free \n" ); 99 } 100 else if( this->owner == thrd) { 101 // We already have the monitor, just note how many times we took it 102 this->recursion += 1; 103 104 __cfaabi_dbg_print_safe( "Kernel : mon already owned \n" ); 105 } 106 else if( is_accepted( this, group) ) { 107 // Some one was waiting for us, enter 108 __set_owner( this, thrd ); 109 110 // Reset mask 111 reset_mask( this ); 112 113 __cfaabi_dbg_print_safe( "Kernel : mon accepts \n" ); 114 } 115 else { 116 __cfaabi_dbg_print_safe( "Kernel : blocking \n" ); 117 118 // Some one else has the monitor, wait in line for it 119 /* paranoid */ verify( thrd->link.next == 0p ); 120 append( this->entry_queue, thrd ); 121 /* paranoid */ verify( thrd->link.next == 1p ); 122 123 unlock( this->lock ); 124 park(); 82 83 84 extern "C" { 85 // Enter single monitor 86 static void __enter_monitor_desc( monitor_desc * this, const __monitor_group_t & group ) { 87 // Lock the monitor spinlock 88 lock( this->lock __cfaabi_dbg_ctx2 ); 89 // Interrupts disable inside critical section 90 thread_desc * thrd = kernelTLS.this_thread; 91 92 __cfaabi_dbg_print_safe( "Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner); 93 94 if( !this->owner ) { 95 // No one has the monitor, just take it 96 set_owner( this, thrd ); 97 98 __cfaabi_dbg_print_safe( "Kernel : mon is free \n" ); 99 } 100 else if( this->owner == thrd) { 101 // We already have the monitor, just note how many times we took it 102 this->recursion += 1; 103 104 __cfaabi_dbg_print_safe( "Kernel : mon already owned \n" ); 105 } 106 else if( is_accepted( this, group) ) { 107 // Some one was waiting for us, enter 108 set_owner( this, thrd ); 109 110 // Reset mask 111 reset_mask( this ); 112 113 __cfaabi_dbg_print_safe( "Kernel : mon accepts \n" ); 114 } 115 else { 116 __cfaabi_dbg_print_safe( "Kernel : blocking \n" ); 117 118 // Some one else has the monitor, wait in line for it 119 append( this->entry_queue, thrd ); 120 121 BlockInternal( &this->lock ); 122 123 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 124 125 // BlockInternal will unlock spinlock, no need to unlock ourselves 126 return; 127 } 125 128 126 129 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 127 130 128 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 129 return; 130 } 131 132 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 133 134 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 135 /* paranoid */ verify( this->lock.lock ); 136 137 // Release the lock and leave 138 unlock( this->lock ); 139 return; 140 } 141 142 static void __dtor_enter( $monitor * this, fptr_t func, bool join ) { 143 $thread * thrd = active_thread(); 144 #if defined( __CFA_WITH_VERIFY__ ) 145 bool is_thrd = this == &thrd->self_mon; 146 #endif 147 148 // Lock the monitor spinlock 149 lock( this->lock __cfaabi_dbg_ctx2 ); 150 151 __cfaabi_dbg_print_safe( "Kernel : %10p Entering dtor for mon %p (%p)\n", thrd, this, this->owner); 152 153 154 if( !this->owner ) { 155 __cfaabi_dbg_print_safe( "Kernel : Destroying free mon %p\n", this); 156 157 // No one has the monitor, just take it 158 __set_owner( this, thrd ); 159 160 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 161 /* paranoid */ verify( !is_thrd || thrd->state == Halted || thrd->state == Cancelled ); 162 131 // Release the lock and leave 163 132 unlock( this->lock ); 164 133 return; 165 134 } 166 else if( this->owner == thrd && !join) { 167 // We already have the monitor... but where about to destroy it so the nesting will fail 168 // Abort! 169 abort( "Attempt to destroy monitor %p by thread \"%.256s\" (%p) in nested mutex.", this, thrd->self_cor.name, thrd ); 170 } 171 // SKULLDUGGERY: join will act as a dtor so it would normally trigger to above check 172 // because join will not release the monitor after it executed. 173 // to avoid that it sets the owner to the special value thrd | 1p before exiting 174 else if( this->owner == ($thread*)(1 | (uintptr_t)thrd) ) { 175 // restore the owner and just return 176 __cfaabi_dbg_print_safe( "Kernel : Destroying free mon %p\n", this); 177 178 // No one has the monitor, just take it 179 __set_owner( this, thrd ); 180 181 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 182 /* paranoid */ verify( !is_thrd || thrd->state == Halted || thrd->state == Cancelled ); 183 135 136 static void __enter_monitor_dtor( monitor_desc * this, fptr_t func ) { 137 // Lock the monitor spinlock 138 lock( this->lock __cfaabi_dbg_ctx2 ); 139 // Interrupts disable inside critical section 140 thread_desc * thrd = kernelTLS.this_thread; 141 142 __cfaabi_dbg_print_safe( "Kernel : %10p Entering dtor for mon %p (%p)\n", thrd, this, this->owner); 143 144 145 if( !this->owner ) { 146 __cfaabi_dbg_print_safe( "Kernel : Destroying free mon %p\n", this); 147 148 // No one has the monitor, just take it 149 set_owner( this, thrd ); 150 151 unlock( this->lock ); 152 return; 153 } 154 else if( this->owner == thrd) { 155 // We already have the monitor... but where about to destroy it so the nesting will fail 156 // Abort! 157 abort( "Attempt to destroy monitor %p by thread \"%.256s\" (%p) in nested mutex.", this, thrd->self_cor.name, thrd ); 158 } 159 160 __lock_size_t count = 1; 161 monitor_desc ** monitors = &this; 162 __monitor_group_t group = { &this, 1, func }; 163 if( is_accepted( this, group) ) { 164 __cfaabi_dbg_print_safe( "Kernel : mon accepts dtor, block and signal it \n" ); 165 166 // Wake the thread that is waiting for this 167 __condition_criterion_t * urgent = pop( this->signal_stack ); 168 verify( urgent ); 169 170 // Reset mask 171 reset_mask( this ); 172 173 // Create the node specific to this wait operation 174 wait_ctx_primed( thrd, 0 ) 175 176 // Some one else has the monitor, wait for him to finish and then run 177 BlockInternal( &this->lock, urgent->owner->waiting_thread ); 178 179 // Some one was waiting for us, enter 180 set_owner( this, thrd ); 181 } 182 else { 183 __cfaabi_dbg_print_safe( "Kernel : blocking \n" ); 184 185 wait_ctx( thrd, 0 ) 186 this->dtor_node = &waiter; 187 188 // Some one else has the monitor, wait in line for it 189 append( this->entry_queue, thrd ); 190 BlockInternal( &this->lock ); 191 192 // BlockInternal will unlock spinlock, no need to unlock ourselves 193 return; 194 } 195 196 __cfaabi_dbg_print_safe( "Kernel : Destroying %p\n", this); 197 198 } 199 200 // Leave single monitor 201 void __leave_monitor_desc( monitor_desc * this ) { 202 // Lock the monitor spinlock 203 lock( this->lock __cfaabi_dbg_ctx2 ); 204 205 __cfaabi_dbg_print_safe( "Kernel : %10p Leaving mon %p (%p)\n", kernelTLS.this_thread, this, this->owner); 206 207 verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this ); 208 209 // Leaving a recursion level, decrement the counter 210 this->recursion -= 1; 211 212 // If we haven't left the last level of recursion 213 // it means we don't need to do anything 214 if( this->recursion != 0) { 215 __cfaabi_dbg_print_safe( "Kernel : recursion still %d\n", this->recursion); 216 unlock( this->lock ); 217 return; 218 } 219 220 // Get the next thread, will be null on low contention monitor 221 thread_desc * new_owner = next_thread( this ); 222 223 // We can now let other threads in safely 184 224 unlock( this->lock ); 185 return; 186 } 187 188 // The monitor is busy, if this is a thread and the thread owns itself, it better be active 189 /* paranoid */ verify( !is_thrd || this->owner != thrd || (thrd->state != Halted && thrd->state != Cancelled) ); 190 191 __lock_size_t count = 1; 192 $monitor ** monitors = &this; 193 __monitor_group_t group = { &this, 1, func }; 194 if( is_accepted( this, group) ) { 195 __cfaabi_dbg_print_safe( "Kernel : mon accepts dtor, block and signal it \n" ); 196 197 // Wake the thread that is waiting for this 198 __condition_criterion_t * urgent = pop( this->signal_stack ); 199 /* paranoid */ verify( urgent ); 200 201 // Reset mask 202 reset_mask( this ); 203 204 // Create the node specific to this wait operation 205 wait_ctx_primed( thrd, 0 ) 206 207 // Some one else has the monitor, wait for him to finish and then run 208 unlock( this->lock ); 209 210 // Release the next thread 211 /* paranoid */ verifyf( urgent->owner->waiting_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 212 unpark( urgent->owner->waiting_thread ); 213 214 // Park current thread waiting 215 park(); 216 217 // Some one was waiting for us, enter 218 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 219 220 __cfaabi_dbg_print_safe( "Kernel : Destroying %p\n", this); 221 return; 222 } 223 else { 224 __cfaabi_dbg_print_safe( "Kernel : blocking \n" ); 225 226 wait_ctx( thrd, 0 ) 227 this->dtor_node = &waiter; 228 229 // Some one else has the monitor, wait in line for it 230 /* paranoid */ verify( thrd->link.next == 0p ); 231 append( this->entry_queue, thrd ); 232 /* paranoid */ verify( thrd->link.next == 1p ); 233 unlock( this->lock ); 234 235 // Park current thread waiting 236 park(); 237 238 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 239 return; 240 } 241 } 242 243 // Leave single monitor 244 void __leave( $monitor * this ) { 245 // Lock the monitor spinlock 246 lock( this->lock __cfaabi_dbg_ctx2 ); 247 248 __cfaabi_dbg_print_safe( "Kernel : %10p Leaving mon %p (%p)\n", active_thread(), this, this->owner); 249 250 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 251 252 // Leaving a recursion level, decrement the counter 253 this->recursion -= 1; 254 255 // If we haven't left the last level of recursion 256 // it means we don't need to do anything 257 if( this->recursion != 0) { 258 __cfaabi_dbg_print_safe( "Kernel : recursion still %d\n", this->recursion); 259 unlock( this->lock ); 260 return; 261 } 262 263 // Get the next thread, will be null on low contention monitor 264 $thread * new_owner = next_thread( this ); 265 266 // Check the new owner is consistent with who we wake-up 267 // new_owner might be null even if someone owns the monitor when the owner is still waiting for another monitor 268 /* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this ); 269 270 // We can now let other threads in safely 271 unlock( this->lock ); 272 273 //We need to wake-up the thread 274 /* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this ); 275 unpark( new_owner ); 276 } 277 278 // Leave single monitor for the last time 279 void __dtor_leave( $monitor * this, bool join ) { 280 __cfaabi_dbg_debug_do( 281 if( active_thread() != this->owner ) { 282 abort( "Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, active_thread(), this->owner); 283 } 284 if( this->recursion != 1 && !join ) { 285 abort( "Destroyed monitor %p has %d outstanding nested calls.\n", this, this->recursion - 1); 286 } 287 ) 288 289 this->owner = ($thread*)(1 | (uintptr_t)this->owner); 290 } 291 292 void __thread_finish( $thread * thrd ) { 293 $monitor * this = &thrd->self_mon; 294 295 // Lock the monitor now 296 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary ); 297 /* paranoid */ verify( this->lock.lock ); 298 /* paranoid */ verify( thrd->context.SP ); 299 /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) > ((uintptr_t)__get_stack(thrd->curr_cor)->limit), "ERROR : $thread %p has been corrupted.\n StackPointer too large.\n", thrd ); 300 /* paranoid */ verifyf( ((uintptr_t)thrd->context.SP) < ((uintptr_t)__get_stack(thrd->curr_cor)->base ), "ERROR : $thread %p has been corrupted.\n StackPointer too small.\n", thrd ); 301 /* paranoid */ verify( ! __preemption_enabled() ); 302 303 /* paranoid */ verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this ); 304 /* paranoid */ verify( thrd->state == Halting ); 305 /* paranoid */ verify( this->recursion == 1 ); 306 307 // Leaving a recursion level, decrement the counter 308 this->recursion -= 1; 309 this->owner = 0p; 310 311 // Fetch the next thread, can be null 312 $thread * new_owner = next_thread( this ); 313 314 // Mark the state as fully halted 315 thrd->state = Halted; 316 317 // Release the monitor lock 318 unlock( this->lock ); 319 320 // Unpark the next owner if needed 321 /* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this ); 322 /* paranoid */ verify( ! __preemption_enabled() ); 323 /* paranoid */ verify( thrd->state == Halted ); 324 unpark( new_owner ); 225 226 //We need to wake-up the thread 227 WakeThread( new_owner ); 228 } 229 230 // Leave single monitor for the last time 231 void __leave_dtor_monitor_desc( monitor_desc * this ) { 232 __cfaabi_dbg_debug_do( 233 if( TL_GET( this_thread ) != this->owner ) { 234 abort( "Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, TL_GET( this_thread ), this->owner); 235 } 236 if( this->recursion != 1 ) { 237 abort( "Destroyed monitor %p has %d outstanding nested calls.\n", this, this->recursion - 1); 238 } 239 ) 240 } 241 242 // Leave the thread monitor 243 // last routine called by a thread. 244 // Should never return 245 void __leave_thread_monitor() { 246 thread_desc * thrd = TL_GET( this_thread ); 247 monitor_desc * this = &thrd->self_mon; 248 249 // Lock the monitor now 250 lock( this->lock __cfaabi_dbg_ctx2 ); 251 252 disable_interrupts(); 253 254 thrd->self_cor.state = Halted; 255 256 verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this ); 257 258 // Leaving a recursion level, decrement the counter 259 this->recursion -= 1; 260 261 // If we haven't left the last level of recursion 262 // it must mean there is an error 263 if( this->recursion != 0) { abort( "Thread internal monitor has unbalanced recursion" ); } 264 265 // Fetch the next thread, can be null 266 thread_desc * new_owner = next_thread( this ); 267 268 // Leave the thread, this will unlock the spinlock 269 // Use leave thread instead of BlockInternal which is 270 // specialized for this case and supports null new_owner 271 LeaveThread( &this->lock, new_owner ); 272 273 // Control flow should never reach here! 274 } 325 275 } 326 276 … … 329 279 static inline void enter( __monitor_group_t monitors ) { 330 280 for( __lock_size_t i = 0; i < monitors.size; i++) { 331 __enter ( monitors[i], monitors );281 __enter_monitor_desc( monitors[i], monitors ); 332 282 } 333 283 } … … 335 285 // Leave multiple monitor 336 286 // relies on the monitor array being sorted 337 static inline void leave( $monitor* monitors [], __lock_size_t count) {287 static inline void leave(monitor_desc * monitors [], __lock_size_t count) { 338 288 for( __lock_size_t i = count - 1; i >= 0; i--) { 339 __leave ( monitors[i] );289 __leave_monitor_desc( monitors[i] ); 340 290 } 341 291 } … … 343 293 // Ctor for monitor guard 344 294 // Sorts monitors before entering 345 void ?{}( monitor_guard_t & this, $monitor* m [], __lock_size_t count, fptr_t func ) {346 $thread * thrd = active_thread();295 void ?{}( monitor_guard_t & this, monitor_desc * m [], __lock_size_t count, fptr_t func ) { 296 thread_desc * thrd = TL_GET( this_thread ); 347 297 348 298 // Store current array … … 379 329 380 330 // Restore thread context 381 active_thread()->monitors = this.prev;331 TL_GET( this_thread )->monitors = this.prev; 382 332 } 383 333 384 334 // Ctor for monitor guard 385 335 // Sorts monitors before entering 386 void ?{}( monitor_dtor_guard_t & this, $monitor * m [], fptr_t func, bool join) {336 void ?{}( monitor_dtor_guard_t & this, monitor_desc * m [], fptr_t func ) { 387 337 // optimization 388 $thread * thrd = active_thread();338 thread_desc * thrd = TL_GET( this_thread ); 389 339 390 340 // Store current array … … 394 344 this.prev = thrd->monitors; 395 345 396 // Save whether we are in a join or not397 this.join = join;398 399 346 // Update thread context (needed for conditions) 400 347 (thrd->monitors){m, 1, func}; 401 348 402 __ dtor_enter( this.m, func, join);349 __enter_monitor_dtor( this.m, func ); 403 350 } 404 351 … … 406 353 void ^?{}( monitor_dtor_guard_t & this ) { 407 354 // Leave the monitors in order 408 __ dtor_leave( this.m, this.join);355 __leave_dtor_monitor_desc( this.m ); 409 356 410 357 // Restore thread context 411 active_thread()->monitors = this.prev;358 TL_GET( this_thread )->monitors = this.prev; 412 359 } 413 360 414 361 //----------------------------------------------------------------------------- 415 362 // Internal scheduling types 416 void ?{}(__condition_node_t & this, $thread* waiting_thread, __lock_size_t count, uintptr_t user_info ) {363 void ?{}(__condition_node_t & this, thread_desc * waiting_thread, __lock_size_t count, uintptr_t user_info ) { 417 364 this.waiting_thread = waiting_thread; 418 365 this.count = count; … … 428 375 } 429 376 430 void ?{}(__condition_criterion_t & this, $monitor* target, __condition_node_t & owner ) {377 void ?{}(__condition_criterion_t & this, monitor_desc * target, __condition_node_t & owner ) { 431 378 this.ready = false; 432 379 this.target = target; … … 449 396 450 397 // Create the node specific to this wait operation 451 wait_ctx( active_thread(), user_info );398 wait_ctx( TL_GET( this_thread ), user_info ); 452 399 453 400 // Append the current wait operation to the ones already queued on the condition 454 401 // We don't need locks for that since conditions must always be waited on inside monitor mutual exclusion 455 /* paranoid */ verify( waiter.next == 0p );456 402 append( this.blocked, &waiter ); 457 /* paranoid */ verify( waiter.next == 1p );458 403 459 404 // Lock all monitors (aggregates the locks as well) … … 462 407 // Find the next thread(s) to run 463 408 __lock_size_t thread_count = 0; 464 $thread* threads[ count ];409 thread_desc * threads[ count ]; 465 410 __builtin_memset( threads, 0, sizeof( threads ) ); 466 411 … … 470 415 // Remove any duplicate threads 471 416 for( __lock_size_t i = 0; i < count; i++) { 472 $thread* new_owner = next_thread( monitors[i] );417 thread_desc * new_owner = next_thread( monitors[i] ); 473 418 insert_unique( threads, thread_count, new_owner ); 474 419 } 475 420 476 // Unlock the locks, we don't need them anymore477 for(int i = 0; i < count; i++) {478 unlock( *locks[i] );479 }480 481 // Wake the threads482 for(int i = 0; i < thread_count; i++) {483 unpark( threads[i] );484 }485 486 421 // Everything is ready to go to sleep 487 park();422 BlockInternal( locks, count, threads, thread_count ); 488 423 489 424 // We are back, restore the owners and recursions … … 500 435 //Some more checking in debug 501 436 __cfaabi_dbg_debug_do( 502 $thread * this_thrd = active_thread();437 thread_desc * this_thrd = TL_GET( this_thread ); 503 438 if ( this.monitor_count != this_thrd->monitors.size ) { 504 439 abort( "Signal on condition %p made with different number of monitor(s), expected %zi got %zi", &this, this.monitor_count, this_thrd->monitors.size ); … … 548 483 549 484 // Create the node specific to this wait operation 550 wait_ctx_primed( active_thread(), 0 )485 wait_ctx_primed( kernelTLS.this_thread, 0 ) 551 486 552 487 //save contexts … … 554 489 555 490 //Find the thread to run 556 $thread* signallee = pop_head( this.blocked )->waiting_thread;557 __set_owner( monitors, count, signallee );491 thread_desc * signallee = pop_head( this.blocked )->waiting_thread; 492 set_owner( monitors, count, signallee ); 558 493 559 494 __cfaabi_dbg_print_buffer_decl( "Kernel : signal_block condition %p (s: %p)\n", &this, signallee ); 560 495 561 // unlock all the monitors562 unlock_all( locks, count );563 564 // unpark the thread we signalled565 unpark( signallee );566 567 496 //Everything is ready to go to sleep 568 park();497 BlockInternal( locks, count, &signallee, 1 ); 569 498 570 499 … … 607 536 // Create one! 608 537 __lock_size_t max = count_max( mask ); 609 $monitor* mon_storage[max];538 monitor_desc * mon_storage[max]; 610 539 __builtin_memset( mon_storage, 0, sizeof( mon_storage ) ); 611 540 __lock_size_t actual_count = aggregate( mon_storage, mask ); … … 625 554 { 626 555 // Check if the entry queue 627 $thread* next; int index;556 thread_desc * next; int index; 628 557 [next, index] = search_entry_queue( mask, monitors, count ); 629 558 … … 635 564 verifyf( accepted.size == 1, "ERROR: Accepted dtor has more than 1 mutex parameter." ); 636 565 637 $monitor* mon2dtor = accepted[0];566 monitor_desc * mon2dtor = accepted[0]; 638 567 verifyf( mon2dtor->dtor_node, "ERROR: Accepted monitor has no dtor_node." ); 639 568 … … 647 576 648 577 // Create the node specific to this wait operation 649 wait_ctx_primed( active_thread(), 0 );578 wait_ctx_primed( kernelTLS.this_thread, 0 ); 650 579 651 580 // Save monitor states … … 661 590 662 591 // Set the owners to be the next thread 663 __set_owner( monitors, count, next ); 664 665 // unlock all the monitors 666 unlock_all( locks, count ); 667 668 // unpark the thread we signalled 669 unpark( next ); 670 671 //Everything is ready to go to sleep 672 park(); 592 set_owner( monitors, count, next ); 593 594 // Everything is ready to go to sleep 595 BlockInternal( locks, count, &next, 1 ); 673 596 674 597 // We are back, restore the owners and recursions … … 699 622 700 623 // Create the node specific to this wait operation 701 wait_ctx_primed( active_thread(), 0 );624 wait_ctx_primed( kernelTLS.this_thread, 0 ); 702 625 703 626 monitor_save; … … 705 628 706 629 for( __lock_size_t i = 0; i < count; i++) { 707 verify( monitors[i]->owner == active_thread() ); 708 } 709 710 // unlock all the monitors 711 unlock_all( locks, count ); 630 verify( monitors[i]->owner == kernelTLS.this_thread ); 631 } 712 632 713 633 //Everything is ready to go to sleep 714 park();634 BlockInternal( locks, count ); 715 635 716 636 … … 729 649 // Utilities 730 650 731 static inline void __set_owner( $monitor * this, $thread* owner ) {732 / * paranoid */ verify( this->lock.lock);651 static inline void set_owner( monitor_desc * this, thread_desc * owner ) { 652 // __cfaabi_dbg_print_safe( "Kernal : Setting owner of %p to %p ( was %p)\n", this, owner, this->owner ); 733 653 734 654 //Pass the monitor appropriately … … 739 659 } 740 660 741 static inline void __set_owner( $monitor * monitors [], __lock_size_t count, $thread * owner ) { 742 /* paranoid */ verify ( monitors[0]->lock.lock ); 743 /* paranoid */ verifyf( monitors[0]->owner == active_thread(), "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), monitors[0]->owner, monitors[0]->recursion, monitors[0] ); 744 monitors[0]->owner = owner; 745 monitors[0]->recursion = 1; 661 static inline void set_owner( monitor_desc * monitors [], __lock_size_t count, thread_desc * owner ) { 662 monitors[0]->owner = owner; 663 monitors[0]->recursion = 1; 746 664 for( __lock_size_t i = 1; i < count; i++ ) { 747 /* paranoid */ verify ( monitors[i]->lock.lock ); 748 /* paranoid */ verifyf( monitors[i]->owner == active_thread(), "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), monitors[i]->owner, monitors[i]->recursion, monitors[i] ); 749 monitors[i]->owner = owner; 750 monitors[i]->recursion = 0; 751 } 752 } 753 754 static inline void set_mask( $monitor * storage [], __lock_size_t count, const __waitfor_mask_t & mask ) { 665 monitors[i]->owner = owner; 666 monitors[i]->recursion = 0; 667 } 668 } 669 670 static inline void set_mask( monitor_desc * storage [], __lock_size_t count, const __waitfor_mask_t & mask ) { 755 671 for( __lock_size_t i = 0; i < count; i++) { 756 672 storage[i]->mask = mask; … … 758 674 } 759 675 760 static inline void reset_mask( $monitor* this ) {676 static inline void reset_mask( monitor_desc * this ) { 761 677 this->mask.accepted = 0p; 762 678 this->mask.data = 0p; … … 764 680 } 765 681 766 static inline $thread * next_thread( $monitor* this ) {682 static inline thread_desc * next_thread( monitor_desc * this ) { 767 683 //Check the signaller stack 768 684 __cfaabi_dbg_print_safe( "Kernel : mon %p AS-stack top %p\n", this, this->signal_stack.top); … … 772 688 //regardless of if we are ready to baton pass, 773 689 //we need to set the monitor as in use 774 /* paranoid */ verifyf( !this->owner || active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 775 __set_owner( this, urgent->owner->waiting_thread ); 690 set_owner( this, urgent->owner->waiting_thread ); 776 691 777 692 return check_condition( urgent ); … … 780 695 // No signaller thread 781 696 // Get the next thread in the entry_queue 782 $thread * new_owner = pop_head( this->entry_queue ); 783 /* paranoid */ verifyf( !this->owner || active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 784 /* paranoid */ verify( !new_owner || new_owner->link.next == 0p ); 785 __set_owner( this, new_owner ); 697 thread_desc * new_owner = pop_head( this->entry_queue ); 698 set_owner( this, new_owner ); 786 699 787 700 return new_owner; 788 701 } 789 702 790 static inline bool is_accepted( $monitor* this, const __monitor_group_t & group ) {703 static inline bool is_accepted( monitor_desc * this, const __monitor_group_t & group ) { 791 704 __acceptable_t * it = this->mask.data; // Optim 792 705 __lock_size_t count = this->mask.size; … … 810 723 } 811 724 812 static inline void init( __lock_size_t count, $monitor* monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] ) {725 static inline void init( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] ) { 813 726 for( __lock_size_t i = 0; i < count; i++) { 814 727 (criteria[i]){ monitors[i], waiter }; … … 818 731 } 819 732 820 static inline void init_push( __lock_size_t count, $monitor* monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] ) {733 static inline void init_push( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria [] ) { 821 734 for( __lock_size_t i = 0; i < count; i++) { 822 735 (criteria[i]){ monitors[i], waiter }; … … 834 747 } 835 748 836 static inline void lock_all( $monitor* source [], __spinlock_t * /*out*/ locks [], __lock_size_t count ) {749 static inline void lock_all( monitor_desc * source [], __spinlock_t * /*out*/ locks [], __lock_size_t count ) { 837 750 for( __lock_size_t i = 0; i < count; i++ ) { 838 751 __spinlock_t * l = &source[i]->lock; … … 848 761 } 849 762 850 static inline void unlock_all( $monitor* locks [], __lock_size_t count ) {763 static inline void unlock_all( monitor_desc * locks [], __lock_size_t count ) { 851 764 for( __lock_size_t i = 0; i < count; i++ ) { 852 765 unlock( locks[i]->lock ); … … 855 768 856 769 static inline void save( 857 $monitor* ctx [],770 monitor_desc * ctx [], 858 771 __lock_size_t count, 859 772 __attribute((unused)) __spinlock_t * locks [], … … 868 781 869 782 static inline void restore( 870 $monitor* ctx [],783 monitor_desc * ctx [], 871 784 __lock_size_t count, 872 785 __spinlock_t * locks [], … … 886 799 // 2 - Checks if all the monitors are ready to run 887 800 // if so return the thread to run 888 static inline $thread* check_condition( __condition_criterion_t * target ) {801 static inline thread_desc * check_condition( __condition_criterion_t * target ) { 889 802 __condition_node_t * node = target->owner; 890 803 unsigned short count = node->count; … … 904 817 } 905 818 906 __cfaabi_dbg_print_safe( "Kernel : Runing %i (%p)\n", ready2run, ready2run ? (thread*)node->waiting_thread : (thread*)0p );819 __cfaabi_dbg_print_safe( "Kernel : Runing %i (%p)\n", ready2run, ready2run ? node->waiting_thread : 0p ); 907 820 return ready2run ? node->waiting_thread : 0p; 908 821 } 909 822 910 823 static inline void brand_condition( condition & this ) { 911 $thread * thrd = active_thread();824 thread_desc * thrd = TL_GET( this_thread ); 912 825 if( !this.monitors ) { 913 826 // __cfaabi_dbg_print_safe( "Branding\n" ); … … 915 828 this.monitor_count = thrd->monitors.size; 916 829 917 this.monitors = ( $monitor**)malloc( this.monitor_count * sizeof( *this.monitors ) );830 this.monitors = (monitor_desc **)malloc( this.monitor_count * sizeof( *this.monitors ) ); 918 831 for( int i = 0; i < this.monitor_count; i++ ) { 919 832 this.monitors[i] = thrd->monitors[i]; … … 922 835 } 923 836 924 static inline [ $thread *, int] search_entry_queue( const __waitfor_mask_t & mask, $monitor* monitors [], __lock_size_t count ) {925 926 __queue_t( $thread) & entry_queue = monitors[0]->entry_queue;837 static inline [thread_desc *, int] search_entry_queue( const __waitfor_mask_t & mask, monitor_desc * monitors [], __lock_size_t count ) { 838 839 __queue_t(thread_desc) & entry_queue = monitors[0]->entry_queue; 927 840 928 841 // For each thread in the entry-queue 929 for( $thread** thrd_it = &entry_queue.head;930 (*thrd_it) != 1p;931 thrd_it = &(*thrd_it)-> link.next842 for( thread_desc ** thrd_it = &entry_queue.head; 843 *thrd_it; 844 thrd_it = &(*thrd_it)->next 932 845 ) { 933 846 // For each acceptable check if it matches … … 971 884 } 972 885 973 static inline __lock_size_t aggregate( $monitor* storage [], const __waitfor_mask_t & mask ) {886 static inline __lock_size_t aggregate( monitor_desc * storage [], const __waitfor_mask_t & mask ) { 974 887 __lock_size_t size = 0; 975 888 for( __lock_size_t i = 0; i < mask.size; i++ ) { -
libcfa/src/concurrency/monitor.hfa
reef8dfb rbdfc032 23 23 24 24 trait is_monitor(dtype T) { 25 $monitor* get_monitor( T & );25 monitor_desc * get_monitor( T & ); 26 26 void ^?{}( T & mutex ); 27 27 }; 28 28 29 static inline void ?{}( $monitor& this) with( this ) {29 static inline void ?{}(monitor_desc & this) with( this ) { 30 30 lock{}; 31 31 entry_queue{}; … … 39 39 } 40 40 41 static inline void ^?{}( $monitor& ) {}41 static inline void ^?{}(monitor_desc & ) {} 42 42 43 43 struct monitor_guard_t { 44 $monitor** m;44 monitor_desc ** m; 45 45 __lock_size_t count; 46 46 __monitor_group_t prev; 47 47 }; 48 48 49 void ?{}( monitor_guard_t & this, $monitor** m, __lock_size_t count, void (*func)() );49 void ?{}( monitor_guard_t & this, monitor_desc ** m, __lock_size_t count, void (*func)() ); 50 50 void ^?{}( monitor_guard_t & this ); 51 51 52 52 struct monitor_dtor_guard_t { 53 $monitor* m;53 monitor_desc * m; 54 54 __monitor_group_t prev; 55 bool join;56 55 }; 57 56 58 void ?{}( monitor_dtor_guard_t & this, $monitor ** m, void (*func)(), bool join);57 void ?{}( monitor_dtor_guard_t & this, monitor_desc ** m, void (*func)() ); 59 58 void ^?{}( monitor_dtor_guard_t & this ); 60 59 … … 73 72 74 73 // The monitor this criterion concerns 75 $monitor* target;74 monitor_desc * target; 76 75 77 76 // The parent node to which this criterion belongs … … 88 87 struct __condition_node_t { 89 88 // Thread that needs to be woken when all criteria are met 90 $thread* waiting_thread;89 thread_desc * waiting_thread; 91 90 92 91 // Array of criteria (Criterions are contiguous in memory) … … 107 106 } 108 107 109 void ?{}(__condition_node_t & this, $thread* waiting_thread, __lock_size_t count, uintptr_t user_info );108 void ?{}(__condition_node_t & this, thread_desc * waiting_thread, __lock_size_t count, uintptr_t user_info ); 110 109 void ?{}(__condition_criterion_t & this ); 111 void ?{}(__condition_criterion_t & this, $monitor* target, __condition_node_t * owner );110 void ?{}(__condition_criterion_t & this, monitor_desc * target, __condition_node_t * owner ); 112 111 113 112 struct condition { … … 116 115 117 116 // Array of monitor pointers (Monitors are NOT contiguous in memory) 118 $monitor** monitors;117 monitor_desc ** monitors; 119 118 120 119 // Number of monitors in the array … … 132 131 133 132 void wait ( condition & this, uintptr_t user_info = 0 ); 134 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; }135 133 bool signal ( condition & this ); 136 134 bool signal_block( condition & this ); 137 static inline bool signal_all ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; }135 static inline bool is_empty ( condition & this ) { return !this.blocked.head; } 138 136 uintptr_t front ( condition & this ); 139 137 -
libcfa/src/concurrency/mutex.cfa
reef8dfb rbdfc032 30 30 this.lock{}; 31 31 this.blocked_threads{}; 32 this.is_locked = false;33 32 } 34 33 … … 40 39 lock( lock __cfaabi_dbg_ctx2 ); 41 40 if( is_locked ) { 42 append( blocked_threads, active_thread() ); 43 unlock( lock ); 44 park(); 41 append( blocked_threads, kernelTLS.this_thread ); 42 BlockInternal( &lock ); 45 43 } 46 44 else { … … 64 62 lock( this.lock __cfaabi_dbg_ctx2 ); 65 63 this.is_locked = (this.blocked_threads != 0); 66 unpark(64 WakeThread( 67 65 pop_head( this.blocked_threads ) 68 66 ); … … 86 84 lock( lock __cfaabi_dbg_ctx2 ); 87 85 if( owner == 0p ) { 88 owner = active_thread();86 owner = kernelTLS.this_thread; 89 87 recursion_count = 1; 90 88 unlock( lock ); 91 89 } 92 else if( owner == active_thread()) {90 else if( owner == kernelTLS.this_thread ) { 93 91 recursion_count++; 94 92 unlock( lock ); 95 93 } 96 94 else { 97 append( blocked_threads, active_thread() ); 98 unlock( lock ); 99 park(); 95 append( blocked_threads, kernelTLS.this_thread ); 96 BlockInternal( &lock ); 100 97 } 101 98 } … … 105 102 lock( lock __cfaabi_dbg_ctx2 ); 106 103 if( owner == 0p ) { 107 owner = active_thread();104 owner = kernelTLS.this_thread; 108 105 recursion_count = 1; 109 106 ret = true; 110 107 } 111 else if( owner == active_thread()) {108 else if( owner == kernelTLS.this_thread ) { 112 109 recursion_count++; 113 110 ret = true; … … 121 118 recursion_count--; 122 119 if( recursion_count == 0 ) { 123 $thread* thrd = pop_head( blocked_threads );120 thread_desc * thrd = pop_head( blocked_threads ); 124 121 owner = thrd; 125 122 recursion_count = (thrd ? 1 : 0); 126 unpark( thrd );123 WakeThread( thrd ); 127 124 } 128 125 unlock( lock ); … … 141 138 void notify_one(condition_variable & this) with(this) { 142 139 lock( lock __cfaabi_dbg_ctx2 ); 143 unpark(140 WakeThread( 144 141 pop_head( this.blocked_threads ) 145 142 ); … … 150 147 lock( lock __cfaabi_dbg_ctx2 ); 151 148 while(this.blocked_threads) { 152 unpark(149 WakeThread( 153 150 pop_head( this.blocked_threads ) 154 151 ); … … 159 156 void wait(condition_variable & this) { 160 157 lock( this.lock __cfaabi_dbg_ctx2 ); 161 append( this.blocked_threads, active_thread() ); 162 unlock( this.lock ); 163 park(); 158 append( this.blocked_threads, kernelTLS.this_thread ); 159 BlockInternal( &this.lock ); 164 160 } 165 161 … … 167 163 void wait(condition_variable & this, L & l) { 168 164 lock( this.lock __cfaabi_dbg_ctx2 ); 169 append( this.blocked_threads, active_thread() ); 170 unlock(l); 171 unlock(this.lock); 172 park(); 165 append( this.blocked_threads, kernelTLS.this_thread ); 166 void __unlock(void) { 167 unlock(l); 168 unlock(this.lock); 169 } 170 BlockInternal( __unlock ); 173 171 lock(l); 174 172 } -
libcfa/src/concurrency/mutex.hfa
reef8dfb rbdfc032 36 36 37 37 // List of blocked threads 38 __queue_t(struct $thread) blocked_threads;38 __queue_t(struct thread_desc) blocked_threads; 39 39 40 40 // Locked flag … … 55 55 56 56 // List of blocked threads 57 __queue_t(struct $thread) blocked_threads;57 __queue_t(struct thread_desc) blocked_threads; 58 58 59 59 // Current thread owning the lock 60 struct $thread* owner;60 struct thread_desc * owner; 61 61 62 62 // Number of recursion level … … 83 83 84 84 // List of blocked threads 85 __queue_t(struct $thread) blocked_threads;85 __queue_t(struct thread_desc) blocked_threads; 86 86 }; 87 87 -
libcfa/src/concurrency/preemption.cfa
reef8dfb rbdfc032 10 10 // Created On : Mon Jun 5 14:20:42 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Nov 6 07:42:13 202013 // Update Count : 5412 // Last Modified On : Thu Dec 5 16:34:05 2019 13 // Update Count : 43 14 14 // 15 15 … … 19 19 #include <assert.h> 20 20 21 extern "C" { 21 22 #include <errno.h> 22 23 #include <stdio.h> … … 24 25 #include <unistd.h> 25 26 #include <limits.h> // PTHREAD_STACK_MIN 27 } 26 28 27 29 #include "bits/signal.hfa" 28 #include "kernel_private.hfa"29 30 30 31 #if !defined(__CFA_DEFAULT_PREEMPTION__) … … 38 39 // FwdDeclarations : timeout handlers 39 40 static void preempt( processor * this ); 40 static void timeout( $thread* this );41 static void timeout( thread_desc * this ); 41 42 42 43 // FwdDeclarations : Signal handlers 43 44 static void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ); 44 static void sigHandler_alarm ( __CFA_SIGPARMS__ );45 45 static void sigHandler_segv ( __CFA_SIGPARMS__ ); 46 46 static void sigHandler_ill ( __CFA_SIGPARMS__ ); … … 56 56 #elif defined( __x86_64 ) 57 57 #define CFA_REG_IP gregs[REG_RIP] 58 #elif defined( __ arm__)58 #elif defined( __ARM_ARCH ) 59 59 #define CFA_REG_IP arm_pc 60 #elif defined( __aarch64__ )61 #define CFA_REG_IP pc62 60 #else 63 #error un supportedhardware architecture61 #error unknown hardware architecture 64 62 #endif 65 63 … … 85 83 // Get next expired node 86 84 static inline alarm_node_t * get_expired( alarm_list_t * alarms, Time currtime ) { 87 if( ! & (*alarms)`first) return 0p; // If no alarms return null88 if( (*alarms)`first.alarm >= currtime ) return 0p; // If alarms head not expired return null85 if( !alarms->head ) return 0p; // If no alarms return null 86 if( alarms->head->alarm >= currtime ) return 0p; // If alarms head not expired return null 89 87 return pop(alarms); // Otherwise just pop head 90 88 } 91 89 92 90 // Tick one frame of the Discrete Event Simulation for alarms 93 static void tick_preemption( void) {91 static void tick_preemption() { 94 92 alarm_node_t * node = 0p; // Used in the while loop but cannot be declared in the while condition 95 93 alarm_list_t * alarms = &event_kernel->alarms; // Local copy for ease of reading … … 99 97 while( node = get_expired( alarms, currtime ) ) { 100 98 // __cfaabi_dbg_print_buffer_decl( " KERNEL: preemption tick.\n" ); 99 100 // Check if this is a kernel 101 if( node->kernel_alarm ) { 102 preempt( node->proc ); 103 } 104 else { 105 timeout( node->thrd ); 106 } 107 108 // Check if this is a periodic alarm 101 109 Duration period = node->period; 102 if( period == 0) {103 node->set = false; // Node is one-shot, just mark it as not pending104 }105 106 // Check if this is a kernel107 if( node->type == Kernel ) {108 preempt( node->proc );109 }110 else if( node->type == User ) {111 timeout( node->thrd );112 }113 else {114 node->callback(*node);115 }116 117 // Check if this is a periodic alarm118 110 if( period > 0 ) { 119 111 // __cfaabi_dbg_print_buffer_local( " KERNEL: alarm period is %lu.\n", period.tv ); … … 121 113 insert( alarms, node ); // Reinsert the node for the next time it triggers 122 114 } 115 else { 116 node->set = false; // Node is one-shot, just mark it as not pending 117 } 123 118 } 124 119 125 120 // If there are still alarms pending, reset the timer 126 if( & (*alarms)`first) {127 __cfa dbg_print_buffer_decl(preemption," KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv);128 Duration delta = (*alarms)`first.alarm - currtime;129 Duration cap ped = max(delta, 50`us);121 if( alarms->head ) { 122 __cfaabi_dbg_print_buffer_decl( " KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv); 123 Duration delta = alarms->head->alarm - currtime; 124 Duration caped = max(delta, 50`us); 130 125 // itimerval tim = { caped }; 131 126 // __cfaabi_dbg_print_buffer_local( " Values are %lu, %lu, %lu %lu.\n", delta.tv, caped.tv, tim.it_value.tv_sec, tim.it_value.tv_usec); 132 127 133 __kernel_set_timer( cap ped );128 __kernel_set_timer( caped ); 134 129 } 135 130 } … … 163 158 // Kernel Signal Tools 164 159 //============================================================================================= 165 // In a user-level threading system, there are handful of thread-local variables where this problem occurs on the ARM. 166 // 167 // For each kernel thread running user-level threads, there is a flag variable to indicate if interrupts are 168 // enabled/disabled for that kernel thread. Therefore, this variable is made thread local. 169 // 170 // For example, this code fragment sets the state of the "interrupt" variable in thread-local memory. 171 // 172 // _Thread_local volatile int interrupts; 173 // int main() { 174 // interrupts = 0; // disable interrupts } 175 // 176 // which generates the following code on the ARM 177 // 178 // (gdb) disassemble main 179 // Dump of assembler code for function main: 180 // 0x0000000000000610 <+0>: mrs x1, tpidr_el0 181 // 0x0000000000000614 <+4>: mov w0, #0x0 // #0 182 // 0x0000000000000618 <+8>: add x1, x1, #0x0, lsl #12 183 // 0x000000000000061c <+12>: add x1, x1, #0x10 184 // 0x0000000000000620 <+16>: str wzr, [x1] 185 // 0x0000000000000624 <+20>: ret 186 // 187 // The mrs moves a pointer from coprocessor register tpidr_el0 into register x1. Register w0 is set to 0. The two adds 188 // increase the TLS pointer with the displacement (offset) 0x10, which is the location in the TSL of variable 189 // "interrupts". Finally, 0 is stored into "interrupts" through the pointer in register x1 that points into the 190 // TSL. Now once x1 has the pointer to the location of the TSL for kernel thread N, it can be be preempted at a 191 // user-level and the user thread is put on the user-level ready-queue. When the preempted thread gets to the front of 192 // the user-level ready-queue it is run on kernel thread M. It now stores 0 into "interrupts" back on kernel thread N, 193 // turning off interrupt on the wrong kernel thread. 194 // 195 // On the x86, the following code is generated for the same code fragment. 196 // 197 // (gdb) disassemble main 198 // Dump of assembler code for function main: 199 // 0x0000000000400420 <+0>: movl $0x0,%fs:0xfffffffffffffffc 200 // 0x000000000040042c <+12>: xor %eax,%eax 201 // 0x000000000040042e <+14>: retq 202 // 203 // and there is base-displacement addressing used to atomically reset variable "interrupts" off of the TSL pointer in 204 // register "fs". 205 // 206 // Hence, the ARM has base-displacement address for the general purpose registers, BUT not to the coprocessor 207 // registers. As a result, generating the address for the write into variable "interrupts" is no longer atomic. 208 // 209 // Note this problem does NOT occur when just using multiple kernel threads because the preemption ALWAYS restarts the 210 // thread on the same kernel thread. 211 // 212 // The obvious question is why does ARM use a coprocessor register to store the TSL pointer given that coprocessor 213 // registers are second-class registers with respect to the instruction set. One possible answer is that they did not 214 // want to dedicate one of the general registers to hold the TLS pointer and there was a free coprocessor register 215 // available. 216 217 //----------------------------------------------------------------------------- 218 // Some assembly required 219 #define __cfaasm_label(label, when) when: asm volatile goto(".global __cfaasm_" #label "_" #when "\n" "__cfaasm_" #label "_" #when ":":::"memory":when) 220 221 //---------- 222 // special case for preemption since used often 223 bool __preemption_enabled() { 224 // create a assembler label before 225 // marked as clobber all to avoid movement 226 __cfaasm_label(check, before); 227 228 // access tls as normal 229 bool enabled = __cfaabi_tls.preemption_state.enabled; 230 231 // create a assembler label after 232 // marked as clobber all to avoid movement 233 __cfaasm_label(check, after); 234 return enabled; 235 } 236 237 struct asm_region { 238 void * before; 239 void * after; 240 }; 241 242 static inline bool __cfaasm_in( void * ip, struct asm_region & region ) { 243 return ip >= region.before && ip <= region.after; 244 } 245 246 247 //---------- 248 // Get data from the TLS block 249 // struct asm_region __cfaasm_get; 250 uintptr_t __cfatls_get( unsigned long int offset ) __attribute__((__noinline__)); //no inline to avoid problems 251 uintptr_t __cfatls_get( unsigned long int offset ) { 252 // create a assembler label before 253 // marked as clobber all to avoid movement 254 __cfaasm_label(get, before); 255 256 // access tls as normal (except for pointer arithmetic) 257 uintptr_t val = *(uintptr_t*)((uintptr_t)&__cfaabi_tls + offset); 258 259 // create a assembler label after 260 // marked as clobber all to avoid movement 261 __cfaasm_label(get, after); 262 return val; 263 } 160 161 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; ) 264 162 265 163 extern "C" { 266 164 // Disable interrupts by incrementing the counter 267 165 void disable_interrupts() { 268 // create a assembler label before 269 // marked as clobber all to avoid movement 270 __cfaasm_label(dsable, before); 271 272 with( __cfaabi_tls.preemption_state ) { 166 with( kernelTLS.preemption_state ) { 273 167 #if GCC_VERSION > 50000 274 168 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free"); … … 287 181 verify( new_val < 65_000u ); // If this triggers someone is disabling interrupts without enabling them 288 182 } 289 290 // create a assembler label after291 // marked as clobber all to avoid movement292 __cfaasm_label(dsable, after);293 294 183 } 295 184 296 185 // Enable interrupts by decrementing the counter 297 // If counter reaches 0, execute any pending __cfactx_switch186 // If counter reaches 0, execute any pending CtxSwitch 298 187 void enable_interrupts( __cfaabi_dbg_ctx_param ) { 299 // Cache the processor now since interrupts can start happening after the atomic store 300 processor * proc = __cfaabi_tls.this_processor; 301 /* paranoid */ verify( proc ); 302 303 with( __cfaabi_tls.preemption_state ){ 188 processor * proc = kernelTLS.this_processor; // Cache the processor now since interrupts can start happening after the atomic store 189 thread_desc * thrd = kernelTLS.this_thread; // Cache the thread now since interrupts can start happening after the atomic store 190 191 with( kernelTLS.preemption_state ){ 304 192 unsigned short prev = disable_count; 305 193 disable_count -= 1; 306 307 // If this triggers someone is enabled already enabled interruptsverify( prev != 0u ); 308 /* paranoid */ verify( prev != 0u ); 194 verify( prev != 0u ); // If this triggers someone is enabled already enabled interruptsverify( prev != 0u ); 309 195 310 196 // Check if we need to prempt the thread because an interrupt was missed 311 197 if( prev == 1 ) { 312 198 #if GCC_VERSION > 50000 313 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free");199 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free"); 314 200 #endif 315 201 … … 323 209 if( proc->pending_preemption ) { 324 210 proc->pending_preemption = false; 325 force_yield( __POLL_PREEMPTION);211 BlockInternal( thrd ); 326 212 } 327 213 } … … 333 219 334 220 // Disable interrupts by incrementint the counter 335 // Don't execute any pending __cfactx_switch even if counter reaches 0221 // Don't execute any pending CtxSwitch even if counter reaches 0 336 222 void enable_interrupts_noPoll() { 337 unsigned short prev = __cfaabi_tls.preemption_state.disable_count; 338 __cfaabi_tls.preemption_state.disable_count -= 1; 339 // If this triggers someone is enabled already enabled interrupts 340 /* paranoid */ verifyf( prev != 0u, "Incremented from %u\n", prev ); 223 unsigned short prev = kernelTLS.preemption_state.disable_count; 224 kernelTLS.preemption_state.disable_count -= 1; 225 verifyf( prev != 0u, "Incremented from %u\n", prev ); // If this triggers someone is enabled already enabled interrupts 341 226 if( prev == 1 ) { 342 227 #if GCC_VERSION > 50000 343 static_assert(__atomic_always_lock_free(sizeof(__cfaabi_tls.preemption_state.enabled), &__cfaabi_tls.preemption_state.enabled), "Must be lock-free");228 static_assert(__atomic_always_lock_free(sizeof(kernelTLS.preemption_state.enabled), &kernelTLS.preemption_state.enabled), "Must be lock-free"); 344 229 #endif 345 230 // Set enabled flag to true 346 231 // should be atomic to avoid preemption in the middle of the operation. 347 232 // use memory order RELAXED since there is no inter-thread on this variable requirements 348 __atomic_store_n(& __cfaabi_tls.preemption_state.enabled, true, __ATOMIC_RELAXED);233 __atomic_store_n(&kernelTLS.preemption_state.enabled, true, __ATOMIC_RELAXED); 349 234 350 235 // Signal the compiler that a fence is needed but only for signal handlers … … 353 238 } 354 239 } 355 356 //-----------------------------------------------------------------------------357 // Kernel Signal Debug358 void __cfaabi_check_preemption() {359 bool ready = __preemption_enabled();360 if(!ready) { abort("Preemption should be ready"); }361 362 __cfaasm_label(debug, before);363 364 sigset_t oldset;365 int ret;366 ret = pthread_sigmask(0, ( const sigset_t * ) 0p, &oldset); // workaround trac#208: cast should be unnecessary367 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); }368 369 ret = sigismember(&oldset, SIGUSR1);370 if(ret < 0) { abort("ERROR sigismember returned %d", ret); }371 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); }372 373 ret = sigismember(&oldset, SIGALRM);374 if(ret < 0) { abort("ERROR sigismember returned %d", ret); }375 if(ret == 0) { abort("ERROR SIGALRM is enabled"); }376 377 ret = sigismember(&oldset, SIGTERM);378 if(ret < 0) { abort("ERROR sigismember returned %d", ret); }379 if(ret == 1) { abort("ERROR SIGTERM is disabled"); }380 381 __cfaasm_label(debug, after);382 }383 384 #ifdef __CFA_WITH_VERIFY__385 bool __cfaabi_dbg_in_kernel() {386 return !__preemption_enabled();387 }388 #endif389 390 #undef __cfaasm_label391 392 //-----------------------------------------------------------------------------393 // Signal handling394 240 395 241 // sigprocmask wrapper : unblock a single signal … … 411 257 412 258 if ( pthread_sigmask( SIG_BLOCK, &mask, 0p ) == -1 ) { 413 abort( "internal error, pthread_sigmask" );259 abort( "internal error, pthread_sigmask" ); 414 260 } 415 261 } … … 422 268 423 269 // reserved for future use 424 static void timeout( $thread * this ) { 425 unpark( this ); 426 } 427 428 //----------------------------------------------------------------------------- 429 // Some assembly required 430 #if defined( __i386 ) 431 #ifdef __PIC__ 432 #define RELOC_PRELUDE( label ) \ 433 "calll .Lcfaasm_prelude_" #label "$pb\n\t" \ 434 ".Lcfaasm_prelude_" #label "$pb:\n\t" \ 435 "popl %%eax\n\t" \ 436 ".Lcfaasm_prelude_" #label "_end:\n\t" \ 437 "addl $_GLOBAL_OFFSET_TABLE_+(.Lcfaasm_prelude_" #label "_end-.Lcfaasm_prelude_" #label "$pb), %%eax\n\t" 438 #define RELOC_PREFIX "" 439 #define RELOC_SUFFIX "@GOT(%%eax)" 440 #else 441 #define RELOC_PREFIX "$" 442 #define RELOC_SUFFIX "" 443 #endif 444 #define __cfaasm_label( label ) struct asm_region label = \ 445 ({ \ 446 struct asm_region region; \ 447 asm( \ 448 RELOC_PRELUDE( label ) \ 449 "movl " RELOC_PREFIX "__cfaasm_" #label "_before" RELOC_SUFFIX ", %[vb]\n\t" \ 450 "movl " RELOC_PREFIX "__cfaasm_" #label "_after" RELOC_SUFFIX ", %[va]\n\t" \ 451 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 452 ); \ 453 region; \ 454 }); 455 #elif defined( __x86_64 ) 456 #ifdef __PIC__ 457 #define RELOC_PREFIX "" 458 #define RELOC_SUFFIX "@GOTPCREL(%%rip)" 459 #else 460 #define RELOC_PREFIX "$" 461 #define RELOC_SUFFIX "" 462 #endif 463 #define __cfaasm_label( label ) struct asm_region label = \ 464 ({ \ 465 struct asm_region region; \ 466 asm( \ 467 "movq " RELOC_PREFIX "__cfaasm_" #label "_before" RELOC_SUFFIX ", %[vb]\n\t" \ 468 "movq " RELOC_PREFIX "__cfaasm_" #label "_after" RELOC_SUFFIX ", %[va]\n\t" \ 469 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 470 ); \ 471 region; \ 472 }); 473 #elif defined( __aarch64__ ) 474 #ifdef __PIC__ 475 // Note that this works only for gcc 476 #define __cfaasm_label( label ) struct asm_region label = \ 477 ({ \ 478 struct asm_region region; \ 479 asm( \ 480 "adrp %[vb], _GLOBAL_OFFSET_TABLE_" "\n\t" \ 481 "ldr %[vb], [%[vb], #:gotpage_lo15:__cfaasm_" #label "_before]" "\n\t" \ 482 "adrp %[va], _GLOBAL_OFFSET_TABLE_" "\n\t" \ 483 "ldr %[va], [%[va], #:gotpage_lo15:__cfaasm_" #label "_after]" "\n\t" \ 484 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 485 ); \ 486 region; \ 487 }); 488 #else 489 #error this is not the right thing to do 490 /* 491 #define __cfaasm_label( label ) struct asm_region label = \ 492 ({ \ 493 struct asm_region region; \ 494 asm( \ 495 "adrp %[vb], __cfaasm_" #label "_before" "\n\t" \ 496 "add %[vb], %[vb], :lo12:__cfaasm_" #label "_before" "\n\t" \ 497 "adrp %[va], :got:__cfaasm_" #label "_after" "\n\t" \ 498 "add %[va], %[va], :lo12:__cfaasm_" #label "_after" "\n\t" \ 499 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 500 ); \ 501 region; \ 502 }); 503 */ 504 #endif 505 #else 506 #error unknown hardware architecture 507 #endif 270 static void timeout( thread_desc * this ) { 271 //TODO : implement waking threads 272 } 508 273 509 274 // KERNEL ONLY 510 // Check if a __cfactx_switch signal handler shoud defer275 // Check if a CtxSwitch signal handler shoud defer 511 276 // If true : preemption is safe 512 277 // If false : preemption is unsafe and marked as pending 513 static inline bool preemption_ready( void * ip ) { 514 // Get all the region for which it is not safe to preempt 515 __cfaasm_label( get ); 516 __cfaasm_label( check ); 517 __cfaasm_label( dsable ); 518 __cfaasm_label( debug ); 519 278 static inline bool preemption_ready() { 520 279 // Check if preemption is safe 521 bool ready = true; 522 if( __cfaasm_in( ip, get ) ) { ready = false; goto EXIT; }; 523 if( __cfaasm_in( ip, check ) ) { ready = false; goto EXIT; }; 524 if( __cfaasm_in( ip, dsable ) ) { ready = false; goto EXIT; }; 525 if( __cfaasm_in( ip, debug ) ) { ready = false; goto EXIT; }; 526 if( !__cfaabi_tls.preemption_state.enabled) { ready = false; goto EXIT; }; 527 if( __cfaabi_tls.preemption_state.in_progress ) { ready = false; goto EXIT; }; 528 529 EXIT: 280 bool ready = kernelTLS.preemption_state.enabled && ! kernelTLS.preemption_state.in_progress; 281 530 282 // Adjust the pending flag accordingly 531 __cfaabi_tls.this_processor->pending_preemption = !ready;283 kernelTLS.this_processor->pending_preemption = !ready; 532 284 return ready; 533 285 } … … 539 291 // Startup routine to activate preemption 540 292 // Called from kernel_startup 541 void __kernel_alarm_startup() {293 void kernel_start_preemption() { 542 294 __cfaabi_dbg_print_safe( "Kernel : Starting preemption\n" ); 543 295 544 296 // Start with preemption disabled until ready 545 __cfaabi_tls.preemption_state.enabled = false;546 __cfaabi_tls.preemption_state.disable_count = 1;297 kernelTLS.preemption_state.enabled = false; 298 kernelTLS.preemption_state.disable_count = 1; 547 299 548 300 // Initialize the event kernel … … 551 303 552 304 // Setup proper signal handlers 553 __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO | SA_RESTART ); // __cfactx_switch handler 554 __cfaabi_sigaction( SIGALRM, sigHandler_alarm , SA_SIGINFO | SA_RESTART ); // debug handler 305 __cfaabi_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO | SA_RESTART ); // CtxSwitch handler 555 306 556 307 signal_block( SIGALRM ); 557 308 558 alarm_stack = __create_pthread( &alarm_thread, alarm_loop, 0p );309 alarm_stack = create_pthread( &alarm_thread, alarm_loop, 0p ); 559 310 } 560 311 561 312 // Shutdown routine to deactivate preemption 562 313 // Called from kernel_shutdown 563 void __kernel_alarm_shutdown() {314 void kernel_stop_preemption() { 564 315 __cfaabi_dbg_print_safe( "Kernel : Preemption stopping\n" ); 565 316 … … 575 326 // Wait for the preemption thread to finish 576 327 577 __destroy_pthread( alarm_thread, alarm_stack, 0p ); 328 pthread_join( alarm_thread, 0p ); 329 free( alarm_stack ); 578 330 579 331 // Preemption is now fully stopped … … 601 353 // Kernel Signal Handlers 602 354 //============================================================================================= 603 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )604 355 605 356 // Context switch signal handler 606 357 // Receives SIGUSR1 signal and causes the current thread to yield 607 358 static void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) { 608 void * ip = (void *)(cxt->uc_mcontext.CFA_REG_IP); 609 __cfaabi_dbg_debug_do( last_interrupt = ip; ) 359 __cfaabi_dbg_debug_do( last_interrupt = (void *)(cxt->uc_mcontext.CFA_REG_IP); ) 610 360 611 361 // SKULLDUGGERY: if a thread creates a processor and the immediately deletes it, 612 362 // the interrupt that is supposed to force the kernel thread to preempt might arrive 613 // before the kernel thread has even started running. When that happens , an interrupt614 // w itha null 'this_processor' will be caught, just ignore it.615 if(! __cfaabi_tls.this_processor ) return;363 // before the kernel thread has even started running. When that happens an iterrupt 364 // we a null 'this_processor' will be caught, just ignore it. 365 if(! kernelTLS.this_processor ) return; 616 366 617 367 choose(sfp->si_value.sival_int) { 618 368 case PREEMPT_NORMAL : ;// Normal case, nothing to do here 619 case PREEMPT_TERMINATE: verify( __atomic_load_n( & __cfaabi_tls.this_processor->do_terminate, __ATOMIC_SEQ_CST ) );369 case PREEMPT_TERMINATE: verify( __atomic_load_n( &kernelTLS.this_processor->do_terminate, __ATOMIC_SEQ_CST ) ); 620 370 default: 621 371 abort( "internal error, signal value is %d", sfp->si_value.sival_int ); … … 623 373 624 374 // Check if it is safe to preempt here 625 if( !preemption_ready( ip) ) { return; }626 627 __cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", __cfaabi_tls.this_processor, __cfaabi_tls.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) );375 if( !preemption_ready() ) { return; } 376 377 __cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", kernelTLS.this_processor, kernelTLS.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) ); 628 378 629 379 // Sync flag : prevent recursive calls to the signal handler 630 __cfaabi_tls.preemption_state.in_progress = true;380 kernelTLS.preemption_state.in_progress = true; 631 381 632 382 // Clear sighandler mask before context switching. … … 638 388 } 639 389 390 // TODO: this should go in finish action 640 391 // Clear the in progress flag 641 __cfaabi_tls.preemption_state.in_progress = false;392 kernelTLS.preemption_state.in_progress = false; 642 393 643 394 // Preemption can occur here 644 395 645 force_yield( __ALARM_PREEMPTION ); // Do the actual __cfactx_switch 646 } 647 648 static void sigHandler_alarm( __CFA_SIGPARMS__ ) { 649 abort("SIGALRM should never reach the signal handler"); 650 } 651 652 #if !defined(__CFA_NO_STATISTICS__) 653 int __print_alarm_stats = 0; 654 #endif 396 BlockInternal( kernelTLS.this_thread ); // Do the actual CtxSwitch 397 } 655 398 656 399 // Main of the alarm thread 657 400 // Waits on SIGALRM and send SIGUSR1 to whom ever needs it 658 401 static void * alarm_loop( __attribute__((unused)) void * args ) { 659 __processor_id_t id;660 id.full_proc = false;661 id.id = doregister(&id);662 __cfaabi_tls.this_proc_id = &id;663 664 #if !defined(__CFA_NO_STATISTICS__)665 struct __stats_t local_stats;666 __cfaabi_tls.this_stats = &local_stats;667 __init_stats( &local_stats );668 #endif669 670 402 // Block sigalrms to control when they arrive 671 403 sigset_t mask; … … 725 457 EXIT: 726 458 __cfaabi_dbg_print_safe( "Kernel : Preemption thread stopping\n" ); 727 unregister(&id);728 729 #if !defined(__CFA_NO_STATISTICS__)730 if( 0 != __print_alarm_stats ) {731 __print_stats( &local_stats, __print_alarm_stats, "Alarm", "Thread", 0p );732 }733 #endif734 459 return 0p; 735 460 } 461 462 //============================================================================================= 463 // Kernel Signal Debug 464 //============================================================================================= 465 466 void __cfaabi_check_preemption() { 467 bool ready = kernelTLS.preemption_state.enabled; 468 if(!ready) { abort("Preemption should be ready"); } 469 470 sigset_t oldset; 471 int ret; 472 ret = pthread_sigmask(0, 0p, &oldset); 473 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); } 474 475 ret = sigismember(&oldset, SIGUSR1); 476 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 477 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); } 478 479 ret = sigismember(&oldset, SIGALRM); 480 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 481 if(ret == 0) { abort("ERROR SIGALRM is enabled"); } 482 483 ret = sigismember(&oldset, SIGTERM); 484 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 485 if(ret == 1) { abort("ERROR SIGTERM is disabled"); } 486 } 487 488 #ifdef __CFA_WITH_VERIFY__ 489 bool __cfaabi_dbg_in_kernel() { 490 return !kernelTLS.preemption_state.enabled; 491 } 492 #endif 736 493 737 494 // Local Variables: // -
libcfa/src/concurrency/preemption.hfa
reef8dfb rbdfc032 16 16 #pragma once 17 17 18 #include "bits/locks.hfa"19 18 #include "alarm.hfa" 19 #include "kernel_private.hfa" 20 20 21 struct event_kernel_t { 22 alarm_list_t alarms; 23 __spinlock_t lock; 24 }; 25 26 extern event_kernel_t * event_kernel; 27 21 void kernel_start_preemption(); 22 void kernel_stop_preemption(); 28 23 void update_preemption( processor * this, Duration duration ); 29 24 -
libcfa/src/concurrency/thread.cfa
reef8dfb rbdfc032 19 19 20 20 #include "kernel_private.hfa" 21 #include "exception.hfa"22 21 23 22 #define __CFA_INVOKE_PRIVATE__ 24 23 #include "invoke.h" 25 24 25 extern "C" { 26 #include <fenv.h> 27 #include <stddef.h> 28 } 29 30 //extern volatile thread_local processor * this_processor; 31 26 32 //----------------------------------------------------------------------------- 27 33 // Thread ctors and dtors 28 void ?{}( $thread& this, const char * const name, cluster & cl, void * storage, size_t storageSize ) with( this ) {34 void ?{}(thread_desc & this, const char * const name, cluster & cl, void * storage, size_t storageSize ) with( this ) { 29 35 context{ 0p, 0p }; 30 36 self_cor{ name, storage, storageSize }; 31 ticket = TICKET_RUNNING;32 37 state = Start; 33 preempted = __NO_PREEMPTION;34 38 curr_cor = &self_cor; 35 39 self_mon.owner = &this; … … 37 41 self_mon_p = &self_mon; 38 42 curr_cluster = &cl; 39 link.next = 0p; 40 link.prev = 0p; 41 link.preferred = -1; 42 #if defined( __CFA_WITH_VERIFY__ ) 43 canary = 0x0D15EA5E0D15EA5Ep; 44 #endif 45 46 seqable.next = 0p; 47 seqable.back = 0p; 43 next = 0p; 48 44 49 45 node.next = 0p; … … 54 50 } 55 51 56 void ^?{}($thread& this) with( this ) { 57 #if defined( __CFA_WITH_VERIFY__ ) 58 canary = 0xDEADDEADDEADDEADp; 59 #endif 52 void ^?{}(thread_desc& this) with( this ) { 60 53 unregister(curr_cluster, this); 61 54 ^self_cor{}; 62 55 } 63 56 64 FORALL_DATA_INSTANCE(ThreadCancelled, (dtype thread_t), (thread_t))65 66 forall(dtype T)67 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) {68 dst->virtual_table = src->virtual_table;69 dst->the_thread = src->the_thread;70 dst->the_exception = src->the_exception;71 }72 73 forall(dtype T)74 const char * msg(ThreadCancelled(T) *) {75 return "ThreadCancelled";76 }77 78 forall(dtype T)79 static void default_thread_cancel_handler(ThreadCancelled(T) & ) {80 abort( "Unhandled thread cancellation.\n" );81 }82 83 forall(dtype T | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)))84 void ?{}( thread_dtor_guard_t & this,85 T & thrd, void(*defaultResumptionHandler)(ThreadCancelled(T) &)) {86 $monitor * m = get_monitor(thrd);87 $thread * desc = get_thread(thrd);88 89 // Setup the monitor guard90 void (*dtor)(T& mutex this) = ^?{};91 bool join = defaultResumptionHandler != (void(*)(ThreadCancelled(T)&))0;92 (this.mg){&m, (void(*)())dtor, join};93 94 95 /* paranoid */ verifyf( Halted == desc->state || Cancelled == desc->state, "Expected thread to be Halted or Cancelled, was %d\n", (int)desc->state );96 97 // After the guard set-up and any wait, check for cancellation.98 struct _Unwind_Exception * cancellation = desc->self_cor.cancellation;99 if ( likely( 0p == cancellation ) ) {100 return;101 } else if ( Cancelled == desc->state ) {102 return;103 }104 desc->state = Cancelled;105 if (!join) {106 defaultResumptionHandler = default_thread_cancel_handler;107 }108 109 ThreadCancelled(T) except;110 // TODO: Remove explitate vtable set once trac#186 is fixed.111 except.virtual_table = &get_exception_vtable(&except);112 except.the_thread = &thrd;113 except.the_exception = __cfaehm_cancellation_exception( cancellation );114 throwResume except;115 116 except.the_exception->virtual_table->free( except.the_exception );117 free( cancellation );118 desc->self_cor.cancellation = 0p;119 }120 121 void ^?{}( thread_dtor_guard_t & this ) {122 ^(this.mg){};123 }124 125 //-----------------------------------------------------------------------------126 // Starting and stopping threads127 forall( dtype T | is_thread(T) )128 void __thrd_start( T & this, void (*main_p)(T &) ) {129 $thread * this_thrd = get_thread(this);130 131 disable_interrupts();132 __cfactx_start(main_p, get_coroutine(this), this, __cfactx_invoke_thread);133 134 this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP];135 /* paranoid */ verify( this_thrd->context.SP );136 137 __schedule_thread( this_thrd );138 enable_interrupts( __cfaabi_dbg_ctx );139 }140 141 //-----------------------------------------------------------------------------142 // Support for threads that don't ues the thread keyword143 57 forall( dtype T | sized(T) | is_thread(T) | { void ?{}(T&); } ) 144 58 void ?{}( scoped(T)& this ) with( this ) { … … 159 73 160 74 //----------------------------------------------------------------------------- 161 forall(dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T))) 162 T & join( T & this ) { 163 thread_dtor_guard_t guard = { this, defaultResumptionHandler }; 164 return this; 75 // Starting and stopping threads 76 forall( dtype T | is_thread(T) ) 77 void __thrd_start( T & this, void (*main_p)(T &) ) { 78 thread_desc * this_thrd = get_thread(this); 79 thread_desc * curr_thrd = TL_GET( this_thread ); 80 81 disable_interrupts(); 82 CtxStart(main_p, get_coroutine(this), this, CtxInvokeThread); 83 84 this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP]; 85 verify( this_thrd->context.SP ); 86 // CtxSwitch( &curr_thrd->context, &this_thrd->context ); 87 88 ScheduleThread(this_thrd); 89 enable_interrupts( __cfaabi_dbg_ctx ); 165 90 } 166 91 167 uint64_t thread_rand() { 168 disable_interrupts(); 169 uint64_t ret = __tls_rand(); 170 enable_interrupts( __cfaabi_dbg_ctx ); 171 return ret; 92 void yield( void ) { 93 // Safety note : This could cause some false positives due to preemption 94 verify( TL_GET( preemption_state.enabled ) ); 95 BlockInternal( TL_GET( this_thread ) ); 96 // Safety note : This could cause some false positives due to preemption 97 verify( TL_GET( preemption_state.enabled ) ); 98 } 99 100 void yield( unsigned times ) { 101 for( unsigned i = 0; i < times; i++ ) { 102 yield(); 103 } 172 104 } 173 105 -
libcfa/src/concurrency/thread.hfa
reef8dfb rbdfc032 22 22 #include "kernel.hfa" 23 23 #include "monitor.hfa" 24 #include "exception.hfa"25 24 26 25 //----------------------------------------------------------------------------- 27 26 // thread trait 28 27 trait is_thread(dtype T) { 29 void ^?{}(T& mutex this);30 void main(T& this);31 $thread* get_thread(T& this);28 void ^?{}(T& mutex this); 29 void main(T& this); 30 thread_desc* get_thread(T& this); 32 31 }; 33 32 34 FORALL_DATA_EXCEPTION(ThreadCancelled, (dtype thread_t), (thread_t)) ( 35 thread_t * the_thread; 36 exception_t * the_exception; 37 ); 38 39 forall(dtype T) 40 void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src); 41 42 forall(dtype T) 43 const char * msg(ThreadCancelled(T) *); 44 45 // define that satisfies the trait without using the thread keyword 46 #define DECL_THREAD(X) $thread* get_thread(X& this) __attribute__((const)) { return &this.__thrd; } void main(X& this) 47 48 // Inline getters for threads/coroutines/monitors 49 forall( dtype T | is_thread(T) ) 50 static inline $coroutine* get_coroutine(T & this) __attribute__((const)) { return &get_thread(this)->self_cor; } 33 #define DECL_THREAD(X) thread_desc* get_thread(X& this) { return &this.__thrd; } void main(X& this) 51 34 52 35 forall( dtype T | is_thread(T) ) 53 static inline $monitor * get_monitor (T & this) __attribute__((const)) { return &get_thread(this)->self_mon; } 36 static inline coroutine_desc* get_coroutine(T & this) { 37 return &get_thread(this)->self_cor; 38 } 54 39 55 static inline $coroutine* get_coroutine($thread * this) __attribute__((const)) { return &this->self_cor; } 56 static inline $monitor * get_monitor ($thread * this) __attribute__((const)) { return &this->self_mon; } 40 forall( dtype T | is_thread(T) ) 41 static inline monitor_desc* get_monitor(T & this) { 42 return &get_thread(this)->self_mon; 43 } 57 44 58 //----------------------------------------------------------------------------- 59 // forward declarations needed for threads 45 static inline coroutine_desc* get_coroutine(thread_desc * this) { 46 return &this->self_cor; 47 } 48 49 static inline monitor_desc* get_monitor(thread_desc * this) { 50 return &this->self_mon; 51 } 52 60 53 extern struct cluster * mainCluster; 61 54 … … 65 58 //----------------------------------------------------------------------------- 66 59 // Ctors and dtors 67 void ?{}( $thread& this, const char * const name, struct cluster & cl, void * storage, size_t storageSize );68 void ^?{}( $thread& this);60 void ?{}(thread_desc & this, const char * const name, struct cluster & cl, void * storage, size_t storageSize ); 61 void ^?{}(thread_desc & this); 69 62 70 static inline void ?{}($thread & this) { this{ "Anonymous Thread", *mainCluster, 0p, 65000 }; } 71 static inline void ?{}($thread & this, size_t stackSize ) { this{ "Anonymous Thread", *mainCluster, 0p, stackSize }; } 72 static inline void ?{}($thread & this, void * storage, size_t storageSize ) { this{ "Anonymous Thread", *mainCluster, storage, storageSize }; } 73 static inline void ?{}($thread & this, struct cluster & cl ) { this{ "Anonymous Thread", cl, 0p, 65000 }; } 74 static inline void ?{}($thread & this, struct cluster & cl, size_t stackSize ) { this{ "Anonymous Thread", cl, 0p, stackSize }; } 75 static inline void ?{}($thread & this, struct cluster & cl, void * storage, size_t storageSize ) { this{ "Anonymous Thread", cl, storage, storageSize }; } 76 static inline void ?{}($thread & this, const char * const name) { this{ name, *mainCluster, 0p, 65000 }; } 77 static inline void ?{}($thread & this, const char * const name, struct cluster & cl ) { this{ name, cl, 0p, 65000 }; } 78 static inline void ?{}($thread & this, const char * const name, struct cluster & cl, size_t stackSize ) { this{ name, cl, 0p, stackSize }; } 79 80 struct thread_dtor_guard_t { 81 monitor_dtor_guard_t mg; 82 }; 83 84 forall( dtype T | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) ) 85 void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) ); 86 void ^?{}( thread_dtor_guard_t & this ); 63 static inline void ?{}(thread_desc & this) { this{ "Anonymous Thread", *mainCluster, 0p, 65000 }; } 64 static inline void ?{}(thread_desc & this, size_t stackSize ) { this{ "Anonymous Thread", *mainCluster, 0p, stackSize }; } 65 static inline void ?{}(thread_desc & this, void * storage, size_t storageSize ) { this{ "Anonymous Thread", *mainCluster, storage, storageSize }; } 66 static inline void ?{}(thread_desc & this, struct cluster & cl ) { this{ "Anonymous Thread", cl, 0p, 65000 }; } 67 static inline void ?{}(thread_desc & this, struct cluster & cl, size_t stackSize ) { this{ "Anonymous Thread", cl, 0p, stackSize }; } 68 static inline void ?{}(thread_desc & this, struct cluster & cl, void * storage, size_t storageSize ) { this{ "Anonymous Thread", cl, storage, storageSize }; } 69 static inline void ?{}(thread_desc & this, const char * const name) { this{ name, *mainCluster, 0p, 65000 }; } 70 static inline void ?{}(thread_desc & this, const char * const name, struct cluster & cl ) { this{ name, cl, 0p, 65000 }; } 71 static inline void ?{}(thread_desc & this, const char * const name, struct cluster & cl, size_t stackSize ) { this{ name, cl, 0p, stackSize }; } 87 72 88 73 //----------------------------------------------------------------------------- … … 103 88 void ^?{}( scoped(T)& this ); 104 89 105 //----------------------------------------------------------------------------- 106 // Scheduler API 90 void yield(); 91 void yield( unsigned times ); 107 92 108 //---------- 109 // Park thread: block until corresponding call to unpark, won't block if unpark is already called 110 void park( void ); 111 112 //---------- 113 // Unpark a thread, if the thread is already blocked, schedule it 114 // if the thread is not yet block, signal that it should rerun immediately 115 void unpark( $thread * this ); 116 117 forall( dtype T | is_thread(T) ) 118 static inline void unpark( T & this ) { if(!&this) return; unpark( get_thread( this ) );} 119 120 //---------- 121 // Yield: force thread to block and be rescheduled 122 bool force_yield( enum __Preemption_Reason ); 123 124 //---------- 125 // sleep: force thread to block and be rescheduled after Duration duration 126 void sleep( Duration duration ); 127 128 //---------- 129 // join 130 forall( dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) ) 131 T & join( T & this ); 93 static inline struct thread_desc * active_thread () { return TL_GET( this_thread ); } 132 94 133 95 // Local Variables: // -
libcfa/src/containers/vector.hfa
reef8dfb rbdfc032 10 10 // Created On : Tue Jul 5 18:00:07 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 17 11:02:46 202013 // Update Count : 412 // Last Modified On : Sat Jul 22 10:01:18 2017 13 // Update Count : 3 14 14 // 15 15 16 16 #pragma once 17 17 18 extern "C" { 18 19 #include <stdbool.h> 20 } 19 21 20 22 //------------------------------------------------------------------------------ -
libcfa/src/exception.c
reef8dfb rbdfc032 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : T ue Oct 27 16:27:00 202013 // Update Count : 3511 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 22 18:17:34 2018 13 // Update Count : 11 14 14 // 15 15 16 // Normally we would get this from the CFA prelude.17 16 #include <stddef.h> // for size_t 18 17 19 #include <unwind.h> // for struct _Unwind_Exception {...};20 21 18 #include "exception.h" 19 20 // Implementation of the secret header. 22 21 23 22 #include <stdlib.h> 24 23 #include <stdio.h> 24 #include <unwind.h> 25 25 #include <bits/debug.hfa> 26 #include "concurrency/invoke.h" 27 #include "stdhdr/assert.h" 28 29 #if defined( __ARM_ARCH ) 30 #warning FIX ME: temporary hack to keep ARM build working 26 27 // FIX ME: temporary hack to keep ARM build working 31 28 #ifndef _URC_FATAL_PHASE1_ERROR 32 #define _URC_FATAL_PHASE1_ERROR 329 #define _URC_FATAL_PHASE1_ERROR 2 33 30 #endif // ! _URC_FATAL_PHASE1_ERROR 34 31 #ifndef _URC_FATAL_PHASE2_ERROR 35 32 #define _URC_FATAL_PHASE2_ERROR 2 36 33 #endif // ! _URC_FATAL_PHASE2_ERROR 37 #endif // __ARM_ARCH38 34 39 35 #include "lsda.h" 40 36 41 /* The exception class for our exceptions. Because of the vendor component42 * its value would not be standard.43 * Vendor: UWPL44 * Language: CFA\045 */46 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;47 37 48 38 // Base exception vtable is abstract, you should not have base exceptions. 49 struct __cfa ehm_base_exception_t_vtable50 ___cfa ehm_base_exception_t_vtable_instance = {39 struct __cfaabi_ehm__base_exception_t_vtable 40 ___cfaabi_ehm__base_exception_t_vtable_instance = { 51 41 .parent = NULL, 52 42 .size = 0, … … 57 47 58 48 49 // Temperary global exception context. Does not work with concurency. 50 struct exception_context_t { 51 struct __cfaabi_ehm__try_resume_node * top_resume; 52 struct __cfaabi_ehm__try_resume_node * current_resume; 53 54 exception_t * current_exception; 55 int current_handler_index; 56 } shared_stack = {NULL, NULL, 0, 0}; 57 59 58 // Get the current exception context. 60 59 // There can be a single global until multithreading occurs, then each stack 61 // needs its own. We get this from libcfathreads (no weak attribute). 62 __attribute__((weak)) struct exception_context_t * this_exception_context() { 63 static struct exception_context_t shared_stack = {NULL, NULL}; 60 // needs its own. It will have to be updated to handle that. 61 struct exception_context_t * this_exception_context() { 64 62 return &shared_stack; 65 63 } 64 //#define SAVE_EXCEPTION_CONTEXT(to_name) 65 //struct exception_context_t * to_name = this_exception_context(); 66 //exception * this_exception() { 67 // return this_exception_context()->current_exception; 68 //} 69 70 71 // This macro should be the only thing that needs to change across machines. 72 // Used in the personality function, way down in termination. 73 // struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception_t *) 74 #define MATCHER_FROM_CONTEXT(ptr_to_context) \ 75 (*(_Unwind_Reason_Code(**)(exception_t *))(_Unwind_GetCFA(ptr_to_context) + 8)) 66 76 67 77 68 78 // RESUMPTION ================================================================ 69 79 70 static void reset_top_resume(struct __cfaehm_try_resume_node ** store) { 71 this_exception_context()->top_resume = *store; 72 } 73 74 void __cfaehm_throw_resume(exception_t * except, void (*defaultHandler)(exception_t *)) { 75 struct exception_context_t * context = this_exception_context(); 76 77 __cfadbg_print_safe(exception, "Throwing resumption exception\n"); 78 79 { 80 __attribute__((cleanup(reset_top_resume))) 81 struct __cfaehm_try_resume_node * original_head = context->top_resume; 82 struct __cfaehm_try_resume_node * current = context->top_resume; 83 84 for ( ; current ; current = current->next) { 85 context->top_resume = current->next; 86 if (current->handler(except)) { 87 return; 88 } 80 void __cfaabi_ehm__throw_resume(exception_t * except) { 81 82 __cfaabi_dbg_print_safe("Throwing resumption exception\n"); 83 84 struct __cfaabi_ehm__try_resume_node * original_head = shared_stack.current_resume; 85 struct __cfaabi_ehm__try_resume_node * current = 86 (original_head) ? original_head->next : shared_stack.top_resume; 87 88 for ( ; current ; current = current->next) { 89 shared_stack.current_resume = current; 90 if (current->handler(except)) { 91 shared_stack.current_resume = original_head; 92 return; 89 93 } 90 } // End the search and return to the top of the stack. 91 92 // No handler found, fall back to the default operation. 93 __cfadbg_print_safe(exception, "Unhandled exception\n"); 94 defaultHandler(except); 94 } 95 96 __cfaabi_dbg_print_safe("Unhandled exception\n"); 97 shared_stack.current_resume = original_head; 98 99 // Fall back to termination: 100 __cfaabi_ehm__throw_terminate(except); 101 // TODO: Default handler for resumption. 95 102 } 96 103 … … 99 106 // be added after the node is built but before it is made the top node. 100 107 101 void __cfa ehm_try_resume_setup(struct __cfaehm_try_resume_node * node,108 void __cfaabi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node, 102 109 _Bool (*handler)(exception_t * except)) { 110 node->next = shared_stack.top_resume; 111 node->handler = handler; 112 shared_stack.top_resume = node; 113 } 114 115 void __cfaabi_ehm__try_resume_cleanup(struct __cfaabi_ehm__try_resume_node * node) { 116 shared_stack.top_resume = node->next; 117 } 118 119 120 // TERMINATION =============================================================== 121 122 // MEMORY MANAGEMENT (still for integers) 123 // May have to move to cfa for constructors and destructors (references). 124 125 struct __cfaabi_ehm__node { 126 struct __cfaabi_ehm__node * next; 127 }; 128 129 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node))) 130 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1) 131 132 // Creates a copy of the indicated exception and sets current_exception to it. 133 static void __cfaabi_ehm__allocate_exception( exception_t * except ) { 103 134 struct exception_context_t * context = this_exception_context(); 104 node->next = context->top_resume;105 node->handler = handler;106 context->top_resume = node;107 }108 109 void __cfaehm_try_resume_cleanup(struct __cfaehm_try_resume_node * node) {110 struct exception_context_t * context = this_exception_context();111 context->top_resume = node->next;112 }113 114 115 // MEMORY MANAGEMENT =========================================================116 117 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))118 #define EXCEPT_TO_NODE(except) ((struct __cfaehm_node *)(except) - 1)119 #define UNWIND_TO_NODE(unwind) ((struct __cfaehm_node *)(unwind))120 #define NULL_MAP(map, ptr) ((ptr) ? (map(ptr)) : NULL)121 122 // How to clean up an exception in various situations.123 static void __cfaehm_exception_cleanup(124 _Unwind_Reason_Code reason,125 struct _Unwind_Exception * exception) {126 switch (reason) {127 case _URC_FOREIGN_EXCEPTION_CAUGHT:128 // This one we could clean-up to allow cross-language exceptions.129 case _URC_FATAL_PHASE1_ERROR:130 case _URC_FATAL_PHASE2_ERROR:131 default:132 abort();133 }134 }135 136 // Creates a copy of the indicated exception and sets current_exception to it.137 static void __cfaehm_allocate_exception( exception_t * except ) {138 struct exception_context_t * context = this_exception_context();139 135 140 136 // Allocate memory for the exception. 141 struct __cfa ehm_node * store = malloc(142 sizeof( struct __cfa ehm_node ) + except->virtual_table->size );137 struct __cfaabi_ehm__node * store = malloc( 138 sizeof( struct __cfaabi_ehm__node ) + except->virtual_table->size ); 143 139 144 140 if ( ! store ) { … … 147 143 } 148 144 149 // Initialize the node:150 exception_t * except_store = NODE_TO_EXCEPT(store);151 store->unwind_exception.exception_class = __cfaehm_exception_class;152 store->unwind_exception.exception_cleanup = __cfaehm_exception_cleanup;153 store->handler_index = 0;154 except->virtual_table->copy( except_store, except );155 156 145 // Add the node to the list: 157 store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception); 158 context->current_exception = except_store; 146 store->next = EXCEPT_TO_NODE(context->current_exception); 147 context->current_exception = NODE_TO_EXCEPT(store); 148 149 // Copy the exception to storage. 150 except->virtual_table->copy( context->current_exception, except ); 159 151 } 160 152 161 153 // Delete the provided exception, unsetting current_exception if relivant. 162 static void __cfa ehm_delete_exception( exception_t * except ) {154 static void __cfaabi_ehm__delete_exception( exception_t * except ) { 163 155 struct exception_context_t * context = this_exception_context(); 164 156 165 __cfa dbg_print_safe(exception,"Deleting Exception\n");157 __cfaabi_dbg_print_safe("Deleting Exception\n"); 166 158 167 159 // Remove the exception from the list. 168 struct __cfa ehm_node * to_free = EXCEPT_TO_NODE(except);169 struct __cfa ehm_node * node;160 struct __cfaabi_ehm__node * to_free = EXCEPT_TO_NODE(except); 161 struct __cfaabi_ehm__node * node; 170 162 171 163 if ( context->current_exception == except ) { 172 164 node = to_free->next; 173 context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node);165 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0; 174 166 } else { 175 167 node = EXCEPT_TO_NODE(context->current_exception); 176 168 // It may always be in the first or second position. 177 while ( to_free != node->next ) {169 while( to_free != node->next ) { 178 170 node = node->next; 179 171 } … … 186 178 } 187 179 188 // CANCELLATION ============================================================== 180 // If this isn't a rethrow (*except==0), delete the provided exception. 181 void __cfaabi_ehm__cleanup_terminate( void * except ) { 182 if ( *(void**)except ) __cfaabi_ehm__delete_exception( *(exception_t **)except ); 183 } 184 185 186 // We need a piece of storage to raise the exception 187 struct _Unwind_Exception this_exception_storage; 189 188 190 189 // Function needed by force unwind … … 193 192 int version, 194 193 _Unwind_Action actions, 195 _Unwind_Exception_Class exception _class,194 _Unwind_Exception_Class exceptionClass, 196 195 struct _Unwind_Exception * unwind_exception, 197 struct _Unwind_Context * unwind_context, 198 void * stop_param) { 199 // Verify actions follow the rules we expect. 200 verify(actions & _UA_CLEANUP_PHASE); 201 verify(actions & _UA_FORCE_UNWIND); 202 verify(!(actions & _UA_SEARCH_PHASE)); 203 verify(!(actions & _UA_HANDLER_FRAME)); 204 205 if ( actions & _UA_END_OF_STACK ) { 206 abort(); 207 } else { 208 return _URC_NO_REASON; 209 } 210 } 211 212 __attribute__((weak)) _Unwind_Reason_Code 213 __cfaehm_cancellation_unwind( struct _Unwind_Exception * exception ) { 214 return _Unwind_ForcedUnwind( exception, _Stop_Fn, (void*)0x22 ); 215 } 216 217 // Cancel the current stack, prefroming approprate clean-up and messaging. 218 void __cfaehm_cancel_stack( exception_t * exception ) { 219 __cfaehm_allocate_exception( exception ); 220 221 struct exception_context_t * context = this_exception_context(); 222 struct __cfaehm_node * node = EXCEPT_TO_NODE(context->current_exception); 223 224 // Preform clean-up of any extra active exceptions. 225 while ( node->next ) { 226 struct __cfaehm_node * to_free = node->next; 227 node->next = to_free->next; 228 exception_t * except = NODE_TO_EXCEPT( to_free ); 229 except->virtual_table->free( except ); 230 free( to_free ); 231 } 232 233 _Unwind_Reason_Code ret; 234 ret = __cfaehm_cancellation_unwind( &node->unwind_exception ); 235 printf("UNWIND ERROR %d after force unwind\n", ret); 236 abort(); 237 } 238 239 240 // TERMINATION =============================================================== 241 242 // If this isn't a rethrow (*except==0), delete the provided exception. 243 void __cfaehm_cleanup_terminate( void * except ) { 244 if ( *(void**)except ) __cfaehm_delete_exception( *(exception_t **)except ); 245 } 246 247 static void __cfaehm_cleanup_default( exception_t ** except ) { 248 __cfaehm_delete_exception( *except ); 249 *except = NULL; 196 struct _Unwind_Context * context, 197 void * some_param) { 198 if( actions & _UA_END_OF_STACK ) exit(1); 199 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON; 200 201 return _URC_FATAL_PHASE2_ERROR; 250 202 } 251 203 252 204 // The exception that is being thrown must already be stored. 253 static void __cfaehm_begin_unwind(void(*defaultHandler)(exception_t *)) { 254 struct exception_context_t * context = this_exception_context(); 255 if ( NULL == context->current_exception ) { 205 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) { 206 if ( ! this_exception_context()->current_exception ) { 256 207 printf("UNWIND ERROR missing exception in begin unwind\n"); 257 208 abort(); 258 209 } 259 struct _Unwind_Exception * storage = 260 &EXCEPT_TO_NODE(context->current_exception)->unwind_exception; 210 261 211 262 212 // Call stdlibc to raise the exception 263 __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context); 264 _Unwind_Reason_Code ret = _Unwind_RaiseException( storage ); 213 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage ); 265 214 266 215 // If we reach here it means something happened. For resumption to work we need to find a way … … 271 220 // the whole stack. 272 221 222 if( ret == _URC_END_OF_STACK ) { 223 // No proper handler was found. This can be handled in many ways, C++ calls std::terminate. 224 // Here we force unwind the stack, basically raising a cancellation. 225 printf("Uncaught exception %p\n", &this_exception_storage); 226 227 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 ); 228 printf("UNWIND ERROR %d after force unwind\n", ret); 229 abort(); 230 } 231 273 232 // We did not simply reach the end of the stack without finding a handler. This is an error. 274 if ( ret != _URC_END_OF_STACK ) { 275 printf("UNWIND ERROR %d after raise exception\n", ret); 276 abort(); 277 } 278 279 // No handler found, go to the default operation. 280 __cfadbg_print_safe(exception, "Uncaught exception %p\n", storage); 281 282 __attribute__((cleanup(__cfaehm_cleanup_default))) 283 exception_t * exception = context->current_exception; 284 defaultHandler( exception ); 285 } 286 287 void __cfaehm_throw_terminate( exception_t * val, void (*defaultHandler)(exception_t *) ) { 288 __cfadbg_print_safe(exception, "Throwing termination exception\n"); 289 290 __cfaehm_allocate_exception( val ); 291 __cfaehm_begin_unwind( defaultHandler ); 292 } 293 294 static __attribute__((noreturn)) void __cfaehm_rethrow_adapter( exception_t * except ) { 295 // TODO: Print some error message. 296 (void)except; 233 printf("UNWIND ERROR %d after raise exception\n", ret); 297 234 abort(); 298 235 } 299 236 300 void __cfaehm_rethrow_terminate(void) { 301 __cfadbg_print_safe(exception, "Rethrowing termination exception\n"); 302 303 __cfaehm_begin_unwind( __cfaehm_rethrow_adapter ); 237 void __cfaabi_ehm__throw_terminate( exception_t * val ) { 238 __cfaabi_dbg_print_safe("Throwing termination exception\n"); 239 240 __cfaabi_ehm__allocate_exception( val ); 241 __cfaabi_ehm__begin_unwind(); 242 } 243 244 void __cfaabi_ehm__rethrow_terminate(void) { 245 __cfaabi_dbg_print_safe("Rethrowing termination exception\n"); 246 247 __cfaabi_ehm__begin_unwind(); 248 } 249 250 #if defined(PIC) 251 #warning Exceptions not yet supported when using Position-Independent Code 252 __attribute__((noinline)) 253 void __cfaabi_ehm__try_terminate(void (*try_block)(), 254 void (*catch_block)(int index, exception_t * except), 255 __attribute__((unused)) int (*match_block)(exception_t * except)) { 304 256 abort(); 305 257 } 306 307 #if defined( __x86_64 ) || defined( __i386 ) 258 #else // PIC 308 259 // This is our personality routine. For every stack frame annotated with 309 260 // ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding. 310 261 // Once in the search phase and once in the cleanup phase. 311 _Unwind_Reason_Code __gcfa_personality_v0( 312 int version, 313 _Unwind_Action actions, 314 unsigned long long exception_class, 315 struct _Unwind_Exception * unwind_exception, 316 struct _Unwind_Context * unwind_context) 262 _Unwind_Reason_Code __gcfa_personality_v0 ( 263 int version, _Unwind_Action actions, unsigned long long exceptionClass, 264 struct _Unwind_Exception* unwind_exception, 265 struct _Unwind_Context* context) 317 266 { 318 267 319 //__cfadbg_print_safe(exception, "CFA: 0x%lx\n", _Unwind_GetCFA(context)); 320 __cfadbg_print_safe(exception, "Personality function (%d, %x, %llu, %p, %p):", 321 version, actions, exception_class, unwind_exception, unwind_context); 322 323 // Verify that actions follow the rules we expect. 324 // This function should never be called at the end of the stack. 325 verify(!(actions & _UA_END_OF_STACK)); 326 // Either only the search phase flag is set or... 268 //__cfaabi_dbg_print_safe("CFA: 0x%lx\n", _Unwind_GetCFA(context)); 269 __cfaabi_dbg_print_safe("Personality function (%d, %x, %llu, %p, %p):", 270 version, actions, exceptionClass, unwind_exception, context); 271 272 // If we've reached the end of the stack then there is nothing much we can do... 273 if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK; 274 327 275 if (actions & _UA_SEARCH_PHASE) { 328 verify(actions == _UA_SEARCH_PHASE); 329 __cfadbg_print_safe(exception, " lookup phase"); 330 // ... we are in clean-up phase. 331 } else { 332 verify(actions & _UA_CLEANUP_PHASE); 333 __cfadbg_print_safe(exception, " cleanup phase"); 334 // We shouldn't be the handler frame during forced unwind. 335 if (actions & _UA_HANDLER_FRAME) { 336 verify(!(actions & _UA_FORCE_UNWIND)); 337 __cfadbg_print_safe(exception, " (handler frame)"); 338 } else if (actions & _UA_FORCE_UNWIND) { 339 __cfadbg_print_safe(exception, " (force unwind)"); 340 } 276 __cfaabi_dbg_print_safe(" lookup phase"); 277 } 278 else if (actions & _UA_CLEANUP_PHASE) { 279 __cfaabi_dbg_print_safe(" cleanup phase"); 280 } 281 // Just in case, probably can't actually happen 282 else { 283 printf(" error\n"); 284 return _URC_FATAL_PHASE1_ERROR; 341 285 } 342 286 343 287 // Get a pointer to the language specific data from which we will read what we need 344 const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context );345 346 if ( !lsd ) { //Nothing to do, keep unwinding288 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context ); 289 290 if( !lsd ) { //Nothing to do, keep unwinding 347 291 printf(" no LSD"); 348 292 goto UNWIND; … … 351 295 // Get the instuction pointer and a reading pointer into the exception table 352 296 lsda_header_info lsd_info; 353 const unsigned char * cur_ptr = parse_lsda_header(unwind_context, lsd, &lsd_info); 354 _Unwind_Ptr instruction_ptr = _Unwind_GetIP(unwind_context); 355 356 struct exception_context_t * context = this_exception_context(); 297 const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info); 298 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context ); 357 299 358 300 // Linearly search the table for stuff to do 359 while ( cur_ptr < lsd_info.action_table ) {301 while( cur_ptr < lsd_info.action_table ) { 360 302 _Unwind_Ptr callsite_start; 361 303 _Unwind_Ptr callsite_len; … … 370 312 371 313 // Have we reach the correct frame info yet? 372 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {314 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) { 373 315 #ifdef __CFA_DEBUG_PRINT__ 374 316 void * ls = (void*)lsd_info.Start; … … 378 320 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len; 379 321 void * ip = (void*)instruction_ptr; 380 __cfa dbg_print_safe(exception,"\nfound %p - %p (%p, %p, %p), looking for %p\n",322 __cfaabi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n", 381 323 bp, ep, ls, cs, cl, ip); 382 324 #endif // __CFA_DEBUG_PRINT__ … … 385 327 386 328 // Have we gone too far? 387 if ( lsd_info.Start + callsite_start > instruction_ptr ) {329 if( lsd_info.Start + callsite_start > instruction_ptr ) { 388 330 printf(" gone too far"); 389 331 break; 390 332 } 391 333 392 // Check for what we must do: 393 if ( 0 == callsite_landing_pad ) { 394 // Nothing to do, move along 395 __cfadbg_print_safe(exception, " no landing pad"); 396 } else if (actions & _UA_SEARCH_PHASE) { 397 // In search phase, these means we found a potential handler we must check. 398 399 // We have arbitrarily decided that 0 means nothing to do and 1 means there is 400 // a potential handler. This doesn't seem to conflict the gcc default behavior. 401 if (callsite_action != 0) { 402 // Now we want to run some code to see if the handler matches 403 // This is the tricky part where we want to the power to run arbitrary code 404 // However, generating a new exception table entry and try routine every time 405 // is way more expansive than we might like 406 // The information we have is : 407 // - The GR (Series of registers) 408 // GR1=GP Global Pointer of frame ref by context 409 // - The instruction pointer 410 // - The instruction pointer info (???) 411 // - The CFA (Canonical Frame Address) 412 // - The BSP (Probably the base stack pointer) 413 414 // The current apprach uses one exception table entry per try block 415 _uleb128_t imatcher; 416 // Get the relative offset to the {...}? 417 cur_ptr = read_uleb128(cur_ptr, &imatcher); 418 419 _Unwind_Word match_pos = 420 # if defined( __x86_64 ) 421 _Unwind_GetCFA(unwind_context) + 8; 422 # elif defined( __i386 ) 423 _Unwind_GetCFA(unwind_context) + 24; 424 # elif defined( __ARM_ARCH ) 425 # warning FIX ME: check if anything needed for ARM 426 42; 427 # endif 428 int (*matcher)(exception_t *) = *(int(**)(exception_t *))match_pos; 429 430 int index = matcher(context->current_exception); 431 _Unwind_Reason_Code ret = (0 == index) 432 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 433 UNWIND_TO_NODE(unwind_exception)->handler_index = index; 434 435 // Based on the return value, check if we matched the exception 436 if (ret == _URC_HANDLER_FOUND) { 437 __cfadbg_print_safe(exception, " handler found\n"); 438 } else { 439 // TODO: Continue the search if there is more in the table. 440 __cfadbg_print_safe(exception, " no handler\n"); 334 // Something to do? 335 if( callsite_landing_pad ) { 336 // Which phase are we in 337 if (actions & _UA_SEARCH_PHASE) { 338 // In search phase, these means we found a potential handler we must check. 339 340 // We have arbitrarily decided that 0 means nothing to do and 1 means there is 341 // a potential handler. This doesn't seem to conflict the gcc default behavior. 342 if (callsite_action != 0) { 343 // Now we want to run some code to see if the handler matches 344 // This is the tricky part where we want to the power to run arbitrary code 345 // However, generating a new exception table entry and try routine every time 346 // is way more expansive than we might like 347 // The information we have is : 348 // - The GR (Series of registers) 349 // GR1=GP Global Pointer of frame ref by context 350 // - The instruction pointer 351 // - The instruction pointer info (???) 352 // - The CFA (Canonical Frame Address) 353 // - The BSP (Probably the base stack pointer) 354 355 356 // The current apprach uses one exception table entry per try block 357 _uleb128_t imatcher; 358 // Get the relative offset to the {...}? 359 cur_ptr = read_uleb128(cur_ptr, &imatcher); 360 361 _Unwind_Reason_Code (*matcher)(exception_t *) = 362 MATCHER_FROM_CONTEXT(context); 363 int index = matcher(shared_stack.current_exception); 364 _Unwind_Reason_Code ret = (0 == index) 365 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 366 shared_stack.current_handler_index = index; 367 368 // Based on the return value, check if we matched the exception 369 if( ret == _URC_HANDLER_FOUND) { 370 __cfaabi_dbg_print_safe(" handler found\n"); 371 } else { 372 __cfaabi_dbg_print_safe(" no handler\n"); 373 } 374 return ret; 441 375 } 442 return ret; 376 377 // This is only a cleanup handler, ignore it 378 __cfaabi_dbg_print_safe(" no action"); 443 379 } 444 445 // This is only a cleanup handler, ignore it 446 __cfadbg_print_safe(exception, " no action"); 447 } else { 448 // In clean-up phase, no destructors here but this could be the handler. 449 450 if ( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 451 // If this is a potential exception handler 452 // but not the one that matched the exception in the seach phase, 453 // just ignore it 454 goto UNWIND; 380 else if (actions & _UA_CLEANUP_PHASE) { 381 382 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 383 // If this is a potential exception handler 384 // but not the one that matched the exception in the seach phase, 385 // just ignore it 386 goto UNWIND; 387 } 388 389 // We need to run some clean-up or a handler 390 // These statment do the right thing but I don't know any specifics at all 391 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception ); 392 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 ); 393 394 // I assume this sets the instruction pointer to the adress of the landing pad 395 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT 396 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) ); 397 398 __cfaabi_dbg_print_safe(" action\n"); 399 400 // Return have some action to run 401 return _URC_INSTALL_CONTEXT; 455 402 } 456 457 // We need to run some clean-up or a handler458 // These statment do the right thing but I don't know any specifics at all459 _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(0),460 (_Unwind_Ptr)unwind_exception );461 _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(1), 0 );462 463 // I assume this sets the instruction pointer to the adress of the landing pad464 // It doesn't actually set it, it only state the value that needs to be set once we465 // return _URC_INSTALL_CONTEXT466 _Unwind_SetIP( unwind_context, ((lsd_info.LPStart) + (callsite_landing_pad)) );467 468 __cfadbg_print_safe(exception, " action\n");469 470 // Return have some action to run471 return _URC_INSTALL_CONTEXT;472 403 } 404 405 // Nothing to do, move along 406 __cfaabi_dbg_print_safe(" no landing pad"); 473 407 } 474 408 // No handling found 475 __cfa dbg_print_safe(exception, " table end reached");409 __cfaabi_dbg_print_safe(" table end reached\n"); 476 410 477 411 UNWIND: 478 __cfa dbg_print_safe(exception," unwind\n");412 __cfaabi_dbg_print_safe(" unwind\n"); 479 413 480 414 // Keep unwinding the stack 481 415 return _URC_CONTINUE_UNWIND; 482 416 } 483 484 #pragma GCC push_options485 #pragma GCC optimize(0)486 417 487 418 // Try statements are hoisted out see comments for details. While this could probably be unique 488 419 // and simply linked from libcfa but there is one problem left, see the exception table for details 489 420 __attribute__((noinline)) 490 void __cfa ehm_try_terminate(void (*try_block)(),421 void __cfaabi_ehm__try_terminate(void (*try_block)(), 491 422 void (*catch_block)(int index, exception_t * except), 492 423 __attribute__((unused)) int (*match_block)(exception_t * except)) { … … 494 425 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy); 495 426 427 // Setup statments: These 2 statments won't actually result in any code, they only setup global tables. 428 // However, they clobber gcc cancellation support from gcc. We can replace the personality routine but 429 // replacing the exception table gcc generates is not really doable, it generates labels based on how the 430 // assembly works. 431 496 432 // Setup the personality routine and exception table. 497 // Unforturnately these clobber gcc cancellation support which means we can't get access to498 // the attribute cleanup tables at the same time. We would have to inspect the assembly to499 // create a new set ourselves.500 #ifdef __PIC__501 asm volatile (".cfi_personality 0x9b,CFA.ref.__gcfa_personality_v0");502 asm volatile (".cfi_lsda 0x1b, .LLSDACFA2");503 #else504 433 asm volatile (".cfi_personality 0x3,__gcfa_personality_v0"); 505 434 asm volatile (".cfi_lsda 0x3, .LLSDACFA2"); 506 #endif507 435 508 436 // Label which defines the start of the area for which the handler is setup. … … 522 450 // Label which defines the end of the area for which the handler is setup. 523 451 asm volatile (".TRYEND:"); 524 // Label which defines the start of the exception landing pad. Basically what is called when525 // the exception is caught. Note, if multiple handlers are given, the multiplexing should be526 // done by the generated code, not theexception runtime.452 // Label which defines the start of the exception landing pad. Basically what is called when the exception is 453 // caught. Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the 454 // exception runtime. 527 455 asm volatile (".CATCH:"); 528 456 529 457 // Exception handler 530 // Note: Saving the exception context on the stack breaks termination exceptions. 531 catch_block( EXCEPT_TO_NODE( this_exception_context()->current_exception )->handler_index, 532 this_exception_context()->current_exception ); 458 catch_block( shared_stack.current_handler_index, 459 shared_stack.current_exception ); 533 460 } 534 461 … … 537 464 // have a single call to the try routine. 538 465 539 #ifdef __PIC__ 540 asm ( 541 // HEADER 542 ".LFECFA1:\n" 543 " .globl __gcfa_personality_v0\n" 544 " .section .gcc_except_table,\"a\",@progbits\n" 545 // TABLE HEADER (important field is the BODY length at the end) 546 ".LLSDACFA2:\n" 547 " .byte 0xff\n" 548 " .byte 0xff\n" 549 " .byte 0x1\n" 550 " .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n" 551 // BODY (language specific data) 552 // This uses language specific data and can be modified arbitrarily 553 // We use handled area offset, handled area length, 554 // handler landing pad offset and 1 (action code, gcc seems to use 0). 555 ".LLSDACSBCFA2:\n" 556 " .uleb128 .TRYSTART-__cfaehm_try_terminate\n" 557 " .uleb128 .TRYEND-.TRYSTART\n" 558 " .uleb128 .CATCH-__cfaehm_try_terminate\n" 559 " .uleb128 1\n" 560 ".LLSDACSECFA2:\n" 561 // TABLE FOOTER 562 " .text\n" 563 " .size __cfaehm_try_terminate, .-__cfaehm_try_terminate\n" 564 ); 565 566 // Somehow this piece of helps with the resolution of debug symbols. 567 __attribute__((unused)) static const int dummy = 0; 568 569 asm ( 570 // Add a hidden symbol which points at the function. 571 " .hidden CFA.ref.__gcfa_personality_v0\n" 572 " .weak CFA.ref.__gcfa_personality_v0\n" 573 // No clue what this does specifically 574 " .section .data.rel.local.CFA.ref.__gcfa_personality_v0,\"awG\",@progbits,CFA.ref.__gcfa_personality_v0,comdat\n" 575 " .align 8\n" 576 " .type CFA.ref.__gcfa_personality_v0, @object\n" 577 " .size CFA.ref.__gcfa_personality_v0, 8\n" 578 "CFA.ref.__gcfa_personality_v0:\n" 579 #if defined( __x86_64 ) 580 " .quad __gcfa_personality_v0\n" 581 #else // then __i386 582 " .long __gcfa_personality_v0\n" 583 #endif 584 ); 585 #else // __PIC__ 466 #if defined( __i386 ) || defined( __x86_64 ) 586 467 asm ( 587 468 // HEADER … … 598 479 ".LLSDACSBCFA2:\n" 599 480 // Handled area start (relative to start of function) 600 " .uleb128 .TRYSTART-__cfa ehm_try_terminate\n"481 " .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n" 601 482 // Handled area length 602 483 " .uleb128 .TRYEND-.TRYSTART\n" 603 484 // Handler landing pad address (relative to start of function) 604 " .uleb128 .CATCH-__cfa ehm_try_terminate\n"485 " .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n" 605 486 // Action code, gcc seems to always use 0. 606 487 " .uleb128 1\n" … … 608 489 ".LLSDACSECFA2:\n" 609 490 " .text\n" 610 " .size __cfa ehm_try_terminate, .-__cfaehm_try_terminate\n"491 " .size __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n" 611 492 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n" 612 " .section .note.GNU-stack,\"x\",@progbits\n"493 // " .section .note.GNU-stack,\"x\",@progbits\n" 613 494 ); 614 #endif // __PIC__ 615 616 #pragma GCC pop_options 617 618 #elif defined( __ARM_ARCH ) 619 _Unwind_Reason_Code __gcfa_personality_v0( 620 int version, 621 _Unwind_Action actions, 622 unsigned long long exception_class, 623 struct _Unwind_Exception * unwind_exception, 624 struct _Unwind_Context * unwind_context) { 625 return _URC_CONTINUE_UNWIND; 626 } 627 628 __attribute__((noinline)) 629 void __cfaehm_try_terminate(void (*try_block)(), 630 void (*catch_block)(int index, exception_t * except), 631 __attribute__((unused)) int (*match_block)(exception_t * except)) { 632 } 633 #else 634 #error unsupported hardware architecture 635 #endif // __x86_64 || __i386 495 #endif // __i386 || __x86_64 496 #endif // PIC -
libcfa/src/exception.h
reef8dfb rbdfc032 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // exception.h -- Internal exception handling definitions.7 // exception.h -- Builtins for exception handling. 8 8 // 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Jun 26 15:11:00 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : T ue Oct 27 14:45:00 202013 // Update Count : 1111 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 22 18:11:15 2018 13 // Update Count : 8 14 14 // 15 15 16 16 #pragma once 17 17 18 // This could be considered several headers. All are internal to the exception19 // system but needed to depending on whether they are C/Cforall code and20 // whether or not they are part of the builtins.21 18 22 19 #ifdef __cforall … … 24 21 #endif 25 22 26 // Included in C code or the built-ins. 27 #if !defined(__cforall) || defined(__cforall_builtins__) 28 29 struct __cfaehm_base_exception_t; 30 typedef struct __cfaehm_base_exception_t exception_t; 31 struct __cfaehm_base_exception_t_vtable { 32 const struct __cfaehm_base_exception_t_vtable * parent; 23 struct __cfaabi_ehm__base_exception_t; 24 typedef struct __cfaabi_ehm__base_exception_t exception_t; 25 struct __cfaabi_ehm__base_exception_t_vtable { 26 const struct __cfaabi_ehm__base_exception_t_vtable * parent; 33 27 size_t size; 34 void (*copy)(struct __cfa ehm_base_exception_t *this,35 struct __cfa ehm_base_exception_t * other);36 void (*free)(struct __cfa ehm_base_exception_t *this);37 const char * (*msg)(struct __cfa ehm_base_exception_t *this);28 void (*copy)(struct __cfaabi_ehm__base_exception_t *this, 29 struct __cfaabi_ehm__base_exception_t * other); 30 void (*free)(struct __cfaabi_ehm__base_exception_t *this); 31 const char * (*msg)(struct __cfaabi_ehm__base_exception_t *this); 38 32 }; 39 struct __cfa ehm_base_exception_t {40 struct __cfa ehm_base_exception_t_vtable const * virtual_table;33 struct __cfaabi_ehm__base_exception_t { 34 struct __cfaabi_ehm__base_exception_t_vtable const * virtual_table; 41 35 }; 42 extern struct __cfa ehm_base_exception_t_vtable43 ___cfa ehm_base_exception_t_vtable_instance;36 extern struct __cfaabi_ehm__base_exception_t_vtable 37 ___cfaabi_ehm__base_exception_t_vtable_instance; 44 38 45 39 46 void __cfaehm_cancel_stack(exception_t * except) __attribute__((noreturn));47 48 40 // Used in throw statement translation. 49 void __cfa ehm_throw_terminate(exception_t * except, void (*)(exception_t *));50 void __cfa ehm_rethrow_terminate() __attribute__((noreturn));51 void __cfa ehm_throw_resume(exception_t * except, void (*)(exception_t *));41 void __cfaabi_ehm__throw_terminate(exception_t * except) __attribute__((noreturn)); 42 void __cfaabi_ehm__rethrow_terminate() __attribute__((noreturn)); 43 void __cfaabi_ehm__throw_resume(exception_t * except); 52 44 53 45 // Function catches termination exceptions. 54 void __cfa ehm_try_terminate(55 void (*try_block)(),56 void (*catch_block)(int index, exception_t * except),57 int (*match_block)(exception_t * except));46 void __cfaabi_ehm__try_terminate( 47 void (*try_block)(), 48 void (*catch_block)(int index, exception_t * except), 49 int (*match_block)(exception_t * except)); 58 50 59 51 // Clean-up the exception in catch blocks. 60 void __cfa ehm_cleanup_terminate(void * except);52 void __cfaabi_ehm__cleanup_terminate(void * except); 61 53 62 54 // Data structure creates a list of resume handlers. 63 struct __cfa ehm_try_resume_node {64 struct __cfaehm_try_resume_node * next;65 _Bool (*handler)(exception_t * except);55 struct __cfaabi_ehm__try_resume_node { 56 struct __cfaabi_ehm__try_resume_node * next; 57 _Bool (*handler)(exception_t * except); 66 58 }; 67 59 68 60 // These act as constructor and destructor for the resume node. 69 void __cfa ehm_try_resume_setup(70 struct __cfaehm_try_resume_node * node,71 _Bool (*handler)(exception_t * except));72 void __cfa ehm_try_resume_cleanup(73 struct __cfaehm_try_resume_node * node);61 void __cfaabi_ehm__try_resume_setup( 62 struct __cfaabi_ehm__try_resume_node * node, 63 _Bool (*handler)(exception_t * except)); 64 void __cfaabi_ehm__try_resume_cleanup( 65 struct __cfaabi_ehm__try_resume_node * node); 74 66 75 67 // Check for a standard way to call fake deconstructors. 76 struct __cfaehm_cleanup_hook {}; 77 78 #endif 79 80 // Included in C code and the library. 81 #if !defined(__cforall) || !defined(__cforall_builtins__) 82 struct __cfaehm_node { 83 struct _Unwind_Exception unwind_exception; 84 struct __cfaehm_node * next; 85 int handler_index; 86 }; 87 88 static inline exception_t * __cfaehm_cancellation_exception( 89 struct _Unwind_Exception * unwind_exception ) { 90 return (exception_t *)(1 + (struct __cfaehm_node *)unwind_exception); 91 } 92 #endif 68 struct __cfaabi_ehm__cleanup_hook {}; 93 69 94 70 #ifdef __cforall 95 71 } 96 97 // Built-ins not visible in C.98 #if defined(__cforall_builtins__)99 100 // Not all the built-ins can be expressed in C. These can't be101 // implemented in the .c file either so they all have to be inline.102 103 trait is_exception(dtype exceptT, dtype virtualT) {104 /* The first field must be a pointer to a virtual table.105 * That virtual table must be a decendent of the base exception virtual table.106 */107 virtualT const & get_exception_vtable(exceptT *);108 // Always returns the virtual table for this type (associated types hack).109 };110 111 trait is_termination_exception(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) {112 void defaultTerminationHandler(exceptT &);113 };114 115 trait is_resumption_exception(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) {116 void defaultResumptionHandler(exceptT &);117 };118 119 forall(dtype exceptT, dtype virtualT | is_termination_exception(exceptT, virtualT))120 static inline void $throw(exceptT & except) {121 __cfaehm_throw_terminate(122 (exception_t *)&except,123 (void(*)(exception_t *))defaultTerminationHandler124 );125 }126 127 forall(dtype exceptT, dtype virtualT | is_resumption_exception(exceptT, virtualT))128 static inline void $throwResume(exceptT & except) {129 __cfaehm_throw_resume(130 (exception_t *)&except,131 (void(*)(exception_t *))defaultResumptionHandler132 );133 }134 135 forall(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT))136 static inline void cancel_stack(exceptT & except) __attribute__((noreturn)) {137 __cfaehm_cancel_stack( (exception_t *)&except );138 }139 140 forall(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT))141 static inline void defaultTerminationHandler(exceptT & except) {142 return cancel_stack( except );143 }144 145 forall(dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT))146 static inline void defaultResumptionHandler(exceptT & except) {147 throw except;148 }149 150 72 #endif 151 152 #endif -
libcfa/src/executor.cfa
reef8dfb rbdfc032 4 4 // buffer. 5 5 6 #include <bits/containers.hfa> 6 7 #include <thread.hfa> 7 #include < containers/list.hfa>8 #include <stdio.h> 8 9 9 forall( dtype T | $dlistable(T, T) ) { 10 monitor Buffer { // unbounded buffer 11 dlist( T, T ) queue; // unbounded list of work requests 12 condition delay; 13 }; // Buffer 10 forall( dtype T ) 11 monitor Buffer { // unbounded buffer 12 __queue_t( T ) queue; // unbounded list of work requests 13 condition delay; 14 }; // Buffer 15 forall( dtype T | is_node(T) ) { 16 void insert( Buffer( T ) & mutex buf, T * elem ) with(buf) { 17 append( queue, elem ); // insert element into buffer 18 signal( delay ); // restart 19 } // insert 14 20 15 void insert( Buffer(T) & mutex buf, T * elem) with(buf) {16 dlist( T, T ) * qptr = &queue; // workaround https://cforall.uwaterloo.ca/trac/ticket/16617 insert_last( *qptr, *elem ); // insert element into buffer 18 signal( delay ); // restart 19 } // insert 21 T * remove( Buffer( T ) & mutex buf ) with(buf) { 22 if ( queue.head != 0 ) wait( delay ); // no request to process ? => wait 23 // return pop_head( queue ); 24 } // remove 25 } // distribution 20 26 21 T * remove( Buffer(T) & mutex buf ) with(buf) { 22 dlist( T, T ) * qptr = &queue; // workaround https://cforall.uwaterloo.ca/trac/ticket/166 23 // if ( (*qptr)`is_empty ) wait( delay ); // no request to process ? => wait 24 if ( (*qptr)`is_empty ) return 0p; // no request to process ? => wait 25 return &pop_first( *qptr ); 26 } // remove 27 } // forall 27 struct WRequest { // client request, no return 28 void (* action)( void ); 29 WRequest * next; // intrusive queue field 30 }; // WRequest 28 31 29 struct WRequest { // client request, no return 30 void (* action)( void ); 31 DLISTED_MGD_IMPL_IN(WRequest) 32 }; // WRequest 33 DLISTED_MGD_IMPL_OUT(WRequest) 34 35 void ?{}( WRequest & req ) with(req) { action = 0; } 36 void ?{}( WRequest & req, void (* action)( void ) ) with(req) { req.action = action; } 32 WRequest *& get_next( WRequest & this ) { return this.next; } 33 void ?{}( WRequest & req ) with(req) { action = 0; next = 0; } 34 void ?{}( WRequest & req, void (* action)( void ) ) with(req) { req.action = action; next = 0; } 37 35 bool stop( WRequest & req ) { return req.action == 0; } 38 36 void doit( WRequest & req ) { req.action(); } 39 37 40 // Each worker has its own set (when requests buffers > workers) of work buffers to reduce contention between client41 // a nd server, where work requests arrive and are distributed into buffers in a roughly round-robin order.38 // Each worker has its own work buffer to reduce contention between client and server. Hence, work requests arrive and 39 // are distributed into buffers in a roughly round-robin order. 42 40 43 41 thread Worker { 44 Buffer(WRequest) * requests; 45 WRequest * request; 46 unsigned int start, range; 42 Buffer( WRequest ) * requests; 43 unsigned int start, range; 47 44 }; // Worker 48 45 49 46 void main( Worker & w ) with(w) { 50 for ( int i = 0;; i = (i + 1) % range ) {51 request = remove( requests[i + start] );52 if ( ! request ) { yield(); continue; }53 if ( stop( *request ) ) break;54 doit( *request );55 delete( request );56 } // for47 for ( int i = 0;; i = (i + 1) % range ) { 48 WRequest * request = remove( requests[i + start] ); 49 if ( ! request ) { yield(); continue; } 50 if ( stop( *request ) ) break; 51 doit( *request ); 52 delete( request ); 53 } // for 57 54 } // Worker::main 58 55 59 void ?{}( Worker & worker, cluster * wc, Buffer( WRequest) * requests, unsigned int start, unsigned int range ) {60 ((thread &)worker){ *wc }; 61 worker.[requests, request, start, range] = [requests, 0p, start, range];56 void ?{}( Worker & worker, cluster * wc, Buffer( WRequest ) * requests, unsigned int start, unsigned int range ) { 57 (*get_thread(worker)){ *wc }; // create on given cluster 58 worker.[requests, start, range] = [requests, start, range]; 62 59 } // ?{} 63 60 64 WRequest * current_request( Worker & worker ) { return worker.request; }65 66 61 struct Executor { 67 cluster * cluster; // if workers execute on separate cluster 68 processor ** processors; // array of virtual processors adding parallelism for workers 69 Buffer(WRequest) * requests; // list of work requests 70 Worker ** workers; // array of workers executing work requests 71 unsigned int nprocessors, nworkers, nrqueues; // number of processors/threads/request queues 72 bool sepClus; // use same or separate cluster for executor 73 unsigned int next; // demultiplexed across worker buffers 62 cluster * cluster; // if workers execute on separate cluster 63 processor ** processors; // array of virtual processors adding parallelism for workers 64 Buffer( WRequest ) * requests; // list of work requests 65 Worker ** workers; // array of workers executing work requests 66 unsigned int nprocessors, nworkers, nmailboxes; // number of mailboxes/workers/processor tasks 67 bool sepClus; // use same or separate cluster for executor 74 68 }; // Executor 75 69 70 static thread_local unsigned int next; // demultiplexed across worker buffers 76 71 unsigned int tickets( Executor & ex ) with(ex) { 77 //return uFetchAdd( next, 1 ) % nrqueues;78 return next++ % nrqueues;// no locking, interference randomizes72 //return uFetchAdd( next, 1 ) % nmailboxes; 73 return next++ % nmailboxes; // no locking, interference randomizes 79 74 } // tickets 80 75 81 void ?{}( Executor & ex, unsigned int np, unsigned int nw, unsigned int n r, bool sc = false ) with(ex) {82 [nprocessors, nworkers, nrqueues, sepClus] = [np, nw, nr, sc];83 assert( nrqueues >= nworkers );84 cluster = sepClus ? new( "Executor" ) : active_cluster();85 processors = aalloc( nprocessors );86 requests = anew( nrqueues );87 workers = aalloc( nworkers );76 void ?{}( Executor & ex, unsigned int np, unsigned int nw, unsigned int nm, bool sc = false ) with(ex) { 77 [nprocessors, nworkers, nmailboxes, sepClus] = [np, nw, nm, sc]; 78 assert( nmailboxes >= nworkers ); 79 cluster = sepClus ? new( "Executor" ) : active_cluster(); 80 processors = (processor **)anew( nprocessors ); 81 requests = anew( nmailboxes ); 82 workers = (Worker **)anew( nworkers ); 88 83 89 for ( i; nprocessors ) {90 processors[i] = new( *cluster );91 } // for84 for ( i; nprocessors ) { 85 processors[ i ] = new( *cluster ); 86 } // for 92 87 93 unsigned int reqPerWorker = nrqueues / nworkers, extras = nrqueues % nworkers; 94 // for ( unsigned int i = 0, start = 0, range; i < nworkers; i += 1, start += range ) { 95 for ( i; nworkers : start; 0u ~ @ ~ range : range; ) { 96 range = reqPerWorker + ( i < extras ? 1 : 0 ); 97 workers[i] = new( cluster, requests, start, range ); 98 } // for 88 unsigned int reqPerWorker = nmailboxes / nworkers, extras = nmailboxes % nworkers; 89 for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker + ( i < extras ? 1 : 0 ) ) { 90 workers[ i ] = new( cluster, requests, step, reqPerWorker + ( i < extras ? 1 : 0 ) ); 91 } // for 99 92 } // ?{} 100 93 101 94 void ?{}( Executor & ex, unsigned int nprocessors, unsigned int nworkers, bool sepClus = false ) { 102 ex{ nprocessors, nworkers, nworkers, sepClus };95 ex{ nprocessors, nworkers, nworkers, sepClus }; 103 96 } 104 97 void ?{}( Executor & ex, unsigned int nprocessors, bool sepClus = false ) { 105 ex{ nprocessors, nprocessors, nprocessors, sepClus };98 ex{ nprocessors, nprocessors, nprocessors, sepClus }; 106 99 } 107 void ?{}( Executor & ex ) { // special for current cluster, no processors added108 ex{ 0, active_cluster()->nprocessors, false };100 void ?{}( Executor & ex ) { // special for current cluster 101 ex{ 0, active_cluster()->nprocessors, false }; 109 102 } 110 103 void ^?{}( Executor & ex ) with(ex) { 111 // Add one sentinel per worker to stop them. Since in destructor, no new external work should be queued. Cannot112 // combine next two loops and only have a single sentinel because workers arrive in arbitrary order, so worker1 may 113 // take thesingle sentinel while waiting for worker 0 to end.104 // Add one sentinel per worker to stop them. Since in destructor, no new work should be queued. Cannot combine next 105 // two loops and only have a single sentinel because workers arrive in arbitrary order, so worker1 may take the 106 // single sentinel while waiting for worker 0 to end. 114 107 115 WRequest sentinel[nworkers];116 unsigned int reqPerWorker = nrqueues / nworkers;117 for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker ) {118 insert( requests[step], &sentinel[i] );// force eventually termination119 } // for120 for ( i; nworkers ) {121 delete( workers[i] );122 } // for123 for ( i; nprocessors ) {124 delete( processors[i] );125 } // for108 WRequest sentinel[nworkers]; 109 unsigned int reqPerWorker = nmailboxes / nworkers; 110 for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker ) { 111 insert( requests[step], &sentinel[i] ); // force eventually termination 112 } // for 113 for ( i; nworkers ) { 114 delete( workers[ i ] ); 115 } // for 116 for ( i; nprocessors ) { 117 delete( processors[ i ] ); 118 } // for 126 119 127 free( workers ); 128 // adelete( nrqueues, requests ); 129 for ( i; nrqueues ) ^?{}( requests[i] ); // FIX ME: problem with resolver 130 free( requests ); 131 free( processors ); 132 if ( sepClus ) { delete( cluster ); } 120 delete( workers ); 121 delete( requests ); 122 delete( processors ); 123 if ( sepClus ) { delete( cluster ); } 133 124 } // ^?{} 134 125 135 126 void send( Executor & ex, void (* action)( void ) ) { // asynchronous call, no return value 136 WRequest * node = new( action );137 insert( ex.requests[tickets( ex )], node );127 WRequest * node = new( action ); 128 insert( ex.requests[tickets( ex )], node ); 138 129 } // send 139 140 130 141 131 int counter = 0; 142 132 143 void work ( void ) {144 __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST );145 //fprintf( stderr, "workie\n" );133 void workie( void ) { 134 __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST ); 135 // fprintf( stderr, "workie\n" ); 146 136 } 147 137 148 int main( int argc, char * argv[] ) { 149 int times = 1_000_000; 150 if ( argc == 2 ) times = atoi( argv[1] ); 151 processor p[7]; 152 { 153 Executor exector; 154 for ( i; times ) { 155 send( exector, work ); 156 if ( i % 100 == 0 ) yield(); 157 } // for 158 } 159 printf( "%d\n", counter ); 138 int main() { 139 { 140 Executor exector; 141 for ( i; 3000 ) { 142 send( exector, workie ); 143 if ( i % 100 ) yield(); 144 } // for 145 } 146 printf( "%d\n", counter ); 160 147 } 161 148 162 149 // Local Variables: // 163 // tab-width: 4" //164 150 // compile-command: "cfa executor.cfa" // 165 151 // End: // -
libcfa/src/fstream.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 16:24:54 202013 // Update Count : 3 8412 // Last Modified On : Fri Nov 29 06:56:46 2019 13 // Update Count : 355 14 14 // 15 15 … … 26 26 27 27 28 // *********************************** ofstream ***********************************28 //*********************************** ofstream *********************************** 29 29 30 30 … … 32 32 33 33 void ?{}( ofstream & os, void * file ) { 34 os.$file = file; 35 os.$sepDefault = true; 36 os.$sepOnOff = false; 37 os.$nlOnOff = true; 38 os.$prt = false; 39 os.$sawNL = false; 40 $sepSetCur( os, sepGet( os ) ); 34 os.file = file; 35 os.sepDefault = true; 36 os.sepOnOff = false; 37 os.nlOnOff = true; 38 os.prt = false; 39 os.sawNL = false; 41 40 sepSet( os, " " ); 41 sepSetCur( os, sepGet( os ) ); 42 42 sepSetTuple( os, ", " ); 43 43 } // ?{} 44 44 45 45 // private 46 bool $sepPrt( ofstream & os ) { $setNL( os, false ); return os.$sepOnOff; }47 void $sepReset( ofstream & os ) { os.$sepOnOff = os.$sepDefault; }48 void $sepReset( ofstream & os, bool reset ) { os.$sepDefault = reset; os.$sepOnOff = os.$sepDefault; }49 const char * $sepGetCur( ofstream & os ) { return os.$sepCur; }50 void $sepSetCur( ofstream & os, const char sepCur[] ) { os.$sepCur = sepCur; }51 bool $getNL( ofstream & os ) { return os.$sawNL; }52 void $setNL( ofstream & os, bool state ) { os.$sawNL = state; }53 bool $getANL( ofstream & os ) { return os.$nlOnOff; }54 bool $getPrt( ofstream & os ) { return os.$prt; }55 void $setPrt( ofstream & os, bool state ) { os.$prt = state; }46 bool sepPrt( ofstream & os ) { setNL( os, false ); return os.sepOnOff; } 47 void sepReset( ofstream & os ) { os.sepOnOff = os.sepDefault; } 48 void sepReset( ofstream & os, bool reset ) { os.sepDefault = reset; os.sepOnOff = os.sepDefault; } 49 const char * sepGetCur( ofstream & os ) { return os.sepCur; } 50 void sepSetCur( ofstream & os, const char * sepCur ) { os.sepCur = sepCur; } 51 bool getNL( ofstream & os ) { return os.sawNL; } 52 void setNL( ofstream & os, bool state ) { os.sawNL = state; } 53 bool getANL( ofstream & os ) { return os.nlOnOff; } 54 bool getPrt( ofstream & os ) { return os.prt; } 55 void setPrt( ofstream & os, bool state ) { os.prt = state; } 56 56 57 57 // public 58 void ?{}( ofstream & os ) { os. $file = 0p; }59 60 void ?{}( ofstream & os, const char name[], const char mode[]) {58 void ?{}( ofstream & os ) { os.file = 0; } 59 60 void ?{}( ofstream & os, const char * name, const char * mode ) { 61 61 open( os, name, mode ); 62 62 } // ?{} 63 63 64 void ?{}( ofstream & os, const char name[]) {64 void ?{}( ofstream & os, const char * name ) { 65 65 open( os, name, "w" ); 66 66 } // ?{} … … 70 70 } // ^?{} 71 71 72 void sepOn( ofstream & os ) { os. $sepOnOff = ! $getNL( os ); }73 void sepOff( ofstream & os ) { os. $sepOnOff = false; }72 void sepOn( ofstream & os ) { os.sepOnOff = ! getNL( os ); } 73 void sepOff( ofstream & os ) { os.sepOnOff = false; } 74 74 75 75 bool sepDisable( ofstream & os ) { 76 bool temp = os. $sepDefault;77 os. $sepDefault = false;78 $sepReset( os );76 bool temp = os.sepDefault; 77 os.sepDefault = false; 78 sepReset( os ); 79 79 return temp; 80 80 } // sepDisable 81 81 82 82 bool sepEnable( ofstream & os ) { 83 bool temp = os. $sepDefault;84 os. $sepDefault = true;85 if ( os. $sepOnOff ) $sepReset( os );// start of line ?83 bool temp = os.sepDefault; 84 os.sepDefault = true; 85 if ( os.sepOnOff ) sepReset( os ); // start of line ? 86 86 return temp; 87 87 } // sepEnable 88 88 89 void nlOn( ofstream & os ) { os. $nlOnOff = true; }90 void nlOff( ofstream & os ) { os. $nlOnOff = false; }91 92 const char * sepGet( ofstream & os ) { return os. $separator; }93 void sepSet( ofstream & os, const char s[]) {89 void nlOn( ofstream & os ) { os.nlOnOff = true; } 90 void nlOff( ofstream & os ) { os.nlOnOff = false; } 91 92 const char * sepGet( ofstream & os ) { return os.separator; } 93 void sepSet( ofstream & os, const char * s ) { 94 94 assert( s ); 95 strncpy( os. $separator, s, sepSize - 1 );96 os. $separator[sepSize - 1] = '\0';95 strncpy( os.separator, s, sepSize - 1 ); 96 os.separator[sepSize - 1] = '\0'; 97 97 } // sepSet 98 98 99 const char * sepGetTuple( ofstream & os ) { return os. $tupleSeparator; }100 void sepSetTuple( ofstream & os, const char s[]) {99 const char * sepGetTuple( ofstream & os ) { return os.tupleSeparator; } 100 void sepSetTuple( ofstream & os, const char * s ) { 101 101 assert( s ); 102 strncpy( os. $tupleSeparator, s, sepSize - 1 );103 os. $tupleSeparator[sepSize - 1] = '\0';102 strncpy( os.tupleSeparator, s, sepSize - 1 ); 103 os.tupleSeparator[sepSize - 1] = '\0'; 104 104 } // sepSet 105 105 106 106 void ends( ofstream & os ) { 107 if ( $getANL( os ) ) nl( os );108 else $setPrt( os, false ); // turn off107 if ( getANL( os ) ) nl( os ); 108 else setPrt( os, false ); // turn off 109 109 if ( &os == &exit ) exit( EXIT_FAILURE ); 110 110 if ( &os == &abort ) abort(); … … 112 112 113 113 int fail( ofstream & os ) { 114 return os. $file == 0 || ferror( (FILE *)(os.$file) );114 return os.file == 0 || ferror( (FILE *)(os.file) ); 115 115 } // fail 116 116 117 117 int flush( ofstream & os ) { 118 return fflush( (FILE *)(os. $file) );118 return fflush( (FILE *)(os.file) ); 119 119 } // flush 120 120 121 void open( ofstream & os, const char name[], const char mode[]) {121 void open( ofstream & os, const char * name, const char * mode ) { 122 122 FILE * file = fopen( name, mode ); 123 123 #ifdef __CFA_DEBUG__ 124 if ( file == 0p ) { 125 throw (Open_Failure){ os }; 126 // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 124 if ( file == 0 ) { 125 abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 127 126 } // if 128 127 #endif // __CFA_DEBUG__ … … 130 129 } // open 131 130 132 void open( ofstream & os, const char name[]) {131 void open( ofstream & os, const char * name ) { 133 132 open( os, name, "w" ); 134 133 } // open 135 134 136 135 void close( ofstream & os ) { 137 if ( (FILE *)(os.$file) == 0p ) return; 138 if ( (FILE *)(os.$file) == (FILE *)stdout || (FILE *)(os.$file) == (FILE *)stderr ) return; 139 140 if ( fclose( (FILE *)(os.$file) ) == EOF ) { 136 if ( (FILE *)(os.file) == stdout || (FILE *)(os.file) == stderr ) return; 137 138 if ( fclose( (FILE *)(os.file) ) == EOF ) { 141 139 abort | IO_MSG "close output" | nl | strerror( errno ); 142 140 } // if 143 os.$file = 0p;144 141 } // close 145 142 146 ofstream & write( ofstream & os, const char data[], size_t size ) {143 ofstream & write( ofstream & os, const char * data, size_t size ) { 147 144 if ( fail( os ) ) { 148 145 abort | IO_MSG "attempt write I/O on failed stream"; 149 146 } // if 150 147 151 if ( fwrite( data, 1, size, (FILE *)(os. $file) ) != size ) {148 if ( fwrite( data, 1, size, (FILE *)(os.file) ) != size ) { 152 149 abort | IO_MSG "write" | nl | strerror( errno ); 153 150 } // if … … 158 155 va_list args; 159 156 va_start( args, format ); 160 int len = vfprintf( (FILE *)(os. $file), format, args );157 int len = vfprintf( (FILE *)(os.file), format, args ); 161 158 if ( len == EOF ) { 162 if ( ferror( (FILE *)(os. $file) ) ) {159 if ( ferror( (FILE *)(os.file) ) ) { 163 160 abort | IO_MSG "invalid write"; 164 161 } // if … … 166 163 va_end( args ); 167 164 168 $setPrt( os, true );// called in output cascade169 $sepReset( os );// reset separator165 setPrt( os, true ); // called in output cascade 166 sepReset( os ); // reset separator 170 167 return len; 171 168 } // fmt … … 182 179 183 180 184 // *********************************** ifstream ***********************************181 //*********************************** ifstream *********************************** 185 182 186 183 187 184 // private 188 185 void ?{}( ifstream & is, void * file ) { 189 is. $file = file;190 is. $nlOnOff = false;186 is.file = file; 187 is.nlOnOff = false; 191 188 } // ?{} 192 189 193 190 // public 194 void ?{}( ifstream & is ) { is.$file = 0p; }195 196 void ?{}( ifstream & is, const char name[], const char mode[]) {191 void ?{}( ifstream & is ) { is.file = 0; } 192 193 void ?{}( ifstream & is, const char * name, const char * mode ) { 197 194 open( is, name, mode ); 198 195 } // ?{} 199 196 200 void ?{}( ifstream & is, const char name[]) {197 void ?{}( ifstream & is, const char * name ) { 201 198 open( is, name, "r" ); 202 199 } // ?{} … … 206 203 } // ^?{} 207 204 208 void nlOn( ifstream & os ) { os. $nlOnOff = true; }209 void nlOff( ifstream & os ) { os. $nlOnOff = false; }210 bool getANL( ifstream & os ) { return os. $nlOnOff; }205 void nlOn( ifstream & os ) { os.nlOnOff = true; } 206 void nlOff( ifstream & os ) { os.nlOnOff = false; } 207 bool getANL( ifstream & os ) { return os.nlOnOff; } 211 208 212 209 int fail( ifstream & is ) { 213 return is. $file == 0p || ferror( (FILE *)(is.$file) );210 return is.file == 0 || ferror( (FILE *)(is.file) ); 214 211 } // fail 215 212 216 213 int eof( ifstream & is ) { 217 return feof( (FILE *)(is. $file) );214 return feof( (FILE *)(is.file) ); 218 215 } // eof 219 216 220 void open( ifstream & is, const char name[], const char mode[]) {217 void open( ifstream & is, const char * name, const char * mode ) { 221 218 FILE * file = fopen( name, mode ); 222 219 #ifdef __CFA_DEBUG__ 223 if ( file == 0p ) { 224 throw (Open_Failure){ is }; 225 // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 220 if ( file == 0 ) { 221 abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 226 222 } // if 227 223 #endif // __CFA_DEBUG__ 228 is. $file = file;229 } // open 230 231 void open( ifstream & is, const char name[]) {224 is.file = file; 225 } // open 226 227 void open( ifstream & is, const char * name ) { 232 228 open( is, name, "r" ); 233 229 } // open 234 230 235 231 void close( ifstream & is ) { 236 if ( (FILE *)(is.$file) == 0p ) return; 237 if ( (FILE *)(is.$file) == (FILE *)stdin ) return; 238 239 if ( fclose( (FILE *)(is.$file) ) == EOF ) { 232 if ( (FILE *)(is.file) == stdin ) return; 233 234 if ( fclose( (FILE *)(is.file) ) == EOF ) { 240 235 abort | IO_MSG "close input" | nl | strerror( errno ); 241 236 } // if 242 is.$file = 0p;243 237 } // close 244 238 … … 248 242 } // if 249 243 250 if ( fread( data, size, 1, (FILE *)(is. $file) ) == 0 ) {244 if ( fread( data, size, 1, (FILE *)(is.file) ) == 0 ) { 251 245 abort | IO_MSG "read" | nl | strerror( errno ); 252 246 } // if … … 259 253 } // if 260 254 261 if ( ungetc( c, (FILE *)(is. $file) ) == EOF ) {255 if ( ungetc( c, (FILE *)(is.file) ) == EOF ) { 262 256 abort | IO_MSG "ungetc" | nl | strerror( errno ); 263 257 } // if … … 269 263 270 264 va_start( args, format ); 271 int len = vfscanf( (FILE *)(is. $file), format, args );265 int len = vfscanf( (FILE *)(is.file), format, args ); 272 266 if ( len == EOF ) { 273 if ( ferror( (FILE *)(is. $file) ) ) {267 if ( ferror( (FILE *)(is.file) ) ) { 274 268 abort | IO_MSG "invalid read"; 275 269 } // if … … 282 276 ifstream & sin = sinFile, & stdin = sinFile; 283 277 284 285 // *********************************** exceptions ***********************************286 287 288 void ?{}( Open_Failure & this, ofstream & ostream ) {289 VTABLE_INIT(this, Open_Failure);290 this.ostream = &ostream;291 this.tag = 1;292 }293 void ?{}( Open_Failure & this, ifstream & istream ) {294 VTABLE_INIT(this, Open_Failure);295 this.istream = &istream;296 this.tag = 0;297 }298 const char * Open_Failure_msg(Open_Failure * this) {299 return "Open_Failure";300 }301 VTABLE_INSTANCE(Open_Failure)(Open_Failure_msg);302 void throwOpen_Failure( ofstream & ostream ) {303 Open_Failure exc = { ostream };304 }305 void throwOpen_Failure( ifstream & istream ) {306 Open_Failure exc = { istream };307 }308 309 278 // Local Variables: // 310 279 // tab-width: 4 // -
libcfa/src/fstream.hfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 16:29:17 202013 // Update Count : 1 8912 // Last Modified On : Fri Nov 29 06:56:02 2019 13 // Update Count : 168 14 14 // 15 15 … … 17 17 18 18 #include "iostream.hfa" 19 #include <exception.hfa>20 19 21 20 22 // *********************************** ofstream ***********************************21 //*********************************** ofstream *********************************** 23 22 24 23 25 24 enum { sepSize = 16 }; 26 25 struct ofstream { 27 void * $file;28 bool $sepDefault;29 bool $sepOnOff;30 bool $nlOnOff;31 bool $prt; // print text32 bool $sawNL;33 const char * $sepCur;34 char $separator[sepSize];35 char $tupleSeparator[sepSize];26 void * file; 27 bool sepDefault; 28 bool sepOnOff; 29 bool nlOnOff; 30 bool prt; // print text 31 bool sawNL; 32 const char * sepCur; 33 char separator[sepSize]; 34 char tupleSeparator[sepSize]; 36 35 }; // ofstream 37 36 38 37 // private 39 bool $sepPrt( ofstream & );40 void $sepReset( ofstream & );41 void $sepReset( ofstream &, bool );42 const char * $sepGetCur( ofstream & );43 void $sepSetCur( ofstream &, const char []);44 bool $getNL( ofstream & );45 void $setNL( ofstream &, bool );46 bool $getANL( ofstream & );47 bool $getPrt( ofstream & );48 void $setPrt( ofstream &, bool );38 bool sepPrt( ofstream & ); 39 void sepReset( ofstream & ); 40 void sepReset( ofstream &, bool ); 41 const char * sepGetCur( ofstream & ); 42 void sepSetCur( ofstream &, const char * ); 43 bool getNL( ofstream & ); 44 void setNL( ofstream &, bool ); 45 bool getANL( ofstream & ); 46 bool getPrt( ofstream & ); 47 void setPrt( ofstream &, bool ); 49 48 50 49 // public … … 57 56 58 57 const char * sepGet( ofstream & ); 59 void sepSet( ofstream &, const char []);58 void sepSet( ofstream &, const char * ); 60 59 const char * sepGetTuple( ofstream & ); 61 void sepSetTuple( ofstream &, const char []);60 void sepSetTuple( ofstream &, const char * ); 62 61 63 62 void ends( ofstream & os ); 64 63 int fail( ofstream & ); 65 64 int flush( ofstream & ); 66 void open( ofstream &, const char name[], const char mode[]);67 void open( ofstream &, const char name[]);65 void open( ofstream &, const char * name, const char * mode ); 66 void open( ofstream &, const char * name ); 68 67 void close( ofstream & ); 69 ofstream & write( ofstream &, const char data[], size_t size );70 int fmt( ofstream &, const char format[], ... ) __attribute__(( format(printf, 2, 3) ));68 ofstream & write( ofstream &, const char * data, size_t size ); 69 int fmt( ofstream &, const char format[], ... ); 71 70 72 71 void ?{}( ofstream & os ); 73 void ?{}( ofstream & os, const char name[], const char mode[]);74 void ?{}( ofstream & os, const char name[]);72 void ?{}( ofstream & os, const char * name, const char * mode ); 73 void ?{}( ofstream & os, const char * name ); 75 74 void ^?{}( ofstream & os ); 76 75 … … 79 78 80 79 81 // *********************************** ifstream ***********************************80 //*********************************** ifstream *********************************** 82 81 83 82 84 83 struct ifstream { 85 void * $file;86 bool $nlOnOff;84 void * file; 85 bool nlOnOff; 87 86 }; // ifstream 88 87 … … 93 92 int fail( ifstream & is ); 94 93 int eof( ifstream & is ); 95 void open( ifstream & is, const char name[], const char mode[]);96 void open( ifstream & is, const char name[]);94 void open( ifstream & is, const char * name, const char * mode ); 95 void open( ifstream & is, const char * name ); 97 96 void close( ifstream & is ); 98 97 ifstream & read( ifstream & is, char * data, size_t size ); 99 98 ifstream & ungetc( ifstream & is, char c ); 100 int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));99 int fmt( ifstream &, const char format[], ... ); 101 100 102 101 void ?{}( ifstream & is ); 103 void ?{}( ifstream & is, const char name[], const char mode[]);104 void ?{}( ifstream & is, const char name[]);102 void ?{}( ifstream & is, const char * name, const char * mode ); 103 void ?{}( ifstream & is, const char * name ); 105 104 void ^?{}( ifstream & is ); 106 105 107 106 extern ifstream & sin, & stdin; // aliases 108 109 110 // *********************************** exceptions ***********************************111 112 113 DATA_EXCEPTION(Open_Failure)(114 union {115 ofstream * ostream;116 ifstream * istream;117 };118 // TEMPORARY: need polymorphic exceptions119 int tag; // 1 => ostream; 0 => istream120 );121 122 void ?{}( Open_Failure & this, ofstream & ostream );123 void ?{}( Open_Failure & this, ifstream & istream );124 107 125 108 // Local Variables: // -
libcfa/src/gmp.hfa
reef8dfb rbdfc032 10 10 // Created On : Tue Apr 19 08:43:43 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : S un Feb 9 09:56:54 202013 // Update Count : 3112 // Last Modified On : Sat Jul 13 15:25:05 2019 13 // Update Count : 27 14 14 // 15 15 … … 24 24 25 25 static inline { 26 // constructor , zero_t/one_t are unnecessary because of relationship with signed/unsigned int26 // constructor 27 27 void ?{}( Int & this ) { mpz_init( this.mpz ); } 28 28 void ?{}( Int & this, Int init ) { mpz_init_set( this.mpz, init.mpz ); } 29 void ?{}( Int & this, zero_t ) { mpz_init_set_si( this.mpz, 0 ); } 30 void ?{}( Int & this, one_t ) { mpz_init_set_si( this.mpz, 1 ); } 29 31 void ?{}( Int & this, signed long int init ) { mpz_init_set_si( this.mpz, init ); } 30 32 void ?{}( Int & this, unsigned long int init ) { mpz_init_set_ui( this.mpz, init ); } 31 void ?{}( Int & this, const char val[]) { if ( mpz_init_set_str( this.mpz, val, 0 ) ) abort(); }33 void ?{}( Int & this, const char * val ) { if ( mpz_init_set_str( this.mpz, val, 0 ) ) abort(); } 32 34 void ^?{}( Int & this ) { mpz_clear( this.mpz ); } 33 35 … … 35 37 Int ?`mp( signed long int init ) { return (Int){ init }; } 36 38 Int ?`mp( unsigned long int init ) { return (Int){ init }; } 37 Int ?`mp( const char init[]) { return (Int){ init }; }39 Int ?`mp( const char * init ) { return (Int){ init }; } 38 40 39 41 // assignment … … 41 43 Int ?=?( Int & lhs, long int rhs ) { mpz_set_si( lhs.mpz, rhs ); return lhs; } 42 44 Int ?=?( Int & lhs, unsigned long int rhs ) { mpz_set_ui( lhs.mpz, rhs ); return lhs; } 43 Int ?=?( Int & lhs, const char rhs[]) { if ( mpz_set_str( lhs.mpz, rhs, 0 ) ) { abort | "invalid string conversion"; } return lhs; }45 Int ?=?( Int & lhs, const char * rhs ) { if ( mpz_set_str( lhs.mpz, rhs, 0 ) ) { abort | "invalid string conversion"; } return lhs; } 44 46 45 47 char ?=?( char & lhs, Int rhs ) { char val = mpz_get_si( rhs.mpz ); lhs = val; return lhs; } … … 263 265 forall( dtype ostype | ostream( ostype ) ) { 264 266 ostype & ?|?( ostype & os, Int mp ) { 265 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );267 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 266 268 gmp_printf( "%Zd", mp.mpz ); 267 269 sepOn( os ); -
libcfa/src/heap.cfa
reef8dfb rbdfc032 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // heap.c fa--7 // heap.c -- 8 8 // 9 9 // Author : Peter A. Buhr 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Dec 16 12:28:25 202013 // Update Count : 102312 // Last Modified On : Sun Dec 8 21:01:31 2019 13 // Update Count : 647 14 14 // 15 15 16 16 #include <unistd.h> // sbrk, sysconf 17 #include <stdlib.h> // EXIT_FAILURE18 17 #include <stdbool.h> // true, false 19 18 #include <stdio.h> // snprintf, fileno 20 19 #include <errno.h> // errno 21 20 #include <string.h> // memset, memcpy 22 #include <limits.h> // ULONG_MAX 23 #include <malloc.h> // memalign, malloc_usable_size 21 extern "C" { 24 22 #include <sys/mman.h> // mmap, munmap 25 26 #include "bits/align.hfa" // libAlign 23 } // extern "C" 24 25 // #comment TD : Many of these should be merged into math I believe 26 #include "bits/align.hfa" // libPow2 27 27 #include "bits/defs.hfa" // likely, unlikely 28 28 #include "bits/locks.hfa" // __spinlock_t 29 29 #include "startup.hfa" // STARTUP_PRIORITY_MEMORY 30 #include "math.hfa" // ceiling 31 #include "bitmanip.hfa" // is_pow2, ceiling2 30 //#include "stdlib.hfa" // bsearchl 31 #include "malloc.h" 32 33 #define MIN(x, y) (y > x ? x : y) 32 34 33 35 static bool traceHeap = false; … … 72 74 // Define the default extension heap amount in units of bytes. When the uC++ supplied heap reaches the brk address, 73 75 // the brk address is extended by the extension amount. 74 __CFA_DEFAULT_HEAP_EXPANSION__ = (1 0* 1024 * 1024),76 __CFA_DEFAULT_HEAP_EXPANSION__ = (1 * 1024 * 1024), 75 77 76 78 // Define the mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; … … 89 91 90 92 #ifdef __CFA_DEBUG__ 91 static size_t allocUnfreed;// running total of allocations minus frees93 static unsigned int allocFree; // running total of allocations minus frees 92 94 93 95 static void prtUnfreed() { 94 if ( alloc Unfreed!= 0 ) {96 if ( allocFree != 0 ) { 95 97 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT. 96 98 char helpText[512]; 97 int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with % zu(0x%zx) bytes of storage allocated but not freed.\n"99 int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with %u(0x%x) bytes of storage allocated but not freed.\n" 98 100 "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n", 99 (long int)getpid(), alloc Unfreed, allocUnfreed); // always print the UNIX pid101 (long int)getpid(), allocFree, allocFree ); // always print the UNIX pid 100 102 __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug 101 103 } // if … … 104 106 extern "C" { 105 107 void heapAppStart() { // called by __cfaabi_appready_startup 106 alloc Unfreed= 0;108 allocFree = 0; 107 109 } // heapAppStart 108 110 … … 116 118 117 119 // statically allocated variables => zero filled. 118 size_t __page_size; // architecture pagesize 119 int __map_prot; // common mmap/mprotect protection 120 static size_t pageSize; // architecture pagesize 120 121 static size_t heapExpand; // sbrk advance 121 122 static size_t mmapStart; // cross over point for mmap … … 126 127 #define LOCKFREE 1 127 128 #define BUCKETLOCK SPINLOCK 128 #if BUCKETLOCK == SPINLOCK 129 #elif BUCKETLOCK == LOCKFREE 130 #include <stackLockFree.hfa> 131 #else 132 #error undefined lock type for bucket lock 129 #if BUCKETLOCK == LOCKFREE 130 #include <uStackLF.h> 133 131 #endif // LOCKFREE 134 132 … … 138 136 139 137 struct HeapManager { 138 // struct FreeHeader; // forward declaration 139 140 140 struct Storage { 141 141 struct Header { // header … … 145 145 struct { // 4-byte word => 8-byte header, 8-byte word => 16-byte header 146 146 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ && __SIZEOF_POINTER__ == 4 147 uint 64_t padding; // unused, force home/blocksize to overlay alignment in fake header147 uint32_t padding; // unused, force home/blocksize to overlay alignment in fake header 148 148 #endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ && __SIZEOF_POINTER__ == 4 149 149 150 150 union { 151 // FreeHeader * home; // allocated block points back to home locations (must overlay alignment) 152 // 2nd low-order bit => zero filled 151 // FreeHeader * home; // allocated block points back to home locations (must overlay alignment) 153 152 void * home; // allocated block points back to home locations (must overlay alignment) 154 153 size_t blockSize; // size for munmap (must overlay alignment) 155 #if BUCK ETLOCK == SPINLOCK154 #if BUCKLOCK == SPINLOCK 156 155 Storage * next; // freed block points next freed block of same size 157 156 #endif // SPINLOCK 158 157 }; 159 size_t size; // allocation size in bytes160 158 161 159 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_POINTER__ == 4 162 uint 64_t padding; // unused, force home/blocksize to overlay alignment in fake header160 uint32_t padding; // unused, force home/blocksize to overlay alignment in fake header 163 161 #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_POINTER__ == 4 164 162 }; 165 #if BUCKETLOCK == LOCKFREE 166 Link(Storage) next; // freed block points next freed block of same size (double-wide) 163 // future code 164 #if BUCKLOCK == LOCKFREE 165 Stack<Storage>::Link next; // freed block points next freed block of same size (double-wide) 167 166 #endif // LOCKFREE 168 167 }; 169 168 } real; // RealHeader 170 171 169 struct FakeHeader { 172 170 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 173 uint32_t alignment; // 1st low-order bit => fake header & alignment171 uint32_t alignment; // low-order bits of home/blockSize used for tricks 174 172 #endif // __ORDER_LITTLE_ENDIAN__ 175 173 … … 189 187 190 188 struct FreeHeader { 191 #if BUCK ETLOCK == SPINLOCK189 #if BUCKLOCK == SPINLOCK 192 190 __spinlock_t lock; // must be first field for alignment 193 191 Storage * freeList; 192 #elif BUCKLOCK == LOCKFREE 193 // future code 194 StackLF<Storage> freeList; 194 195 #else 195 StackLF(Storage) freeList;196 #endif // BUCKETLOCK196 #error undefined lock type for bucket lock 197 #endif // SPINLOCK 197 198 size_t blockSize; // size of allocations on this list 198 199 }; // FreeHeader … … 207 208 }; // HeapManager 208 209 209 #if BUCKETLOCK == LOCKFREE210 static inline {211 Link(HeapManager.Storage) * ?`next( HeapManager.Storage * this ) { return &this->header.kind.real.next; }212 void ?{}( HeapManager.FreeHeader & ) {}213 void ^?{}( HeapManager.FreeHeader & ) {}214 } // distribution215 #endif // LOCKFREE216 217 210 static inline size_t getKey( const HeapManager.FreeHeader & freeheader ) { return freeheader.blockSize; } 218 211 … … 221 214 #define __STATISTICS__ 222 215 223 // Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16. 224 // Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size. 225 // malloc(0) returns 0p, so no bucket is necessary for 0 bytes returning an address that can be freed. 216 // Bucket size must be multiple of 16. 217 // Powers of 2 are common allocation sizes, so make powers of 2 generate the minimum required size. 226 218 static const unsigned int bucketSizes[] @= { // different bucket sizes 227 16 + sizeof(HeapManager.Storage), 32 + sizeof(HeapManager.Storage), 48 + sizeof(HeapManager.Storage), 64 + sizeof(HeapManager.Storage), // 4228 96 + sizeof(HeapManager.Storage), 112 + sizeof(HeapManager.Storage), 128 + sizeof(HeapManager.Storage), // 3219 16, 32, 48, 64 + sizeof(HeapManager.Storage), // 4 220 96, 112, 128 + sizeof(HeapManager.Storage), // 3 229 221 160, 192, 224, 256 + sizeof(HeapManager.Storage), // 4 230 222 320, 384, 448, 512 + sizeof(HeapManager.Storage), // 4 … … 244 236 }; 245 237 246 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );238 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0]), "size of bucket array wrong" ); 247 239 248 240 #ifdef FASTLOOKUP … … 251 243 #endif // FASTLOOKUP 252 244 253 static const off_t mmapFd = -1;// fake or actual fd for anonymous file245 static int mmapFd = -1; // fake or actual fd for anonymous file 254 246 #ifdef __CFA_DEBUG__ 255 247 static bool heapBoot = 0; // detect recursion during boot 256 248 #endif // __CFA_DEBUG__ 257 258 // The constructor for heapManager is called explicitly in memory_startup.259 249 static HeapManager heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing 260 250 … … 262 252 #ifdef __STATISTICS__ 263 253 // Heap statistics counters. 254 static unsigned long long int mmap_storage; 255 static unsigned int mmap_calls; 256 static unsigned long long int munmap_storage; 257 static unsigned int munmap_calls; 258 static unsigned long long int sbrk_storage; 259 static unsigned int sbrk_calls; 260 static unsigned long long int malloc_storage; 264 261 static unsigned int malloc_calls; 265 static unsigned long long int malloc_storage;266 static unsigned int aalloc_calls;267 static unsigned long long int aalloc_storage;262 static unsigned long long int free_storage; 263 static unsigned int free_calls; 264 static unsigned long long int calloc_storage; 268 265 static unsigned int calloc_calls; 269 static unsigned long long int calloc_storage;266 static unsigned long long int memalign_storage; 270 267 static unsigned int memalign_calls; 271 static unsigned long long int memalign_storage; 272 static unsigned int amemalign_calls; 273 static unsigned long long int amemalign_storage; 268 static unsigned long long int cmemalign_storage; 274 269 static unsigned int cmemalign_calls; 275 static unsigned long long int cmemalign_storage; 276 static unsigned int resize_calls; 277 static unsigned long long int resize_storage; 270 static unsigned long long int realloc_storage; 278 271 static unsigned int realloc_calls; 279 static unsigned long long int realloc_storage;280 static unsigned int free_calls;281 static unsigned long long int free_storage;282 static unsigned int mmap_calls;283 static unsigned long long int mmap_storage;284 static unsigned int munmap_calls;285 static unsigned long long int munmap_storage;286 static unsigned int sbrk_calls;287 static unsigned long long int sbrk_storage;288 272 // Statistics file descriptor (changed by malloc_stats_fd). 289 static int stat _fd = STDERR_FILENO; // default stderr273 static int statfd = STDERR_FILENO; // default stderr 290 274 291 275 // Use "write" because streams may be shutdown when calls are made. 292 276 static void printStats() { 293 char helpText[ 1024];277 char helpText[512]; 294 278 __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText), 295 279 "\nHeap statistics:\n" 296 280 " malloc: calls %u / storage %llu\n" 297 " aalloc: calls %u / storage %llu\n"298 281 " calloc: calls %u / storage %llu\n" 299 282 " memalign: calls %u / storage %llu\n" 300 " amemalign: calls %u / storage %llu\n"301 283 " cmemalign: calls %u / storage %llu\n" 302 " resize: calls %u / storage %llu\n"303 284 " realloc: calls %u / storage %llu\n" 304 285 " free: calls %u / storage %llu\n" … … 307 288 " sbrk: calls %u / storage %llu\n", 308 289 malloc_calls, malloc_storage, 309 aalloc_calls, aalloc_storage,310 290 calloc_calls, calloc_storage, 311 291 memalign_calls, memalign_storage, 312 amemalign_calls, amemalign_storage,313 292 cmemalign_calls, cmemalign_storage, 314 resize_calls, resize_storage,315 293 realloc_calls, realloc_storage, 316 294 free_calls, free_storage, … … 322 300 323 301 static int printStatsXML( FILE * stream ) { // see malloc_info 324 char helpText[ 1024];302 char helpText[512]; 325 303 int len = snprintf( helpText, sizeof(helpText), 326 304 "<malloc version=\"1\">\n" … … 329 307 "</sizes>\n" 330 308 "<total type=\"malloc\" count=\"%u\" size=\"%llu\"/>\n" 331 "<total type=\"aalloc\" count=\"%u\" size=\"%llu\"/>\n"332 309 "<total type=\"calloc\" count=\"%u\" size=\"%llu\"/>\n" 333 310 "<total type=\"memalign\" count=\"%u\" size=\"%llu\"/>\n" 334 "<total type=\"amemalign\" count=\"%u\" size=\"%llu\"/>\n"335 311 "<total type=\"cmemalign\" count=\"%u\" size=\"%llu\"/>\n" 336 "<total type=\"resize\" count=\"%u\" size=\"%llu\"/>\n"337 312 "<total type=\"realloc\" count=\"%u\" size=\"%llu\"/>\n" 338 313 "<total type=\"free\" count=\"%u\" size=\"%llu\"/>\n" … … 342 317 "</malloc>", 343 318 malloc_calls, malloc_storage, 344 aalloc_calls, aalloc_storage,345 319 calloc_calls, calloc_storage, 346 320 memalign_calls, memalign_storage, 347 amemalign_calls, amemalign_storage,348 321 cmemalign_calls, cmemalign_storage, 349 resize_calls, resize_storage,350 322 realloc_calls, realloc_storage, 351 323 free_calls, free_storage, … … 358 330 } // printStatsXML 359 331 #endif // __STATISTICS__ 332 333 334 // static inline void noMemory() { 335 // abort( "Heap memory exhausted at %zu bytes.\n" 336 // "Possible cause is very large memory allocation and/or large amount of unfreed storage allocated by the program or system/library routines.", 337 // ((char *)(sbrk( 0 )) - (char *)(heapManager.heapBegin)) ); 338 // } // noMemory 339 340 341 static inline void checkAlign( size_t alignment ) { 342 if ( alignment < libAlign() || ! libPow2( alignment ) ) { 343 abort( "Alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() ); 344 } // if 345 } // checkAlign 346 347 348 static inline bool setHeapExpand( size_t value ) { 349 if ( heapExpand < pageSize ) return true; 350 heapExpand = value; 351 return false; 352 } // setHeapExpand 360 353 361 354 … … 376 369 377 370 static inline bool setMmapStart( size_t value ) { // true => mmapped, false => sbrk 378 if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false;371 if ( value < pageSize || bucketSizes[NoBucketSizes - 1] < value ) return true; 379 372 mmapStart = value; // set global 380 373 … … 383 376 assert( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 384 377 assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? 385 return true;378 return false; 386 379 } // setMmapStart 387 380 388 381 389 // <-------+----------------------------------------------------> bsize (bucket size) 390 // |header |addr 391 //================================================================================== 392 // align/offset | 393 // <-----------------<------------+-----------------------------> bsize (bucket size) 394 // |fake-header | addr 395 #define headerAddr( addr ) ((HeapManager.Storage.Header *)( (char *)addr - sizeof(HeapManager.Storage) )) 396 #define realHeader( header ) ((HeapManager.Storage.Header *)((char *)header - header->kind.fake.offset)) 397 398 // <-------<<--------------------- dsize ---------------------->> bsize (bucket size) 399 // |header |addr 400 //================================================================================== 401 // align/offset | 402 // <------------------------------<<---------- dsize --------->>> bsize (bucket size) 403 // |fake-header |addr 404 #define dataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header )) 405 406 407 static inline void checkAlign( size_t alignment ) { 408 if ( alignment < libAlign() || ! is_pow2( alignment ) ) { 409 abort( "Alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() ); 410 } // if 411 } // checkAlign 412 413 414 static inline void checkHeader( bool check, const char name[], void * addr ) { 382 static inline void checkHeader( bool check, const char * name, void * addr ) { 415 383 if ( unlikely( check ) ) { // bad address ? 416 384 abort( "Attempt to %s storage %p with address outside the heap.\n" … … 423 391 static inline void fakeHeader( HeapManager.Storage.Header *& header, size_t & alignment ) { 424 392 if ( unlikely( (header->kind.fake.alignment & 1) == 1 ) ) { // fake header ? 393 size_t offset = header->kind.fake.offset; 425 394 alignment = header->kind.fake.alignment & -2; // remove flag from value 426 395 #ifdef __CFA_DEBUG__ 427 396 checkAlign( alignment ); // check alignment 428 397 #endif // __CFA_DEBUG__ 429 header = realHeader( header ); // backup from fake to real header 430 } else { 431 alignment = libAlign(); // => no fake header 398 header = (HeapManager.Storage.Header *)((char *)header - offset); 432 399 } // if 433 400 } // fakeHeader 434 401 435 402 436 static inline bool headers( const char name[] __attribute__(( unused )), void * addr, HeapManager.Storage.Header *& header, HeapManager.FreeHeader *& freeElem, 437 size_t & size, size_t & alignment ) with( heapManager ) { 403 // <-------+----------------------------------------------------> bsize (bucket size) 404 // |header |addr 405 //================================================================================== 406 // | alignment 407 // <-----------------<------------+-----------------------------> bsize (bucket size) 408 // |fake-header | addr 409 #define headerAddr( addr ) ((HeapManager.Storage.Header *)( (char *)addr - sizeof(HeapManager.Storage) )) 410 411 // <-------<<--------------------- dsize ---------------------->> bsize (bucket size) 412 // |header |addr 413 //================================================================================== 414 // | alignment 415 // <------------------------------<<---------- dsize --------->>> bsize (bucket size) 416 // |fake-header |addr 417 #define dataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header )) 418 419 420 static inline bool headers( const char * name __attribute__(( unused )), void * addr, HeapManager.Storage.Header *& header, HeapManager.FreeHeader *& freeElem, size_t & size, size_t & alignment ) with ( heapManager ) { 438 421 header = headerAddr( addr ); 439 422 440 if ( unlikely( addr < heapBegin || heapEnd < addr ) ) {// mmapped ?423 if ( unlikely( heapEnd < addr ) ) { // mmapped ? 441 424 fakeHeader( header, alignment ); 442 425 size = header->kind.real.blockSize & -3; // mmap size … … 445 428 446 429 #ifdef __CFA_DEBUG__ 447 checkHeader( header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ?430 checkHeader( addr < heapBegin || header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ? 448 431 #endif // __CFA_DEBUG__ 449 432 … … 466 449 } // headers 467 450 468 #ifdef __CFA_DEBUG__ 469 #if __SIZEOF_POINTER__ == 4 470 #define MASK 0xdeadbeef 471 #else 472 #define MASK 0xdeadbeefdeadbeef 473 #endif 474 #define STRIDE size_t 475 476 static void * Memset( void * addr, STRIDE size ) { // debug only 477 if ( size % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, size %zd not multiple of %zd.", size, sizeof(STRIDE) ); 478 if ( (STRIDE)addr % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, addr %p not multiple of %zd.", addr, sizeof(STRIDE) ); 479 480 STRIDE * end = (STRIDE *)addr + size / sizeof(STRIDE); 481 for ( STRIDE * p = (STRIDE *)addr; p < end; p += 1 ) *p = MASK; 482 return addr; 483 } // Memset 484 #endif // __CFA_DEBUG__ 485 486 487 #define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes." 488 489 static inline void * extend( size_t size ) with( heapManager ) { 451 452 static inline void * extend( size_t size ) with ( heapManager ) { 490 453 lock( extlock __cfaabi_dbg_ctx2 ); 491 454 ptrdiff_t rem = heapRemaining - size; … … 493 456 // If the size requested is bigger than the current remaining storage, increase the size of the heap. 494 457 495 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size ); 496 // Do not call abort or strerror( errno ) as they may call malloc. 497 if ( sbrk( increase ) == (void *)-1 ) { // failed, no memory ? 458 size_t increase = libCeiling( size > heapExpand ? size : heapExpand, libAlign() ); 459 if ( sbrk( increase ) == (void *)-1 ) { 498 460 unlock( extlock ); 499 __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size ); 500 _exit( EXIT_FAILURE ); 501 } // if 502 if ( mprotect( (char *)heapEnd + heapRemaining, increase, __map_prot ) ) { 503 unlock( extlock ); 504 __cfaabi_bits_print_nolock( STDERR_FILENO, "extend() : internal error, mprotect failure, heapEnd:%p size:%zd, errno:%d.\n", heapEnd, increase, errno ); 505 _exit( EXIT_FAILURE ); 461 errno = ENOMEM; 462 return 0p; 506 463 } // if 507 464 #ifdef __STATISTICS__ … … 511 468 #ifdef __CFA_DEBUG__ 512 469 // Set new memory to garbage so subsequent uninitialized usages might fail. 513 memset( (char *)heapEnd + heapRemaining, '\xde', increase ); 514 //Memset( (char *)heapEnd + heapRemaining, increase ); 470 memset( (char *)heapEnd + heapRemaining, '\377', increase ); 515 471 #endif // __CFA_DEBUG__ 516 472 rem = heapRemaining + increase - size; … … 525 481 526 482 527 static inline void * doMalloc( size_t size ) with ( heapManager ) {483 static inline void * doMalloc( size_t size ) with ( heapManager ) { 528 484 HeapManager.Storage * block; // pointer to new block of storage 529 485 … … 531 487 // along with the block and is a multiple of the alignment size. 532 488 533 if ( unlikely( size > ULONG_MAX- sizeof(HeapManager.Storage) ) ) return 0p;489 if ( unlikely( size > ~0ul - sizeof(HeapManager.Storage) ) ) return 0p; 534 490 size_t tsize = size + sizeof(HeapManager.Storage); 535 491 if ( likely( tsize < mmapStart ) ) { // small size => sbrk … … 541 497 posn = Bsearchl( (unsigned int)tsize, bucketSizes, (size_t)maxBucketsUsed ); 542 498 HeapManager.FreeHeader * freeElem = &freeLists[posn]; 543 verify( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ? 544 verify( tsize <= freeElem->blockSize ); // search failure ? 499 // #ifdef FASTLOOKUP 500 // if ( tsize < LookupSizes ) 501 // freeElem = &freeLists[lookup[tsize]]; 502 // else 503 // #endif // FASTLOOKUP 504 // freeElem = bsearchl( tsize, freeLists, (size_t)maxBucketsUsed ); // binary search 505 // HeapManager.FreeHeader * freeElem = 506 // #ifdef FASTLOOKUP 507 // tsize < LookupSizes ? &freeLists[lookup[tsize]] : 508 // #endif // FASTLOOKUP 509 // bsearchl( tsize, freeLists, (size_t)maxBucketsUsed ); // binary search 510 assert( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ? 511 assert( tsize <= freeElem->blockSize ); // search failure ? 545 512 tsize = freeElem->blockSize; // total space needed for request 546 513 547 514 // Spin until the lock is acquired for this particular size of block. 548 515 549 #if BUCKETLOCK == SPINLOCK516 #if defined( SPINLOCK ) 550 517 lock( freeElem->lock __cfaabi_dbg_ctx2 ); 551 518 block = freeElem->freeList; // remove node from stack 552 519 #else 553 block = pop( freeElem->freeList);554 #endif // BUCKETLOCK520 block = freeElem->freeList.pop(); 521 #endif // SPINLOCK 555 522 if ( unlikely( block == 0p ) ) { // no free block ? 556 #if BUCKETLOCK == SPINLOCK523 #if defined( SPINLOCK ) 557 524 unlock( freeElem->lock ); 558 #endif // BUCKETLOCK525 #endif // SPINLOCK 559 526 560 527 // Freelist for that size was empty, so carve it out of the heap if there's enough left, or get some more … … 562 529 563 530 block = (HeapManager.Storage *)extend( tsize ); // mutual exclusion on call 564 #if BUCKETLOCK == SPINLOCK 531 if ( unlikely( block == 0p ) ) return 0p; 532 #if defined( SPINLOCK ) 565 533 } else { 566 534 freeElem->freeList = block->header.kind.real.next; 567 535 unlock( freeElem->lock ); 568 #endif // BUCKETLOCK536 #endif // SPINLOCK 569 537 } // if 570 538 571 539 block->header.kind.real.home = freeElem; // pointer back to free list of apropriate size 572 540 } else { // large size => mmap 573 if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;574 tsize = ceiling2( tsize, __page_size ); // must be multiple of page size541 if ( unlikely( size > ~0ul - pageSize ) ) return 0p; 542 tsize = libCeiling( tsize, pageSize ); // must be multiple of page size 575 543 #ifdef __STATISTICS__ 576 544 __atomic_add_fetch( &mmap_calls, 1, __ATOMIC_SEQ_CST ); 577 545 __atomic_add_fetch( &mmap_storage, tsize, __ATOMIC_SEQ_CST ); 578 546 #endif // __STATISTICS__ 579 580 block = (HeapManager.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 ); 581 if ( block == (HeapManager.Storage *)MAP_FAILED ) { // failed ? 582 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory 547 block = (HeapManager.Storage *)mmap( 0, tsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 ); 548 if ( block == (HeapManager.Storage *)MAP_FAILED ) { 583 549 // Do not call strerror( errno ) as it may call malloc. 584 abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu err no:%d.", &heapManager, tsize, errno );585 } // if550 abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu error:%d.", &heapManager, tsize, errno ); 551 } // if 586 552 #ifdef __CFA_DEBUG__ 587 553 // Set new memory to garbage so subsequent uninitialized usages might fail. 588 memset( block, '\xde', tsize ); 589 //Memset( block, tsize ); 554 memset( block, '\377', tsize ); 590 555 #endif // __CFA_DEBUG__ 591 556 block->header.kind.real.blockSize = tsize; // storage size for munmap 592 557 } // if 593 558 594 block->header.kind.real.size = size; // store allocation size595 559 void * addr = &(block->data); // adjust off header to user bytes 596 verify( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ?597 560 598 561 #ifdef __CFA_DEBUG__ 599 __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST ); 562 assert( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ? 563 __atomic_add_fetch( &allocFree, tsize, __ATOMIC_SEQ_CST ); 600 564 if ( traceHeap() ) { 601 565 enum { BufferSize = 64 }; 602 566 char helpText[BufferSize]; 603 567 int len = snprintf( helpText, BufferSize, "%p = Malloc( %zu ) (allocated %zu)\n", addr, size, tsize ); 568 // int len = snprintf( helpText, BufferSize, "Malloc %p %zu\n", addr, size ); 604 569 __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug 605 570 } // if … … 610 575 611 576 612 static inline void doFree( void * addr ) with ( heapManager ) {577 static inline void doFree( void * addr ) with ( heapManager ) { 613 578 #ifdef __CFA_DEBUG__ 614 579 if ( unlikely( heapManager.heapBegin == 0p ) ) { … … 627 592 #endif // __STATISTICS__ 628 593 if ( munmap( header, size ) == -1 ) { 594 #ifdef __CFA_DEBUG__ 629 595 abort( "Attempt to deallocate storage %p not allocated or with corrupt header.\n" 630 596 "Possible cause is invalid pointer.", 631 597 addr ); 598 #endif // __CFA_DEBUG__ 632 599 } // if 633 600 } else { 634 601 #ifdef __CFA_DEBUG__ 635 602 // Set free memory to garbage so subsequent usages might fail. 636 memset( ((HeapManager.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( HeapManager.Storage ) ); 637 //Memset( ((HeapManager.Storage *)header)->data, freeElem->blockSize - sizeof( HeapManager.Storage ) ); 603 memset( ((HeapManager.Storage *)header)->data, '\377', freeElem->blockSize - sizeof( HeapManager.Storage ) ); 638 604 #endif // __CFA_DEBUG__ 639 605 … … 641 607 free_storage += size; 642 608 #endif // __STATISTICS__ 643 #if BUCKETLOCK == SPINLOCK609 #if defined( SPINLOCK ) 644 610 lock( freeElem->lock __cfaabi_dbg_ctx2 ); // acquire spin lock 645 611 header->kind.real.next = freeElem->freeList; // push on stack … … 647 613 unlock( freeElem->lock ); // release spin lock 648 614 #else 649 push( freeElem->freeList,*(HeapManager.Storage *)header );650 #endif // BUCKETLOCK615 freeElem->freeList.push( *(HeapManager.Storage *)header ); 616 #endif // SPINLOCK 651 617 } // if 652 618 653 619 #ifdef __CFA_DEBUG__ 654 __atomic_add_fetch( &alloc Unfreed, -size, __ATOMIC_SEQ_CST );620 __atomic_add_fetch( &allocFree, -size, __ATOMIC_SEQ_CST ); 655 621 if ( traceHeap() ) { 656 char helpText[64]; 622 enum { BufferSize = 64 }; 623 char helpText[BufferSize]; 657 624 int len = snprintf( helpText, sizeof(helpText), "Free( %p ) size:%zu\n", addr, size ); 658 625 __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug … … 662 629 663 630 664 size_t prtFree( HeapManager & manager ) with ( manager ) {631 size_t prtFree( HeapManager & manager ) with ( manager ) { 665 632 size_t total = 0; 666 633 #ifdef __STATISTICS__ … … 674 641 #endif // __STATISTICS__ 675 642 676 #if BUCKETLOCK == SPINLOCK643 #if defined( SPINLOCK ) 677 644 for ( HeapManager.Storage * p = freeLists[i].freeList; p != 0p; p = p->header.kind.real.next ) { 678 645 #else 679 for(;;) { 680 // for ( HeapManager.Storage * p = top( freeLists[i].freeList ); p != 0p; p = (p)`next->top ) { 681 // for ( HeapManager.Storage * p = top( freeLists[i].freeList ); p != 0p; /* p = getNext( p )->top */) { 682 // HeapManager.Storage * temp = p->header.kind.real.next.top; // FIX ME: direct assignent fails, initialization works` 683 // typeof(p) temp = (( p )`next)->top; // FIX ME: direct assignent fails, initialization works` 684 // p = temp; 685 #endif // BUCKETLOCK 646 for ( HeapManager.Storage * p = freeLists[i].freeList.top(); p != 0p; p = p->header.kind.real.next.top ) { 647 #endif // SPINLOCK 686 648 total += size; 687 649 #ifdef __STATISTICS__ … … 703 665 704 666 705 static void ?{}( HeapManager & manager ) with( manager ) { 706 __page_size = sysconf( _SC_PAGESIZE ); 707 __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 667 static void ?{}( HeapManager & manager ) with ( manager ) { 668 pageSize = sysconf( _SC_PAGESIZE ); 708 669 709 670 for ( unsigned int i = 0; i < NoBucketSizes; i += 1 ) { // initialize the free lists … … 719 680 #endif // FASTLOOKUP 720 681 721 if ( !setMmapStart( default_mmap_start() ) ) {682 if ( setMmapStart( default_mmap_start() ) ) { 722 683 abort( "HeapManager : internal error, mmap start initialization failure." ); 723 684 } // if … … 725 686 726 687 char * end = (char *)sbrk( 0 ); 727 heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, __page_size ) - end ); // move start of heap to multiple of alignment 688 sbrk( (char *)libCeiling( (long unsigned int)end, libAlign() ) - end ); // move start of heap to multiple of alignment 689 heapBegin = heapEnd = sbrk( 0 ); // get new start point 728 690 } // HeapManager 729 691 … … 733 695 if ( traceHeapTerm() ) { 734 696 printStats(); 735 // prtUnfreed() called in heapAppStop()697 // if ( prtfree() ) prtFree( heapManager, true ); 736 698 } // if 737 699 #endif // __STATISTICS__ … … 742 704 void memory_startup( void ) { 743 705 #ifdef __CFA_DEBUG__ 744 if ( heapBoot ) { // check for recursion during system boot 706 if ( unlikely( heapBoot ) ) { // check for recursion during system boot 707 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT. 745 708 abort( "boot() : internal error, recursively invoked during system boot." ); 746 709 } // if … … 748 711 #endif // __CFA_DEBUG__ 749 712 750 // verify( heapManager.heapBegin != 0 );713 //assert( heapManager.heapBegin != 0 ); 751 714 //heapManager{}; 752 if ( heapManager.heapBegin == 0p ) heapManager{}; // sanity check715 if ( heapManager.heapBegin == 0p ) heapManager{}; 753 716 } // memory_startup 754 717 … … 760 723 761 724 static inline void * mallocNoStats( size_t size ) { // necessary for malloc statistics 762 verify( heapManager.heapBegin != 0p ); // called before memory_startup ? 763 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 764 765 #if __SIZEOF_POINTER__ == 8 766 verify( size < ((typeof(size_t))1 << 48) ); 767 #endif // __SIZEOF_POINTER__ == 8 768 return doMalloc( size ); 725 //assert( heapManager.heapBegin != 0 ); 726 if ( unlikely( heapManager.heapBegin == 0p ) ) heapManager{}; // called before memory_startup ? 727 void * addr = doMalloc( size ); 728 if ( unlikely( addr == 0p ) ) errno = ENOMEM; // POSIX 729 return addr; 769 730 } // mallocNoStats 770 731 771 732 772 static inline void * callocNoStats( size_t dim, size_t elemSize ) { 773 size_t size = dim * elemSize; 774 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 733 static inline void * callocNoStats( size_t noOfElems, size_t elemSize ) { 734 size_t size = noOfElems * elemSize; 775 735 char * addr = (char *)mallocNoStats( size ); 736 if ( unlikely( addr == 0p ) ) return 0p; 776 737 777 738 HeapManager.Storage.Header * header; 778 739 HeapManager.FreeHeader * freeElem; 779 740 size_t bsize, alignment; 741 bool mapped __attribute__(( unused )) = headers( "calloc", addr, header, freeElem, bsize, alignment ); 780 742 #ifndef __CFA_DEBUG__ 781 bool mapped =782 #endif // __CFA_DEBUG__783 headers( "calloc", addr, header, freeElem, bsize, alignment );784 #ifndef __CFA_DEBUG__785 786 743 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. 787 744 if ( ! mapped ) 788 745 #endif // __CFA_DEBUG__ 789 // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined 746 // Zero entire data space even when > than size => realloc without a new allocation and zero fill works. 747 // <-------00000000000000000000000000000000000000000000000000000> bsize (bucket size) 790 748 // `-header`-addr `-size 791 memset( addr, '\0', size );// set to zeros749 memset( addr, '\0', bsize - sizeof(HeapManager.Storage) ); // set to zeros 792 750 793 751 header->kind.real.blockSize |= 2; // mark as zero filled … … 796 754 797 755 798 static inline void * memalignNoStats( size_t alignment, size_t size ) { 799 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 800 756 static inline void * memalignNoStats( size_t alignment, size_t size ) { // necessary for malloc statistics 801 757 #ifdef __CFA_DEBUG__ 802 758 checkAlign( alignment ); // check alignment … … 816 772 // add sizeof(Storage) for fake header 817 773 char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(HeapManager.Storage) ); 774 if ( unlikely( addr == 0p ) ) return addr; 818 775 819 776 // address in the block of the "next" alignment address 820 char * user = (char *) ceiling2( (uintptr_t)(addr + sizeof(HeapManager.Storage)), alignment );777 char * user = (char *)libCeiling( (uintptr_t)(addr + sizeof(HeapManager.Storage)), alignment ); 821 778 822 779 // address of header from malloc 823 780 HeapManager.Storage.Header * realHeader = headerAddr( addr ); 824 realHeader->kind.real.size = size; // correct size to eliminate above alignment offset825 781 // address of fake header * before* the alignment location 826 782 HeapManager.Storage.Header * fakeHeader = headerAddr( user ); … … 834 790 835 791 836 static inline void * cmemalignNoStats( size_t alignment, size_t dim, size_t elemSize ) { 837 size_t size = dim * elemSize; 838 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 792 static inline void * cmemalignNoStats( size_t alignment, size_t noOfElems, size_t elemSize ) { 793 size_t size = noOfElems * elemSize; 839 794 char * addr = (char *)memalignNoStats( alignment, size ); 840 795 if ( unlikely( addr == 0p ) ) return 0p; 841 796 HeapManager.Storage.Header * header; 842 797 HeapManager.FreeHeader * freeElem; 843 798 size_t bsize; 799 bool mapped __attribute__(( unused )) = headers( "cmemalign", addr, header, freeElem, bsize, alignment ); 844 800 #ifndef __CFA_DEBUG__ 845 bool mapped =846 #endif // __CFA_DEBUG__847 headers( "cmemalign", addr, header, freeElem, bsize, alignment );848 849 801 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. 850 #ifndef __CFA_DEBUG__851 802 if ( ! mapped ) 852 803 #endif // __CFA_DEBUG__ 853 // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined 854 // `-header`-addr `-size 855 memset( addr, '\0', size ); // set to zeros 856 857 header->kind.real.blockSize |= 2; // mark as zero filled 804 memset( addr, '\0', dataStorage( bsize, addr, header ) ); // set to zeros 805 header->kind.real.blockSize |= 2; // mark as zero filled 806 858 807 return addr; 859 808 } // cmemalignNoStats 860 809 861 810 811 // supported mallopt options 812 #ifndef M_MMAP_THRESHOLD 813 #define M_MMAP_THRESHOLD (-1) 814 #endif // M_TOP_PAD 815 #ifndef M_TOP_PAD 816 #define M_TOP_PAD (-2) 817 #endif // M_TOP_PAD 818 819 862 820 extern "C" { 863 // Allocates size bytes and returns a pointer to the allocated memory. The contents are undefined. If size is 0, 864 // then malloc() returns a unique pointer value that can later be successfully passed to free(). 821 // The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not 822 // initialized. If size is 0, then malloc() returns either 0p, or a unique pointer value that can later be 823 // successfully passed to free(). 865 824 void * malloc( size_t size ) { 866 825 #ifdef __STATISTICS__ … … 872 831 } // malloc 873 832 874 875 // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes. 876 void * aalloc( size_t dim, size_t elemSize ) { 877 size_t size = dim * elemSize; 878 #ifdef __STATISTICS__ 879 __atomic_add_fetch( &aalloc_calls, 1, __ATOMIC_SEQ_CST ); 880 __atomic_add_fetch( &aalloc_storage, size, __ATOMIC_SEQ_CST ); 881 #endif // __STATISTICS__ 882 883 return mallocNoStats( size ); 884 } // aalloc 885 886 887 // Same as aalloc() with memory set to zero. 888 void * calloc( size_t dim, size_t elemSize ) { 833 // The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to 834 // the allocated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either 0p, or a 835 // unique pointer value that can later be successfully passed to free(). 836 void * calloc( size_t noOfElems, size_t elemSize ) { 889 837 #ifdef __STATISTICS__ 890 838 __atomic_add_fetch( &calloc_calls, 1, __ATOMIC_SEQ_CST ); 891 __atomic_add_fetch( &calloc_storage, dim* elemSize, __ATOMIC_SEQ_CST );892 #endif // __STATISTICS__ 893 894 return callocNoStats( dim, elemSize );839 __atomic_add_fetch( &calloc_storage, noOfElems * elemSize, __ATOMIC_SEQ_CST ); 840 #endif // __STATISTICS__ 841 842 return callocNoStats( noOfElems, elemSize ); 895 843 } // calloc 896 844 897 898 // Change the size of the memory block pointed to by oaddr to size bytes. The contents are undefined. If oaddr is 899 // 0p, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and oaddr is 900 // not 0p, then the call is equivalent to free(oaddr). Unless oaddr is 0p, it must have been returned by an earlier 901 // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done. 902 void * resize( void * oaddr, size_t size ) { 903 #ifdef __STATISTICS__ 904 __atomic_add_fetch( &resize_calls, 1, __ATOMIC_SEQ_CST ); 845 // The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be 846 // unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size 847 // is larger than the old size, the added memory will not be initialized. If ptr is 0p, then the call is 848 // equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not 0p, then the call 849 // is equivalent to free(ptr). Unless ptr is 0p, it must have been returned by an earlier call to malloc(), 850 // calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done. 851 void * realloc( void * oaddr, size_t size ) { 852 #ifdef __STATISTICS__ 853 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 905 854 #endif // __STATISTICS__ 906 855 907 856 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 908 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 909 if ( unlikely( oaddr == 0p ) ) { 910 #ifdef __STATISTICS__ 911 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 912 #endif // __STATISTICS__ 913 return mallocNoStats( size ); 914 } // if 857 if ( unlikely( size == 0 ) ) { free( oaddr ); return mallocNoStats( size ); } // special cases 858 if ( unlikely( oaddr == 0p ) ) return mallocNoStats( size ); 915 859 916 860 HeapManager.Storage.Header * header; 917 861 HeapManager.FreeHeader * freeElem; 918 size_t bsize, oalign; 919 headers( "resize", oaddr, header, freeElem, bsize, oalign ); 862 size_t bsize, oalign = 0; 863 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 864 920 865 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 921 922 // same size, DO NOT preserve STICKY PROPERTIES. 923 if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size 924 header->kind.real.blockSize &= -2; // no alignment and turn off 0 fill 925 header->kind.real.size = size; // reset allocation size 866 if ( size <= odsize && odsize <= size * 2 ) { // allow up to 50% wasted storage in smaller size 867 // Do not know size of original allocation => cannot do 0 fill for any additional space because do not know 868 // where to start filling, i.e., do not overwrite existing values in space. 869 // 870 // This case does not result in a new profiler entry because the previous one still exists and it must match with 871 // the free for this memory. Hence, this realloc does not appear in the profiler output. 926 872 return oaddr; 927 873 } // if 928 874 929 875 #ifdef __STATISTICS__ 930 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 931 #endif // __STATISTICS__ 932 933 // change size, DO NOT preserve STICKY PROPERTIES. 934 free( oaddr ); 935 return mallocNoStats( size ); // create new area 936 } // resize 937 938 939 // Same as resize() but the contents are unchanged in the range from the start of the region up to the minimum of 940 // the old and new sizes. 941 void * realloc( void * oaddr, size_t size ) { 942 #ifdef __STATISTICS__ 943 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 944 #endif // __STATISTICS__ 945 946 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 947 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 948 if ( unlikely( oaddr == 0p ) ) { 949 #ifdef __STATISTICS__ 950 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 951 #endif // __STATISTICS__ 952 return mallocNoStats( size ); 953 } // if 954 955 HeapManager.Storage.Header * header; 956 HeapManager.FreeHeader * freeElem; 957 size_t bsize, oalign; 958 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 959 960 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 961 size_t osize = header->kind.real.size; // old allocation size 962 bool ozfill = (header->kind.real.blockSize & 2); // old allocation zero filled 963 if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage 964 header->kind.real.size = size; // reset allocation size 965 if ( unlikely( ozfill ) && size > osize ) { // previous request zero fill and larger ? 966 memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage 967 } // if 968 return oaddr; 969 } // if 970 971 #ifdef __STATISTICS__ 972 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 876 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 973 877 #endif // __STATISTICS__ 974 878 … … 976 880 977 881 void * naddr; 978 if ( likely( oalign == libAlign() ) ) { // previous request not aligned ? 979 naddr = mallocNoStats( size ); // create new area 882 if ( unlikely( oalign != 0 ) ) { // previous request memalign? 883 if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill 884 naddr = cmemalignNoStats( oalign, 1, size ); // create new aligned area 885 } else { 886 naddr = memalignNoStats( oalign, size ); // create new aligned area 887 } // if 980 888 } else { 981 naddr = memalignNoStats( oalign, size ); // create new aligned area 982 } // if 983 984 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 985 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 986 free( oaddr ); 987 988 if ( unlikely( ozfill ) ) { // previous request zero fill ? 989 header->kind.real.blockSize |= 2; // mark new request as zero filled 990 if ( size > osize ) { // previous request larger ? 991 memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage 889 if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill 890 naddr = callocNoStats( 1, size ); // create new area 891 } else { 892 naddr = mallocNoStats( size ); // create new area 992 893 } // if 993 894 } // if 895 if ( unlikely( naddr == 0p ) ) return 0p; 896 897 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 898 size_t ndsize = dataStorage( bsize, naddr, header ); // data storage avilable in bucket 899 // To preserve prior fill, the entire bucket must be copied versus the size. 900 memcpy( naddr, oaddr, MIN( odsize, ndsize ) ); // copy bytes 901 free( oaddr ); 994 902 return naddr; 995 903 } // realloc 996 904 997 998 // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete)905 // The obsolete function memalign() allocates size bytes and returns a pointer to the allocated memory. The memory 906 // address will be a multiple of alignment, which must be a power of two. 999 907 void * memalign( size_t alignment, size_t size ) { 1000 908 #ifdef __STATISTICS__ … … 1007 915 1008 916 1009 // Same as aalloc() with memory alignment. 1010 void * amemalign( size_t alignment, size_t dim, size_t elemSize ) { 1011 size_t size = dim * elemSize; 917 // The cmemalign() function is the same as calloc() with memory alignment. 918 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ) { 1012 919 #ifdef __STATISTICS__ 1013 920 __atomic_add_fetch( &cmemalign_calls, 1, __ATOMIC_SEQ_CST ); 1014 __atomic_add_fetch( &cmemalign_storage, size, __ATOMIC_SEQ_CST ); 1015 #endif // __STATISTICS__ 1016 1017 return memalignNoStats( alignment, size ); 1018 } // amemalign 1019 1020 1021 // Same as calloc() with memory alignment. 1022 void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) { 1023 #ifdef __STATISTICS__ 1024 __atomic_add_fetch( &cmemalign_calls, 1, __ATOMIC_SEQ_CST ); 1025 __atomic_add_fetch( &cmemalign_storage, dim * elemSize, __ATOMIC_SEQ_CST ); 1026 #endif // __STATISTICS__ 1027 1028 return cmemalignNoStats( alignment, dim, elemSize ); 921 __atomic_add_fetch( &cmemalign_storage, noOfElems * elemSize, __ATOMIC_SEQ_CST ); 922 #endif // __STATISTICS__ 923 924 return cmemalignNoStats( alignment, noOfElems, elemSize ); 1029 925 } // cmemalign 1030 926 1031 1032 // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple 1033 // of alignment. This requirement is universally ignored. 927 // The function aligned_alloc() is the same as memalign(), except for the added restriction that size should be a 928 // multiple of alignment. 1034 929 void * aligned_alloc( size_t alignment, size_t size ) { 1035 930 return memalign( alignment, size ); … … 1037 932 1038 933 1039 // Allocates size bytes and places the address of the allocated memory in *memptr. The address of the allocated1040 // memory shall be a multiple of alignment, which must be a power of two and a multiple of sizeof(void *). If size1041 // is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later be successfully passed to1042 // free(3).934 // The function posix_memalign() allocates size bytes and places the address of the allocated memory in *memptr. The 935 // address of the allocated memory will be a multiple of alignment, which must be a power of two and a multiple of 936 // sizeof(void *). If size is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later 937 // be successfully passed to free(3). 1043 938 int posix_memalign( void ** memptr, size_t alignment, size_t size ) { 1044 if ( alignment < libAlign() || ! is_pow2( alignment ) ) return EINVAL; // check alignment939 if ( alignment < sizeof(void *) || ! libPow2( alignment ) ) return EINVAL; // check alignment 1045 940 * memptr = memalign( alignment, size ); 941 if ( unlikely( * memptr == 0p ) ) return ENOMEM; 1046 942 return 0; 1047 943 } // posix_memalign 1048 944 1049 1050 // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the 1051 // page size. It is equivalent to memalign(sysconf(_SC_PAGESIZE),size). 945 // The obsolete function valloc() allocates size bytes and returns a pointer to the allocated memory. The memory 946 // address will be a multiple of the page size. It is equivalent to memalign(sysconf(_SC_PAGESIZE),size). 1052 947 void * valloc( size_t size ) { 1053 return memalign( __page_size, size );948 return memalign( pageSize, size ); 1054 949 } // valloc 1055 950 1056 951 1057 // Same as valloc but rounds size to multiple of page size. 1058 void * pvalloc( size_t size ) { 1059 return memalign( __page_size, ceiling2( size, __page_size ) ); 1060 } // pvalloc 1061 1062 1063 // Frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() 1064 // or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is 1065 // 0p, no operation is performed. 952 // The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to 953 // malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior 954 // occurs. If ptr is 0p, no operation is performed. 1066 955 void free( void * addr ) { 1067 956 #ifdef __STATISTICS__ … … 1084 973 1085 974 1086 // Returns the alignment of anallocation.975 // The malloc_alignment() function returns the alignment of the allocation. 1087 976 size_t malloc_alignment( void * addr ) { 1088 977 if ( unlikely( addr == 0p ) ) return libAlign(); // minimum alignment … … 1091 980 return header->kind.fake.alignment & -2; // remove flag from value 1092 981 } else { 1093 return libAlign (); // minimum alignment982 return libAlign (); // minimum alignment 1094 983 } // if 1095 984 } // malloc_alignment 1096 985 1097 986 1098 // Set the alignment for an the allocation and return previous alignment or 0 if no alignment. 1099 size_t $malloc_alignment_set( void * addr, size_t alignment ) { 1100 if ( unlikely( addr == 0p ) ) return libAlign(); // minimum alignment 1101 size_t ret; 1102 HeapManager.Storage.Header * header = headerAddr( addr ); 1103 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1104 ret = header->kind.fake.alignment & -2; // remove flag from old value 1105 header->kind.fake.alignment = alignment | 1; // add flag to new value 1106 } else { 1107 ret = 0; // => no alignment to change 1108 } // if 1109 return ret; 1110 } // $malloc_alignment_set 1111 1112 1113 // Returns true if the allocation is zero filled, e.g., allocated by calloc(). 987 // The malloc_zero_fill() function returns true if the allocation is zero filled, i.e., initially allocated by calloc(). 1114 988 bool malloc_zero_fill( void * addr ) { 1115 989 if ( unlikely( addr == 0p ) ) return false; // null allocation is not zero fill 1116 990 HeapManager.Storage.Header * header = headerAddr( addr ); 1117 991 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1118 header = realHeader( header ); // backup from fake to real header992 header = (HeapManager.Storage.Header *)((char *)header - header->kind.fake.offset); 1119 993 } // if 1120 return (header->kind.real.blockSize & 2) != 0; // zero filled ?994 return (header->kind.real.blockSize & 2) != 0; // zero filled (calloc/cmemalign) ? 1121 995 } // malloc_zero_fill 1122 996 1123 // Set allocation is zero filled and return previous zero filled. 1124 bool $malloc_zero_fill_set( void * addr ) { 1125 if ( unlikely( addr == 0p ) ) return false; // null allocation is not zero fill 1126 HeapManager.Storage.Header * header = headerAddr( addr ); 1127 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1128 header = realHeader( header ); // backup from fake to real header 1129 } // if 1130 bool ret = (header->kind.real.blockSize & 2) != 0; // zero filled ? 1131 header->kind.real.blockSize |= 2; // mark as zero filled 1132 return ret; 1133 } // $malloc_zero_fill_set 1134 1135 1136 // Returns original total allocation size (not bucket size) => array size is dimension * sizeif(T). 1137 size_t malloc_size( void * addr ) { 1138 if ( unlikely( addr == 0p ) ) return 0; // null allocation has zero size 1139 HeapManager.Storage.Header * header = headerAddr( addr ); 1140 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1141 header = realHeader( header ); // backup from fake to real header 1142 } // if 1143 return header->kind.real.size; 1144 } // malloc_size 1145 1146 // Set allocation size and return previous size. 1147 size_t $malloc_size_set( void * addr, size_t size ) { 1148 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size 1149 HeapManager.Storage.Header * header = headerAddr( addr ); 1150 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1151 header = realHeader( header ); // backup from fake to real header 1152 } // if 1153 size_t ret = header->kind.real.size; 1154 header->kind.real.size = size; 1155 return ret; 1156 } // $malloc_size_set 1157 1158 1159 // Returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by 1160 // malloc or a related function. 997 998 // The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to 999 // a block of memory allocated by malloc(3) or a related function. 1161 1000 size_t malloc_usable_size( void * addr ) { 1162 1001 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size … … 1166 1005 1167 1006 headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment ); 1168 return dataStorage( bsize, addr, header ); // data storage in bucket1007 return dataStorage( bsize, addr, header ); // data storage in bucket 1169 1008 } // malloc_usable_size 1170 1009 1171 1010 1172 // Prints (on default standard error) statistics about memory allocated by malloc and related functions. 1011 // The malloc_stats() function prints (on default standard error) statistics about memory allocated by malloc(3) and 1012 // related functions. 1173 1013 void malloc_stats( void ) { 1174 1014 #ifdef __STATISTICS__ … … 1178 1018 } // malloc_stats 1179 1019 1180 1181 // Changes the file descripter where malloc_stats() writes statistics. 1020 // The malloc_stats_fd() function changes the file descripter where malloc_stats() writes the statistics. 1182 1021 int malloc_stats_fd( int fd __attribute__(( unused )) ) { 1183 1022 #ifdef __STATISTICS__ 1184 int temp = stat _fd;1185 stat _fd = fd;1023 int temp = statfd; 1024 statfd = fd; 1186 1025 return temp; 1187 1026 #else … … 1191 1030 1192 1031 1193 // Adjusts parameters that control the behaviour of the memory-allocation functions (see malloc). The param argument 1194 // specifies the parameter to be modified, and value specifies the new value for that parameter. 1032 // The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see 1033 // malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that 1034 // parameter. 1195 1035 int mallopt( int option, int value ) { 1196 1036 choose( option ) { 1197 1037 case M_TOP_PAD: 1198 heapExpand = ceiling2( value, __page_size );return 1;1038 if ( setHeapExpand( value ) ) return 1; 1199 1039 case M_MMAP_THRESHOLD: 1200 1040 if ( setMmapStart( value ) ) return 1; 1201 break;1202 1041 } // switch 1203 1042 return 0; // error, unsupported 1204 1043 } // mallopt 1205 1044 1206 1207 // Attempt to release free memory at the top of the heap (by calling sbrk with asuitable argument).1045 // The malloc_trim() function attempts to release free memory at the top of the heap (by calling sbrk(2) with a 1046 // suitable argument). 1208 1047 int malloc_trim( size_t ) { 1209 1048 return 0; // => impossible to release memory … … 1211 1050 1212 1051 1213 // Exports an XML string that describes the current state of the memory-allocation implementation in the caller.1214 // The string is printed on the file stream stream. The exported string includes information about all arenas (see1215 // malloc).1052 // The malloc_info() function exports an XML string that describes the current state of the memory-allocation 1053 // implementation in the caller. The string is printed on the file stream stream. The exported string includes 1054 // information about all arenas (see malloc(3)). 1216 1055 int malloc_info( int options, FILE * stream ) { 1217 if ( options != 0 ) { errno = EINVAL; return -1; } 1218 #ifdef __STATISTICS__ 1056 if ( options != 0 ) { errno = EINVAL; return -1; } 1219 1057 return printStatsXML( stream ); 1220 #else1221 return 0; // unsupported1222 #endif // __STATISTICS__1223 1058 } // malloc_info 1224 1059 1225 1060 1226 // Records the current state of all malloc internal bookkeeping variables (but not the actual contents of the heap1227 // or the state of malloc_hook functions pointers). The state is recorded in a system-dependent opaque data1228 // structure dynamically allocated via malloc, and a pointer to that data structure is returned as the function1229 // result. (The caller must freethis memory.)1061 // The malloc_get_state() function records the current state of all malloc(3) internal bookkeeping variables (but 1062 // not the actual contents of the heap or the state of malloc_hook(3) functions pointers). The state is recorded in 1063 // a system-dependent opaque data structure dynamically allocated via malloc(3), and a pointer to that data 1064 // structure is returned as the function result. (It is the caller's responsibility to free(3) this memory.) 1230 1065 void * malloc_get_state( void ) { 1231 1066 return 0p; // unsupported … … 1233 1068 1234 1069 1235 // Restores the state of all malloc internal bookkeeping variables to the values recorded in the opaque data1236 // structure pointed to by state.1237 int malloc_set_state( void * ) {1070 // The malloc_set_state() function restores the state of all malloc(3) internal bookkeeping variables to the values 1071 // recorded in the opaque data structure pointed to by state. 1072 int malloc_set_state( void * ptr ) { 1238 1073 return 0; // unsupported 1239 1074 } // malloc_set_state … … 1242 1077 1243 1078 // Must have CFA linkage to overload with C linkage realloc. 1244 void * re size( void * oaddr, size_t nalign, size_t size ) {1079 void * realloc( void * oaddr, size_t nalign, size_t size ) { 1245 1080 #ifdef __STATISTICS__ 1246 __atomic_add_fetch( &re size_calls, 1, __ATOMIC_SEQ_CST );1081 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 1247 1082 #endif // __STATISTICS__ 1248 1083 1249 if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum 1084 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1085 if ( unlikely( size == 0 ) ) { free( oaddr ); return mallocNoStats( size ); } // special cases 1086 if ( unlikely( oaddr == 0p ) ) return mallocNoStats( size ); 1087 1088 if ( unlikely( nalign == 0 ) ) nalign = libAlign(); // reset alignment to minimum 1250 1089 #ifdef __CFA_DEBUG__ 1251 1090 else … … 1253 1092 #endif // __CFA_DEBUG__ 1254 1093 1255 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1256 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 1257 if ( unlikely( oaddr == 0p ) ) { 1258 #ifdef __STATISTICS__ 1259 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 1260 #endif // __STATISTICS__ 1261 return memalignNoStats( nalign, size ); 1262 } // if 1263 1264 // Attempt to reuse existing alignment. 1265 HeapManager.Storage.Header * header = headerAddr( oaddr ); 1266 bool isFakeHeader = header->kind.fake.alignment & 1; // old fake header ? 1267 size_t oalign; 1268 if ( isFakeHeader ) { 1269 oalign = header->kind.fake.alignment & -2; // old alignment 1270 if ( (uintptr_t)oaddr % nalign == 0 // lucky match ? 1271 && ( oalign <= nalign // going down 1272 || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ? 1273 ) { 1274 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1275 HeapManager.FreeHeader * freeElem; 1276 size_t bsize, oalign; 1277 headers( "resize", oaddr, header, freeElem, bsize, oalign ); 1278 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 1279 1280 if ( size <= odsize && odsize <= size * 2 ) { // allow 50% wasted data storage 1281 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1282 1283 header->kind.real.blockSize &= -2; // turn off 0 fill 1284 header->kind.real.size = size; // reset allocation size 1285 return oaddr; 1286 } // if 1287 } // if 1288 } else if ( ! isFakeHeader // old real header (aligned on libAlign) ? 1289 && nalign == libAlign() ) { // new alignment also on libAlign => no fake header needed 1290 return resize( oaddr, size ); // duplicate special case checks 1094 HeapManager.Storage.Header * header; 1095 HeapManager.FreeHeader * freeElem; 1096 size_t bsize, oalign = 0; 1097 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 1098 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 1099 1100 if ( oalign != 0 && (uintptr_t)oaddr % nalign == 0 ) { // has alignment and just happens to work out 1101 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1102 return realloc( oaddr, size ); 1291 1103 } // if 1292 1104 1293 1105 #ifdef __STATISTICS__ 1294 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST );1295 #endif // __STATISTICS__1296 1297 // change size, DO NOT preserve STICKY PROPERTIES.1298 free( oaddr );1299 return memalignNoStats( nalign, size ); // create new aligned area1300 } // resize1301 1302 1303 void * realloc( void * oaddr, size_t nalign, size_t size ) {1304 if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum1305 #ifdef __CFA_DEBUG__1306 else1307 checkAlign( nalign ); // check alignment1308 #endif // __CFA_DEBUG__1309 1310 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.1311 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases1312 if ( unlikely( oaddr == 0p ) ) {1313 #ifdef __STATISTICS__1314 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );1315 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );1316 #endif // __STATISTICS__1317 return memalignNoStats( nalign, size );1318 } // if1319 1320 // Attempt to reuse existing alignment.1321 HeapManager.Storage.Header * header = headerAddr( oaddr );1322 bool isFakeHeader = header->kind.fake.alignment & 1; // old fake header ?1323 size_t oalign;1324 if ( isFakeHeader ) {1325 oalign = header->kind.fake.alignment & -2; // old alignment1326 if ( (uintptr_t)oaddr % nalign == 0 // lucky match ?1327 && ( oalign <= nalign // going down1328 || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ?1329 ) {1330 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same)1331 return realloc( oaddr, size ); // duplicate alignment and special case checks1332 } // if1333 } else if ( ! isFakeHeader // old real header (aligned on libAlign) ?1334 && nalign == libAlign() ) // new alignment also on libAlign => no fake header needed1335 return realloc( oaddr, size ); // duplicate alignment and special case checks1336 1337 #ifdef __STATISTICS__1338 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST );1339 1106 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 1340 1107 #endif // __STATISTICS__ 1341 1108 1342 HeapManager.FreeHeader * freeElem;1343 size_t bsize;1344 headers( "realloc", oaddr, header, freeElem, bsize, oalign );1345 1346 1109 // change size and copy old content to new storage 1347 1110 1348 size_t osize = header->kind.real.size; // old allocation size 1349 bool ozfill = (header->kind.real.blockSize & 2); // old allocation zero filled 1350 1351 void * naddr = memalignNoStats( nalign, size ); // create new aligned area 1111 void * naddr; 1112 if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill 1113 naddr = cmemalignNoStats( nalign, 1, size ); // create new aligned area 1114 } else { 1115 naddr = memalignNoStats( nalign, size ); // create new aligned area 1116 } // if 1352 1117 1353 1118 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 1354 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 1119 size_t ndsize = dataStorage( bsize, naddr, header ); // data storage avilable in bucket 1120 // To preserve prior fill, the entire bucket must be copied versus the size. 1121 memcpy( naddr, oaddr, MIN( odsize, ndsize ) ); // copy bytes 1355 1122 free( oaddr ); 1356 1357 if ( unlikely( ozfill ) ) { // previous request zero fill ?1358 header->kind.real.blockSize |= 2; // mark new request as zero filled1359 if ( size > osize ) { // previous request larger ?1360 memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage1361 } // if1362 } // if1363 1123 return naddr; 1364 1124 } // realloc -
libcfa/src/interpose.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Mar 29 16:10:31 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Mar 13 17:35:37202013 // Update Count : 1 7812 // Last Modified On : Thu Jan 30 17:47:32 2020 13 // Update Count : 156 14 14 // 15 15 16 16 #include <stdarg.h> // va_start, va_end 17 #include <stdio.h>18 17 #include <string.h> // strlen 19 18 #include <unistd.h> // _exit, getpid … … 30 29 #include "bits/signal.hfa" // sigHandler_? 31 30 #include "startup.hfa" // STARTUP_PRIORITY_CORE 32 #include <assert.h>33 31 34 32 //============================================================================================= … … 42 40 43 41 typedef void (* generic_fptr_t)(void); 44 generic_fptr_t interpose_symbol( const char symbol[], const char version[]) {42 generic_fptr_t interpose_symbol( const char * symbol, const char * version ) { 45 43 const char * error; 46 44 … … 144 142 void abort( const char fmt[], ... ) __attribute__(( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ )); 145 143 void abort( bool signalAbort, const char fmt[], ... ) __attribute__(( format(printf, 2, 3), __nothrow__, __leaf__, __noreturn__ )); 146 void __abort( bool signalAbort, const char fmt[], va_list args ) __attribute__(( __nothrow__, __leaf__, __noreturn__ ));147 144 148 145 extern "C" { 149 146 void abort( void ) __attribute__(( __nothrow__, __leaf__, __noreturn__ )) { 150 abort( false, "%s", "" );147 abort( false, NULL ); // FIX ME: 0p does not work 151 148 } 152 149 … … 154 151 va_list argp; 155 152 va_start( argp, fmt ); 156 __abort( false, fmt, argp );153 abort( false, fmt, argp ); 157 154 va_end( argp ); 158 155 } … … 164 161 165 162 void * kernel_abort( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 0p; } 166 void kernel_abort_msg( void * data, char buffer[], int size ) __attribute__(( __nothrow__, __leaf__, __weak__ )) {}163 void kernel_abort_msg( void * data, char * buffer, int size ) __attribute__(( __nothrow__, __leaf__, __weak__ )) {} 167 164 // See concurrency/kernel.cfa for strong definition used in multi-processor mode. 168 165 int kernel_abort_lastframe( void ) __attribute__(( __nothrow__, __leaf__, __weak__ )) { return 4; } … … 172 169 173 170 static void __cfaabi_backtrace( int start ) { 174 enum { Frames = 50, }; // maximum number of stack frames 171 enum { 172 Frames = 50, // maximum number of stack frames 173 }; 175 174 int last = kernel_abort_lastframe(); // skip last N stack frames 176 175 177 176 void * array[Frames]; 178 177 size_t size = backtrace( array, Frames ); 179 char ** messages = backtrace_symbols( array, size ); // does not demangle names178 char ** messages = backtrace_symbols( array, size ); 180 179 181 180 *index( messages[0], '(' ) = '\0'; // find executable name … … 185 184 char * name = 0p, * offset_begin = 0p, * offset_end = 0p; 186 185 187 for ( char * p = messages[i]; *p; p += 1 ) {// find parantheses and +offset186 for ( char * p = messages[i]; *p; ++p ) { // find parantheses and +offset 188 187 //__cfaabi_bits_print_nolock( "X %s\n", p); 189 188 if ( *p == '(' ) { … … 220 219 } 221 220 222 static volatile int __abort_stage = 0; 223 224 // Cannot forward va_list. 225 void __abort( bool signalAbort, const char fmt[], va_list args ) { 226 int stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 227 228 // First stage: stop the cforall kernel and print 229 if(stage == 1) { 230 // increment stage 231 stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 232 233 // must be done here to lock down kernel 234 void * kernel_data = kernel_abort(); 235 int len; 236 237 signal( SIGABRT, SIG_DFL ); // prevent final "real" abort from recursing to handler 238 239 len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid) 221 void abort( bool signalAbort, const char fmt[], ... ) { 222 void * kernel_data = kernel_abort(); // must be done here to lock down kernel 223 int len; 224 225 signal( SIGABRT, SIG_DFL ); // prevent final "real" abort from recursing to handler 226 227 len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid) 228 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 229 230 if ( fmt ) { 231 va_list args; 232 va_start( args, fmt ); 233 234 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 235 va_end( args ); 240 236 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 241 237 242 assert( fmt ); 243 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 244 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 245 246 // add optional newline if missing at the end of the format text 247 if ( fmt[strlen( fmt ) - 1] != '\n' ) { 248 __cfaabi_bits_write( STDERR_FILENO, "\n", 1 ); 249 } // if 250 kernel_abort_msg( kernel_data, abort_text, abort_text_size ); 251 } 252 253 // Second stage: print the backtrace 254 if(stage == 2) { 255 // increment stage 256 stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 257 258 // print stack trace in handler 259 __cfaabi_backtrace( signalAbort ? 4 : 2 ); 260 } 261 262 do { 263 // Finally call abort 264 __cabi_libc.abort(); 265 266 // Loop so that we never return 267 } while(true); 238 if ( fmt[strlen( fmt ) - 1] != '\n' ) { // add optional newline if missing at the end of the format text 239 __cfaabi_dbg_write( "\n", 1 ); 240 } 241 } 242 243 kernel_abort_msg( kernel_data, abort_text, abort_text_size ); 244 __cfaabi_backtrace( signalAbort ? 4 : 3 ); 245 246 __cabi_libc.abort(); // print stack trace in handler 268 247 } 269 248 … … 271 250 va_list args; 272 251 va_start( args, fmt ); 273 __abort( false, fmt, args ); 274 // CONTROL NEVER REACHES HERE! 252 abort( false, fmt, args ); 275 253 va_end( args ); 276 }277 278 void abort( bool signalAbort, const char fmt[], ... ) {279 va_list args;280 va_start( args, fmt );281 __abort( signalAbort, fmt, args );282 // CONTROL NEVER REACHES HERE!283 va_end( args );284 254 } 285 255 -
libcfa/src/iostream.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 24 08:31:35 202013 // Update Count : 113012 // Last Modified On : Sat Jul 13 08:07:59 2019 13 // Update Count : 821 14 14 // 15 15 16 16 #include "iostream.hfa" 17 17 18 extern "C" { 18 19 #include <stdio.h> 19 20 #include <stdbool.h> // true/false 20 #include <stdint.h> // UINT64_MAX 21 #include <float.h> // DBL_DIG, LDBL_DIG 22 #include <complex.h> // creal, cimag 23 //#include <string.h> // strlen, strcmp, memcpy 24 extern "C" { 21 //#include <string.h> // strlen, strcmp 25 22 extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))); 26 23 extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2))); 27 24 extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); 28 25 extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); 26 #include <float.h> // DBL_DIG, LDBL_DIG 27 #include <math.h> // isfinite 28 #include <complex.h> // creal, cimag 29 29 } // extern "C" 30 30 31 #include "math.hfa" // isfinite, floor, ceiling_div 32 #include "bitmanip.hfa" // high1 33 34 35 // *********************************** ostream *********************************** 31 32 //*********************************** ostream *********************************** 36 33 37 34 38 35 forall( dtype ostype | ostream( ostype ) ) { 36 ostype & ?|?( ostype & os, zero_t ) { 37 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 38 fmt( os, "%d", 0n ); 39 return os; 40 } // ?|? 41 void ?|?( ostype & os, zero_t z ) { 42 (ostype &)(os | z); ends( os ); 43 } // ?|? 44 45 ostype & ?|?( ostype & os, one_t ) { 46 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 47 fmt( os, "%d", 1n ); 48 return os; 49 } // ?|? 50 void ?|?( ostype & os, one_t o ) { 51 (ostype &)(os | o); ends( os ); 52 } // ?|? 53 39 54 ostype & ?|?( ostype & os, bool b ) { 40 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );55 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 41 56 fmt( os, "%s", b ? "true" : "false" ); 42 57 return os; … … 48 63 ostype & ?|?( ostype & os, char c ) { 49 64 fmt( os, "%c", c ); 50 if ( c == '\n' ) $setNL( os, true );65 if ( c == '\n' ) setNL( os, true ); 51 66 return sepOff( os ); 52 67 } // ?|? … … 56 71 57 72 ostype & ?|?( ostype & os, signed char sc ) { 58 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );73 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 59 74 fmt( os, "%hhd", sc ); 60 75 return os; … … 65 80 66 81 ostype & ?|?( ostype & os, unsigned char usc ) { 67 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );82 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 68 83 fmt( os, "%hhu", usc ); 69 84 return os; … … 74 89 75 90 ostype & ?|?( ostype & os, short int si ) { 76 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );91 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 77 92 fmt( os, "%hd", si ); 78 93 return os; … … 83 98 84 99 ostype & ?|?( ostype & os, unsigned short int usi ) { 85 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );100 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 86 101 fmt( os, "%hu", usi ); 87 102 return os; … … 92 107 93 108 ostype & ?|?( ostype & os, int i ) { 94 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );109 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 95 110 fmt( os, "%d", i ); 96 111 return os; … … 101 116 102 117 ostype & ?|?( ostype & os, unsigned int ui ) { 103 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );118 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 104 119 fmt( os, "%u", ui ); 105 120 return os; … … 110 125 111 126 ostype & ?|?( ostype & os, long int li ) { 112 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );127 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 113 128 fmt( os, "%ld", li ); 114 129 return os; … … 119 134 120 135 ostype & ?|?( ostype & os, unsigned long int uli ) { 121 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );136 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 122 137 fmt( os, "%lu", uli ); 123 138 return os; … … 128 143 129 144 ostype & ?|?( ostype & os, long long int lli ) { 130 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );145 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 131 146 fmt( os, "%lld", lli ); 132 147 return os; … … 137 152 138 153 ostype & ?|?( ostype & os, unsigned long long int ulli ) { 139 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );154 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 140 155 fmt( os, "%llu", ulli ); 141 156 return os; … … 144 159 (ostype &)(os | ulli); ends( os ); 145 160 } // ?|? 146 147 #if defined( __SIZEOF_INT128__ )148 // UINT64_MAX 18_446_744_073_709_551_615_ULL149 #define P10_UINT64 10_000_000_000_000_000_000_ULL // 19 zeroes150 151 static inline void base10_128( ostype & os, unsigned int128 val ) {152 #if defined(__GNUC__) && __GNUC_PREREQ(7,0) // gcc version >= 7153 if ( val > P10_UINT64 ) {154 #else155 if ( (uint64_t)(val >> 64) != 0 || (uint64_t)val > P10_UINT64 ) { // patch gcc 5 & 6 -O3 bug156 #endif // __GNUC_PREREQ(7,0)157 base10_128( os, val / P10_UINT64 ); // recursive158 fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) );159 } else {160 fmt( os, "%lu", (uint64_t)val );161 } // if162 } // base10_128163 164 static inline void base10_128( ostype & os, int128 val ) {165 if ( val < 0 ) {166 fmt( os, "-" ); // leading negative sign167 val = -val;168 } // if169 base10_128( os, (unsigned int128)val ); // print zero/positive value170 } // base10_128171 172 ostype & ?|?( ostype & os, int128 llli ) {173 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );174 base10_128( os, llli );175 return os;176 } // ?|?177 void & ?|?( ostype & os, int128 llli ) {178 (ostype &)(os | llli); ends( os );179 } // ?|?180 181 ostype & ?|?( ostype & os, unsigned int128 ullli ) {182 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );183 base10_128( os, ullli );184 return os;185 } // ?|?186 void & ?|?( ostype & os, unsigned int128 ullli ) {187 (ostype &)(os | ullli); ends( os );188 } // ?|?189 #endif // __SIZEOF_INT128__190 161 191 162 #define PrintWithDP( os, format, val, ... ) \ … … 204 175 205 176 ostype & ?|?( ostype & os, float f ) { 206 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );177 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 207 178 PrintWithDP( os, "%g", f ); 208 179 return os; … … 213 184 214 185 ostype & ?|?( ostype & os, double d ) { 215 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );186 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 216 187 PrintWithDP( os, "%.*lg", d, DBL_DIG ); 217 188 return os; … … 222 193 223 194 ostype & ?|?( ostype & os, long double ld ) { 224 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );195 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 225 196 PrintWithDP( os, "%.*Lg", ld, LDBL_DIG ); 226 197 return os; … … 231 202 232 203 ostype & ?|?( ostype & os, float _Complex fc ) { 233 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );204 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 234 205 // os | crealf( fc ) | nonl; 235 206 PrintWithDP( os, "%g", crealf( fc ) ); … … 243 214 244 215 ostype & ?|?( ostype & os, double _Complex dc ) { 245 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );216 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 246 217 // os | creal( dc ) | nonl; 247 218 PrintWithDP( os, "%.*lg", creal( dc ), DBL_DIG ); … … 255 226 256 227 ostype & ?|?( ostype & os, long double _Complex ldc ) { 257 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );228 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 258 229 // os | creall( ldc ) || nonl; 259 230 PrintWithDP( os, "%.*Lg", creall( ldc ), LDBL_DIG ); … … 266 237 } // ?|? 267 238 268 ostype & ?|?( ostype & os, const char str[]) {239 ostype & ?|?( ostype & os, const char * str ) { 269 240 enum { Open = 1, Close, OpenClose }; 270 241 static const unsigned char mask[256] @= { … … 286 257 // first character IS NOT spacing or closing punctuation => add left separator 287 258 unsigned char ch = str[0]; // must make unsigned 288 if ( $sepPrt( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) {289 fmt( os, "%s", $sepGetCur( os ) );259 if ( sepPrt( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) { 260 fmt( os, "%s", sepGetCur( os ) ); 290 261 } // if 291 262 292 263 // if string starts line, must reset to determine open state because separator is off 293 $sepReset( os );// reset separator264 sepReset( os ); // reset separator 294 265 295 266 // last character IS spacing or opening punctuation => turn off separator for next item 296 267 size_t len = strlen( str ); 297 268 ch = str[len - 1]; // must make unsigned 298 if ( $sepPrt( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) {269 if ( sepPrt( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) { 299 270 sepOn( os ); 300 271 } else { 301 272 sepOff( os ); 302 273 } // if 303 if ( ch == '\n' ) $setNL( os, true ); // check *AFTER* $sepPrt call above as it resets NL flag274 if ( ch == '\n' ) setNL( os, true ); // check *AFTER* sepPrt call above as it resets NL flag 304 275 return write( os, str, len ); 305 276 } // ?|? 306 307 void ?|?( ostype & os, const char str[] ) { 277 void ?|?( ostype & os, const char * str ) { 308 278 (ostype &)(os | str); ends( os ); 309 279 } // ?|? 310 280 311 281 // ostype & ?|?( ostype & os, const char16_t * str ) { 312 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );282 // if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 313 283 // fmt( os, "%ls", str ); 314 284 // return os; … … 317 287 // #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous 318 288 // ostype & ?|?( ostype & os, const char32_t * str ) { 319 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );289 // if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 320 290 // fmt( os, "%ls", str ); 321 291 // return os; … … 324 294 325 295 // ostype & ?|?( ostype & os, const wchar_t * str ) { 326 // if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );296 // if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 327 297 // fmt( os, "%ls", str ); 328 298 // return os; … … 330 300 331 301 ostype & ?|?( ostype & os, const void * p ) { 332 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );302 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 333 303 fmt( os, "%p", p ); 334 304 return os; … … 345 315 void ?|?( ostype & os, ostype & (* manip)( ostype & ) ) { 346 316 (ostype &)(manip( os )); 347 if ( $getPrt( os ) ) ends( os );// something printed ?348 $setPrt( os, false ); // turn off317 if ( getPrt( os ) ) ends( os ); // something printed ? 318 setPrt( os, false ); // turn off 349 319 } // ?|? 350 320 … … 359 329 ostype & nl( ostype & os ) { 360 330 (ostype &)(os | '\n'); 361 $setPrt( os, false ); // turn off362 $setNL( os, true );331 setPrt( os, false ); // turn off 332 setNL( os, true ); 363 333 flush( os ); 364 334 return sepOff( os ); // prepare for next line … … 366 336 367 337 ostype & nonl( ostype & os ) { 368 $setPrt( os, false ); // turn off338 setPrt( os, false ); // turn off 369 339 return os; 370 340 } // nonl … … 405 375 ostype & ?|?( ostype & os, T arg, Params rest ) { 406 376 (ostype &)(os | arg); // print first argument 407 $sepSetCur( os, sepGetTuple( os ) );// switch to tuple separator377 sepSetCur( os, sepGetTuple( os ) ); // switch to tuple separator 408 378 (ostype &)(os | rest); // print remaining arguments 409 $sepSetCur( os, sepGet( os ) ); // switch to regular separator379 sepSetCur( os, sepGet( os ) ); // switch to regular separator 410 380 return os; 411 381 } // ?|? … … 413 383 // (ostype &)(?|?( os, arg, rest )); ends( os ); 414 384 (ostype &)(os | arg); // print first argument 415 $sepSetCur( os, sepGetTuple( os ) );// switch to tuple separator385 sepSetCur( os, sepGetTuple( os ) ); // switch to tuple separator 416 386 (ostype &)(os | rest); // print remaining arguments 417 $sepSetCur( os, sepGet( os ) ); // switch to regular separator387 sepSetCur( os, sepGet( os ) ); // switch to regular separator 418 388 ends( os ); 419 389 } // ?|? … … 433 403 } // distribution 434 404 435 // *********************************** manipulators ***********************************436 437 // *********************************** integral ***********************************405 //*********************************** manipulators *********************************** 406 407 //*********************************** integral *********************************** 438 408 439 409 static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; … … 441 411 442 412 // Default prefix for non-decimal prints is 0b, 0, 0x. 443 #define IntegralFMTImpl( T, IFMTNP, IFMTP ) \413 #define IntegralFMTImpl( T, CODE, IFMTNP, IFMTP ) \ 444 414 forall( dtype ostype | ostream( ostype ) ) { \ 445 415 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 446 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \416 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); \ 447 417 \ 448 418 if ( f.base == 'b' || f.base == 'B' ) { /* bespoke binary format */ \ 449 int bits = high1( f.val ); /* position of most significant bit */ \ 450 if ( bits == 0 ) bits = 1; /* 0 value => force one bit to print */ \ 451 int spaces; \ 419 int bits; \ 420 if ( f.val == (T){0} ) bits = 1; /* force at least one bit to print */ \ 421 else bits = sizeof(long long int) * 8 - __builtin_clzll( f.val ); /* position of most significant bit */ \ 422 bits = bits > sizeof(f.val) * 8 ? sizeof(f.val) * 8 : bits; \ 423 int spaces = f.wd - bits; /* can be negative */ \ 424 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 425 /* printf( "%d %d\n", bits, spaces ); */ \ 452 426 if ( ! f.flags.left ) { /* right justified ? */ \ 453 427 /* Note, base prefix then zero padding or spacing then prefix. */ \ 454 if ( f.flags.pc ) { \ 455 spaces = f.wd - f.pc; \ 456 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 428 if ( f.flags.pad0 || f.flags.pc ) { \ 429 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \ 430 if ( f.flags.pc ) spaces = f.pc - bits; \ 431 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \ 432 } else { \ 457 433 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \ 458 434 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \ 459 spaces = f.pc - bits; \460 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \461 } else { \462 spaces = f.wd - bits; \463 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \464 if ( f.flags.pad0 ) { \465 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \466 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \467 } else { \468 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \469 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \470 } /* if */ \471 435 } /* if */ \ 472 } else { \ 473 if ( ! f.flags.nobsdp ) fmt( os, "0%c", f.base ); \ 474 if ( f.flags.pc ) { \ 475 spaces = f.pc - bits; \ 476 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \ 477 spaces = f.wd - f.pc; \ 478 } else { /* pad0 flag ignored with left flag */ \ 479 spaces = f.wd - bits; \ 480 } /* if */ \ 481 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 436 } else if ( ! f.flags.nobsdp ) { \ 437 fmt( os, "0%c", f.base ); \ 482 438 } /* if */ \ 483 int shift = floor( bits - 1, 4 );\439 int shift = (bits - 1) / 4 * 4; /* floor( bits - 1, 4 ) */ \ 484 440 typeof( f.val ) temp = f.val; \ 485 441 fmt( os, "%s", shortbin[(temp >> shift) & 0xf] ); \ … … 492 448 if ( f.flags.left && spaces > 0 ) fmt( os, "%*s", spaces, " " ); \ 493 449 return os; \ 494 } /* if */ \450 } /* if */ \ 495 451 \ 496 452 char fmtstr[sizeof(IFMTP)]; /* sizeof includes '\0' */ \ … … 502 458 if ( ! f.flags.nobsdp ) { fmtstr[star] = '#'; star -= 1; } \ 503 459 if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \ 504 if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \460 if ( f.flags.sign && f.base == CODE ) { fmtstr[star] = '+'; star -= 1; } \ 505 461 if ( f.flags.pad0 && ! f.flags.pc ) { fmtstr[star] = '0'; star -= 1; } \ 506 462 fmtstr[star] = '%'; \ 507 463 \ 508 464 if ( ! f.flags.pc ) { /* no precision */ \ 465 /* printf( "%s\n", &fmtstr[star] ); */ \ 509 466 fmtstr[sizeof(IFMTNP)-2] = f.base; /* sizeof includes '\0' */ \ 510 /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \511 467 fmt( os, &fmtstr[star], f.wd, f.val ); \ 512 468 } else { /* precision */ \ 513 469 fmtstr[sizeof(IFMTP)-2] = f.base; /* sizeof includes '\0' */ \ 514 /* printf( "%s %c\n", &fmtstr[star], f.base); */ \470 /* printf( "%s\n", &fmtstr[star] ); */ \ 515 471 fmt( os, &fmtstr[star], f.wd, f.pc, f.val ); \ 516 472 } /* if */ \ … … 520 476 } // distribution 521 477 522 IntegralFMTImpl( signed char, "% *hh ", "% *.*hh " ) 523 IntegralFMTImpl( unsigned char, "% *hh ", "% *.*hh " ) 524 IntegralFMTImpl( signed short int, "% *h ", "% *.*h " ) 525 IntegralFMTImpl( unsigned short int, "% *h ", "% *.*h " ) 526 IntegralFMTImpl( signed int, "% * ", "% *.* " ) 527 IntegralFMTImpl( unsigned int, "% * ", "% *.* " ) 528 IntegralFMTImpl( signed long int, "% *l ", "% *.*l " ) 529 IntegralFMTImpl( unsigned long int, "% *l ", "% *.*l " ) 530 IntegralFMTImpl( signed long long int, "% *ll ", "% *.*ll " ) 531 IntegralFMTImpl( unsigned long long int, "% *ll ", "% *.*ll " ) 532 533 #if 0 534 #if defined( __SIZEOF_INT128__ ) 535 // Default prefix for non-decimal prints is 0b, 0, 0x. 536 #define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \ 537 forall( dtype ostype | ostream( ostype ) ) \ 538 static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \ 539 if ( f.val > UINT64_MAX ) { \ 540 unsigned long long int lsig = f.val % P10_UINT64; \ 541 f.val /= P10_UINT64; /* msig */ \ 542 base10_128( os, f ); /* recursion */ \ 543 _Ostream_Manip(unsigned long long int) fmt @= { lsig, 0, 19, 'u', { .all : 0 } }; \ 544 fmt.flags.nobsdp = true; \ 545 /* printf( "fmt1 %c %lld %d\n", fmt.base, fmt.val, fmt.all ); */ \ 546 sepOff( os ); \ 547 (ostype &)(os | fmt); \ 548 } else { \ 549 /* printf( "fmt2 %c %lld %d\n", f.base, (unsigned long long int)f.val, f.all ); */ \ 550 _Ostream_Manip(SIGNED long long int) fmt @= { (SIGNED long long int)f.val, f.wd, f.pc, f.base, { .all : f.all } }; \ 551 (ostype &)(os | fmt); \ 552 } /* if */ \ 553 } /* base10_128 */ \ 554 forall( dtype ostype | ostream( ostype ) ) { \ 555 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 556 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ 557 \ 558 if ( f.base == 'b' | f.base == 'B' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \ 559 unsigned long long int msig = (unsigned long long int)(f.val >> 64); \ 560 unsigned long long int lsig = (unsigned long long int)(f.val); \ 561 _Ostream_Manip(SIGNED long long int) fmt @= { msig, f.wd, f.pc, f.base, { .all : f.all } }; \ 562 _Ostream_Manip(unsigned long long int) fmt2 @= { lsig, 0, 0, f.base, { .all : 0 } }; \ 563 if ( msig == 0 ) { \ 564 fmt.val = lsig; \ 565 (ostype &)(os | fmt); \ 566 } else { \ 567 fmt2.flags.pad0 = fmt2.flags.nobsdp = true; \ 568 if ( f.base == 'b' | f.base == 'B' ) { \ 569 if ( fmt.flags.pc && fmt.pc > 64 ) fmt.pc -= 64; else { fmt.flags.pc = false; fmt.pc = 0; } \ 570 if ( fmt.flags.left ) { \ 571 fmt.flags.left = false; \ 572 fmt.wd = 0; \ 573 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 574 fmt2.flags.left = true; \ 575 int msigd = high1( msig ); \ 576 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 577 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0b base specifier */ \ 578 if ( (int)fmt2.wd < 64 ) fmt2.wd = 64; /* cast deals with negative value */ \ 579 fmt2.flags.pc = true; fmt2.pc = 64; \ 580 } else { \ 581 if ( fmt.wd > 64 ) fmt.wd -= 64; \ 582 else fmt.wd = 1; \ 583 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 584 fmt2.wd = 64; \ 585 } /* if */ \ 586 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 587 (ostype &)(os | fmt | "" | fmt2); \ 588 } else if ( f.base == 'o' ) { \ 589 if ( fmt.flags.pc && fmt.pc > 22 ) fmt.pc -= 22; else { fmt.flags.pc = false; fmt.pc = 0; } \ 590 fmt.val = (unsigned long long int)fmt.val >> 2; \ 591 fmt2.val = ((msig & 0x3) << 1) + ((lsig & 0x8000000000000000U) != 0); \ 592 if ( fmt.flags.left ) { \ 593 fmt.flags.left = false; \ 594 fmt.wd = 0; \ 595 /* printf( "L %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 596 (ostype &)(os | fmt | "" | fmt2); \ 597 sepOff( os ); \ 598 fmt2.flags.left = true; \ 599 int msigd = ceiling_div( high1( fmt.val ), 3 ); \ 600 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 601 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 1; /* compensate for 0 base specifier */ \ 602 if ( (int)fmt2.wd < 21 ) fmt2.wd = 21; /* cast deals with negative value */ \ 603 fmt2.flags.pc = true; fmt2.pc = 21; \ 604 } else { \ 605 if ( fmt.wd > 22 ) fmt.wd -= 22; \ 606 else fmt.wd = 1; \ 607 /* printf( "R %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 608 (ostype &)(os | fmt | "" | fmt2); \ 609 sepOff( os ); \ 610 fmt2.wd = 21; \ 611 } /* if */ \ 612 fmt2.val = lsig & 0x7fffffffffffffffU; \ 613 /* printf( "\nC %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 614 (ostype &)(os | fmt2); \ 615 } else { /* f.base == 'x' | f.base == 'X' */ \ 616 if ( fmt.flags.pc && fmt.pc > 16 ) fmt.pc -= 16; else { fmt.flags.pc = false; fmt.pc = 0; } \ 617 if ( fmt.flags.left ) { \ 618 fmt.flags.left = false; \ 619 fmt.wd = 0; \ 620 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 621 fmt2.flags.left = true; \ 622 int msigd = high1( msig ); \ 623 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 624 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0x base specifier */ \ 625 if ( (int)fmt2.wd < 16 ) fmt2.wd = 16; /* cast deals with negative value */ \ 626 fmt2.flags.pc = true; fmt2.pc = 16; \ 627 } else { \ 628 if ( fmt.wd > 16 ) fmt.wd -= 16; \ 629 else fmt.wd = 1; \ 630 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 631 fmt2.wd = 16; \ 632 } /* if */ \ 633 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 634 (ostype &)(os | fmt | "" | fmt2); \ 635 } /* if */ \ 636 } /* if */ \ 637 } else { \ 638 if ( CODE == 'd' ) { \ 639 if ( f.val < 0 ) { fmt( os, "-" ); sepOff( os ); f.val = -f.val; f.flags.sign = false; } \ 640 } /* if */ \ 641 base10_128( os, f ); \ 642 } /* if */ \ 643 return os; \ 644 } /* ?|? */ \ 645 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 646 } // distribution 647 648 IntegralFMTImpl128( int128, signed, 'd', "% *ll ", "% *.*ll " ) 649 IntegralFMTImpl128( unsigned int128, unsigned, 'u', "% *ll ", "% *.*ll " ) 650 #endif // __SIZEOF_INT128__ 651 #endif // 0 652 653 #if 1 654 #if defined( __SIZEOF_INT128__ ) 655 // Default prefix for non-decimal prints is 0b, 0, 0x. 656 forall( dtype ostype | ostream( ostype ) ) 657 static inline void base_128( ostype & os, unsigned int128 val, unsigned int128 power, _Ostream_Manip(uint64_t) & f, unsigned int maxdig, unsigned int bits, unsigned int cnt = 0 ) { 658 int wd = 1; // f.wd is never 0 because 0 implies left-pad 659 if ( val > power ) { // subdivide value into printable 64-bit values 660 base_128( os, val / power, power, f, maxdig, bits, cnt + 1 ); // recursive 661 f.val = val % power; 662 if ( cnt == 1 && f.flags.left ) { wd = f.wd; f.wd = maxdig; } // copy f.wd and reset for printing middle chunk 663 // printf( "R val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n", 664 // f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 ); 665 (ostype &)(os | f); 666 if ( cnt == 1 ) { 667 if ( f.flags.left ) { wd -= maxdig; f.wd = wd < 0 ? 1 : wd; } // update and restore f.wd for printing end chunk 668 sepOff( os ); // no seperator between chunks 669 } // if 670 } else { // print start chunk 671 f.val = val; 672 // f.pc is unsigned => use wd 673 if ( f.flags.pc && f.pc > maxdig * cnt ) { wd = f.pc - maxdig * cnt; f.pc = wd < 0 ? 0 : wd; } 674 else { f.flags.pc = false; f.pc = 0; } 675 676 if ( ! f.flags.left ) { // right justify 677 wd = f.wd - maxdig * cnt; 678 f.wd = wd < 0 ? 1 : wd; 679 wd = maxdig; 680 } else { // left justify 681 if ( cnt != 0 ) { // value >= 2^64 ? 682 unsigned int dig, bs = 0; 683 // compute size of prefix digits and base 684 if ( f.base == 'd' || f.base == 'u' ) { // no base prefix 685 dig = ceil( log10( f.val ) ); // use floating-point 686 if ( f.base == 'd' && (f.flags.neg || f.flags.sign) ) bs = 1; // sign ? 687 } else { 688 dig = ceiling_div( high1( f.val ), bits ); 689 if ( ! f.flags.nobsdp ) { // base prefix ? 690 if ( f.base == 'o' ) { 691 // 0 prefix for octal is not added for precision with leading zero 692 if ( f.pc <= dig ) bs = 1; // 1 character prefix 693 } else bs = 2; // 2 character prefix 694 } // if 695 } // if 696 wd = f.wd - (f.pc > dig ? f.pc : dig) - bs; // precision > leading digits ? 697 if ( wd < 0 ) wd = 1; 698 f.wd = 1; 699 } // if 700 // all manipulators handled implicitly for value < 2^64 701 } // if 702 // prior checks ensure wd not negative 703 704 if ( f.flags.neg ) f.val = -f.val; 705 // printf( "L val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n", 706 // f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 ); 707 (ostype &)(os | f); 708 709 // remaining middle and end chunks are padded with 0s on the left 710 if ( ! f.flags.left ) { f.flags.pad0 = true; f.flags.pc = false; } // left pad with 0s 711 else { f.pc = maxdig; f.flags.pc = true; } // left pad with precision 712 713 if ( cnt != 0 ) sepOff( os ); // no seperator between chunks 714 f.wd = wd; // reset f.wd for next chunk 715 f.flags.sign = false; // no leading +/- sign 716 f.flags.nobsdp = true; // no leading base prefix 717 } // if 718 } // base_128 719 720 #define IntegralFMTImpl128( T ) \ 721 forall( dtype ostype | ostream( ostype ) ) { \ 722 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 723 _Ostream_Manip(uint64_t) fmt; \ 724 fmt.[wd, pc, base, all] = f.[wd, pc, base, all]; \ 725 if ( f.base == 'b' | f.base == 'B' ) { \ 726 base_128( os, f.val, (unsigned int128)1 << 64, fmt, 64, 1 ); \ 727 } else if ( f.base == 'o' ) { \ 728 base_128( os, f.val, (unsigned int128)1 << 63, fmt, 21, 3 ); \ 729 } else if ( f.base == 'd' || f.base == 'u' ) { \ 730 if ( f.base == 'd' && f.val < 0 ) { f.val = -f.val; fmt.flags.neg = true; } \ 731 base_128( os, f.val, (unsigned int128)10_000_000_000_000_000_000UL, fmt, 19, 0 ); \ 732 } else { \ 733 base_128( os, f.val, (unsigned int128)1 << 64, fmt, 16, 4 ); \ 734 } /* if */ \ 735 return os; \ 736 } /* ?|? */ \ 737 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 738 } // distribution 739 740 IntegralFMTImpl128( int128 ) 741 IntegralFMTImpl128( unsigned int128 ) 742 #endif // __SIZEOF_INT128__ 743 #endif // 0 744 745 // *********************************** floating point *********************************** 478 IntegralFMTImpl( signed char, 'd', "% *hh ", "% *.*hh " ) 479 IntegralFMTImpl( unsigned char, 'u', "% *hh ", "% *.*hh " ) 480 IntegralFMTImpl( signed short int, 'd', "% *h ", "% *.*h " ) 481 IntegralFMTImpl( unsigned short int, 'u', "% *h ", "% *.*h " ) 482 IntegralFMTImpl( signed int, 'd', "% * ", "% *.* " ) 483 IntegralFMTImpl( unsigned int, 'u', "% * ", "% *.* " ) 484 IntegralFMTImpl( signed long int, 'd', "% *l ", "% *.*l " ) 485 IntegralFMTImpl( unsigned long int, 'u', "% *l ", "% *.*l " ) 486 IntegralFMTImpl( signed long long int, 'd', "% *ll ", "% *.*ll " ) 487 IntegralFMTImpl( unsigned long long int, 'u', "% *ll ", "% *.*ll " ) 488 489 //*********************************** floating point *********************************** 746 490 747 491 #define PrintWithDP2( os, format, val, ... ) \ … … 769 513 forall( dtype ostype | ostream( ostype ) ) { \ 770 514 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 771 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \515 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); \ 772 516 char fmtstr[sizeof(DFMTP)]; /* sizeof includes '\0' */ \ 773 517 if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \ … … 792 536 return os; \ 793 537 } /* ?|? */ \ 794 \795 538 void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \ 796 539 } // distribution … … 799 542 FloatingPointFMTImpl( long double, "% *L ", "% *.*L " ) 800 543 801 // *********************************** character ***********************************544 //*********************************** character *********************************** 802 545 803 546 forall( dtype ostype | ostream( ostype ) ) { … … 812 555 } // if 813 556 814 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );557 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 815 558 816 559 #define CFMTNP "% * " … … 828 571 return os; 829 572 } // ?|? 830 831 573 void ?|?( ostype & os, _Ostream_Manip(char) f ) { (ostype &)(os | f); ends( os ); } 832 574 } // distribution 833 575 834 // *********************************** C string ***********************************576 //*********************************** C string *********************************** 835 577 836 578 forall( dtype ostype | ostream( ostype ) ) { … … 850 592 } // if 851 593 852 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );594 if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); 853 595 854 596 #define SFMTNP "% * " … … 874 616 return os; 875 617 } // ?|? 876 877 618 void ?|?( ostype & os, _Ostream_Manip(const char *) f ) { (ostype &)(os | f); ends( os ); } 878 619 } // distribution 879 620 880 621 881 // *********************************** istream ***********************************622 //*********************************** istream *********************************** 882 623 883 624 … … 956 697 } // ?|? 957 698 958 #if defined( __SIZEOF_INT128__ )959 istype & ?|?( istype & is, int128 & i128 ) {960 return (istype &)(is | (unsigned int128 &)i128);961 } // ?|?962 963 istype & ?|?( istype & is, unsigned int128 & ui128 ) {964 char s[40];965 bool sign = false;966 967 if ( fmt( is, " %[-]", s ) == 1 ) sign = true; // skip whitespace, negative sign ?968 // If the input is too large, the value returned is undefined. If there is no input, no value is returned969 if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) { // take first 39 characters, ignore remaining970 ui128 = 0;971 for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) {972 ui128 = ui128 * 10 + s[i] - '0';973 } // for974 if ( sign ) ui128 = -ui128;975 } else if ( sign ) ungetc( is, '-' ); // return minus when no digits976 return is;977 } // ?|?978 #endif // __SIZEOF_INT128__979 699 980 700 istype & ?|?( istype & is, float & f ) { … … 1015 735 } // ?|? 1016 736 1017 // istype & ?|?( istype & is, const char fmt[]) {737 // istype & ?|?( istype & is, const char * fmt ) { 1018 738 // fmt( is, fmt, "" ); 1019 739 // return is; … … 1046 766 } // distribution 1047 767 1048 // *********************************** manipulators ***********************************768 //*********************************** manipulators *********************************** 1049 769 1050 770 forall( dtype istype | istream( istype ) ) -
libcfa/src/iostream.hfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Aug 11 22:16:14 202013 // Update Count : 3 5012 // Last Modified On : Fri Jul 12 12:08:38 2019 13 // Update Count : 334 14 14 // 15 15 … … 19 19 20 20 21 // *********************************** ostream ***********************************21 //*********************************** ostream *********************************** 22 22 23 23 24 24 trait ostream( dtype ostype ) { 25 25 // private 26 bool $sepPrt( ostype & ); // get separator state (on/off)27 void $sepReset( ostype & ); // set separator state to default state28 void $sepReset( ostype &, bool ); // set separator and default state29 const char * $sepGetCur( ostype & );// get current separator string30 void $sepSetCur( ostype &, const char []); // set current separator string31 bool $getNL( ostype & );// check newline32 void $setNL( ostype &, bool ); // saw newline33 bool $getANL( ostype & ); // get auto newline (on/off)34 bool $getPrt( ostype & ); // get fmt called in output cascade35 void $setPrt( ostype &, bool ); // set fmt called in output cascade26 bool sepPrt( ostype & ); // get separator state (on/off) 27 void sepReset( ostype & ); // set separator state to default state 28 void sepReset( ostype &, bool ); // set separator and default state 29 const char * sepGetCur( ostype & ); // get current separator string 30 void sepSetCur( ostype &, const char * ); // set current separator string 31 bool getNL( ostype & ); // check newline 32 void setNL( ostype &, bool ); // saw newline 33 bool getANL( ostype & ); // get auto newline (on/off) 34 bool getPrt( ostype & ); // get fmt called in output cascade 35 void setPrt( ostype &, bool ); // set fmt called in output cascade 36 36 // public 37 37 void sepOn( ostype & ); // turn separator state on … … 43 43 44 44 const char * sepGet( ostype & ); // get separator string 45 void sepSet( ostype &, const char []); // set separator to string (15 character maximum)45 void sepSet( ostype &, const char * ); // set separator to string (15 character maximum) 46 46 const char * sepGetTuple( ostype & ); // get tuple separator string 47 void sepSetTuple( ostype &, const char [] );// set tuple separator to string (15 character maximum)47 void sepSetTuple( ostype &, const char * ); // set tuple separator to string (15 character maximum) 48 48 49 49 void ends( ostype & os ); // end of output statement 50 50 int fail( ostype & ); 51 51 int flush( ostype & ); 52 void open( ostype & os, const char name[], const char mode[]);52 void open( ostype & os, const char * name, const char * mode ); 53 53 void close( ostype & os ); 54 ostype & write( ostype &, const char [], size_t );54 ostype & write( ostype &, const char *, size_t ); 55 55 int fmt( ostype &, const char format[], ... ) __attribute__(( format(printf, 2, 3) )); 56 56 }; // ostream … … 67 67 68 68 forall( dtype ostype | ostream( ostype ) ) { 69 ostype & ?|?( ostype &, zero_t ); 70 void ?|?( ostype &, zero_t ); 71 ostype & ?|?( ostype &, one_t ); 72 void ?|?( ostype &, one_t ); 73 69 74 ostype & ?|?( ostype &, bool ); 70 75 void ?|?( ostype &, bool ); … … 93 98 ostype & ?|?( ostype &, unsigned long long int ); 94 99 void ?|?( ostype &, unsigned long long int ); 95 #if defined( __SIZEOF_INT128__ )96 ostype & ?|?( ostype &, int128 );97 void ?|?( ostype &, int128 );98 ostype & ?|?( ostype &, unsigned int128 );99 void ?|?( ostype &, unsigned int128 );100 #endif // __SIZEOF_INT128__101 100 102 101 ostype & ?|?( ostype &, float ); … … 114 113 void ?|?( ostype &, long double _Complex ); 115 114 116 ostype & ?|?( ostype &, const char []);117 void ?|?( ostype &, const char []);115 ostype & ?|?( ostype &, const char * ); 116 void ?|?( ostype &, const char * ); 118 117 // ostype & ?|?( ostype &, const char16_t * ); 119 118 #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous … … 151 150 } // distribution 152 151 153 // *********************************** manipulators ***********************************152 //*********************************** manipulators *********************************** 154 153 155 154 forall( otype T ) … … 161 160 unsigned char all; 162 161 struct { 163 unsigned char neg:1; // val is negative164 162 unsigned char pc:1; // precision specified 165 163 unsigned char left:1; // left justify … … 171 169 }; // _Ostream_Manip 172 170 173 // *********************************** integral ***********************************171 //*********************************** integral *********************************** 174 172 175 173 // See 6.7.9. 19) The initialization shall occur in initializer list order, each initializer provided for a particular … … 208 206 IntegralFMTDecl( signed long long int, 'd' ) 209 207 IntegralFMTDecl( unsigned long long int, 'u' ) 210 #if defined( __SIZEOF_INT128__ ) 211 IntegralFMTDecl( int128, 'd' ) 212 IntegralFMTDecl( unsigned int128, 'u' ) 213 #endif // __SIZEOF_INT128__ 214 215 // *********************************** floating point *********************************** 208 209 //*********************************** floating point *********************************** 216 210 217 211 // Default suffix for values with no fraction is "." … … 242 236 FloatingPointFMTDecl( long double ) 243 237 244 // *********************************** character ***********************************238 //*********************************** character *********************************** 245 239 246 240 static inline { … … 259 253 } // ?|? 260 254 261 // *********************************** C string ***********************************255 //*********************************** C string *********************************** 262 256 263 257 static inline { 264 _Ostream_Manip(const char *) bin( const char s[]) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'b', { .all : 0 } }; }265 _Ostream_Manip(const char *) oct( const char s[]) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'o', { .all : 0 } }; }266 _Ostream_Manip(const char *) hex( const char s[]) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'x', { .all : 0 } }; }267 _Ostream_Manip(const char *) wd( unsigned int w, const char s[]) { return (_Ostream_Manip(const char *))@{ s, w, 0, 's', { .all : 0 } }; }268 _Ostream_Manip(const char *) wd( unsigned int w, unsigned char pc, const char s[]) { return (_Ostream_Manip(const char *))@{ s, w, pc, 's', { .flags.pc : true } }; }258 _Ostream_Manip(const char *) bin( const char * s ) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'b', { .all : 0 } }; } 259 _Ostream_Manip(const char *) oct( const char * s ) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'o', { .all : 0 } }; } 260 _Ostream_Manip(const char *) hex( const char * s ) { return (_Ostream_Manip(const char *))@{ s, 1, 0, 'x', { .all : 0 } }; } 261 _Ostream_Manip(const char *) wd( unsigned int w, const char * s ) { return (_Ostream_Manip(const char *))@{ s, w, 0, 's', { .all : 0 } }; } 262 _Ostream_Manip(const char *) wd( unsigned int w, unsigned char pc, const char * s ) { return (_Ostream_Manip(const char *))@{ s, w, pc, 's', { .flags.pc : true } }; } 269 263 _Ostream_Manip(const char *) & wd( unsigned int w, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; return fmt; } 270 264 _Ostream_Manip(const char *) & wd( unsigned int w, unsigned char pc, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } … … 278 272 279 273 280 // *********************************** istream ***********************************274 //*********************************** istream *********************************** 281 275 282 276 … … 287 281 int fail( istype & ); 288 282 int eof( istype & ); 289 void open( istype & is, const char name[]);283 void open( istype & is, const char * name ); 290 284 void close( istype & is ); 291 285 istype & read( istype &, char *, size_t ); … … 310 304 istype & ?|?( istype &, unsigned int & ); 311 305 istype & ?|?( istype &, long int & ); 306 istype & ?|?( istype &, long long int & ); 312 307 istype & ?|?( istype &, unsigned long int & ); 313 istype & ?|?( istype &, long long int & );314 308 istype & ?|?( istype &, unsigned long long int & ); 315 #if defined( __SIZEOF_INT128__ )316 istype & ?|?( istype &, int128 & );317 istype & ?|?( istype &, unsigned int128 & );318 #endif // __SIZEOF_INT128__319 309 320 310 istype & ?|?( istype &, float & ); … … 326 316 istype & ?|?( istype &, long double _Complex & ); 327 317 328 // istype & ?|?( istype &, const char []);318 // istype & ?|?( istype &, const char * ); 329 319 istype & ?|?( istype &, char * ); 330 320 … … 336 326 } // distribution 337 327 338 // *********************************** manipulators ***********************************328 //*********************************** manipulators *********************************** 339 329 340 330 struct _Istream_Cstr { … … 353 343 static inline { 354 344 _Istream_Cstr skip( unsigned int n ) { return (_Istream_Cstr){ 0p, 0p, n, { .all : 0 } }; } 355 _Istream_Cstr skip( const char scanset[]) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; }356 _Istream_Cstr incl( const char scanset[], char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : false } }; }357 _Istream_Cstr & incl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }358 _Istream_Cstr excl( const char scanset[], char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : true } }; }359 _Istream_Cstr & excl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }360 _Istream_Cstr ignore( c har s[]) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; }345 _Istream_Cstr skip( const char * scanset ) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; } 346 _Istream_Cstr incl( const char * scanset, char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : false } }; } 347 _Istream_Cstr & incl( const char * scanset, _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; } 348 _Istream_Cstr excl( const char * scanset, char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : true } }; } 349 _Istream_Cstr & excl( const char * scanset, _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; } 350 _Istream_Cstr ignore( const char * s ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; } 361 351 _Istream_Cstr & ignore( _Istream_Cstr & fmt ) { fmt.flags.ignore = true; return fmt; } 362 _Istream_Cstr wdi( unsigned int w, char s[]) { return (_Istream_Cstr)@{ s, 0p, w, { .all : 0 } }; }352 _Istream_Cstr wdi( unsigned int w, char * s ) { return (_Istream_Cstr)@{ s, 0p, w, { .all : 0 } }; } 363 353 _Istream_Cstr & wdi( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; } 364 354 } // distribution … … 370 360 371 361 static inline { 372 _Istream_Char ignore( const char ) { return (_Istream_Char)@{ true }; }362 _Istream_Char ignore( const char c ) { return (_Istream_Char)@{ true }; } 373 363 _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; } 374 364 } // distribution 375 365 forall( dtype istype | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f ); 376 366 377 forall( dtype T | sized( T ))367 forall( otype T ) 378 368 struct _Istream_Manip { 379 369 T & val; // polymorphic base-type … … 413 403 414 404 415 // *********************************** time ***********************************405 //*********************************** time *********************************** 416 406 417 407 -
libcfa/src/math.hfa
reef8dfb rbdfc032 10 10 // Created On : Mon Apr 18 23:37:04 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Aug 24 08:56:20 202013 // Update Count : 1 2612 // Last Modified On : Fri Jul 13 11:02:15 2018 13 // Update Count : 116 14 14 // 15 15 … … 19 19 #include <complex.h> 20 20 21 //---------------------- General ---------------------- 22 23 static inline float ?%?( float x, float y ) { return fmodf( x, y ); } 24 static inline float fmod( float x, float y ) { return fmodf( x, y ); } 25 static inline double ?%?( double x, double y ) { return fmod( x, y ); } 26 // extern "C" { double fmod( double, double ); } 27 static inline long double ?%?( long double x, long double y ) { return fmodl( x, y ); } 28 static inline long double fmod( long double x, long double y ) { return fmodl( x, y ); } 29 30 static inline float remainder( float x, float y ) { return remainderf( x, y ); } 31 // extern "C" { double remainder( double, double ); } 32 static inline long double remainder( long double x, long double y ) { return remainderl( x, y ); } 33 34 static inline float remquo( float x, float y, int * quo ) { return remquof( x, y, quo ); } 35 // extern "C" { double remquo( double x, double y, int * quo ); } 36 static inline long double remquo( long double x, long double y, int * quo ) { return remquol( x, y, quo ); } 37 static inline [ int, float ] remquo( float x, float y ) { int quo; x = remquof( x, y, &quo ); return [ quo, x ]; } 38 static inline [ int, double ] remquo( double x, double y ) { int quo; x = remquo( x, y, &quo ); return [ quo, x ]; } 39 static inline [ int, long double ] remquo( long double x, long double y ) { int quo; x = remquol( x, y, &quo ); return [ quo, x ]; } 40 41 static inline [ float, float ] div( float x, float y ) { y = modff( x / y, &x ); return [ x, y ]; } 42 static inline [ double, double ] div( double x, double y ) { y = modf( x / y, &x ); return [ x, y ]; } 43 static inline [ long double, long double ] div( long double x, long double y ) { y = modfl( x / y, &x ); return [ x, y ]; } 44 45 static inline float fma( float x, float y, float z ) { return fmaf( x, y, z ); } 46 // extern "C" { double fma( double, double, double ); } 47 static inline long double fma( long double x, long double y, long double z ) { return fmal( x, y, z ); } 48 49 static inline float fdim( float x, float y ) { return fdimf( x, y ); } 50 // extern "C" { double fdim( double, double ); } 51 static inline long double fdim( long double x, long double y ) { return fdiml( x, y ); } 52 53 static inline float nan( const char * tag ) { return nanf( tag ); } 54 // extern "C" { double nan( const char * ); } 55 static inline long double nan( const char * tag ) { return nanl( tag ); } 56 57 //---------------------- Exponential ---------------------- 58 59 static inline float exp( float x ) { return expf( x ); } 60 // extern "C" { double exp( double ); } 61 static inline long double exp( long double x ) { return expl( x ); } 62 static inline float _Complex exp( float _Complex x ) { return cexpf( x ); } 63 static inline double _Complex exp( double _Complex x ) { return cexp( x ); } 64 static inline long double _Complex exp( long double _Complex x ) { return cexpl( x ); } 65 66 static inline float exp2( float x ) { return exp2f( x ); } 67 // extern "C" { double exp2( double ); } 68 static inline long double exp2( long double x ) { return exp2l( x ); } 69 //static inline float _Complex exp2( float _Complex x ) { return cexp2f( x ); } 70 //static inline double _Complex exp2( double _Complex x ) { return cexp2( x ); } 71 //static inline long double _Complex exp2( long double _Complex x ) { return cexp2l( x ); } 72 73 static inline float expm1( float x ) { return expm1f( x ); } 74 // extern "C" { double expm1( double ); } 75 static inline long double expm1( long double x ) { return expm1l( x ); } 76 77 static inline float pow( float x, float y ) { return powf( x, y ); } 78 // extern "C" { double pow( double, double ); } 79 static inline long double pow( long double x, long double y ) { return powl( x, y ); } 80 static inline float _Complex pow( float _Complex x, float _Complex y ) { return cpowf( x, y ); } 81 static inline double _Complex pow( double _Complex x, double _Complex y ) { return cpow( x, y ); } 82 static inline long double _Complex pow( long double _Complex x, long double _Complex y ) { return cpowl( x, y ); } 83 84 //---------------------- Logarithm ---------------------- 85 86 static inline float log( float x ) { return logf( x ); } 87 // extern "C" { double log( double ); } 88 static inline long double log( long double x ) { return logl( x ); } 89 static inline float _Complex log( float _Complex x ) { return clogf( x ); } 90 static inline double _Complex log( double _Complex x ) { return clog( x ); } 91 static inline long double _Complex log( long double _Complex x ) { return clogl( x ); } 92 93 static inline float log2( float x ) { return log2f( x ); } 94 // extern "C" { double log2( double ); } 95 static inline long double log2( long double x ) { return log2l( x ); } 96 // static inline float _Complex log2( float _Complex x ) { return clog2f( x ); } 97 // static inline double _Complex log2( double _Complex x ) { return clog2( x ); } 98 // static inline long double _Complex log2( long double _Complex x ) { return clog2l( x ); } 99 100 static inline float log10( float x ) { return log10f( x ); } 101 // extern "C" { double log10( double ); } 102 static inline long double log10( long double x ) { return log10l( x ); } 103 // static inline float _Complex log10( float _Complex x ) { return clog10f( x ); } 104 // static inline double _Complex log10( double _Complex x ) { return clog10( x ); } 105 // static inline long double _Complex log10( long double _Complex x ) { return clog10l( x ); } 106 107 static inline float log1p( float x ) { return log1pf( x ); } 108 // extern "C" { double log1p( double ); } 109 static inline long double log1p( long double x ) { return log1pl( x ); } 110 111 static inline int ilogb( float x ) { return ilogbf( x ); } 112 // extern "C" { int ilogb( double ); } 113 static inline int ilogb( long double x ) { return ilogbl( x ); } 114 115 static inline float logb( float x ) { return logbf( x ); } 116 // extern "C" { double logb( double ); } 117 static inline long double logb( long double x ) { return logbl( x ); } 118 119 static inline float sqrt( float x ) { return sqrtf( x ); } 120 // extern "C" { double sqrt( double ); } 121 static inline long double sqrt( long double x ) { return sqrtl( x ); } 122 static inline float _Complex sqrt( float _Complex x ) { return csqrtf( x ); } 123 static inline double _Complex sqrt( double _Complex x ) { return csqrt( x ); } 124 static inline long double _Complex sqrt( long double _Complex x ) { return csqrtl( x ); } 125 126 static inline float cbrt( float x ) { return cbrtf( x ); } 127 // extern "C" { double cbrt( double ); } 128 static inline long double cbrt( long double x ) { return cbrtl( x ); } 129 130 static inline float hypot( float x, float y ) { return hypotf( x, y ); } 131 // extern "C" { double hypot( double, double ); } 132 static inline long double hypot( long double x, long double y ) { return hypotl( x, y ); } 133 134 //---------------------- Trigonometric ---------------------- 135 136 static inline float sin( float x ) { return sinf( x ); } 137 // extern "C" { double sin( double ); } 138 static inline long double sin( long double x ) { return sinl( x ); } 139 static inline float _Complex sin( float _Complex x ) { return csinf( x ); } 140 static inline double _Complex sin( double _Complex x ) { return csin( x ); } 141 static inline long double _Complex sin( long double _Complex x ) { return csinl( x ); } 142 143 static inline float cos( float x ) { return cosf( x ); } 144 // extern "C" { double cos( double ); } 145 static inline long double cos( long double x ) { return cosl( x ); } 146 static inline float _Complex cos( float _Complex x ) { return ccosf( x ); } 147 static inline double _Complex cos( double _Complex x ) { return ccos( x ); } 148 static inline long double _Complex cos( long double _Complex x ) { return ccosl( x ); } 149 150 static inline float tan( float x ) { return tanf( x ); } 151 // extern "C" { double tan( double ); } 152 static inline long double tan( long double x ) { return tanl( x ); } 153 static inline float _Complex tan( float _Complex x ) { return ctanf( x ); } 154 static inline double _Complex tan( double _Complex x ) { return ctan( x ); } 155 static inline long double _Complex tan( long double _Complex x ) { return ctanl( x ); } 156 157 static inline float asin( float x ) { return asinf( x ); } 158 // extern "C" { double asin( double ); } 159 static inline long double asin( long double x ) { return asinl( x ); } 160 static inline float _Complex asin( float _Complex x ) { return casinf( x ); } 161 static inline double _Complex asin( double _Complex x ) { return casin( x ); } 162 static inline long double _Complex asin( long double _Complex x ) { return casinl( x ); } 163 164 static inline float acos( float x ) { return acosf( x ); } 165 // extern "C" { double acos( double ); } 166 static inline long double acos( long double x ) { return acosl( x ); } 167 static inline float _Complex acos( float _Complex x ) { return cacosf( x ); } 168 static inline double _Complex acos( double _Complex x ) { return cacos( x ); } 169 static inline long double _Complex acos( long double _Complex x ) { return cacosl( x ); } 170 171 static inline float atan( float x ) { return atanf( x ); } 172 // extern "C" { double atan( double ); } 173 static inline long double atan( long double x ) { return atanl( x ); } 174 static inline float _Complex atan( float _Complex x ) { return catanf( x ); } 175 static inline double _Complex atan( double _Complex x ) { return catan( x ); } 176 static inline long double _Complex atan( long double _Complex x ) { return catanl( x ); } 177 178 static inline float atan2( float x, float y ) { return atan2f( x, y ); } 179 // extern "C" { double atan2( double, double ); } 180 static inline long double atan2( long double x, long double y ) { return atan2l( x, y ); } 181 182 // alternative name for atan2 183 static inline float atan( float x, float y ) { return atan2f( x, y ); } 184 static inline double atan( double x, double y ) { return atan2( x, y ); } 185 static inline long double atan( long double x, long double y ) { return atan2l( x, y ); } 186 187 //---------------------- Hyperbolic ---------------------- 188 189 static inline float sinh( float x ) { return sinhf( x ); } 190 // extern "C" { double sinh( double ); } 191 static inline long double sinh( long double x ) { return sinhl( x ); } 192 static inline float _Complex sinh( float _Complex x ) { return csinhf( x ); } 193 static inline double _Complex sinh( double _Complex x ) { return csinh( x ); } 194 static inline long double _Complex sinh( long double _Complex x ) { return csinhl( x ); } 195 196 static inline float cosh( float x ) { return coshf( x ); } 197 // extern "C" { double cosh( double ); } 198 static inline long double cosh( long double x ) { return coshl( x ); } 199 static inline float _Complex cosh( float _Complex x ) { return ccoshf( x ); } 200 static inline double _Complex cosh( double _Complex x ) { return ccosh( x ); } 201 static inline long double _Complex cosh( long double _Complex x ) { return ccoshl( x ); } 202 203 static inline float tanh( float x ) { return tanhf( x ); } 204 // extern "C" { double tanh( double ); } 205 static inline long double tanh( long double x ) { return tanhl( x ); } 206 static inline float _Complex tanh( float _Complex x ) { return ctanhf( x ); } 207 static inline double _Complex tanh( double _Complex x ) { return ctanh( x ); } 208 static inline long double _Complex tanh( long double _Complex x ) { return ctanhl( x ); } 209 210 static inline float asinh( float x ) { return asinhf( x ); } 211 // extern "C" { double asinh( double ); } 212 static inline long double asinh( long double x ) { return asinhl( x ); } 213 static inline float _Complex asinh( float _Complex x ) { return casinhf( x ); } 214 static inline double _Complex asinh( double _Complex x ) { return casinh( x ); } 215 static inline long double _Complex asinh( long double _Complex x ) { return casinhl( x ); } 216 217 static inline float acosh( float x ) { return acoshf( x ); } 218 // extern "C" { double acosh( double ); } 219 static inline long double acosh( long double x ) { return acoshl( x ); } 220 static inline float _Complex acosh( float _Complex x ) { return cacoshf( x ); } 221 static inline double _Complex acosh( double _Complex x ) { return cacosh( x ); } 222 static inline long double _Complex acosh( long double _Complex x ) { return cacoshl( x ); } 223 224 static inline float atanh( float x ) { return atanhf( x ); } 225 // extern "C" { double atanh( double ); } 226 static inline long double atanh( long double x ) { return atanhl( x ); } 227 static inline float _Complex atanh( float _Complex x ) { return catanhf( x ); } 228 static inline double _Complex atanh( double _Complex x ) { return catanh( x ); } 229 static inline long double _Complex atanh( long double _Complex x ) { return catanhl( x ); } 230 231 //---------------------- Error / Gamma ---------------------- 232 233 static inline float erf( float x ) { return erff( x ); } 234 // extern "C" { double erf( double ); } 235 static inline long double erf( long double x ) { return erfl( x ); } 236 // float _Complex erf( float _Complex ); 237 // double _Complex erf( double _Complex ); 238 // long double _Complex erf( long double _Complex ); 239 240 static inline float erfc( float x ) { return erfcf( x ); } 241 // extern "C" { double erfc( double ); } 242 static inline long double erfc( long double x ) { return erfcl( x ); } 243 // float _Complex erfc( float _Complex ); 244 // double _Complex erfc( double _Complex ); 245 // long double _Complex erfc( long double _Complex ); 246 247 static inline float lgamma( float x ) { return lgammaf( x ); } 248 // extern "C" { double lgamma( double ); } 249 static inline long double lgamma( long double x ) { return lgammal( x ); } 250 static inline float lgamma( float x, int * sign ) { return lgammaf_r( x, sign ); } 251 static inline double lgamma( double x, int * sign ) { return lgamma_r( x, sign ); } 252 static inline long double lgamma( long double x, int * sign ) { return lgammal_r( x, sign ); } 253 254 static inline float tgamma( float x ) { return tgammaf( x ); } 255 // extern "C" { double tgamma( double ); } 256 static inline long double tgamma( long double x ) { return tgammal( x ); } 257 258 //---------------------- Nearest Integer ---------------------- 259 260 static inline float floor( float x ) { return floorf( x ); } 261 // extern "C" { double floor( double ); } 262 static inline long double floor( long double x ) { return floorl( x ); } 263 264 static inline float ceil( float x ) { return ceilf( x ); } 265 // extern "C" { double ceil( double ); } 266 static inline long double ceil( long double x ) { return ceill( x ); } 267 268 static inline float trunc( float x ) { return truncf( x ); } 269 // extern "C" { double trunc( double ); } 270 static inline long double trunc( long double x ) { return truncl( x ); } 271 272 static inline float rint( float x ) { return rintf( x ); } 273 // extern "C" { double rint( double x ); } 274 static inline long double rint( long double x ) { return rintl( x ); } 275 static inline long int rint( float x ) { return lrintf( x ); } 276 static inline long int rint( double x ) { return lrint( x ); } 277 static inline long int rint( long double x ) { return lrintl( x ); } 278 static inline long long int rint( float x ) { return llrintf( x ); } 279 static inline long long int rint( double x ) { return llrint( x ); } 280 static inline long long int rint( long double x ) { return llrintl( x ); } 281 282 static inline long int lrint( float x ) { return lrintf( x ); } 283 // extern "C" { long int lrint( double ); } 284 static inline long int lrint( long double x ) { return lrintl( x ); } 285 static inline long long int llrint( float x ) { return llrintf( x ); } 286 // extern "C" { long long int llrint( double ); } 287 static inline long long int llrint( long double x ) { return llrintl( x ); } 288 289 static inline float nearbyint( float x ) { return nearbyintf( x ); } 290 // extern "C" { double nearbyint( double ); } 291 static inline long double nearbyint( long double x ) { return nearbyintl( x ); } 292 293 static inline float round( float x ) { return roundf( x ); } 294 // extern "C" { double round( double x ); } 295 static inline long double round( long double x ) { return roundl( x ); } 296 static inline long int round( float x ) { return lroundf( x ); } 297 static inline long int round( double x ) { return lround( x ); } 298 static inline long int round( long double x ) { return lroundl( x ); } 299 static inline long long int round( float x ) { return llroundf( x ); } 300 static inline long long int round( double x ) { return llround( x ); } 301 static inline long long int round( long double x ) { return llroundl( x ); } 302 303 static inline long int lround( float x ) { return lroundf( x ); } 304 // extern "C" { long int lround( double ); } 305 static inline long int lround( long double x ) { return lroundl( x ); } 306 static inline long long int llround( float x ) { return llroundf( x ); } 307 // extern "C" { long long int llround( double ); } 308 static inline long long int llround( long double x ) { return llroundl( x ); } 309 310 //---------------------- Manipulation ---------------------- 311 312 static inline float copysign( float x, float y ) { return copysignf( x, y ); } 313 // extern "C" { double copysign( double, double ); } 314 static inline long double copysign( long double x, long double y ) { return copysignl( x, y ); } 315 316 static inline float frexp( float x, int * ip ) { return frexpf( x, ip ); } 317 // extern "C" { double frexp( double, int * ); } 318 static inline long double frexp( long double x, int * ip ) { return frexpl( x, ip ); } 319 320 static inline float ldexp( float x, int exp2 ) { return ldexpf( x, exp2 ); } 321 // extern "C" { double ldexp( double, int ); } 322 static inline long double ldexp( long double x, int exp2 ) { return ldexpl( x, exp2 ); } 323 324 static inline [ float, float ] modf( float x ) { float i; x = modff( x, &i ); return [ i, x ]; } 325 static inline float modf( float x, float * i ) { return modff( x, i ); } 326 static inline [ double, double ] modf( double x ) { double i; x = modf( x, &i ); return [ i, x ]; } 327 // extern "C" { double modf( double, double * ); } 328 static inline [ long double, long double ] modf( long double x ) { long double i; x = modfl( x, &i ); return [ i, x ]; } 329 static inline long double modf( long double x, long double * i ) { return modfl( x, i ); } 330 331 static inline float nextafter( float x, float y ) { return nextafterf( x, y ); } 332 // extern "C" { double nextafter( double, double ); } 333 static inline long double nextafter( long double x, long double y ) { return nextafterl( x, y ); } 334 335 static inline float nexttoward( float x, long double y ) { return nexttowardf( x, y ); } 336 // extern "C" { double nexttoward( double, long double ); } 337 static inline long double nexttoward( long double x, long double y ) { return nexttowardl( x, y ); } 338 339 static inline float scalbn( float x, int exp ) { return scalbnf( x, exp ); } 340 // extern "C" { double scalbn( double, int ); } 341 static inline long double scalbn( long double x, int exp ) { return scalbnl( x, exp ); } 342 static inline float scalbn( float x, long int exp ) { return scalblnf( x, exp ); } 343 static inline double scalbn( double x, long int exp ) { return scalbln( x, exp ); } 344 static inline long double scalbn( long double x, long int exp ) { return scalblnl( x, exp ); } 345 346 static inline float scalbln( float x, long int exp ) { return scalblnf( x, exp ); } 347 // extern "C" { double scalbln( double, long int ); } 348 static inline long double scalbln( long double x, long int exp ) { return scalblnl( x, exp ); } 349 21 350 //--------------------------------------- 22 351 23 352 #include "common.hfa" 24 353 25 //---------------------- General ----------------------26 27 static inline {28 float ?%?( float x, float y ) { return fmodf( x, y ); }29 float fmod( float x, float y ) { return fmodf( x, y ); }30 double ?%?( double x, double y ) { return fmod( x, y ); }31 // extern "C" { double fmod( double, double ); }32 long double ?%?( long double x, long double y ) { return fmodl( x, y ); }33 long double fmod( long double x, long double y ) { return fmodl( x, y ); }34 35 float remainder( float x, float y ) { return remainderf( x, y ); }36 // extern "C" { double remainder( double, double ); }37 long double remainder( long double x, long double y ) { return remainderl( x, y ); }38 39 float remquo( float x, float y, int * quo ) { return remquof( x, y, quo ); }40 // extern "C" { double remquo( double x, double y, int * quo ); }41 long double remquo( long double x, long double y, int * quo ) { return remquol( x, y, quo ); }42 [ int, float ] remquo( float x, float y ) { int quo; x = remquof( x, y, &quo ); return [ quo, x ]; }43 [ int, double ] remquo( double x, double y ) { int quo; x = remquo( x, y, &quo ); return [ quo, x ]; }44 [ int, long double ] remquo( long double x, long double y ) { int quo; x = remquol( x, y, &quo ); return [ quo, x ]; }45 46 [ float, float ] div( float x, float y ) { y = modff( x / y, &x ); return [ x, y ]; }47 [ double, double ] div( double x, double y ) { y = modf( x / y, &x ); return [ x, y ]; }48 [ long double, long double ] div( long double x, long double y ) { y = modfl( x / y, &x ); return [ x, y ]; }49 50 float fma( float x, float y, float z ) { return fmaf( x, y, z ); }51 // extern "C" { double fma( double, double, double ); }52 long double fma( long double x, long double y, long double z ) { return fmal( x, y, z ); }53 54 float fdim( float x, float y ) { return fdimf( x, y ); }55 // extern "C" { double fdim( double, double ); }56 long double fdim( long double x, long double y ) { return fdiml( x, y ); }57 58 float nan( const char tag[] ) { return nanf( tag ); }59 // extern "C" { double nan( const char [] ); }60 long double nan( const char tag[] ) { return nanl( tag ); }61 } // distribution62 63 //---------------------- Exponential ----------------------64 65 static inline {66 float exp( float x ) { return expf( x ); }67 // extern "C" { double exp( double ); }68 long double exp( long double x ) { return expl( x ); }69 float _Complex exp( float _Complex x ) { return cexpf( x ); }70 double _Complex exp( double _Complex x ) { return cexp( x ); }71 long double _Complex exp( long double _Complex x ) { return cexpl( x ); }72 73 float exp2( float x ) { return exp2f( x ); }74 // extern "C" { double exp2( double ); }75 long double exp2( long double x ) { return exp2l( x ); }76 //float _Complex exp2( float _Complex x ) { return cexp2f( x ); }77 //double _Complex exp2( double _Complex x ) { return cexp2( x ); }78 //long double _Complex exp2( long double _Complex x ) { return cexp2l( x ); }79 80 float expm1( float x ) { return expm1f( x ); }81 // extern "C" { double expm1( double ); }82 long double expm1( long double x ) { return expm1l( x ); }83 84 float pow( float x, float y ) { return powf( x, y ); }85 // extern "C" { double pow( double, double ); }86 long double pow( long double x, long double y ) { return powl( x, y ); }87 float _Complex pow( float _Complex x, float _Complex y ) { return cpowf( x, y ); }88 double _Complex pow( double _Complex x, double _Complex y ) { return cpow( x, y ); }89 long double _Complex pow( long double _Complex x, long double _Complex y ) { return cpowl( x, y ); }90 } // distribution91 92 //---------------------- Logarithm ----------------------93 94 static inline {95 float log( float x ) { return logf( x ); }96 // extern "C" { double log( double ); }97 long double log( long double x ) { return logl( x ); }98 float _Complex log( float _Complex x ) { return clogf( x ); }99 double _Complex log( double _Complex x ) { return clog( x ); }100 long double _Complex log( long double _Complex x ) { return clogl( x ); }101 102 float log2( float x ) { return log2f( x ); }103 // extern "C" { double log2( double ); }104 long double log2( long double x ) { return log2l( x ); }105 // float _Complex log2( float _Complex x ) { return clog2f( x ); }106 // double _Complex log2( double _Complex x ) { return clog2( x ); }107 // long double _Complex log2( long double _Complex x ) { return clog2l( x ); }108 109 float log10( float x ) { return log10f( x ); }110 // extern "C" { double log10( double ); }111 long double log10( long double x ) { return log10l( x ); }112 // float _Complex log10( float _Complex x ) { return clog10f( x ); }113 // double _Complex log10( double _Complex x ) { return clog10( x ); }114 // long double _Complex log10( long double _Complex x ) { return clog10l( x ); }115 116 float log1p( float x ) { return log1pf( x ); }117 // extern "C" { double log1p( double ); }118 long double log1p( long double x ) { return log1pl( x ); }119 120 int ilogb( float x ) { return ilogbf( x ); }121 // extern "C" { int ilogb( double ); }122 int ilogb( long double x ) { return ilogbl( x ); }123 124 float logb( float x ) { return logbf( x ); }125 // extern "C" { double logb( double ); }126 long double logb( long double x ) { return logbl( x ); }127 128 float sqrt( float x ) { return sqrtf( x ); }129 // extern "C" { double sqrt( double ); }130 long double sqrt( long double x ) { return sqrtl( x ); }131 float _Complex sqrt( float _Complex x ) { return csqrtf( x ); }132 double _Complex sqrt( double _Complex x ) { return csqrt( x ); }133 long double _Complex sqrt( long double _Complex x ) { return csqrtl( x ); }134 135 float cbrt( float x ) { return cbrtf( x ); }136 // extern "C" { double cbrt( double ); }137 long double cbrt( long double x ) { return cbrtl( x ); }138 139 float hypot( float x, float y ) { return hypotf( x, y ); }140 // extern "C" { double hypot( double, double ); }141 long double hypot( long double x, long double y ) { return hypotl( x, y ); }142 } // distribution143 144 //---------------------- Trigonometric ----------------------145 146 static inline {147 float sin( float x ) { return sinf( x ); }148 // extern "C" { double sin( double ); }149 long double sin( long double x ) { return sinl( x ); }150 float _Complex sin( float _Complex x ) { return csinf( x ); }151 double _Complex sin( double _Complex x ) { return csin( x ); }152 long double _Complex sin( long double _Complex x ) { return csinl( x ); }153 154 float cos( float x ) { return cosf( x ); }155 // extern "C" { double cos( double ); }156 long double cos( long double x ) { return cosl( x ); }157 float _Complex cos( float _Complex x ) { return ccosf( x ); }158 double _Complex cos( double _Complex x ) { return ccos( x ); }159 long double _Complex cos( long double _Complex x ) { return ccosl( x ); }160 161 float tan( float x ) { return tanf( x ); }162 // extern "C" { double tan( double ); }163 long double tan( long double x ) { return tanl( x ); }164 float _Complex tan( float _Complex x ) { return ctanf( x ); }165 double _Complex tan( double _Complex x ) { return ctan( x ); }166 long double _Complex tan( long double _Complex x ) { return ctanl( x ); }167 168 float asin( float x ) { return asinf( x ); }169 // extern "C" { double asin( double ); }170 long double asin( long double x ) { return asinl( x ); }171 float _Complex asin( float _Complex x ) { return casinf( x ); }172 double _Complex asin( double _Complex x ) { return casin( x ); }173 long double _Complex asin( long double _Complex x ) { return casinl( x ); }174 175 float acos( float x ) { return acosf( x ); }176 // extern "C" { double acos( double ); }177 long double acos( long double x ) { return acosl( x ); }178 float _Complex acos( float _Complex x ) { return cacosf( x ); }179 double _Complex acos( double _Complex x ) { return cacos( x ); }180 long double _Complex acos( long double _Complex x ) { return cacosl( x ); }181 182 float atan( float x ) { return atanf( x ); }183 // extern "C" { double atan( double ); }184 long double atan( long double x ) { return atanl( x ); }185 float _Complex atan( float _Complex x ) { return catanf( x ); }186 double _Complex atan( double _Complex x ) { return catan( x ); }187 long double _Complex atan( long double _Complex x ) { return catanl( x ); }188 189 float atan2( float x, float y ) { return atan2f( x, y ); }190 // extern "C" { double atan2( double, double ); }191 long double atan2( long double x, long double y ) { return atan2l( x, y ); }192 193 // alternative name for atan2194 float atan( float x, float y ) { return atan2f( x, y ); }195 double atan( double x, double y ) { return atan2( x, y ); }196 long double atan( long double x, long double y ) { return atan2l( x, y ); }197 } // distribution198 199 //---------------------- Hyperbolic ----------------------200 201 static inline {202 float sinh( float x ) { return sinhf( x ); }203 // extern "C" { double sinh( double ); }204 long double sinh( long double x ) { return sinhl( x ); }205 float _Complex sinh( float _Complex x ) { return csinhf( x ); }206 double _Complex sinh( double _Complex x ) { return csinh( x ); }207 long double _Complex sinh( long double _Complex x ) { return csinhl( x ); }208 209 float cosh( float x ) { return coshf( x ); }210 // extern "C" { double cosh( double ); }211 long double cosh( long double x ) { return coshl( x ); }212 float _Complex cosh( float _Complex x ) { return ccoshf( x ); }213 double _Complex cosh( double _Complex x ) { return ccosh( x ); }214 long double _Complex cosh( long double _Complex x ) { return ccoshl( x ); }215 216 float tanh( float x ) { return tanhf( x ); }217 // extern "C" { double tanh( double ); }218 long double tanh( long double x ) { return tanhl( x ); }219 float _Complex tanh( float _Complex x ) { return ctanhf( x ); }220 double _Complex tanh( double _Complex x ) { return ctanh( x ); }221 long double _Complex tanh( long double _Complex x ) { return ctanhl( x ); }222 223 float asinh( float x ) { return asinhf( x ); }224 // extern "C" { double asinh( double ); }225 long double asinh( long double x ) { return asinhl( x ); }226 float _Complex asinh( float _Complex x ) { return casinhf( x ); }227 double _Complex asinh( double _Complex x ) { return casinh( x ); }228 long double _Complex asinh( long double _Complex x ) { return casinhl( x ); }229 230 float acosh( float x ) { return acoshf( x ); }231 // extern "C" { double acosh( double ); }232 long double acosh( long double x ) { return acoshl( x ); }233 float _Complex acosh( float _Complex x ) { return cacoshf( x ); }234 double _Complex acosh( double _Complex x ) { return cacosh( x ); }235 long double _Complex acosh( long double _Complex x ) { return cacoshl( x ); }236 237 float atanh( float x ) { return atanhf( x ); }238 // extern "C" { double atanh( double ); }239 long double atanh( long double x ) { return atanhl( x ); }240 float _Complex atanh( float _Complex x ) { return catanhf( x ); }241 double _Complex atanh( double _Complex x ) { return catanh( x ); }242 long double _Complex atanh( long double _Complex x ) { return catanhl( x ); }243 } // distribution244 245 //---------------------- Error / Gamma ----------------------246 247 static inline {248 float erf( float x ) { return erff( x ); }249 // extern "C" { double erf( double ); }250 long double erf( long double x ) { return erfl( x ); }251 // float _Complex erf( float _Complex );252 // double _Complex erf( double _Complex );253 // long double _Complex erf( long double _Complex );254 255 float erfc( float x ) { return erfcf( x ); }256 // extern "C" { double erfc( double ); }257 long double erfc( long double x ) { return erfcl( x ); }258 // float _Complex erfc( float _Complex );259 // double _Complex erfc( double _Complex );260 // long double _Complex erfc( long double _Complex );261 262 float lgamma( float x ) { return lgammaf( x ); }263 // extern "C" { double lgamma( double ); }264 long double lgamma( long double x ) { return lgammal( x ); }265 float lgamma( float x, int * sign ) { return lgammaf_r( x, sign ); }266 double lgamma( double x, int * sign ) { return lgamma_r( x, sign ); }267 long double lgamma( long double x, int * sign ) { return lgammal_r( x, sign ); }268 269 float tgamma( float x ) { return tgammaf( x ); }270 // extern "C" { double tgamma( double ); }271 long double tgamma( long double x ) { return tgammal( x ); }272 } // distribution273 274 //---------------------- Nearest Integer ----------------------275 276 static inline {277 signed char floor( signed char n, signed char align ) { return n / align * align; }278 unsigned char floor( unsigned char n, unsigned char align ) { return n / align * align; }279 short int floor( short int n, short int align ) { return n / align * align; }280 unsigned short int floor( unsigned short int n, unsigned short int align ) { return n / align * align; }281 int floor( int n, int align ) { return n / align * align; }282 unsigned int floor( unsigned int n, unsigned int align ) { return n / align * align; }283 long int floor( long int n, long int align ) { return n / align * align; }284 unsigned long int floor( unsigned long int n, unsigned long int align ) { return n / align * align; }285 long long int floor( long long int n, long long int align ) { return n / align * align; }286 unsigned long long int floor( unsigned long long int n, unsigned long long int align ) { return n / align * align; }287 288 // forall( otype T | { T ?/?( T, T ); T ?*?( T, T ); } )289 // T floor( T n, T align ) { return n / align * align; }290 291 signed char ceiling_div( signed char n, char align ) { return (n + (align - 1)) / align; }292 unsigned char ceiling_div( unsigned char n, unsigned char align ) { return (n + (align - 1)) / align; }293 short int ceiling_div( short int n, short int align ) { return (n + (align - 1)) / align; }294 unsigned short int ceiling_div( unsigned short int n, unsigned short int align ) { return (n + (align - 1)) / align; }295 int ceiling_div( int n, int align ) { return (n + (align - 1)) / align; }296 unsigned int ceiling_div( unsigned int n, unsigned int align ) { return (n + (align - 1)) / align; }297 long int ceiling_div( long int n, long int align ) { return (n + (align - 1)) / align; }298 unsigned long int ceiling_div( unsigned long int n, unsigned long int align ) { return (n + (align - 1)) / align; }299 long long int ceiling_div( long long int n, long long int align ) { return (n + (align - 1)) / align; }300 unsigned long long int ceiling_div( unsigned long long int n, unsigned long long int align ) { return (n + (align - 1)) / align; }301 302 // forall( otype T | { T ?+?( T, T ); T ?-?( T, T ); T ?%?( T, T ); } )303 // T ceiling_div( T n, T align ) { verify( is_pow2( align ) );return (n + (align - 1)) / align; }304 305 // gcc notices the div/mod pair and saves both so only one div.306 signed char ceiling( signed char n, signed char align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }307 unsigned char ceiling( unsigned char n, unsigned char align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }308 short int ceiling( short int n, short int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }309 unsigned short int ceiling( unsigned short int n, unsigned short int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }310 int ceiling( int n, int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }311 unsigned int ceiling( unsigned int n, unsigned int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }312 long int ceiling( long int n, long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }313 unsigned long int ceiling( unsigned long int n, unsigned long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0) , align); }314 long long int ceiling( long long int n, long long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }315 unsigned long long int ceiling( unsigned long long int n, unsigned long long int align ) { return floor( n + (n % align != 0 ? align - 1 : 0), align ); }316 317 // forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T ); T ?/?( T, T ); } )318 // T ceiling( T n, T align ) { return return floor( n + (n % align != 0 ? align - 1 : 0), align ); *}319 320 float floor( float x ) { return floorf( x ); }321 // extern "C" { double floor( double ); }322 long double floor( long double x ) { return floorl( x ); }323 324 float ceil( float x ) { return ceilf( x ); }325 // extern "C" { double ceil( double ); }326 long double ceil( long double x ) { return ceill( x ); }327 328 float trunc( float x ) { return truncf( x ); }329 // extern "C" { double trunc( double ); }330 long double trunc( long double x ) { return truncl( x ); }331 332 float rint( float x ) { return rintf( x ); }333 // extern "C" { double rint( double x ); }334 long double rint( long double x ) { return rintl( x ); }335 long int rint( float x ) { return lrintf( x ); }336 long int rint( double x ) { return lrint( x ); }337 long int rint( long double x ) { return lrintl( x ); }338 long long int rint( float x ) { return llrintf( x ); }339 long long int rint( double x ) { return llrint( x ); }340 long long int rint( long double x ) { return llrintl( x ); }341 342 long int lrint( float x ) { return lrintf( x ); }343 // extern "C" { long int lrint( double ); }344 long int lrint( long double x ) { return lrintl( x ); }345 long long int llrint( float x ) { return llrintf( x ); }346 // extern "C" { long long int llrint( double ); }347 long long int llrint( long double x ) { return llrintl( x ); }348 349 float nearbyint( float x ) { return nearbyintf( x ); }350 // extern "C" { double nearbyint( double ); }351 long double nearbyint( long double x ) { return nearbyintl( x ); }352 353 float round( float x ) { return roundf( x ); }354 // extern "C" { double round( double x ); }355 long double round( long double x ) { return roundl( x ); }356 long int round( float x ) { return lroundf( x ); }357 long int round( double x ) { return lround( x ); }358 long int round( long double x ) { return lroundl( x ); }359 long long int round( float x ) { return llroundf( x ); }360 long long int round( double x ) { return llround( x ); }361 long long int round( long double x ) { return llroundl( x ); }362 363 long int lround( float x ) { return lroundf( x ); }364 // extern "C" { long int lround( double ); }365 long int lround( long double x ) { return lroundl( x ); }366 long long int llround( float x ) { return llroundf( x ); }367 // extern "C" { long long int llround( double ); }368 long long int llround( long double x ) { return llroundl( x ); }369 } // distribution370 371 //---------------------- Manipulation ----------------------372 373 static inline {374 float copysign( float x, float y ) { return copysignf( x, y ); }375 // extern "C" { double copysign( double, double ); }376 long double copysign( long double x, long double y ) { return copysignl( x, y ); }377 378 float frexp( float x, int * ip ) { return frexpf( x, ip ); }379 // extern "C" { double frexp( double, int * ); }380 long double frexp( long double x, int * ip ) { return frexpl( x, ip ); }381 382 float ldexp( float x, int exp2 ) { return ldexpf( x, exp2 ); }383 // extern "C" { double ldexp( double, int ); }384 long double ldexp( long double x, int exp2 ) { return ldexpl( x, exp2 ); }385 386 [ float, float ] modf( float x ) { float i; x = modff( x, &i ); return [ i, x ]; }387 float modf( float x, float * i ) { return modff( x, i ); }388 [ double, double ] modf( double x ) { double i; x = modf( x, &i ); return [ i, x ]; }389 // extern "C" { double modf( double, double * ); }390 [ long double, long double ] modf( long double x ) { long double i; x = modfl( x, &i ); return [ i, x ]; }391 long double modf( long double x, long double * i ) { return modfl( x, i ); }392 393 float nextafter( float x, float y ) { return nextafterf( x, y ); }394 // extern "C" { double nextafter( double, double ); }395 long double nextafter( long double x, long double y ) { return nextafterl( x, y ); }396 397 float nexttoward( float x, long double y ) { return nexttowardf( x, y ); }398 // extern "C" { double nexttoward( double, long double ); }399 long double nexttoward( long double x, long double y ) { return nexttowardl( x, y ); }400 401 float scalbn( float x, int exp ) { return scalbnf( x, exp ); }402 // extern "C" { double scalbn( double, int ); }403 long double scalbn( long double x, int exp ) { return scalbnl( x, exp ); }404 float scalbn( float x, long int exp ) { return scalblnf( x, exp ); }405 double scalbn( double x, long int exp ) { return scalbln( x, exp ); }406 long double scalbn( long double x, long int exp ) { return scalblnl( x, exp ); }407 408 float scalbln( float x, long int exp ) { return scalblnf( x, exp ); }409 // extern "C" { double scalbln( double, long int ); }410 long double scalbln( long double x, long int exp ) { return scalblnl( x, exp ); }411 } // distribution412 413 354 //--------------------------------------- 414 355 415 static inline { 416 forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } ) 417 T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; } 418 419 forall( otype T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } ) 420 T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; } 421 422 forall( otype T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } ) 423 T smoothstep( T edge0, T edge1, T x ) { T t = clamp( (x - edge0) / (edge1 - edge0), (T){0}, (T){1} ); return t * t * ((T){3} - (T){2} * t); } 424 } // distribution 356 forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } ) 357 T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; } 358 359 forall( otype T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } ) 360 T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; } 361 362 forall( otype T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } ) 363 T smoothstep( T edge0, T edge1, T x ) { T t = clamp( (x - edge0) / (edge1 - edge0), (T){0}, (T){1} ); return t * t * ((T){3} - (T){2} * t); } 425 364 426 365 // Local Variables: // -
libcfa/src/rational.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Apr 6 17:54:28 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 8 17:56:36 202013 // Update Count : 18 712 // Last Modified On : Fri Jul 12 18:12:08 2019 13 // Update Count : 184 14 14 // 15 15 … … 56 56 } // rational 57 57 58 void ?{}( Rational(RationalImpl) & r, zero_t ) {59 r{ (RationalImpl){0}, (RationalImpl){1} };60 } // rational61 62 void ?{}( Rational(RationalImpl) & r, one_t ) {63 r{ (RationalImpl){1}, (RationalImpl){1} };64 } // rational65 58 66 59 // getter for numerator/denominator -
libcfa/src/startup.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Jul 24 16:21:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 13:03:18 202013 // Update Count : 3012 // Last Modified On : Fri Dec 13 13:16:45 2019 13 // Update Count : 29 14 14 // 15 15 16 #include <time.h> // tzset 17 #include <locale.h> // setlocale 16 #include <time.h> // tzset 18 17 #include "startup.hfa" 19 18 … … 22 21 void __cfaabi_appready_startup( void ) { 23 22 tzset(); // initialize time global variables 24 setlocale(LC_NUMERIC, "");25 23 #ifdef __CFA_DEBUG__ 26 24 extern void heapAppStart(); … … 43 41 struct __spinlock_t; 44 42 extern "C" { 45 void __cfaabi_dbg_record _lock(struct __spinlock_t & this, const char prev_name[]) __attribute__(( weak )) {}43 void __cfaabi_dbg_record(struct __spinlock_t & this, const char * prev_name) __attribute__(( weak )) {} 46 44 } 47 45 -
libcfa/src/stdhdr/assert.h
reef8dfb rbdfc032 10 10 // Created On : Mon Jul 4 23:25:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 12:58:49 202013 // Update Count : 1 512 // Last Modified On : Mon Jul 31 23:09:32 2017 13 // Update Count : 13 14 14 // 15 15 … … 27 27 #define assertf( expr, fmt, ... ) ((expr) ? ((void)0) : __assert_fail_f(__VSTRINGIFY__(expr), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, ## __VA_ARGS__ )) 28 28 29 void __assert_fail_f( const char assertion[], const char file[], unsigned int line, const char function[], const char fmt[], ... ) __attribute__((noreturn, format( printf, 5, 6) ));29 void __assert_fail_f( const char *assertion, const char *file, unsigned int line, const char *function, const char *fmt, ... ) __attribute__((noreturn, format( printf, 5, 6) )); 30 30 #endif 31 31 … … 33 33 #define verify(x) assert(x) 34 34 #define verifyf(x, ...) assertf(x, __VA_ARGS__) 35 #define verifyfail(...)36 35 #define __CFA_WITH_VERIFY__ 37 36 #else 38 37 #define verify(x) 39 38 #define verifyf(x, ...) 40 #define verifyfail(...)41 39 #endif 42 40 -
libcfa/src/stdhdr/bfdlink.h
reef8dfb rbdfc032 10 10 // Created On : Tue Jul 18 07:26:04 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 7 19:05:08202013 // Update Count : 612 // Last Modified On : Sat Feb 1 07:15:29 2020 13 // Update Count : 5 14 14 // 15 15 16 16 // include file uses the CFA keyword "with". 17 17 #if ! defined( with ) // nesting ? 18 #define with ``with // make keyword an identifier18 #define with ``with`` // make keyword an identifier 19 19 #define __CFA_BFDLINK_H__ 20 20 #endif -
libcfa/src/stdhdr/hwloc.h
reef8dfb rbdfc032 10 10 // Created On : Tue Jul 18 07:45:00 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 7 19:05:18202013 // Update Count : 612 // Last Modified On : Sat Feb 1 07:15:39 2020 13 // Update Count : 5 14 14 // 15 15 16 16 // include file uses the CFA keyword "thread". 17 17 #if ! defined( thread ) // nesting ? 18 #define thread ``thread // make keyword an identifier18 #define thread ``thread`` // make keyword an identifier 19 19 #define __CFA_HWLOC_H__ 20 20 #endif -
libcfa/src/stdhdr/krb5.h
reef8dfb rbdfc032 10 10 // Created On : Tue Jul 18 07:55:44 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 7 19:05:35202013 // Update Count : 612 // Last Modified On : Sat Feb 1 07:15:47 2020 13 // Update Count : 5 14 14 // 15 15 16 16 // include file uses the CFA keyword "enable". 17 17 #if ! defined( enable ) // nesting ? 18 #define enable ``enable // make keyword an identifier18 #define enable ``enable`` // make keyword an identifier 19 19 #define __CFA_KRB5_H__ 20 20 #endif -
libcfa/src/stdhdr/malloc.h
reef8dfb rbdfc032 10 10 // Created On : Thu Jul 20 15:58:16 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed May 27 14:13:14 202013 // Update Count : 1 812 // Last Modified On : Sat Aug 11 09:06:31 2018 13 // Update Count : 10 14 14 // 15 16 17 size_t default_mmap_start(); // CFA extras 18 size_t default_heap_expansion(); 19 20 bool traceHeap(); 21 bool traceHeapOn(); 22 bool traceHeapOff(); 23 24 bool traceHeapTerm(); 25 bool traceHeapTermOn(); 26 bool traceHeapTermOff(); 27 28 bool checkFree(); 29 bool checkFreeOn(); 30 bool checkFreeOff(); 31 32 extern "C" { 33 size_t malloc_alignment( void * ); 34 bool malloc_zero_fill( void * ); 35 int malloc_stats_fd( int fd ); 36 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ); 37 } // extern "C" 15 38 16 39 extern "C" { 17 40 #include_next <malloc.h> // has internal check for multiple expansion 18 41 } // extern "C" 19 20 #include <heap.hfa>21 42 22 43 // Local Variables: // -
libcfa/src/stdhdr/math.h
reef8dfb rbdfc032 10 10 // Created On : Mon Jul 4 23:25:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 7 19:05:27202013 // Update Count : 1 512 // Last Modified On : Sat Feb 1 07:15:58 2020 13 // Update Count : 14 14 14 // 15 15 16 16 extern "C" { 17 17 #if ! defined( exception ) // nesting ? 18 #define exception ``exception // make keyword an identifier18 #define exception ``exception`` // make keyword an identifier 19 19 #define __CFA_MATH_H__ 20 20 #endif -
libcfa/src/stdlib.cfa
reef8dfb rbdfc032 10 10 // Created On : Thu Jan 28 17:10:29 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Nov 12 07:46:09 202013 // Update Count : 50312 // Last Modified On : Wed Nov 20 17:22:47 2019 13 // Update Count : 485 14 14 // 15 15 … … 20 20 #define _XOPEN_SOURCE 600 // posix_memalign, *rand48 21 21 #include <string.h> // memcpy, memset 22 #include <malloc.h> // malloc_usable_size 22 23 //#include <math.h> // fabsf, fabs, fabsl 23 24 #include <complex.h> // _Complex_I … … 26 27 //--------------------------------------- 27 28 28 // Cforall allocation/deallocation and constructor/destructor, array types 29 30 forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) 31 T * anew( size_t dim, TT p ) { 29 forall( dtype T | sized(T) ) { 30 T * alloc_set( T ptr[], size_t dim, char fill ) { // realloc array with fill 31 size_t olen = malloc_usable_size( ptr ); // current allocation 32 void * nptr = (void *)realloc( (void *)ptr, dim * sizeof(T) ); // C realloc 33 size_t nlen = malloc_usable_size( nptr ); // new allocation 34 if ( nlen > olen ) { // larger ? 35 memset( (char *)nptr + olen, (int)fill, nlen - olen ); // initialize added storage 36 } // if 37 return (T *)nptr; 38 } // alloc_set 39 40 T * alloc_align_set( T ptr[], size_t align, char fill ) { // aligned realloc with fill 41 size_t olen = malloc_usable_size( ptr ); // current allocation 42 void * nptr = (void *)realloc( (void *)ptr, align, sizeof(T) ); // CFA realloc 43 // char * nptr = alloc_align( ptr, align ); 44 size_t nlen = malloc_usable_size( nptr ); // new allocation 45 if ( nlen > olen ) { // larger ? 46 memset( (char *)nptr + olen, (int)fill, nlen - olen ); // initialize added storage 47 } // if 48 return (T *)nptr; 49 } // alloc_align_set 50 } // distribution 51 52 // allocation/deallocation and constructor/destructor, non-array types 53 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) 54 T * new( Params p ) { 55 return &(*malloc()){ p }; // run constructor 56 } // new 57 58 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) 59 void delete( T * ptr ) { 60 if ( ptr ) { // ignore null 61 ^(*ptr){}; // run destructor 62 free( ptr ); 63 } // if 64 } // delete 65 66 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } ) 67 void delete( T * ptr, Params rest ) { 68 if ( ptr ) { // ignore null 69 ^(*ptr){}; // run destructor 70 free( ptr ); 71 } // if 72 delete( rest ); 73 } // delete 74 75 76 // allocation/deallocation and constructor/destructor, array types 77 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) 78 T * anew( size_t dim, Params p ) { 32 79 T * arr = alloc( dim ); 33 80 for ( unsigned int i = 0; i < dim; i += 1 ) { … … 38 85 39 86 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) 40 void adelete( T arr[] ) {87 void adelete( size_t dim, T arr[] ) { 41 88 if ( arr ) { // ignore null 42 size_t dim = malloc_size( arr ) / sizeof( T );43 89 for ( int i = dim - 1; i >= 0; i -= 1 ) { // reverse allocation order, must be unsigned 44 90 ^(arr[i]){}; // run destructor … … 48 94 } // adelete 49 95 50 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT | { void adelete( TT); } )51 void adelete( T arr[], TTrest ) {96 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } ) 97 void adelete( size_t dim, T arr[], Params rest ) { 52 98 if ( arr ) { // ignore null 53 size_t dim = malloc_size( arr ) / sizeof( T );54 99 for ( int i = dim - 1; i >= 0; i -= 1 ) { // reverse allocation order, must be unsigned 55 100 ^(arr[i]){}; // run destructor … … 62 107 //--------------------------------------- 63 108 64 float _Complex strto( const char sptr[], char ** eptr ) {109 float _Complex strto( const char * sptr, char ** eptr ) { 65 110 float re, im; 66 111 char * eeptr; … … 73 118 } // strto 74 119 75 double _Complex strto( const char sptr[], char ** eptr ) {120 double _Complex strto( const char * sptr, char ** eptr ) { 76 121 double re, im; 77 122 char * eeptr; … … 84 129 } // strto 85 130 86 long double _Complex strto( const char sptr[], char ** eptr ) {131 long double _Complex strto( const char * sptr, char ** eptr ) { 87 132 long double re, im; 88 133 char * eeptr; … … 210 255 extern "C" { // override C version 211 256 void srandom( unsigned int seed ) { srand48( (long int)seed ); } 212 long int random( void ) { return mrand48(); } // GENERATES POSITIVE AND NEGATIVE VALUES257 long int random( void ) { return mrand48(); } 213 258 } // extern "C" 214 259 -
libcfa/src/stdlib.hfa
reef8dfb rbdfc032 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Dec 12 13:52:34 202013 // Update Count : 53612 // Last Modified On : Fri Nov 29 23:08:02 2019 13 // Update Count : 400 14 14 // 15 15 16 16 #pragma once 17 17 18 #include "bits/defs.hfa" // OPTIONAL_THREAD19 #include "bits/align.hfa" // libAlign18 #include "bits/defs.hfa" 19 #include "bits/align.hfa" 20 20 21 21 #include <stdlib.h> // *alloc, strto*, ato* 22 #include <heap.hfa> 23 24 // Reduce includes by explicitly defining these routines. 22 25 23 extern "C" { 26 void * memalign( size_t alignment, size_t size ); // malloc.h 27 void * pvalloc( size_t size ); // malloc.h 24 void * memalign( size_t align, size_t size ); // malloc.h 28 25 void * memset( void * dest, int fill, size_t size ); // string.h 29 26 void * memcpy( void * dest, const void * src, size_t size ); // string.h 27 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ); // CFA heap 30 28 } // extern "C" 29 30 void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap 31 31 32 32 //--------------------------------------- … … 39 39 //--------------------------------------- 40 40 41 #include "common.hfa" 42 43 //--------------------------------------- 44 45 // Macro because of returns 46 #define $ARRAY_ALLOC( allocation, alignment, dim ) \ 47 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)allocation( dim, (size_t)sizeof(T) ); /* C allocation */ \ 48 else return (T *)alignment( _Alignof(T), dim, sizeof(T) ) 49 50 static inline forall( dtype T | sized(T) ) { 51 // CFA safe equivalents, i.e., implicit size specification 41 static inline forall( dtype T | sized(T) ) { 42 // C dynamic allocation 52 43 53 44 T * malloc( void ) { 54 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( (size_t)sizeof(T) ); // C allocation45 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( (size_t)sizeof(T) ); // C malloc 55 46 else return (T *)memalign( _Alignof(T), sizeof(T) ); 56 47 } // malloc 57 48 58 T * aalloc( size_t dim ) {59 $ARRAY_ALLOC( aalloc, amemalign, dim );60 } // aalloc61 62 49 T * calloc( size_t dim ) { 63 $ARRAY_ALLOC( calloc, cmemalign, dim ); 50 if ( _Alignof(T) <= libAlign() )return (T *)(void *)calloc( dim, sizeof(T) ); // C calloc 51 else return (T *)cmemalign( _Alignof(T), dim, sizeof(T) ); 64 52 } // calloc 65 53 66 T * resize( T * ptr, size_t size ) { // CFA resize, eliminate return-type cast67 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)resize( (void *)ptr, size ); // CFA resize68 else return (T *)(void *)resize( (void *)ptr, _Alignof(T), size ); // CFA resize69 } // resize70 71 54 T * realloc( T * ptr, size_t size ) { // CFA realloc, eliminate return-type cast 72 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)realloc( (void *)ptr, size ); // C realloc 73 else return (T *)(void *)realloc( (void *)ptr, _Alignof(T), size ); // CFA realloc 55 return (T *)(void *)realloc( (void *)ptr, size ); // C realloc 74 56 } // realloc 75 57 … … 78 60 } // memalign 79 61 80 T * amemalign( size_t align, size_t dim ) {81 return (T *)amemalign( align, dim, sizeof(T) ); // CFA amemalign82 } // amemalign83 84 62 T * cmemalign( size_t align, size_t dim ) { 85 63 return (T *)cmemalign( align, dim, sizeof(T) ); // CFA cmemalign … … 94 72 } // posix_memalign 95 73 96 T * valloc( void ) { 97 return (T *)valloc( sizeof(T) ); // C valloc 98 } // valloc 99 100 T * pvalloc( void ) { 101 return (T *)pvalloc( sizeof(T) ); // C pvalloc 102 } // pvalloc 103 } // distribution 104 105 /* 106 FIX ME : fix alloc interface after Ticker Number 214 is resolved, define and add union to S_fill. Then, modify postfix-fill functions to support T * with nmemb, char, and T object of any size. Finally, change alloc_internal. 107 Or, just follow the instructions below for that. 108 109 1. Replace the current forall-block that contains defintions of S_fill and S_realloc with following: 110 forall( dtype T | sized(T) ) { 111 union U_fill { char c; T * a; T t; }; 112 struct S_fill { char tag; U_fill(T) fill; }; 113 struct S_realloc { inline T *; }; 114 } 115 116 2. Replace all current postfix-fill functions with following for updated S_fill: 117 S_fill(T) ?`fill( char a ) { S_fill(T) ret = {'c'}; ret.fill.c = a; return ret; } 118 S_fill(T) ?`fill( T a ) { S_fill(T) ret = {'t'}; memcpy(&ret.fill.t, &a, sizeof(T)); return ret; } 119 S_fill(T) ?`fill( T a[], size_t nmemb ) { S_fill(T) ret = {'a', nmemb}; ret.fill.a = a; return ret; } 120 121 3. Replace the $alloc_internal function which is outside ttype forall-block with following function: 122 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) { 123 T * ptr = NULL; 124 size_t size = sizeof(T); 125 size_t copy_end = 0; 126 127 if(Resize) { 128 ptr = (T*) (void *) resize( (int *)Resize, Align, Dim * size ); 129 } else if (Realloc) { 130 if (Fill.tag != '0') copy_end = min(malloc_size( Realloc ), Dim * size); 131 ptr = (T*) (void *) realloc( (int *)Realloc, Align, Dim * size ); 132 } else { 133 ptr = (T*) (void *) memalign( Align, Dim * size ); 134 } 135 136 if(Fill.tag == 'c') { 137 memset( (char *)ptr + copy_end, (int)Fill.fill.c, Dim * size - copy_end ); 138 } else if(Fill.tag == 't') { 139 for ( int i = copy_end; i <= Dim * size - size ; i += size ) { 140 memcpy( (char *)ptr + i, &Fill.fill.t, size ); 141 } 142 } else if(Fill.tag == 'a') { 143 memcpy( (char *)ptr + copy_end, Fill.fill.a, min(Dim * size - copy_end, size * Fill.nmemb) ); 144 } 145 146 return ptr; 147 } // $alloc_internal 148 */ 149 150 typedef struct S_align { inline size_t; } T_align; 151 typedef struct S_resize { inline void *; } T_resize; 152 153 forall( dtype T ) { 154 struct S_fill { char tag; char c; size_t size; T * at; char t[50]; }; 155 struct S_realloc { inline T *; }; 156 } 157 158 static inline T_align ?`align ( size_t a ) { return (T_align){a}; } 159 static inline T_resize ?`resize ( void * a ) { return (T_resize){a}; } 160 161 static inline forall( dtype T | sized(T) ) { 162 S_fill(T) ?`fill ( T t ) { 163 S_fill(T) ret = { 't' }; 164 size_t size = sizeof(T); 165 if(size > sizeof(ret.t)) { printf("ERROR: const object of size greater than 50 bytes given for dynamic memory fill\n"); exit(1); } 166 memcpy( &ret.t, &t, size ); 167 return ret; 168 } 169 S_fill(T) ?`fill ( char c ) { return (S_fill(T)){ 'c', c }; } 170 S_fill(T) ?`fill ( T * a ) { return (S_fill(T)){ 'T', '0', 0, a }; } 171 S_fill(T) ?`fill ( T a[], size_t nmemb ) { return (S_fill(T)){ 'a', '0', nmemb * sizeof(T), a }; } 172 173 S_realloc(T) ?`realloc ( T * a ) { return (S_realloc(T)){a}; } 174 175 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) { 176 T * ptr = NULL; 177 size_t size = sizeof(T); 178 size_t copy_end = 0; 179 180 if ( Resize ) { 181 ptr = (T*) (void *) resize( (void *)Resize, Align, Dim * size ); 182 } else if ( Realloc ) { 183 if (Fill.tag != '0') copy_end = min(malloc_size( Realloc ), Dim * size); 184 ptr = (T*) (void *) realloc( (void *)Realloc, Align, Dim * size ); 185 } else { 186 ptr = (T*) (void *) memalign( Align, Dim * size ); 187 } 188 189 if(Fill.tag == 'c') { 190 memset( (char *)ptr + copy_end, (int)Fill.c, Dim * size - copy_end ); 191 } else if(Fill.tag == 't') { 192 for ( int i = copy_end; i < Dim * size; i += size ) { 193 memcpy( (char *)ptr + i, &Fill.t, size ); 194 } 195 } else if(Fill.tag == 'a') { 196 memcpy( (char *)ptr + copy_end, Fill.at, min(Dim * size - copy_end, Fill.size) ); 197 } else if(Fill.tag == 'T') { 198 for ( int i = copy_end; i < Dim * size; i += size ) { 199 memcpy( (char *)ptr + i, Fill.at, size ); 200 } 201 } 202 203 return ptr; 204 } // $alloc_internal 205 206 forall( ttype TT | { T * $alloc_internal( void *, T *, size_t, size_t, S_fill(T), TT ); } ) { 207 208 T * $alloc_internal( void * , T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest) { 209 return $alloc_internal( Resize, (T*)0p, Align, Dim, Fill, rest); 210 } 211 212 T * $alloc_internal( void * Resize, T * , size_t Align, size_t Dim, S_fill(T) Fill, S_realloc(T) Realloc, TT rest) { 213 return $alloc_internal( (void*)0p, Realloc, Align, Dim, Fill, rest); 214 } 215 216 T * $alloc_internal( void * Resize, T * Realloc, size_t , size_t Dim, S_fill(T) Fill, T_align Align, TT rest) { 217 return $alloc_internal( Resize, Realloc, Align, Dim, Fill, rest); 218 } 219 220 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) , S_fill(T) Fill, TT rest) { 221 return $alloc_internal( Resize, Realloc, Align, Dim, Fill, rest); 222 } 223 224 T * alloc( TT all ) { 225 return $alloc_internal( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (size_t)1, (S_fill(T)){'0'}, all); 226 } 227 228 T * alloc( size_t dim, TT all ) { 229 return $alloc_internal( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), dim, (S_fill(T)){'0'}, all); 230 } 231 232 } // distribution TT 233 } // distribution T 234 235 static inline forall( dtype T | sized(T) ) { 236 // CFA safe initialization/copy, i.e., implicit size specification, non-array types 74 // Cforall dynamic allocation 75 76 T * alloc( void ) { 77 return malloc(); 78 } // alloc 79 80 T * alloc( size_t dim ) { 81 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( dim * (size_t)sizeof(T) ); 82 else return (T *)memalign( _Alignof(T), dim * sizeof(T) ); 83 } // alloc 84 85 T * alloc( T ptr[], size_t dim ) { // realloc 86 return (T *)(void *)realloc( (void *)ptr, dim * sizeof(T) ); // C realloc 87 } // alloc 88 89 T * alloc_set( char fill ) { 90 return (T *)memset( (T *)alloc(), (int)fill, sizeof(T) ); // initialize with fill value 91 } // alloc 92 93 T * alloc_set( T fill ) { 94 return (T *)memcpy( (T *)alloc(), &fill, sizeof(T) ); // initialize with fill value 95 } // alloc 96 97 T * alloc_set( size_t dim, char fill ) { 98 return (T *)memset( (T *)alloc( dim ), (int)fill, dim * sizeof(T) ); // initialize with fill value 99 } // alloc 100 101 T * alloc_set( size_t dim, T fill ) { 102 T * r = (T *)alloc( dim ); 103 for ( i; dim ) { memcpy( &r[i], &fill, sizeof(T) ); } // initialize with fill value 104 return r; 105 } // alloc 106 107 T * alloc_set( size_t dim, const T fill[] ) { 108 return (T *)memcpy( (T *)alloc( dim ), fill, dim * sizeof(T) ); // initialize with fill value 109 } // alloc 110 } // distribution 111 112 forall( dtype T | sized(T) ) { 113 T * alloc_set( T ptr[], size_t dim, char fill ); // realloc array with fill 114 } // distribution 115 116 static inline forall( dtype T | sized(T) ) { 117 T * alloc_align( size_t align ) { 118 return (T *)memalign( align, sizeof(T) ); 119 } // alloc_align 120 121 T * alloc_align( size_t align, size_t dim ) { 122 return (T *)memalign( align, dim * sizeof(T) ); 123 } // alloc_align 124 125 T * alloc_align( T ptr[], size_t align ) { // aligned realloc array 126 return (T *)(void *)realloc( (void *)ptr, align, sizeof(T) ); // CFA realloc 127 } // alloc_align 128 129 T * alloc_align( T ptr[], size_t align, size_t dim ) { // aligned realloc array 130 return (T *)(void *)realloc( (void *)ptr, align, dim * sizeof(T) ); // CFA realloc 131 } // alloc_align 132 133 T * alloc_align_set( size_t align, char fill ) { 134 return (T *)memset( (T *)alloc_align( align ), (int)fill, sizeof(T) ); // initialize with fill value 135 } // alloc_align 136 137 T * alloc_align_set( size_t align, T fill ) { 138 return (T *)memcpy( (T *)alloc_align( align ), &fill, sizeof(T) ); // initialize with fill value 139 } // alloc_align 140 141 T * alloc_align_set( size_t align, size_t dim, char fill ) { 142 return (T *)memset( (T *)alloc_align( align, dim ), (int)fill, dim * sizeof(T) ); // initialize with fill value 143 } // alloc_align 144 145 T * alloc_align_set( size_t align, size_t dim, T fill ) { 146 T * r = (T *)alloc_align( align, dim ); 147 for ( i; dim ) { memcpy( &r[i], &fill, sizeof(T) ); } // initialize with fill value 148 return r; 149 } // alloc_align 150 151 T * alloc_align_set( size_t align, size_t dim, const T fill[] ) { 152 return (T *)memcpy( (T *)alloc_align( align, dim ), fill, dim * sizeof(T) ); 153 } // alloc_align 154 } // distribution 155 156 forall( dtype T | sized(T) ) { 157 T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); // aligned realloc array with fill 158 } // distribution 159 160 static inline forall( dtype T | sized(T) ) { 161 // data, non-array types 237 162 T * memset( T * dest, char fill ) { 238 163 return (T *)memset( dest, fill, sizeof(T) ); … … 242 167 return (T *)memcpy( dest, src, sizeof(T) ); 243 168 } // memcpy 244 245 // CFA safe initialization/copy, i.e., implicit size specification, array types 169 } // distribution 170 171 static inline forall( dtype T | sized(T) ) { 172 // data, array types 246 173 T * amemset( T dest[], char fill, size_t dim ) { 247 174 return (T *)(void *)memset( dest, fill, dim * sizeof(T) ); // C memset … … 253 180 } // distribution 254 181 255 // CFA deallocation for multiple objects 256 static inline forall( dtype T ) // FIX ME, problems with 0p in list 257 void free( T * ptr ) { 258 free( (void *)ptr ); // C free 259 } // free 260 static inline forall( dtype T, ttype TT | { void free( TT ); } ) 261 void free( T * ptr, TT rest ) { 262 free( ptr ); 263 free( rest ); 264 } // free 265 266 // CFA allocation/deallocation and constructor/destructor, non-array types 267 static inline forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) 268 T * new( TT p ) { 269 return &(*(T *)malloc()){ p }; // run constructor 270 } // new 271 272 static inline forall( dtype T | { void ^?{}( T & ); } ) 273 void delete( T * ptr ) { 274 // special case for 0-sized object => always call destructor 275 if ( ptr || sizeof(ptr) == 0 ) { // ignore null but not 0-sized objects 276 ^(*ptr){}; // run destructor 277 } // if 278 free( ptr ); // always call free 279 } // delete 280 static inline forall( dtype T, ttype TT | { void ^?{}( T & ); void delete( TT ); } ) 281 void delete( T * ptr, TT rest ) { 282 delete( ptr ); 283 delete( rest ); 284 } // delete 285 286 // CFA allocation/deallocation and constructor/destructor, array types 287 forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) T * anew( size_t dim, TT p ); 288 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] ); 289 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT | { void adelete( TT ); } ) void adelete( T arr[], TT rest ); 182 // allocation/deallocation and constructor/destructor, non-array types 183 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p ); 184 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr ); 185 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } ) void delete( T * ptr, Params rest ); 186 187 // allocation/deallocation and constructor/destructor, array types 188 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p ); 189 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] ); 190 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } ) void adelete( size_t dim, T arr[], Params rest ); 290 191 291 192 //--------------------------------------- 292 193 293 194 static inline { 294 int strto( const char sptr[], char ** eptr, int base ) { return (int)strtol( sptr, eptr, base ); }295 unsigned int strto( const char sptr[], char ** eptr, int base ) { return (unsigned int)strtoul( sptr, eptr, base ); }296 long int strto( const char sptr[], char ** eptr, int base ) { return strtol( sptr, eptr, base ); }297 unsigned long int strto( const char sptr[], char ** eptr, int base ) { return strtoul( sptr, eptr, base ); }298 long long int strto( const char sptr[], char ** eptr, int base ) { return strtoll( sptr, eptr, base ); }299 unsigned long long int strto( const char sptr[], char ** eptr, int base ) { return strtoull( sptr, eptr, base ); }300 301 float strto( const char sptr[], char ** eptr ) { return strtof( sptr, eptr ); }302 double strto( const char sptr[], char ** eptr ) { return strtod( sptr, eptr ); }303 long double strto( const char sptr[], char ** eptr ) { return strtold( sptr, eptr ); }304 } // distribution 305 306 float _Complex strto( const char sptr[], char ** eptr );307 double _Complex strto( const char sptr[], char ** eptr );308 long double _Complex strto( const char sptr[], char ** eptr );195 int strto( const char * sptr, char ** eptr, int base ) { return (int)strtol( sptr, eptr, base ); } 196 unsigned int strto( const char * sptr, char ** eptr, int base ) { return (unsigned int)strtoul( sptr, eptr, base ); } 197 long int strto( const char * sptr, char ** eptr, int base ) { return strtol( sptr, eptr, base ); } 198 unsigned long int strto( const char * sptr, char ** eptr, int base ) { return strtoul( sptr, eptr, base ); } 199 long long int strto( const char * sptr, char ** eptr, int base ) { return strtoll( sptr, eptr, base ); } 200 unsigned long long int strto( const char * sptr, char ** eptr, int base ) { return strtoull( sptr, eptr, base ); } 201 202 float strto( const char * sptr, char ** eptr ) { return strtof( sptr, eptr ); } 203 double strto( const char * sptr, char ** eptr ) { return strtod( sptr, eptr ); } 204 long double strto( const char * sptr, char ** eptr ) { return strtold( sptr, eptr ); } 205 } // distribution 206 207 float _Complex strto( const char * sptr, char ** eptr ); 208 double _Complex strto( const char * sptr, char ** eptr ); 209 long double _Complex strto( const char * sptr, char ** eptr ); 309 210 310 211 static inline { 311 int ato( const char sptr[]) { return (int)strtol( sptr, 0p, 10 ); }312 unsigned int ato( const char sptr[]) { return (unsigned int)strtoul( sptr, 0p, 10 ); }313 long int ato( const char sptr[]) { return strtol( sptr, 0p, 10 ); }314 unsigned long int ato( const char sptr[]) { return strtoul( sptr, 0p, 10 ); }315 long long int ato( const char sptr[]) { return strtoll( sptr, 0p, 10 ); }316 unsigned long long int ato( const char sptr[]) { return strtoull( sptr, 0p, 10 ); }317 318 float ato( const char sptr[]) { return strtof( sptr, 0p ); }319 double ato( const char sptr[]) { return strtod( sptr, 0p ); }320 long double ato( const char sptr[]) { return strtold( sptr, 0p ); }321 322 float _Complex ato( const char sptr[]) { return strto( sptr, 0p ); }323 double _Complex ato( const char sptr[]) { return strto( sptr, 0p ); }324 long double _Complex ato( const char sptr[]) { return strto( sptr, 0p ); }212 int ato( const char * sptr ) { return (int)strtol( sptr, 0p, 10 ); } 213 unsigned int ato( const char * sptr ) { return (unsigned int)strtoul( sptr, 0p, 10 ); } 214 long int ato( const char * sptr ) { return strtol( sptr, 0p, 10 ); } 215 unsigned long int ato( const char * sptr ) { return strtoul( sptr, 0p, 10 ); } 216 long long int ato( const char * sptr ) { return strtoll( sptr, 0p, 10 ); } 217 unsigned long long int ato( const char * sptr ) { return strtoull( sptr, 0p, 10 ); } 218 219 float ato( const char * sptr ) { return strtof( sptr, 0p ); } 220 double ato( const char * sptr ) { return strtod( sptr, 0p ); } 221 long double ato( const char * sptr ) { return strtold( sptr, 0p ); } 222 223 float _Complex ato( const char * sptr ) { return strto( sptr, 0p ); } 224 double _Complex ato( const char * sptr ) { return strto( sptr, 0p ); } 225 long double _Complex ato( const char * sptr ) { return strto( sptr, 0p ); } 325 226 } // distribution 326 227 … … 353 254 extern "C" { // override C version 354 255 void srandom( unsigned int seed ); 355 long int random( void ); // GENERATES POSITIVE AND NEGATIVE VALUES 356 // For positive values, use unsigned int, e.g., unsigned int r = random() % 100U; 256 long int random( void ); 357 257 } // extern "C" 358 258 … … 361 261 long int random( long int u ) { if ( u < 0 ) return random( u, 0 ); else return random( 0, u ); } // [0,u) 362 262 unsigned long int random( void ) { return lrand48(); } 263 unsigned long int random( unsigned long int l, unsigned long int u ) { if ( u < l ) [u, l] = [l, u]; return lrand48() % (u - l) + l; } // [l,u) 363 264 unsigned long int random( unsigned long int u ) { return lrand48() % u; } // [0,u) 364 unsigned long int random( unsigned long int l, unsigned long int u ) { if ( u < l ) [u, l] = [l, u]; return lrand48() % (u - l) + l; } // [l,u)365 265 366 266 char random( void ) { return (unsigned long int)random(); } … … 383 283 //--------------------------------------- 384 284 385 extern bool threading_enabled( void ) OPTIONAL_THREAD; 285 #include "common.hfa" 286 287 //--------------------------------------- 288 289 extern bool threading_enabled(void) OPTIONAL_THREAD; 386 290 387 291 // Local Variables: // -
libcfa/src/time.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Mar 27 13:33:14 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 08:24:18202013 // Update Count : 7012 // Last Modified On : Sun Jan 5 17:27:40 2020 13 // Update Count : 69 14 14 // 15 15 … … 129 129 } // dd_mm_yy 130 130 131 size_t strftime( char buf[], size_t size, const char fmt[], Time time ) with( time ) {131 size_t strftime( char * buf, size_t size, const char * fmt, Time time ) with( time ) { 132 132 time_t s = tn / TIMEGRAN; 133 133 tm tm; -
libcfa/src/time.hfa
reef8dfb rbdfc032 10 10 // Created On : Wed Mar 14 23:18:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 17 16:13:00202013 // Update Count : 6 6312 // Last Modified On : Mon Jan 6 12:50:16 2020 13 // Update Count : 653 14 14 // 15 15 … … 20 20 21 21 #include <time.h> // timespec 22 extern "C" { 22 23 #include <sys/time.h> // timeval 24 } 23 25 #include <time_t.hfa> // Duration/Time types 24 26 … … 89 91 int64_t ?`w( Duration dur ) { return dur.tn / (7LL * 24LL * 60LL * 60LL * TIMEGRAN); } 90 92 91 double ?`dns( Duration dur ) { return dur.tn; }92 double ?`dus( Duration dur ) { return dur.tn / ((double)TIMEGRAN / 1_000_000.); }93 double ?`dms( Duration dur ) { return dur.tn / ((double)TIMEGRAN / 1_000.); }94 double ?`ds( Duration dur ) { return dur.tn / (double)TIMEGRAN; }95 double ?`dm( Duration dur ) { return dur.tn / (60. * TIMEGRAN); }96 double ?`dh( Duration dur ) { return dur.tn / (60. * 60. * (double)TIMEGRAN); }97 double ?`dd( Duration dur ) { return dur.tn / (24. * 60. * 60. * (double)TIMEGRAN); }98 double ?`dw( Duration dur ) { return dur.tn / (7. * 24. * 60. * 60. * (double)TIMEGRAN); }99 100 93 Duration max( Duration lhs, Duration rhs ) { return (lhs.tn < rhs.tn) ? rhs : lhs;} 101 94 Duration min( Duration lhs, Duration rhs ) { return !(rhs.tn < lhs.tn) ? lhs : rhs;} … … 198 191 } // dmy 199 192 200 size_t strftime( char buf[], size_t size, const char fmt[], Time time );193 size_t strftime( char * buf, size_t size, const char * fmt, Time time ); 201 194 202 195 //------------------------- timeval (cont) ------------------------- -
libcfa/src/vec/vec.hfa
reef8dfb rbdfc032 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // io/types.hfa --8 //9 // Author : Dimitry Kobets10 // Created On :11 // Last Modified By :12 // Last Modified On :13 // Update Count :14 //15 16 1 #pragma once 17 2 -
libcfa/src/vec/vec2.hfa
reef8dfb rbdfc032 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // io/types.hfa --8 //9 // Author : Dimitry Kobets10 // Created On :11 // Last Modified By :12 // Last Modified On :13 // Update Count :14 //15 16 1 #pragma once 17 2 -
libcfa/src/vec/vec3.hfa
reef8dfb rbdfc032 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // io/types.hfa --8 //9 // Author : Dimitry Kobets10 // Created On :11 // Last Modified By :12 // Last Modified On :13 // Update Count :14 //15 16 1 #pragma once 17 2 -
libcfa/src/vec/vec4.hfa
reef8dfb rbdfc032 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // io/types.hfa --8 //9 // Author : Dimitry Kobets10 // Created On :11 // Last Modified By :12 // Last Modified On :13 // Update Count :14 //15 16 1 #pragma once 17 2 -
longrun_tests/Makefile.am
reef8dfb rbdfc032 18 18 ACLOCAL_AMFLAGS = -I automake 19 19 20 include $(top_srcdir)/ tools/build/cfa.make20 include $(top_srcdir)/src/cfa.make 21 21 22 22 repeats=10 … … 44 44 -DTEST_$(shell cat .type | tr a-z A-Z) 45 45 46 TESTS = block coroutine create disjoint enter enter3 locksprocessor stack wait yield46 TESTS = block coroutine create disjoint enter enter3 processor stack wait yield 47 47 48 48 # .INTERMEDIATE: $(TESTS) -
src/AST/Attribute.hpp
reef8dfb rbdfc032 51 51 template<typename node_t> 52 52 friend node_t * mutate(const node_t * node); 53 template<typename node_t>54 friend node_t * shallowCopy(const node_t * node);55 53 }; 56 54 -
src/AST/CVQualifiers.hpp
reef8dfb rbdfc032 27 27 Restrict = 1 << 1, 28 28 Volatile = 1 << 2, 29 Mutex = 1 << 3, 30 Atomic = 1 << 4, 31 NumQualifiers = 5 29 Lvalue = 1 << 3, 30 Mutex = 1 << 4, 31 Atomic = 1 << 5, 32 NumQualifiers = 6 32 33 }; 33 34 34 35 /// Mask for equivalence-preserving qualfiers 35 enum { EquivQualifiers = ~ Restrict};36 enum { EquivQualifiers = ~(Restrict | Lvalue) }; 36 37 37 38 /// Underlying data for qualifiers … … 43 44 bool is_restrict : 1; 44 45 bool is_volatile : 1; 46 bool is_lvalue : 1; 45 47 bool is_mutex : 1; 46 48 bool is_atomic : 1; -
src/AST/Convert.cpp
reef8dfb rbdfc032 9 9 // Author : Thierry Delisle 10 10 // Created On : Thu May 09 15::37::05 2019 11 // Last Modified By : Andrew Beach12 // Last Modified On : Thr Nov 12 10:07:00 202013 // Update Count : 3 411 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Dec 11 21:39:32 2019 13 // Update Count : 33 14 14 // 15 15 … … 20 20 21 21 #include "AST/Attribute.hpp" 22 #include "AST/Copy.hpp"23 22 #include "AST/Decl.hpp" 24 23 #include "AST/Expr.hpp" 25 24 #include "AST/Init.hpp" 26 25 #include "AST/Stmt.hpp" 27 #include "AST/TranslationUnit.hpp"28 26 #include "AST/TypeSubstitution.hpp" 29 27 … … 48 46 49 47 //================================================================================================ 50 namespace ast{48 namespace { 51 49 52 50 // This is to preserve the FindSpecialDecls hack. It does not (and perhaps should not) 53 51 // allow us to use the same stratagy in the new ast. 54 // xxx - since convert back pass works, this concern seems to be unnecessary. 55 56 // these need to be accessed in new FixInit now 57 ast::ptr<ast::Type> sizeType = nullptr; 58 const ast::FunctionDecl * dereferenceOperator = nullptr; 59 const ast::StructDecl * dtorStruct = nullptr; 60 const ast::FunctionDecl * dtorStructDestroy = nullptr; 52 ast::Type * sizeType = nullptr; 53 ast::FunctionDecl * dereferenceOperator = nullptr; 54 ast::StructDecl * dtorStruct = nullptr; 55 ast::FunctionDecl * dtorStructDestroy = nullptr; 61 56 62 57 } … … 67 62 using Cache = std::unordered_map< const ast::Node *, BaseSyntaxNode * >; 68 63 Cache cache; 69 70 // Statements can no longer be shared.71 // however, since StmtExprResult is now implemented, need to still maintain72 // readonly references.73 Cache readonlyCache;74 64 75 65 template<typename T> … … 163 153 } 164 154 165 const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final { 155 const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final { 156 auto&& bfwd = get<Expression>().accept1( node->bitfieldWidth ); 157 auto&& type = get<Type>().accept1( node->type ); 158 auto&& init = get<Initializer>().accept1( node->init ); 159 auto&& attr = get<Attribute>().acceptL( node->attributes ); 166 160 if ( inCache( node ) ) { 167 161 return nullptr; 168 162 } 169 auto bfwd = get<Expression>().accept1( node->bitfieldWidth );170 auto type = get<Type>().accept1( node->type );171 auto attr = get<Attribute>().acceptL( node->attributes );172 173 163 auto decl = new ObjectDecl( 174 164 node->name, … … 176 166 LinkageSpec::Spec( node->linkage.val ), 177 167 bfwd, 178 type ->clone(),179 nullptr, // prevent infinite loop168 type, 169 init, 180 170 attr, 181 171 Type::FuncSpecifiers( node->funcSpec.val ) 182 172 ); 183 184 // handles the case where node->init references itself 185 // xxx - does it really happen? 186 declWithTypePostamble(decl, node); 187 auto init = get<Initializer>().accept1( node->init ); 188 decl->init = init; 189 190 this->node = decl; 191 return nullptr; 173 return declWithTypePostamble( decl, node ); 192 174 } 193 175 194 176 const ast::DeclWithType * visit( const ast::FunctionDecl * node ) override final { 195 177 if ( inCache( node ) ) return nullptr; 196 197 // function decl contains real variables that the type must use.198 // the structural change means function type in and out of decl199 // must be handled **differently** on convert back to old.200 auto ftype = new FunctionType(201 cv(node->type),202 (bool)node->type->isVarArgs203 );204 ftype->returnVals = get<DeclarationWithType>().acceptL(node->returns);205 ftype->parameters = get<DeclarationWithType>().acceptL(node->params);206 207 ftype->forall = get<TypeDecl>().acceptL( node->type_params );208 if (!node->assertions.empty()) {209 assert(!ftype->forall.empty());210 // find somewhere to place assertions back, for convenience it is the last slot211 ftype->forall.back()->assertions = get<DeclarationWithType>().acceptL(node->assertions);212 }213 214 visitType(node->type, ftype);215 216 178 auto decl = new FunctionDecl( 217 179 node->name, 218 180 Type::StorageClasses( node->storage.val ), 219 181 LinkageSpec::Spec( node->linkage.val ), 220 ftype, 221 //get<FunctionType>().accept1( node->type ), 182 get<FunctionType>().accept1( node->type ), 222 183 {}, 223 184 get<Attribute>().acceptL( node->attributes ), … … 227 188 decl->statements = get<CompoundStmt>().accept1( node->stmts ); 228 189 decl->withExprs = get<Expression>().acceptL( node->withExprs ); 229 if ( ast::dereferenceOperator == node ) {190 if ( dereferenceOperator == node ) { 230 191 Validate::dereferenceOperator = decl; 231 192 } 232 if ( ast::dtorStructDestroy == node ) {193 if ( dtorStructDestroy == node ) { 233 194 Validate::dtorStructDestroy = decl; 234 195 } … … 238 199 const ast::Decl * namedTypePostamble( NamedTypeDecl * decl, const ast::NamedTypeDecl * node ) { 239 200 // base comes from constructor 201 decl->parameters = get<TypeDecl>().acceptL( node->params ); 240 202 decl->assertions = get<DeclarationWithType>().acceptL( node->assertions ); 241 203 declPostamble( decl, node ); … … 288 250 ); 289 251 290 if ( ast::dtorStruct == node ) {252 if ( dtorStruct == node ) { 291 253 Validate::dtorStruct = decl; 292 254 } … … 341 303 342 304 const ast::Stmt * stmtPostamble( Statement * stmt, const ast::Stmt * node ) { 343 // force statements in old tree to be unique. 344 // cache.emplace( node, stmt ); 345 readonlyCache.emplace( node, stmt ); 305 cache.emplace( node, stmt ); 346 306 stmt->location = node->location; 347 307 stmt->labels = makeLabelL( stmt, node->labels ); … … 360 320 if ( inCache( node ) ) return nullptr; 361 321 auto stmt = new ExprStmt( nullptr ); 322 cache.emplace( node, stmt ); 362 323 stmt->expr = get<Expression>().accept1( node->expr ); 363 324 return stmtPostamble( stmt, node ); … … 532 493 } 533 494 534 const ast::Stmt * visit(const ast::SuspendStmt * node ) override final {535 if ( inCache( node ) ) return nullptr;536 auto stmt = new SuspendStmt();537 stmt->then = get<CompoundStmt>().accept1( node->then );538 switch(node->type) {539 case ast::SuspendStmt::None : stmt->type = SuspendStmt::None ; break;540 case ast::SuspendStmt::Coroutine: stmt->type = SuspendStmt::Coroutine; break;541 case ast::SuspendStmt::Generator: stmt->type = SuspendStmt::Generator; break;542 }543 return stmtPostamble( stmt, node );544 }545 546 495 const ast::Stmt * visit( const ast::WaitForStmt * node ) override final { 547 496 if ( inCache( node ) ) return nullptr; … … 607 556 608 557 for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) { 609 rslt->add( src_i->first .typeString(),558 rslt->add( src_i->first, 610 559 get<Type>().accept1(src_i->second) ); 560 } 561 562 for (decltype(src->beginVar()) src_i = src->beginVar(); src_i != src->endVar(); src_i++) { 563 rslt->addVar( src_i->first, 564 get<Expression>().accept1(src_i->second) ); 611 565 } 612 566 … … 621 575 assert( tgtResnSlots.empty() ); 622 576 623 if ( srcInferred. data.inferParams ) {577 if ( srcInferred.mode == ast::Expr::InferUnion::Params ) { 624 578 const ast::InferredParams &srcParams = srcInferred.inferParams(); 625 579 for (auto & srcParam : srcParams) { … … 627 581 srcParam.second.decl, 628 582 get<Declaration>().accept1(srcParam.second.declptr), 629 get<Type>().accept1(srcParam.second.actualType) ->clone(),630 get<Type>().accept1(srcParam.second.formalType) ->clone(),631 get<Expression>().accept1(srcParam.second.expr) ->clone()583 get<Type>().accept1(srcParam.second.actualType), 584 get<Type>().accept1(srcParam.second.formalType), 585 get<Expression>().accept1(srcParam.second.expr) 632 586 )); 633 587 assert(res.second); 634 588 } 635 } 636 if ( srcInferred.data.resnSlots ) { 589 } else if ( srcInferred.mode == ast::Expr::InferUnion::Slots ) { 637 590 const ast::ResnSlots &srcSlots = srcInferred.resnSlots(); 638 591 for (auto srcSlot : srcSlots) { … … 655 608 656 609 tgt->result = get<Type>().accept1(src->result); 657 // Unconditionally use a clone of the result type.658 // We know this will leak some objects: much of the immediate conversion result.659 // In some cases, using the conversion result directly gives unintended object sharing.660 // A parameter (ObjectDecl, a child of a FunctionType) is shared by the weak-ref cache.661 // But tgt->result must be fully owned privately by tgt.662 // Applying these conservative copies here means663 // - weak references point at the declaration's copy, not these expr.result copies (good)664 // - we copy more objects than really needed (bad, tolerated)665 if (tgt->result) {666 tgt->result = tgt->result->clone();667 }668 610 return visitBaseExpr_skipResultType(src, tgt); 669 611 } … … 738 680 new KeywordCastExpr( 739 681 get<Expression>().accept1(node->arg), 740 castTarget, 741 {node->concrete_target.field, node->concrete_target.getter} 682 castTarget 742 683 ) 743 684 ); … … 1026 967 1027 968 const ast::Expr * visit( const ast::StmtExpr * node ) override final { 1028 auto stmts = node->stmts;1029 // disable sharing between multiple StmtExprs explicitly.1030 // this should no longer be true.1031 1032 969 auto rslt = new StmtExpr( 1033 get<CompoundStmt>().accept1( stmts)970 get<CompoundStmt>().accept1(node->stmts) 1034 971 ); 1035 972 1036 973 rslt->returnDecls = get<ObjectDecl>().acceptL(node->returnDecls); 1037 974 rslt->dtors = get<Expression>().acceptL(node->dtors); 1038 if (node->resultExpr) {1039 // this MUST be found by children visit1040 rslt->resultExpr = strict_dynamic_cast<ExprStmt *>(readonlyCache.at(node->resultExpr));1041 }1042 975 1043 976 auto expr = visitBaseExpr( node, rslt ); … … 1056 989 1057 990 auto expr = visitBaseExpr( node, rslt ); 1058 this->node = expr ->clone();991 this->node = expr; 1059 992 return nullptr; 1060 993 } … … 1146 1079 auto type = new BasicType{ cv( node ), (BasicType::Kind)(unsigned)node->kind }; 1147 1080 // I believe this should always be a BasicType. 1148 if ( ast::sizeType == node ) {1081 if ( sizeType == node ) { 1149 1082 Validate::SizeType = type; 1150 1083 } … … 1188 1121 1189 1122 const ast::Type * visit( const ast::FunctionType * node ) override final { 1190 static std::string dummy_paramvar_prefix = "__param_";1191 static std::string dummy_returnvar_prefix = "__retval_";1192 1193 1123 auto ty = new FunctionType { 1194 1124 cv( node ), 1195 1125 (bool)node->isVarArgs 1196 1126 }; 1197 auto returns = get<Type>().acceptL(node->returns); 1198 auto params = get<Type>().acceptL(node->params); 1199 1200 int ret_index = 0; 1201 for (auto t: returns) { 1202 // xxx - LinkageSpec shouldn't matter but needs to be something 1203 ObjectDecl * dummy = new ObjectDecl(dummy_returnvar_prefix + std::to_string(ret_index++), {}, LinkageSpec::C, nullptr, t, nullptr); 1204 ty->returnVals.push_back(dummy); 1205 } 1206 int param_index = 0; 1207 for (auto t: params) { 1208 ObjectDecl * dummy = new ObjectDecl(dummy_paramvar_prefix + std::to_string(param_index++), {}, LinkageSpec::C, nullptr, t, nullptr); 1209 ty->parameters.push_back(dummy); 1210 } 1211 1212 // ty->returnVals = get<DeclarationWithType>().acceptL( node->returns ); 1213 // ty->parameters = get<DeclarationWithType>().acceptL( node->params ); 1214 1215 auto types = get<TypeInstType>().acceptL( node->forall ); 1216 for (auto t : types) { 1217 auto newT = new TypeDecl(*t->baseType); 1218 newT->name = t->name; // converted by typeString() 1219 for (auto asst : newT->assertions) delete asst; 1220 newT->assertions.clear(); 1221 ty->forall.push_back(newT); 1222 } 1223 auto assts = get<VariableExpr>().acceptL( node->assertions ); 1224 if (!assts.empty()) { 1225 assert(!types.empty()); 1226 for (auto asst : assts) { 1227 auto newDecl = new ObjectDecl(*strict_dynamic_cast<ObjectDecl*>(asst->var)); 1228 delete newDecl->type; 1229 newDecl->type = asst->result->clone(); 1230 newDecl->storageClasses.is_extern = true; // hack 1231 ty->forall.back()->assertions.push_back(newDecl); 1232 } 1233 } 1234 1127 ty->returnVals = get<DeclarationWithType>().acceptL( node->returns ); 1128 ty->parameters = get<DeclarationWithType>().acceptL( node->params ); 1129 ty->forall = get<TypeDecl>().acceptL( node->forall ); 1235 1130 return visitType( node, ty ); 1236 1131 } 1237 1132 1238 const ast::Type * postvisit( const ast::BaseInstType * old, ReferenceToType * ty ) { 1133 const ast::Type * postvisit( const ast::ReferenceToType * old, ReferenceToType * ty ) { 1134 ty->forall = get<TypeDecl>().acceptL( old->forall ); 1239 1135 ty->parameters = get<Expression>().acceptL( old->params ); 1240 1136 ty->hoistType = old->hoistType; … … 1319 1215 ty = new TypeInstType{ 1320 1216 cv( node ), 1321 node-> typeString(),1217 node->name, 1322 1218 get<TypeDecl>().accept1( node->base ), 1323 1219 get<Attribute>().acceptL( node->attributes ) … … 1326 1222 ty = new TypeInstType{ 1327 1223 cv( node ), 1328 node-> typeString(),1224 node->name, 1329 1225 node->kind == ast::TypeDecl::Ftype, 1330 1226 get<Attribute>().acceptL( node->attributes ) … … 1423 1319 }; 1424 1320 1425 std::list< Declaration * > convert( const ast::TranslationUnit&& translationUnit ) {1321 std::list< Declaration * > convert( const std::list< ast::ptr< ast::Decl > > && translationUnit ) { 1426 1322 ConverterNewToOld c; 1427 1323 std::list< Declaration * > decls; 1428 for(auto d : translationUnit .decls) {1324 for(auto d : translationUnit) { 1429 1325 decls.emplace_back( c.decl( d ) ); 1430 1326 } … … 1447 1343 ast::Node * node = nullptr; 1448 1344 /// cache of nodes that might be referenced by readonly<> for de-duplication 1449 /// in case that some nodes are dropped by conversion (due to possible structural change) 1450 /// use smart pointers in cache value to prevent accidental invalidation. 1451 /// at conversion stage, all created nodes are guaranteed to be unique, therefore 1452 /// const_casting out of smart pointers is permitted. 1453 std::unordered_map< const BaseSyntaxNode *, ast::readonly<ast::Node> > cache = {}; 1345 std::unordered_map< const BaseSyntaxNode *, ast::Node * > cache = {}; 1454 1346 1455 1347 // Local Utilities: … … 1524 1416 auto it = cache.find( old ); 1525 1417 if ( it == cache.end() ) return false; 1526 node = const_cast<ast::Node *>(it->second.get());1418 node = it->second; 1527 1419 return true; 1528 1420 } … … 1563 1455 virtual void visit( const FunctionDecl * old ) override final { 1564 1456 if ( inCache( old ) ) return; 1565 auto paramVars = GET_ACCEPT_V(type->parameters, DeclWithType);1566 auto returnVars = GET_ACCEPT_V(type->returnVals, DeclWithType);1567 auto forall = GET_ACCEPT_V(type->forall, TypeDecl);1568 1569 // function type is now derived from parameter decls instead of storing them1570 1571 /*1572 auto ftype = new ast::FunctionType((ast::ArgumentFlag)old->type->isVarArgs, cv(old->type));1573 ftype->params.reserve(paramVars.size());1574 ftype->returns.reserve(returnVars.size());1575 1576 for (auto & v: paramVars) {1577 ftype->params.emplace_back(v->get_type());1578 }1579 for (auto & v: returnVars) {1580 ftype->returns.emplace_back(v->get_type());1581 }1582 ftype->forall = std::move(forall);1583 */1584 1585 // can function type have attributes? seems not to be the case.1586 // visitType(old->type, ftype);1587 1588 // collect assertions and put directly in FunctionDecl1589 std::vector<ast::ptr<ast::DeclWithType>> assertions;1590 for (auto & param: forall) {1591 for (auto & asst: param->assertions) {1592 assertf(asst->unique(), "newly converted decl must be unique");1593 assertions.emplace_back(asst);1594 }1595 auto mut = param.get_and_mutate();1596 assertf(mut == param, "newly converted decl must be unique");1597 mut->assertions.clear();1598 }1599 1600 1457 auto decl = new ast::FunctionDecl{ 1601 1458 old->location, 1602 1459 old->name, 1603 // GET_ACCEPT_1(type, FunctionType), 1604 std::move(forall), 1605 std::move(paramVars), 1606 std::move(returnVars), 1460 GET_ACCEPT_1(type, FunctionType), 1607 1461 {}, 1608 1462 { old->storageClasses.val }, 1609 1463 { old->linkage.val }, 1610 1464 GET_ACCEPT_V(attributes, Attribute), 1611 { old->get_funcSpec().val }, 1612 old->type->isVarArgs 1465 { old->get_funcSpec().val } 1613 1466 }; 1614 1615 // decl->type = ftype;1616 1467 cache.emplace( old, decl ); 1617 1618 decl->assertions = std::move(assertions);1619 1468 decl->withExprs = GET_ACCEPT_V(withExprs, Expr); 1620 1469 decl->stmts = GET_ACCEPT_1(statements, CompoundStmt); … … 1629 1478 1630 1479 if ( Validate::dereferenceOperator == old ) { 1631 ast::dereferenceOperator = decl;1480 dereferenceOperator = decl; 1632 1481 } 1633 1482 1634 1483 if ( Validate::dtorStructDestroy == old ) { 1635 ast::dtorStructDestroy = decl;1484 dtorStructDestroy = decl; 1636 1485 } 1637 1486 } … … 1658 1507 1659 1508 if ( Validate::dtorStruct == old ) { 1660 ast::dtorStruct = decl;1509 dtorStruct = decl; 1661 1510 } 1662 1511 } … … 1735 1584 cache.emplace( old, decl ); 1736 1585 decl->assertions = GET_ACCEPT_V(assertions, DeclWithType); 1586 decl->params = GET_ACCEPT_V(parameters, TypeDecl); 1737 1587 decl->extension = old->extension; 1738 1588 decl->uniqueId = old->uniqueId; … … 1750 1600 ); 1751 1601 decl->assertions = GET_ACCEPT_V(assertions, DeclWithType); 1602 decl->params = GET_ACCEPT_V(parameters, TypeDecl); 1752 1603 decl->extension = old->extension; 1753 1604 decl->uniqueId = old->uniqueId; … … 2008 1859 } 2009 1860 2010 virtual void visit( const SuspendStmt * old ) override final {2011 if ( inCache( old ) ) return;2012 ast::SuspendStmt::Type type;2013 switch (old->type) {2014 case SuspendStmt::Coroutine: type = ast::SuspendStmt::Coroutine; break;2015 case SuspendStmt::Generator: type = ast::SuspendStmt::Generator; break;2016 case SuspendStmt::None : type = ast::SuspendStmt::None ; break;2017 default: abort();2018 }2019 this->node = new ast::SuspendStmt(2020 old->location,2021 GET_ACCEPT_1(then , CompoundStmt),2022 type,2023 GET_LABELS_V(old->labels)2024 );2025 cache.emplace( old, this->node );2026 }2027 2028 1861 virtual void visit( const WaitForStmt * old ) override final { 2029 1862 if ( inCache( old ) ) return; … … 2099 1932 } 2100 1933 2101 // TypeSubstitution shouldn't exist yet in old.2102 1934 ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) { 2103 1935 2104 1936 if (!old) return nullptr; 2105 if (old->empty()) return nullptr; 2106 assert(false); 2107 2108 /* 1937 2109 1938 ast::TypeSubstitution *rslt = new ast::TypeSubstitution(); 2110 1939 … … 2114 1943 } 2115 1944 1945 for (decltype(old->beginVar()) old_i = old->beginVar(); old_i != old->endVar(); old_i++) { 1946 rslt->addVar( old_i->first, 1947 getAccept1<ast::Expr>(old_i->second) ); 1948 } 1949 2116 1950 return rslt; 2117 */2118 1951 } 2119 1952 … … 2123 1956 2124 1957 assert( oldInferParams.empty() || oldResnSlots.empty() ); 2125 //assert( newInferred.mode == ast::Expr::InferUnion::Empty );1958 assert( newInferred.mode == ast::Expr::InferUnion::Empty ); 2126 1959 2127 1960 if ( !oldInferParams.empty() ) { … … 2206 2039 old->location, 2207 2040 GET_ACCEPT_1(arg, Expr), 2208 castTarget, 2209 {old->concrete_target.field, old->concrete_target.getter} 2041 castTarget 2210 2042 ) 2211 2043 ); … … 2255 2087 old->location, 2256 2088 GET_ACCEPT_1(member, DeclWithType), 2257 GET_ACCEPT_1(aggregate, Expr), 2258 ast::MemberExpr::NoOpConstructionChosen 2089 GET_ACCEPT_1(aggregate, Expr) 2259 2090 ) 2260 2091 ); … … 2588 2419 // I believe this should always be a BasicType. 2589 2420 if ( Validate::SizeType == old ) { 2590 ast::sizeType = type;2421 sizeType = type; 2591 2422 } 2592 2423 visitType( old, type ); … … 2633 2464 cv( old ) 2634 2465 }; 2635 auto returnVars = GET_ACCEPT_V(returnVals, DeclWithType); 2636 auto paramVars = GET_ACCEPT_V(parameters, DeclWithType); 2637 // ty->returns = GET_ACCEPT_V( returnVals, DeclWithType ); 2638 // ty->params = GET_ACCEPT_V( parameters, DeclWithType ); 2639 for (auto & v: returnVars) { 2640 ty->returns.emplace_back(v->get_type()); 2641 } 2642 for (auto & v: paramVars) { 2643 ty->params.emplace_back(v->get_type()); 2644 } 2645 // xxx - when will this be non-null? 2646 // will have to create dangling (no-owner) decls to be pointed to 2647 auto foralls = GET_ACCEPT_V( forall, TypeDecl ); 2648 2649 for (auto & param : foralls) { 2650 ty->forall.emplace_back(new ast::TypeInstType(param->name, param)); 2651 for (auto asst : param->assertions) { 2652 ty->assertions.emplace_back(new ast::VariableExpr({}, asst)); 2653 } 2654 } 2466 ty->returns = GET_ACCEPT_V( returnVals, DeclWithType ); 2467 ty->params = GET_ACCEPT_V( parameters, DeclWithType ); 2468 ty->forall = GET_ACCEPT_V( forall, TypeDecl ); 2655 2469 visitType( old, ty ); 2656 2470 } 2657 2471 2658 void postvisit( const ReferenceToType * old, ast::BaseInstType * ty ) { 2472 void postvisit( const ReferenceToType * old, ast::ReferenceToType * ty ) { 2473 ty->forall = GET_ACCEPT_V( forall, TypeDecl ); 2659 2474 ty->params = GET_ACCEPT_V( parameters, Expr ); 2660 2475 ty->hoistType = old->hoistType; … … 2801 2616 old->location, 2802 2617 GET_ACCEPT_1(value, Expr), 2803 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast:: NoConstruct2618 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::DoConstruct 2804 2619 ); 2805 2620 } … … 2810 2625 GET_ACCEPT_V(initializers, Init), 2811 2626 GET_ACCEPT_V(designations, Designation), 2812 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast:: NoConstruct2627 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::DoConstruct 2813 2628 ); 2814 2629 } … … 2841 2656 #undef GET_ACCEPT_1 2842 2657 2843 ast::TranslationUnitconvert( const std::list< Declaration * > && translationUnit ) {2658 std::list< ast::ptr< ast::Decl > > convert( const std::list< Declaration * > && translationUnit ) { 2844 2659 ConverterOldToNew c; 2845 ast::TranslationUnit unit; 2846 if (Validate::SizeType) { 2847 // this should be a BasicType. 2848 auto old = strict_dynamic_cast<BasicType *>(Validate::SizeType); 2849 ast::sizeType = new ast::BasicType{ (ast::BasicType::Kind)(unsigned)old->kind }; 2850 } 2851 2660 std::list< ast::ptr< ast::Decl > > decls; 2852 2661 for(auto d : translationUnit) { 2853 2662 d->accept( c ); 2854 unit.decls.emplace_back( c.decl() );2663 decls.emplace_back( c.decl() ); 2855 2664 } 2856 2665 deleteAll(translationUnit); 2857 2858 // Load the local static varables into the global store. 2859 unit.global.sizeType = ast::sizeType; 2860 unit.global.dereference = ast::dereferenceOperator; 2861 unit.global.dtorStruct = ast::dtorStruct; 2862 unit.global.dtorDestroy = ast::dtorStructDestroy; 2863 2864 return unit; 2666 return decls; 2865 2667 } -
src/AST/Convert.hpp
reef8dfb rbdfc032 18 18 #include <list> 19 19 20 #include "AST/Node.hpp" 21 20 22 class Declaration; 21 23 namespace ast { 22 struct TranslationUnit;24 class Decl; 23 25 }; 24 26 25 std::list< Declaration * > convert( const ast::TranslationUnit&& translationUnit );26 ast::TranslationUnitconvert( const std::list< Declaration * > && translationUnit );27 std::list< Declaration * > convert( const std::list< ast::ptr< ast::Decl > > && translationUnit ); 28 std::list< ast::ptr< ast::Decl > > convert( const std::list< Declaration * > && translationUnit ); -
src/AST/Decl.cpp
reef8dfb rbdfc032 49 49 // --- FunctionDecl 50 50 51 FunctionDecl::FunctionDecl( const CodeLocation & loc, const std::string & name,52 std::vector<ptr<TypeDecl>>&& forall,53 std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,54 CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,55 std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)56 : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),57 type_params(std::move(forall)), stmts( stmts ) {58 FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));59 for (auto & param : this->params) {60 ftype->params.emplace_back(param->get_type());61 }62 for (auto & ret : this->returns) {63 ftype->returns.emplace_back(ret->get_type());64 }65 for (auto & tp : this->type_params) {66 ftype->forall.emplace_back(new TypeInstType(tp->name, tp));67 }68 this->type = ftype;69 }70 71 72 51 const Type * FunctionDecl::get_type() const { return type.get(); } 73 void FunctionDecl::set_type( const Type * t ) { 74 type = strict_dynamic_cast< const FunctionType * >( t ); 75 } 52 void FunctionDecl::set_type(Type * t) { type = strict_dynamic_cast< FunctionType* >( t ); } 76 53 77 54 // --- TypeDecl -
src/AST/Decl.hpp
reef8dfb rbdfc032 33 33 34 34 // Must be included in *all* AST classes; should be #undef'd at the end of the file 35 #define MUTATE_FRIEND \ 36 template<typename node_t> friend node_t * mutate(const node_t * node); \ 37 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 35 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node); 38 36 39 37 namespace ast { … … 79 77 ptr<Expr> asmName; 80 78 bool isDeleted = false; 81 bool isTypeFixed = false;82 79 83 80 DeclWithType( const CodeLocation& loc, const std::string& name, Storage::Classes storage, … … 91 88 virtual const Type * get_type() const = 0; 92 89 /// Set type of this declaration. May be verified by subclass 93 virtual void set_type( const Type *) = 0;90 virtual void set_type(Type *) = 0; 94 91 95 92 const DeclWithType * accept( Visitor & v ) const override = 0; … … 114 111 115 112 const Type* get_type() const override { return type; } 116 void set_type( constType * ty ) override { type = ty; }113 void set_type( Type * ty ) override { type = ty; } 117 114 118 115 const DeclWithType * accept( Visitor& v ) const override { return v.visit( this ); } … … 125 122 class FunctionDecl : public DeclWithType { 126 123 public: 127 std::vector<ptr<DeclWithType>> params;128 std::vector<ptr<DeclWithType>> returns;129 std::vector<ptr<TypeDecl>> type_params;130 std::vector<ptr<DeclWithType>> assertions;131 // declared type, derived from parameter declarations132 124 ptr<FunctionType> type; 133 125 ptr<CompoundStmt> stmts; 134 126 std::vector< ptr<Expr> > withExprs; 135 127 136 137 FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall, 138 std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns, 128 FunctionDecl( const CodeLocation & loc, const std::string & name, FunctionType * type, 139 129 CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::C, 140 std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {} , bool isVarArgs = false);141 // : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),142 //stmts( stmts ) {}130 std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}) 131 : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), type( type ), 132 stmts( stmts ) {} 143 133 144 134 const Type * get_type() const override; 145 void set_type( const Type * t) override;135 void set_type(Type * t) override; 146 136 147 137 bool has_body() const { return stmts; } … … 157 147 public: 158 148 ptr<Type> base; 149 std::vector<ptr<TypeDecl>> params; 159 150 std::vector<ptr<DeclWithType>> assertions; 160 151 161 NamedTypeDecl( 162 const CodeLocation & loc, const std::string & name, Storage::Classes storage, 163 const Type * b, Linkage::Spec spec = Linkage::Cforall ) 164 : Decl( loc, name, storage, spec ), base( b ), assertions() {} 152 NamedTypeDecl( const CodeLocation& loc, const std::string& name, Storage::Classes storage, 153 Type* b, Linkage::Spec spec = Linkage::Cforall ) 154 : Decl( loc, name, storage, spec ), base( b ), params(), assertions() {} 165 155 166 156 /// Produces a name for the kind of alias … … 196 186 }; 197 187 198 TypeDecl( 199 const CodeLocation & loc, const std::string & name, Storage::Classes storage, 200 const Type * b, TypeDecl::Kind k, bool s, const Type * i = nullptr ) 201 : NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == TypeDecl::Ttype || s ), 202 init( i ) {} 188 TypeDecl( const CodeLocation & loc, const std::string & name, Storage::Classes storage, Type * b, 189 Kind k, bool s, Type * i = nullptr ) 190 : NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == Ttype || s ), 191 init( i ) {} 203 192 204 193 const char * typeString() const override; … … 270 259 271 260 bool is_coroutine() { return kind == Coroutine; } 272 bool is_generator() { return kind == Generator; } 273 bool is_monitor () { return kind == Monitor ; } 274 bool is_thread () { return kind == Thread ; } 261 bool is_monitor() { return kind == Monitor; } 262 bool is_thread() { return kind == Thread; } 275 263 276 264 const Decl * accept( Visitor & v ) const override { return v.visit( this ); } -
src/AST/DeclReplacer.cpp
reef8dfb rbdfc032 38 38 const ast::TypeInstType * previsit( const ast::TypeInstType * ); 39 39 }; 40 41 struct VarExprReplacer {42 private:43 const ExprMap & exprMap;44 45 public:46 VarExprReplacer(const ExprMap & exprMap): exprMap (exprMap) {}47 48 const Expr * postvisit (const VariableExpr *);49 };50 40 } 51 41 … … 64 54 DeclMap declMap; 65 55 return replace( node, declMap, typeMap, debug ); 66 }67 68 const ast::Node * replace( const ast::Node * node, const ExprMap & exprMap) {69 Pass<VarExprReplacer> replacer = {exprMap};70 return node->accept( replacer );71 56 } 72 57 … … 103 88 return ninst; 104 89 } 105 106 const Expr * VarExprReplacer::postvisit( const VariableExpr * expr ) {107 if (!exprMap.count(expr->var)) return expr;108 109 return exprMap.at(expr->var);110 }111 112 90 } 113 91 } -
src/AST/DeclReplacer.hpp
reef8dfb rbdfc032 23 23 class DeclWithType; 24 24 class TypeDecl; 25 class Expr;26 25 27 26 namespace DeclReplacer { 28 27 using DeclMap = std::unordered_map< const DeclWithType *, const DeclWithType * >; 29 28 using TypeMap = std::unordered_map< const TypeDecl *, const TypeDecl * >; 30 using ExprMap = std::unordered_map< const DeclWithType *, const Expr * >;31 29 32 30 const Node * replace( const Node * node, const DeclMap & declMap, bool debug = false ); 33 31 const Node * replace( const Node * node, const TypeMap & typeMap, bool debug = false ); 34 32 const Node * replace( const Node * node, const DeclMap & declMap, const TypeMap & typeMap, bool debug = false ); 35 const Node * replace( const Node * node, const ExprMap & exprMap);36 33 } 37 34 } -
src/AST/Expr.cpp
reef8dfb rbdfc032 20 20 #include <vector> 21 21 22 #include "Copy.hpp" // for shallowCopy23 #include "Eval.hpp" // for call24 22 #include "GenericSubstitution.hpp" 25 #include "LinkageSpec.hpp"26 23 #include "Stmt.hpp" 27 24 #include "Type.hpp" … … 30 27 #include "Common/SemanticError.h" 31 28 #include "GenPoly/Lvalue.h" // for referencesPermissable 32 #include "InitTweak/InitTweak.h" // for get Function, getPointerBase29 #include "InitTweak/InitTweak.h" // for getPointerBase 33 30 #include "ResolvExpr/typeops.h" // for extractResultType 34 31 #include "Tuples/Tuples.h" // for makeTupleType 35 32 36 33 namespace ast { 37 38 namespace {39 std::set<std::string> const lvalueFunctionNames = {"*?", "?[?]"};40 }41 42 // --- Expr43 bool Expr::get_lvalue() const {44 return false;45 }46 34 47 35 // --- ApplicationExpr … … 58 46 } 59 47 60 bool ApplicationExpr::get_lvalue() const {61 if ( const DeclWithType * func = InitTweak::getFunction( this ) ) {62 return func->linkage == Linkage::Intrinsic && lvalueFunctionNames.count( func->name );63 }64 return false;65 }66 67 48 // --- UntypedExpr 68 49 69 UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, constExpr * arg ) {50 UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, Expr * arg ) { 70 51 assert( arg ); 71 52 72 UntypedExpr * ret = call( loc, "*?", arg ); 53 UntypedExpr * ret = new UntypedExpr{ 54 loc, new NameExpr{loc, "*?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ arg } } 55 }; 73 56 if ( const Type * ty = arg->result ) { 74 57 const Type * base = InitTweak::getPointerBase( ty ); … … 82 65 // base type 83 66 ret->result = base; 67 add_qualifiers( ret->result, CV::Lvalue ); 84 68 } 85 69 } … … 87 71 } 88 72 89 bool UntypedExpr::get_lvalue() const { 90 std::string fname = InitTweak::getFunctionName( this ); 91 return lvalueFunctionNames.count( fname ); 92 } 93 94 UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, const Expr * lhs, const Expr * rhs ) { 73 UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs ) { 95 74 assert( lhs && rhs ); 96 75 97 UntypedExpr * ret = call( loc, "?=?", lhs, rhs ); 76 UntypedExpr * ret = new UntypedExpr{ 77 loc, new NameExpr{loc, "?=?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ lhs }, ptr<Expr>{ rhs } } 78 }; 98 79 if ( lhs->result && rhs->result ) { 99 80 // if both expressions are typed, assumes that this assignment is a C bitwise assignment, … … 102 83 } 103 84 return ret; 104 }105 106 // --- VariableExpr107 108 VariableExpr::VariableExpr( const CodeLocation & loc )109 : Expr( loc ), var( nullptr ) {}110 111 VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v )112 : Expr( loc ), var( v ) {113 assert( var );114 assert( var->get_type() );115 result = shallowCopy( var->get_type() );116 }117 118 bool VariableExpr::get_lvalue() const {119 // It isn't always an lvalue, but it is never an rvalue.120 return true;121 }122 123 VariableExpr * VariableExpr::functionPointer(124 const CodeLocation & loc, const FunctionDecl * decl ) {125 // wrap usually-determined result type in a pointer126 VariableExpr * funcExpr = new VariableExpr{ loc, decl };127 funcExpr->result = new PointerType{ funcExpr->result };128 return funcExpr;129 85 } 130 86 … … 152 108 AddressExpr::AddressExpr( const CodeLocation & loc, const Expr * a ) : Expr( loc ), arg( a ) { 153 109 if ( arg->result ) { 154 if ( arg-> get_lvalue() ) {110 if ( arg->result->is_lvalue() ) { 155 111 // lvalue, retains all levels of reference, and gains a pointer inside the references 156 112 Type * res = addrType( arg->result ); 113 res->set_lvalue( false ); // result of & is never an lvalue 157 114 result = res; 158 115 } else { … … 161 118 dynamic_cast< const ReferenceType * >( arg->result.get() ) ) { 162 119 Type * res = addrType( refType->base ); 120 res->set_lvalue( false ); // result of & is never an lvalue 163 121 result = res; 164 122 } else { … … 181 139 : Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ) {} 182 140 183 bool CastExpr::get_lvalue() const {184 // This is actually wrong by C, but it works with our current set-up.185 return arg->get_lvalue();186 }187 188 141 // --- KeywordCastExpr 189 142 190 143 const char * KeywordCastExpr::targetString() const { 191 144 return AggregateDecl::aggrString( target ); 192 }193 194 // --- UntypedMemberExpr195 196 bool UntypedMemberExpr::get_lvalue() const {197 return aggregate->get_lvalue();198 145 } 199 146 … … 206 153 assert( aggregate->result ); 207 154 155 // take ownership of member type 208 156 result = mem->get_type(); 209 210 157 // substitute aggregate generic parameters into member type 211 158 genericSubstitution( aggregate->result ).apply( result ); 212 // ensure appropriate restrictions from aggregate type 213 add_qualifiers( result, aggregate->result->qualifiers ); 214 } 215 216 MemberExpr::MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg, 217 MemberExpr::NoOpConstruction overloadSelector ) 218 : Expr( loc ), member( mem ), aggregate( agg ) { 219 assert( member ); 220 assert( aggregate ); 221 assert( aggregate->result ); 222 (void) overloadSelector; 223 } 224 225 bool MemberExpr::get_lvalue() const { 226 // This is actually wrong by C, but it works with our current set-up. 227 return true; 159 // ensure lvalue and appropriate restrictions from aggregate type 160 add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue ); 161 } 162 163 // --- VariableExpr 164 165 VariableExpr::VariableExpr( const CodeLocation & loc ) 166 : Expr( loc ), var( nullptr ) {} 167 168 VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v ) 169 : Expr( loc ), var( v ) { 170 assert( var ); 171 assert( var->get_type() ); 172 result = var->get_type(); 173 add_qualifiers( result, CV::Lvalue ); 174 } 175 176 VariableExpr * VariableExpr::functionPointer( 177 const CodeLocation & loc, const FunctionDecl * decl ) { 178 // wrap usually-determined result type in a pointer 179 VariableExpr * funcExpr = new VariableExpr{ loc, decl }; 180 funcExpr->result = new PointerType{ funcExpr->result }; 181 return funcExpr; 228 182 } 229 183 … … 303 257 const CodeLocation & loc, const Expr * a1, const Expr * a2, LogicalFlag ia ) 304 258 : Expr( loc, new BasicType{ BasicType::SignedInt } ), arg1( a1 ), arg2( a2 ), isAnd( ia ) {} 305 306 // --- CommaExpr307 bool CommaExpr::get_lvalue() const {308 // This is wrong by C, but the current implementation uses it.309 // (ex: Specialize, Lvalue and Box)310 return arg2->get_lvalue();311 }312 259 313 260 // --- ConstructorExpr … … 329 276 assert( t && i ); 330 277 result = t; 331 } 332 333 bool CompoundLiteralExpr::get_lvalue() const { 334 return true; 278 add_qualifiers( result, CV::Lvalue ); 335 279 } 336 280 … … 349 293 // like MemberExpr, TupleIndexExpr is always an lvalue 350 294 result = type->types[ index ]; 351 } 352 353 bool TupleIndexExpr::get_lvalue() const { 354 return tuple->get_lvalue(); 295 add_qualifiers( result, CV::Lvalue ); 355 296 } 356 297 -
src/AST/Expr.hpp
reef8dfb rbdfc032 31 31 32 32 // Must be included in *all* AST classes; should be #undef'd at the end of the file 33 #define MUTATE_FRIEND \ 34 template<typename node_t> friend node_t * mutate(const node_t * node); \ 35 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 36 33 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node); 37 34 38 35 class ConverterOldToNew; … … 45 42 struct ParamEntry { 46 43 UniqueId decl; 47 readonly<Decl> declptr;44 ptr<Decl> declptr; 48 45 ptr<Type> actualType; 49 46 ptr<Type> formalType; … … 65 62 class Expr : public ParseNode { 66 63 public: 67 /* 68 * NOTE: the union approach is incorrect until the case of 69 * partial resolution in InferMatcher is eliminated. 70 * it is reverted to allow unresolved and resolved parameters 71 * to coexist in an expression node. 72 */ 64 /// Saves space (~16 bytes) by combining ResnSlots and InferredParams 73 65 struct InferUnion { 74 // mode is now unused75 66 enum { Empty, Slots, Params } mode; 76 struct data_t { 77 // char def; 78 ResnSlots * resnSlots; 79 InferredParams * inferParams; 80 81 data_t(): resnSlots(nullptr), inferParams(nullptr) {} 82 data_t(const data_t &other) = delete; 83 ~data_t() { 84 delete resnSlots; 85 delete inferParams; 86 } 67 union data_t { 68 char def; 69 ResnSlots resnSlots; 70 InferredParams inferParams; 71 72 data_t() : def('\0') {} 73 ~data_t() {} 87 74 } data; 88 75 89 76 /// initializes from other InferUnion 90 77 void init_from( const InferUnion& o ) { 91 if (o.data.resnSlots) { 92 data.resnSlots = new ResnSlots(*o.data.resnSlots); 93 } 94 if (o.data.inferParams) { 95 data.inferParams = new InferredParams(*o.data.inferParams); 78 switch ( o.mode ) { 79 case Empty: return; 80 case Slots: new(&data.resnSlots) ResnSlots{ o.data.resnSlots }; return; 81 case Params: new(&data.inferParams) InferredParams{ o.data.inferParams }; return; 96 82 } 97 83 } … … 99 85 /// initializes from other InferUnion (move semantics) 100 86 void init_from( InferUnion&& o ) { 101 data.resnSlots = o.data.resnSlots; 102 data.inferParams = o.data.inferParams; 103 o.data.resnSlots = nullptr; 104 o.data.inferParams = nullptr; 87 switch ( o.mode ) { 88 case Empty: return; 89 case Slots: new(&data.resnSlots) ResnSlots{ std::move(o.data.resnSlots) }; return; 90 case Params: 91 new(&data.inferParams) InferredParams{ std::move(o.data.inferParams) }; return; 92 } 93 } 94 95 /// clears variant fields 96 void reset() { 97 switch( mode ) { 98 case Empty: return; 99 case Slots: data.resnSlots.~ResnSlots(); return; 100 case Params: data.inferParams.~InferredParams(); return; 101 } 105 102 } 106 103 … … 110 107 InferUnion& operator= ( const InferUnion& ) = delete; 111 108 InferUnion& operator= ( InferUnion&& ) = delete; 112 113 bool hasSlots() const { return data.resnSlots; } 114 bool hasParams() const { return data.inferParams; } 109 ~InferUnion() { reset(); } 115 110 116 111 ResnSlots& resnSlots() { 117 if (!data.resnSlots) { 118 data.resnSlots = new ResnSlots(); 112 switch (mode) { 113 case Empty: new(&data.resnSlots) ResnSlots{}; mode = Slots; // fallthrough 114 case Slots: return data.resnSlots; 115 case Params: assertf(false, "Cannot return to resnSlots from Params"); abort(); 119 116 } 120 return *data.resnSlots;117 assertf(false, "unreachable"); 121 118 } 122 119 123 120 const ResnSlots& resnSlots() const { 124 if ( data.resnSlots) {125 return *data.resnSlots;121 if (mode == Slots) { 122 return data.resnSlots; 126 123 } 127 124 assertf(false, "Mode was not already resnSlots"); … … 130 127 131 128 InferredParams& inferParams() { 132 if (!data.inferParams) { 133 data.inferParams = new InferredParams(); 129 switch (mode) { 130 case Slots: data.resnSlots.~ResnSlots(); // fallthrough 131 case Empty: new(&data.inferParams) InferredParams{}; mode = Params; // fallthrough 132 case Params: return data.inferParams; 134 133 } 135 return *data.inferParams;134 assertf(false, "unreachable"); 136 135 } 137 136 138 137 const InferredParams& inferParams() const { 139 if ( data.inferParams) {140 return *data.inferParams;138 if (mode == Params) { 139 return data.inferParams; 141 140 } 142 141 assertf(false, "Mode was not already Params"); … … 144 143 } 145 144 146 void set_inferParams( InferredParams * ps ) { 147 delete data.resnSlots; 148 data.resnSlots = nullptr; 149 delete data.inferParams; 150 data.inferParams = ps; 145 void set_inferParams( InferredParams && ps ) { 146 switch(mode) { 147 case Slots: 148 data.resnSlots.~ResnSlots(); 149 // fallthrough 150 case Empty: 151 new(&data.inferParams) InferredParams{ std::move( ps ) }; 152 mode = Params; 153 break; 154 case Params: 155 data.inferParams = std::move( ps ); 156 break; 157 } 151 158 } 152 159 … … 154 161 /// and the other is in `Params`. 155 162 void splice( InferUnion && o ) { 156 if (o.data.resnSlots) { 157 if (data.resnSlots) { 158 data.resnSlots->insert( 159 data.resnSlots->end(), o.data.resnSlots->begin(), o.data.resnSlots->end() ); 160 delete o.data.resnSlots; 163 if ( o.mode == Empty ) return; 164 if ( mode == Empty ) { init_from( o ); return; } 165 assert( mode == o.mode && "attempt to splice incompatible InferUnion" ); 166 167 if ( mode == Slots ){ 168 data.resnSlots.insert( 169 data.resnSlots.end(), o.data.resnSlots.begin(), o.data.resnSlots.end() ); 170 } else if ( mode == Params ) { 171 for ( const auto & p : o.data.inferParams ) { 172 data.inferParams[p.first] = std::move(p.second); 161 173 } 162 else { 163 data.resnSlots = o.data.resnSlots; 164 } 165 o.data.resnSlots = nullptr; 166 } 167 168 if (o.data.inferParams) { 169 if (data.inferParams) { 170 for ( const auto & p : *o.data.inferParams ) { 171 (*data.inferParams)[p.first] = std::move(p.second); 172 } 173 delete o.data.inferParams; 174 } 175 else { 176 data.inferParams = o.data.inferParams; 177 } 178 o.data.inferParams = nullptr; 179 } 174 } else assertf(false, "invalid mode"); 180 175 } 181 176 }; … … 190 185 191 186 Expr * set_extension( bool ex ) { extension = ex; return this; } 192 virtual bool get_lvalue() const;193 187 194 188 virtual const Expr * accept( Visitor & v ) const override = 0; … … 207 201 ApplicationExpr( const CodeLocation & loc, const Expr * f, std::vector<ptr<Expr>> && as = {} ); 208 202 209 bool get_lvalue() const final;210 211 203 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 212 204 private: … … 224 216 : Expr( loc ), func( f ), args( std::move(as) ) {} 225 217 226 bool get_lvalue() const final;227 228 218 /// Creates a new dereference expression 229 static UntypedExpr * createDeref( const CodeLocation & loc, constExpr * arg );219 static UntypedExpr * createDeref( const CodeLocation & loc, Expr * arg ); 230 220 /// Creates a new assignment expression 231 static UntypedExpr * createAssign( const CodeLocation & loc, const Expr * lhs, constExpr * rhs );221 static UntypedExpr * createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs ); 232 222 233 223 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 251 241 }; 252 242 253 /// A reference to a named variable.254 class VariableExpr final : public Expr {255 public:256 readonly<DeclWithType> var;257 258 VariableExpr( const CodeLocation & loc );259 VariableExpr( const CodeLocation & loc, const DeclWithType * v );260 261 bool get_lvalue() const final;262 263 /// generates a function pointer for a given function264 static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl );265 266 const Expr * accept( Visitor & v ) const override { return v.visit( this ); }267 private:268 VariableExpr * clone() const override { return new VariableExpr{ *this }; }269 MUTATE_FRIEND270 };271 272 243 /// Address-of expression `&e` 273 244 class AddressExpr final : public Expr { … … 300 271 }; 301 272 302 /// Inidicates whether the cast is introduced by the CFA type system. 303 /// GeneratedCast for casts that the resolver introduces to force a return type 304 /// ExplicitCast for casts from user code 305 /// ExplicitCast for casts from desugaring advanced CFA features into simpler CFA 306 /// example 307 /// int * p; // declaration 308 /// (float *) p; // use, with subject cast 309 /// subject cast being GeneratedCast means we are considering an interpretation with a type mismatch 310 /// subject cast being ExplicitCast means someone in charge wants it that way 273 /// Whether a cast existed in the program source or not 311 274 enum GeneratedFlag { ExplicitCast, GeneratedCast }; 312 275 … … 328 291 CastExpr( const Expr * a ) : CastExpr( a->location, a, GeneratedCast ) {} 329 292 330 bool get_lvalue() const final;331 332 293 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 333 294 private: … … 340 301 public: 341 302 ptr<Expr> arg; 342 struct Concrete {343 std::string field;344 std::string getter;345 346 Concrete() = default;347 Concrete(const Concrete &) = default;348 };349 303 ast::AggregateDecl::Aggregate target; 350 Concrete concrete_target;351 352 304 353 305 KeywordCastExpr( const CodeLocation & loc, const Expr * a, ast::AggregateDecl::Aggregate t ) 354 306 : Expr( loc ), arg( a ), target( t ) {} 355 307 356 KeywordCastExpr( const CodeLocation & loc, const Expr * a, ast::AggregateDecl::Aggregate t, const Concrete & ct )357 : Expr( loc ), arg( a ), target( t ), concrete_target( ct ) {}358 359 308 /// Get a name for the target type 360 309 const char * targetString() const; … … 389 338 : Expr( loc ), member( mem ), aggregate( agg ) { assert( aggregate ); } 390 339 391 bool get_lvalue() const final;392 393 340 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 394 341 private: … … 405 352 MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg ); 406 353 407 bool get_lvalue() const final;408 409 354 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 410 355 private: 411 356 MemberExpr * clone() const override { return new MemberExpr{ *this }; } 412 357 MUTATE_FRIEND 413 414 // Custructor overload meant only for AST conversion 415 enum NoOpConstruction { NoOpConstructionChosen }; 416 MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg, 417 NoOpConstruction overloadSelector ); 418 friend class ::ConverterOldToNew; 419 friend class ::ConverterNewToOld; 358 }; 359 360 /// A reference to a named variable. 361 class VariableExpr final : public Expr { 362 public: 363 readonly<DeclWithType> var; 364 365 VariableExpr( const CodeLocation & loc ); 366 VariableExpr( const CodeLocation & loc, const DeclWithType * v ); 367 368 /// generates a function pointer for a given function 369 static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl ); 370 371 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 372 private: 373 VariableExpr * clone() const override { return new VariableExpr{ *this }; } 374 MUTATE_FRIEND 420 375 }; 421 376 … … 431 386 const CodeLocation & loc, const Type * ty, const std::string & r, 432 387 std::optional<unsigned long long> i ) 433 : Expr( loc, ty ), rep( r ), ival( i ) , underlyer(ty){}388 : Expr( loc, ty ), rep( r ), ival( i ) {} 434 389 435 390 /// Gets the integer value of this constant, if one is appropriate to its type. … … 577 532 578 533 CommaExpr( const CodeLocation & loc, const Expr * a1, const Expr * a2 ) 579 : Expr( loc ), arg1( a1 ), arg2( a2 ) { 580 this->result = a2->result; 581 } 582 583 bool get_lvalue() const final; 534 : Expr( loc ), arg1( a1 ), arg2( a2 ) {} 584 535 585 536 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 626 577 627 578 ImplicitCopyCtorExpr( const CodeLocation& loc, const ApplicationExpr * call ) 628 : Expr( loc, call->result ) , callExpr(call) { assert( call ); assert(call->result); }579 : Expr( loc, call->result ) { assert( call ); } 629 580 630 581 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 654 605 CompoundLiteralExpr( const CodeLocation & loc, const Type * t, const Init * i ); 655 606 656 bool get_lvalue() const final;657 658 607 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 659 608 private: … … 711 660 712 661 TupleIndexExpr( const CodeLocation & loc, const Expr * t, unsigned i ); 713 714 bool get_lvalue() const final;715 662 716 663 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 751 698 std::vector<ptr<Expr>> dtors; ///< destructor(s) for return variable(s) 752 699 753 readonly<ExprStmt> resultExpr;754 755 700 StmtExpr( const CodeLocation & loc, const CompoundStmt * ss ); 756 701 -
src/AST/Fwd.hpp
reef8dfb rbdfc032 10 10 // Created On : Wed May 8 16:05:00 2019 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Thr Jul 23 14:15:00 202013 // Update Count : 212 // Last Modified On : Mon Jun 24 09:48:00 2019 13 // Update Count : 1 14 14 // 15 15 … … 53 53 class CatchStmt; 54 54 class FinallyStmt; 55 class SuspendStmt;56 55 class WaitForStmt; 57 56 class WithStmt; … … 107 106 class QualifiedType; 108 107 class FunctionType; 109 class BaseInstType; 110 template<typename decl_t> class SueInstType; 111 using StructInstType = SueInstType<StructDecl>; 112 using UnionInstType = SueInstType<UnionDecl>; 113 using EnumInstType = SueInstType<EnumDecl>; 108 class ReferenceToType; 109 class StructInstType; 110 class UnionInstType; 111 class EnumInstType; 114 112 class TraitInstType; 115 113 class TypeInstType; … … 137 135 typedef unsigned int UniqueId; 138 136 139 struct TranslationUnit;140 // TODO: Get from the TranslationUnit:141 extern ptr<Type> sizeType;142 extern const FunctionDecl * dereferenceOperator;143 extern const StructDecl * dtorStruct;144 extern const FunctionDecl * dtorStructDestroy;145 146 137 } -
src/AST/GenericSubstitution.cpp
reef8dfb rbdfc032 42 42 private: 43 43 // make substitution for generic type 44 void makeSub( const BaseInstType * ty ) {44 void makeSub( const ReferenceToType * ty ) { 45 45 visit_children = false; 46 46 const AggregateDecl * aggr = ty->aggr(); … … 62 62 Pass<GenericSubstitutionBuilder> builder; 63 63 maybe_accept( ty, builder ); 64 return std::move(builder. core.sub);64 return std::move(builder.pass.sub); 65 65 } 66 66 -
src/AST/Init.hpp
reef8dfb rbdfc032 25 25 26 26 // Must be included in *all* AST classes; should be #undef'd at the end of the file 27 #define MUTATE_FRIEND \ 28 template<typename node_t> friend node_t * mutate(const node_t * node); \ 29 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 27 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node); 30 28 31 29 namespace ast { … … 50 48 51 49 /// Flag for whether to construct from initialzier 52 enum ConstructFlag { NoConstruct, MaybeConstruct };50 enum ConstructFlag { DoConstruct, MaybeConstruct }; 53 51 54 52 /// Object initializer base class … … 71 69 ptr<Expr> value; 72 70 73 SingleInit( const CodeLocation & loc, const Expr * val, ConstructFlag mc = NoConstruct )71 SingleInit( const CodeLocation & loc, const Expr * val, ConstructFlag mc = DoConstruct ) 74 72 : Init( loc, mc ), value( val ) {} 75 73 … … 90 88 91 89 ListInit( const CodeLocation & loc, std::vector<ptr<Init>> && is, 92 std::vector<ptr<Designation>> && ds = {}, ConstructFlag mc = NoConstruct );90 std::vector<ptr<Designation>> && ds = {}, ConstructFlag mc = DoConstruct ); 93 91 94 92 using iterator = std::vector<ptr<Init>>::iterator; … … 118 116 ConstructorInit( 119 117 const CodeLocation & loc, const Stmt * ctor, const Stmt * dtor, const Init * init ) 120 : Init( loc, MaybeConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {}118 : Init( loc, DoConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {} 121 119 122 120 const Init * accept( Visitor & v ) const override { return v.visit( this ); } -
src/AST/Node.cpp
reef8dfb rbdfc032 9 9 // Author : Thierry Delisle 10 10 // Created On : Thu May 16 14:16:00 2019 11 // Last Modified By : Andrew Beach12 // Last Modified On : Fri Jun 5 10:21:00 202013 // Update Count : 111 // Last Modified By : 12 // Last Modified On : 13 // Update Count : 14 14 // 15 15 … … 17 17 #include "Fwd.hpp" 18 18 19 #include <csignal> // MEMORY DEBUG -- for raise20 19 #include <iostream> 21 20 … … 30 29 #include "Print.hpp" 31 30 32 /// MEMORY DEBUG -- allows breaking on ref-count changes of dynamically chosen object. 33 /// Process to use in GDB: 34 /// break ast::Node::_trap() 35 /// run 36 /// set variable MEM_TRAP_OBJ = <target> 37 /// disable <first breakpoint> 38 /// continue 39 void * MEM_TRAP_OBJ = nullptr; 40 41 void _trap( const void * node ) { 42 if ( node == MEM_TRAP_OBJ ) std::raise(SIGTRAP); 43 } 44 45 [[noreturn]] static inline void strict_fail(const ast::Node * node) { 46 assertf(node, "strict_as had nullptr input."); 47 const ast::ParseNode * parse = dynamic_cast<const ast::ParseNode *>( node ); 48 if ( nullptr == parse ) { 49 assertf(nullptr, "%s (no location)", toString(node).c_str()); 50 } else if ( parse->location.isUnset() ) { 51 assertf(nullptr, "%s (unset location)", toString(node).c_str()); 52 } else { 53 assertf(nullptr, "%s (at %s:%d)", toString(node).c_str(), 54 parse->location.filename.c_str(), parse->location.first_line); 55 } 56 } 57 58 template< typename node_t, enum ast::Node::ref_type ref_t > 59 void ast::ptr_base<node_t, ref_t>::_strict_fail() const { 60 strict_fail(node); 61 } 62 63 template< typename node_t, enum ast::Node::ref_type ref_t > 64 void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) { 65 node->increment(ref_t); 66 _trap( node ); 67 } 68 69 template< typename node_t, enum ast::Node::ref_type ref_t > 70 void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node, bool do_delete ) { 71 _trap( node ); 72 node->decrement( ref_t, do_delete ); 73 } 74 75 template< typename node_t, enum ast::Node::ref_type ref_t > 76 void ast::ptr_base<node_t, ref_t>::_check() const { 77 // if(node) assert(node->was_ever_strong == false || node->strong_count > 0); 78 } 31 template< typename node_t, enum ast::Node::ref_type ref_t > 32 void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) { node->increment(ref_t); } 33 34 template< typename node_t, enum ast::Node::ref_type ref_t > 35 void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node ) { node->decrement(ref_t); } 36 37 template< typename node_t, enum ast::Node::ref_type ref_t > 38 void ast::ptr_base<node_t, ref_t>::_check() const { if(node) assert(node->was_ever_strong == false || node->strong_count > 0); } 79 39 80 40 template< typename node_t, enum ast::Node::ref_type ref_t > … … 266 226 template class ast::ptr_base< ast::FunctionType, ast::Node::ref_type::weak >; 267 227 template class ast::ptr_base< ast::FunctionType, ast::Node::ref_type::strong >; 268 template class ast::ptr_base< ast:: BaseInstType, ast::Node::ref_type::weak >;269 template class ast::ptr_base< ast:: BaseInstType, ast::Node::ref_type::strong >;228 template class ast::ptr_base< ast::ReferenceToType, ast::Node::ref_type::weak >; 229 template class ast::ptr_base< ast::ReferenceToType, ast::Node::ref_type::strong >; 270 230 template class ast::ptr_base< ast::StructInstType, ast::Node::ref_type::weak >; 271 231 template class ast::ptr_base< ast::StructInstType, ast::Node::ref_type::strong >; -
src/AST/Node.hpp
reef8dfb rbdfc032 10 10 // Created On : Wed May 8 10:27:04 2019 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Jun 5 9:47:00 202013 // Update Count : 612 // Last Modified On : Mon Jun 3 13:26:00 2019 13 // Update Count : 5 14 14 // 15 15 … … 38 38 Node& operator= (const Node&) = delete; 39 39 Node& operator= (Node&&) = delete; 40 virtual ~Node() {}40 virtual ~Node() = default; 41 41 42 42 virtual const Node * accept( Visitor & v ) const = 0; … … 49 49 50 50 bool unique() const { return strong_count == 1; } 51 bool isManaged() const {return strong_count > 0; }52 51 53 52 private: … … 58 57 template<typename node_t> 59 58 friend node_t * mutate(const node_t * node); 60 template<typename node_t>61 friend node_t * shallowCopy(const node_t * node);62 59 63 60 mutable size_t strong_count = 0; … … 72 69 } 73 70 74 void decrement(ast::Node::ref_type ref , bool do_delete = true) const {71 void decrement(ast::Node::ref_type ref) const { 75 72 switch (ref) { 76 73 case ref_type::strong: strong_count--; break; … … 78 75 } 79 76 80 if( do_delete &&!strong_count && !weak_count) {77 if(!strong_count && !weak_count) { 81 78 delete this; 82 79 } … … 97 94 assertf( 98 95 node->weak_count == 0, 99 "Error: mutating node with weak references to it will invalid atesome references"96 "Error: mutating node with weak references to it will invalided some references" 100 97 ); 101 98 return node->clone(); … … 107 104 // skip mutate if equivalent 108 105 if ( node->*field == val ) return node; 109 106 110 107 // mutate and return 111 108 node_t * ret = mutate( node ); … … 126 123 (ret->*field)[i] = std::forward< field_t >( val ); 127 124 return ret; 128 }129 130 /// Mutate an entire indexed collection by cloning to accepted value131 template<typename node_t, typename parent_t, typename coll_t>132 const node_t * mutate_each( const node_t * node, coll_t parent_t::* field, Visitor & v ) {133 for ( unsigned i = 0; i < (node->*field).size(); ++i ) {134 node = mutate_field_index( node, field, i, (node->*field)[i]->accept( v ) );135 }136 return node;137 125 } 138 126 … … 229 217 const node_t & operator* () const { _check(); return *node; } 230 218 explicit operator bool() const { _check(); return node; } 231 operator const node_t * () const & { _check(); return node; } 232 operator const node_t * () && = delete; 233 234 const node_t * release() { 235 const node_t * ret = node; 236 if ( node ) { 237 _dec(node, false); 238 node = nullptr; 239 } 240 return ret; 241 } 219 operator const node_t * () const { _check(); return node; } 242 220 243 221 /// wrapper for convenient access to dynamic_cast … … 245 223 const o_node_t * as() const { _check(); return dynamic_cast<const o_node_t *>(node); } 246 224 247 /// Wrapper that makes sure dynamic_cast returns non-null.225 /// wrapper for convenient access to strict_dynamic_cast 248 226 template<typename o_node_t> 249 const o_node_t * strict_as() const { 250 if (const o_node_t * ret = as<o_node_t>()) return ret; 251 _strict_fail(); 252 } 253 254 /// Wrapper that makes sure dynamic_cast does not fail. 255 template<typename o_node_t, decltype(nullptr) null> 256 const o_node_t * strict_as() const { return node ? strict_as<o_node_t>() : nullptr; } 227 const o_node_t * strict_as() const { _check(); return strict_dynamic_cast<const o_node_t *>(node); } 257 228 258 229 /// Returns a mutable version of the pointer in this node. … … 273 244 274 245 void _inc( const node_t * other ); 275 void _dec( const node_t * other , bool do_delete = true);246 void _dec( const node_t * other ); 276 247 void _check() const; 277 void _strict_fail() const __attribute__((noreturn));278 248 279 249 const node_t * node; -
src/AST/Pass.hpp
reef8dfb rbdfc032 8 8 // 9 9 // Author : Thierry Delisle 10 // Created On : Thu May 09 15: 37:05 201910 // Created On : Thu May 09 15::37::05 2019 11 11 // Last Modified By : 12 12 // Last Modified On : … … 46 46 // 47 47 // Several additional features are available through inheritance 48 // | PureVisitor - makes the visitor pure, it never modifies nodes in place and always 49 // clones nodes it needs to make changes to 50 // | WithConstTypeSubstitution - provides polymorphic const TypeSubstitution * typeSubs for the 51 // current expression 52 // | WithStmtsToAdd - provides the ability to insert statements before or after the current 53 // statement by adding new statements into stmtsToAddBefore or 54 // stmtsToAddAfter respectively. 55 // | WithDeclsToAdd - provides the ability to insert declarations before or after the 56 // current declarations by adding new DeclStmt into declsToAddBefore or 57 // declsToAddAfter respectively. 58 // | WithShortCircuiting - provides the ability to skip visiting child nodes; set visit_children 59 // to false in pre{visit,visit} to skip visiting children 60 // | WithGuards - provides the ability to save/restore data like a LIFO stack; to save, 61 // call GuardValue with the variable to save, the variable will 62 // automatically be restored to its previous value after the 63 // corresponding postvisit/postmutate teminates. 64 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 65 // | WithSymbolTable - provides symbol table functionality 66 // 67 // Other Special Members: 68 // | result - Either a method that takes no parameters or a field. If a method (or 69 // callable field) get_result calls it, otherwise the value is returned. 48 // | WithTypeSubstitution - provides polymorphic const TypeSubstitution * env for the 49 // current expression 50 // | WithStmtsToAdd - provides the ability to insert statements before or after the current 51 // statement by adding new statements into stmtsToAddBefore or 52 // stmtsToAddAfter respectively. 53 // | WithDeclsToAdd - provides the ability to insert declarations before or after the current 54 // declarations by adding new DeclStmt into declsToAddBefore or 55 // declsToAddAfter respectively. 56 // | WithShortCircuiting - provides the ability to skip visiting child nodes; set visit_children 57 // to false in pre{visit,visit} to skip visiting children 58 // | WithGuards - provides the ability to save/restore data like a LIFO stack; to save, 59 // call GuardValue with the variable to save, the variable will 60 // automatically be restored to its previous value after the corresponding 61 // postvisit/postmutate teminates. 62 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 63 // | WithSymbolTable - provides symbol table functionality 70 64 //------------------------------------------------------------------------------------------------- 71 template< typename core_t >65 template< typename pass_t > 72 66 class Pass final : public ast::Visitor { 73 67 public: 74 using core_type = core_t;75 using type = Pass<core_t>;76 77 68 /// Forward any arguments to the pass constructor 78 69 /// Propagate 'this' if necessary 79 70 template< typename... Args > 80 71 Pass( Args &&... args) 81 : core( std::forward<Args>( args )... )72 : pass( std::forward<Args>( args )... ) 82 73 { 83 74 // After the pass is constructed, check if it wants the have a pointer to the wrapping visitor 84 type * const * visitor = __pass::visitor(core, 0); 75 typedef Pass<pass_t> this_t; 76 this_t * const * visitor = __pass::visitor(pass, 0); 85 77 if(visitor) { 86 *const_cast<t ype**>( visitor ) = this;78 *const_cast<this_t **>( visitor ) = this; 87 79 } 88 80 } … … 90 82 virtual ~Pass() = default; 91 83 92 /// Storage for the actual pass. 93 core_t core; 94 95 /// If the core defines a result, call it if possible, otherwise return it. 96 inline auto get_result() -> decltype( __pass::get_result( core, '0' ) ) { 97 return __pass::get_result( core, '0' ); 98 } 99 100 /// Construct and run a pass on a translation unit. 101 template< typename... Args > 102 static void run( TranslationUnit & decls, Args &&... args ) { 103 Pass<core_t> visitor( std::forward<Args>( args )... ); 104 accept_all( decls, visitor ); 105 } 106 107 /// Contruct and run a pass on a pointer to extract a value. 108 template< typename node_type, typename... Args > 109 static auto read( node_type const * node, Args&&... args ) { 110 Pass<core_t> visitor( std::forward<Args>( args )... ); 111 node_type const * temp = node->accept( visitor ); 112 assert( temp == node ); 113 return visitor.get_result(); 114 } 115 116 // Versions of the above for older compilers. 117 template< typename... Args > 118 static void run( TranslationUnit & decls ) { 119 Pass<core_t> visitor; 120 accept_all( decls, visitor ); 121 } 122 123 template< typename node_type, typename... Args > 124 static auto read( node_type const * node ) { 125 Pass<core_t> visitor; 126 node_type const * temp = node->accept( visitor ); 127 assert( temp == node ); 128 return visitor.get_result(); 129 } 84 /// Storage for the actual pass 85 pass_t pass; 130 86 131 87 /// Visit function declarations … … 155 111 const ast::Stmt * visit( const ast::CatchStmt * ) override final; 156 112 const ast::Stmt * visit( const ast::FinallyStmt * ) override final; 157 const ast::Stmt * visit( const ast::SuspendStmt * ) override final;158 113 const ast::Stmt * visit( const ast::WaitForStmt * ) override final; 159 114 const ast::Decl * visit( const ast::WithStmt * ) override final; … … 223 178 const ast::TypeSubstitution * visit( const ast::TypeSubstitution * ) override final; 224 179 225 template<typename core_type> 226 friend void accept_all( std::list< ptr<Decl> > & decls, Pass<core_type>& visitor ); 227 228 bool isInFunction() const { 229 return inFunction; 230 } 231 180 template<typename pass_type> 181 friend void accept_all( std::list< ptr<Decl> > & decls, Pass<pass_type>& visitor ); 232 182 private: 233 183 234 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children( core, 0); return ptr ? *ptr : true; }184 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(pass, 0); return ptr ? *ptr : true; } 235 185 236 186 private: 237 187 const ast::Stmt * call_accept( const ast::Stmt * ); 238 188 const ast::Expr * call_accept( const ast::Expr * ); 239 240 // requests WithStmtsToAdd directly add to this statement, as if it is a compound.241 242 const ast::Stmt * call_accept_as_compound(const ast::Stmt *);243 189 244 190 template< typename node_t > … … 260 206 void maybe_accept(const node_t * &, child_t parent_t::* child); 261 207 262 template<typename node_t, typename parent_t, typename child_t>263 void maybe_accept_as_compound(const node_t * &, child_t parent_t::* child);264 265 208 private: 266 209 /// Internal RAII guard for symbol table features 267 210 struct guard_symtab { 268 guard_symtab( Pass< core_t> & pass ): pass( pass ) { __pass::symtab::enter(pass.core, 0); }269 ~guard_symtab() { __pass::symtab::leave(pass .core, 0); }270 Pass< core_t> & pass;211 guard_symtab( Pass<pass_t> & pass ): pass( pass ) { __pass::symtab::enter(pass, 0); } 212 ~guard_symtab() { __pass::symtab::leave(pass, 0); } 213 Pass<pass_t> & pass; 271 214 }; 272 215 273 216 /// Internal RAII guard for scope features 274 217 struct guard_scope { 275 guard_scope( Pass<core_t> & pass ): pass( pass ) { __pass::scope::enter(pass.core, 0); } 276 ~guard_scope() { __pass::scope::leave(pass.core, 0); } 277 Pass<core_t> & pass; 278 }; 279 280 /// Internal RAII guard for forall substitutions 281 struct guard_forall_subs { 282 guard_forall_subs( Pass<core_t> & pass, const FunctionType * type ) 283 : pass( pass ), type( type ) { __pass::forall::enter(pass.core, 0, type ); } 284 ~guard_forall_subs() { __pass::forall::leave(pass.core, 0, type ); } 285 Pass<core_t> & pass; 286 const FunctionType * type; 218 guard_scope( Pass<pass_t> & pass ): pass( pass ) { __pass::scope::enter(pass, 0); } 219 ~guard_scope() { __pass::scope::leave(pass, 0); } 220 Pass<pass_t> & pass; 287 221 }; 288 222 289 223 private: 290 224 bool inFunction = false; 291 bool atFunctionTop = false;292 225 }; 293 226 294 227 /// Apply a pass to an entire translation unit 295 template<typename core_t> 296 void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<core_t> & visitor ); 297 298 template<typename core_t> 299 void accept_all( ast::TranslationUnit &, ast::Pass<core_t> & visitor ); 228 template<typename pass_t> 229 void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<pass_t> & visitor ); 300 230 301 231 //------------------------------------------------------------------------------------------------- … … 303 233 //------------------------------------------------------------------------------------------------- 304 234 305 /// If used the visitor will always clone nodes. 306 struct PureVisitor {}; 307 308 /// Keep track of the polymorphic const TypeSubstitution * typeSubs for the current expression. 235 /// Keep track of the polymorphic const TypeSubstitution * env for the current expression 309 236 struct WithConstTypeSubstitution { 310 const TypeSubstitution * typeSubs= nullptr;237 const TypeSubstitution * env = nullptr; 311 238 }; 312 239 … … 340 267 }; 341 268 342 template< typename core_t>343 friend auto __pass::at_cleanup( core_t & core, int ) -> decltype( &core.at_cleanup );269 template< typename pass_t> 270 friend auto __pass::at_cleanup( pass_t & pass, int ) -> decltype( &pass.at_cleanup ); 344 271 public: 345 272 … … 377 304 378 305 /// Used to get a pointer to the pass with its wrapped type 379 template<typename core_t>306 template<typename pass_t> 380 307 struct WithVisitorRef { 381 Pass<core_t> * const visitor = nullptr; 382 383 bool isInFunction() const { 384 return visitor->isInFunction(); 385 } 308 Pass<pass_t> * const visitor = nullptr; 386 309 }; 387 310 … … 390 313 SymbolTable symtab; 391 314 }; 392 393 315 } 394 316 … … 398 320 extern struct PassVisitorStats { 399 321 size_t depth = 0; 400 Stats::Counters::MaxCounter<double> * max ;401 Stats::Counters::AverageCounter<double> * avg ;322 Stats::Counters::MaxCounter<double> * max = nullptr; 323 Stats::Counters::AverageCounter<double> * avg = nullptr; 402 324 } pass_visitor_stats; 403 325 } -
src/AST/Pass.impl.hpp
reef8dfb rbdfc032 20 20 #include <unordered_map> 21 21 22 #include "AST/TranslationUnit.hpp"23 22 #include "AST/TypeSubstitution.hpp" 24 23 … … 26 25 using namespace ast; \ 27 26 /* back-up the visit children */ \ 28 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children( core, 0) ); \27 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children(pass, 0) ); \ 29 28 /* setup the scope for passes that want to run code at exit */ \ 30 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup (core, 0) ); \ 31 /* begin tracing memory allocation if requested by this pass */ \ 32 __pass::beginTrace( core, 0 ); \ 29 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup (pass, 0) ); \ 33 30 /* call the implementation of the previsit of this pass */ \ 34 __pass::previsit( core, node, 0 );31 __pass::previsit( pass, node, 0 ); 35 32 36 33 #define VISIT( code... ) \ … … 43 40 #define VISIT_END( type, node ) \ 44 41 /* call the implementation of the postvisit of this pass */ \ 45 auto __return = __pass::postvisit( core, node, 0 ); \42 auto __return = __pass::postvisit( pass, node, 0 ); \ 46 43 assertf(__return, "post visit should never return null"); \ 47 /* end tracing memory allocation if requested by this pass */ \48 __pass::endTrace( core, 0 ); \49 44 return __return; 50 45 … … 58 53 59 54 namespace ast { 60 template<typename node_t>61 node_t * shallowCopy( const node_t * node );62 63 55 namespace __pass { 64 56 // Check if this is either a null pointer or a pointer to an empty container … … 68 60 } 69 61 70 template< typename core_t, typename node_t >71 static inline node_t* mutate(const node_t *node) {72 return std::is_base_of<PureVisitor, core_t>::value ? ::ast::shallowCopy(node) : ::ast::mutate(node);73 }74 75 62 //------------------------------ 76 63 template<typename it_t, template <class...> class container_t> … … 132 119 } 133 120 134 template< typename core_t >121 template< typename pass_t > 135 122 template< typename node_t > 136 auto ast::Pass< core_t >::call_accept( const node_t * node )123 auto ast::Pass< pass_t >::call_accept( const node_t * node ) 137 124 -> typename std::enable_if< 138 125 !std::is_base_of<ast::Expr, node_t>::value && … … 140 127 , decltype( node->accept(*this) ) 141 128 >::type 129 142 130 { 143 131 __pedantic_pass_assert( __visit_children() ); 144 __pedantic_pass_assert( node);132 __pedantic_pass_assert( expr ); 145 133 146 134 static_assert( !std::is_base_of<ast::Expr, node_t>::value, "ERROR"); … … 150 138 } 151 139 152 template< typename core_t >153 const ast::Expr * ast::Pass< core_t >::call_accept( const ast::Expr * expr ) {140 template< typename pass_t > 141 const ast::Expr * ast::Pass< pass_t >::call_accept( const ast::Expr * expr ) { 154 142 __pedantic_pass_assert( __visit_children() ); 155 143 __pedantic_pass_assert( expr ); 156 144 157 const ast::TypeSubstitution ** typeSubs_ptr = __pass::typeSubs( core, 0);158 if ( typeSubs_ptr && expr->env ) {159 * typeSubs_ptr = expr->env;145 const ast::TypeSubstitution ** env_ptr = __pass::env( pass, 0); 146 if ( env_ptr && expr->env ) { 147 *env_ptr = expr->env; 160 148 } 161 149 … … 163 151 } 164 152 165 template< typename core_t >166 const ast::Stmt * ast::Pass< core_t >::call_accept( const ast::Stmt * stmt ) {153 template< typename pass_t > 154 const ast::Stmt * ast::Pass< pass_t >::call_accept( const ast::Stmt * stmt ) { 167 155 __pedantic_pass_assert( __visit_children() ); 168 156 __pedantic_pass_assert( stmt ); 169 157 170 return stmt->accept( *this );171 }172 173 template< typename core_t >174 const ast::Stmt * ast::Pass< core_t >::call_accept_as_compound( const ast::Stmt * stmt ) {175 __pedantic_pass_assert( __visit_children() );176 __pedantic_pass_assert( stmt );177 178 158 // add a few useful symbols to the scope 179 159 using __pass::empty; 180 160 181 161 // get the stmts/decls that will need to be spliced in 182 auto stmts_before = __pass::stmtsToAddBefore( core, 0);183 auto stmts_after = __pass::stmtsToAddAfter ( core, 0);184 auto decls_before = __pass::declsToAddBefore( core, 0);185 auto decls_after = __pass::declsToAddAfter ( core, 0);162 auto stmts_before = __pass::stmtsToAddBefore( pass, 0); 163 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0); 164 auto decls_before = __pass::declsToAddBefore( pass, 0); 165 auto decls_after = __pass::declsToAddAfter ( pass, 0); 186 166 187 167 // These may be modified by subnode but most be restored once we exit this statemnet. 188 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass:: typeSubs( core, 0) );168 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass::env( pass, 0) ); 189 169 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) >::type > __old_decls_before( stmts_before ); 190 170 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) >::type > __old_decls_after ( stmts_after ); … … 222 202 } 223 203 224 template< typename core_t >204 template< typename pass_t > 225 205 template< template <class...> class container_t > 226 container_t< ptr<Stmt> > ast::Pass< core_t >::call_accept( const container_t< ptr<Stmt> > & statements ) {206 container_t< ptr<Stmt> > ast::Pass< pass_t >::call_accept( const container_t< ptr<Stmt> > & statements ) { 227 207 __pedantic_pass_assert( __visit_children() ); 228 208 if( statements.empty() ) return {}; … … 235 215 236 216 // get the stmts/decls that will need to be spliced in 237 auto stmts_before = __pass::stmtsToAddBefore( core, 0);238 auto stmts_after = __pass::stmtsToAddAfter ( core, 0);239 auto decls_before = __pass::declsToAddBefore( core, 0);240 auto decls_after = __pass::declsToAddAfter ( core, 0);217 auto stmts_before = __pass::stmtsToAddBefore( pass, 0); 218 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0); 219 auto decls_before = __pass::declsToAddBefore( pass, 0); 220 auto decls_after = __pass::declsToAddAfter ( pass, 0); 241 221 242 222 // These may be modified by subnode but most be restored once we exit this statemnet. … … 288 268 } 289 269 290 template< typename core_t >270 template< typename pass_t > 291 271 template< template <class...> class container_t, typename node_t > 292 container_t< ast::ptr<node_t> > ast::Pass< core_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) {272 container_t< ast::ptr<node_t> > ast::Pass< pass_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) { 293 273 __pedantic_pass_assert( __visit_children() ); 294 274 if( container.empty() ) return {}; … … 319 299 } 320 300 321 template< typename core_t >301 template< typename pass_t > 322 302 template<typename node_t, typename parent_t, typename child_t> 323 void ast::Pass< core_t >::maybe_accept(303 void ast::Pass< pass_t >::maybe_accept( 324 304 const node_t * & parent, 325 305 child_t parent_t::*child … … 337 317 338 318 if( __pass::differs(old_val, new_val) ) { 339 auto new_parent = __pass::mutate<core_t>(parent); 340 new_parent->*child = new_val; 341 parent = new_parent; 342 } 343 } 344 345 template< typename core_t > 346 template<typename node_t, typename parent_t, typename child_t> 347 void ast::Pass< core_t >::maybe_accept_as_compound( 348 const node_t * & parent, 349 child_t parent_t::*child 350 ) { 351 static_assert( std::is_base_of<parent_t, node_t>::value, "Error deducing member object" ); 352 353 if(__pass::skip(parent->*child)) return; 354 const auto & old_val = __pass::get(parent->*child, 0); 355 356 static_assert( !std::is_same<const ast::Node * &, decltype(old_val)>::value, "ERROR"); 357 358 auto new_val = call_accept_as_compound( old_val ); 359 360 static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR"); 361 362 if( __pass::differs(old_val, new_val) ) { 363 auto new_parent = __pass::mutate<core_t>(parent); 319 auto new_parent = mutate(parent); 364 320 new_parent->*child = new_val; 365 321 parent = new_parent; … … 377 333 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 378 334 379 template< typename core_t >380 inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< core_t > & visitor ) {335 template< typename pass_t > 336 inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< pass_t > & visitor ) { 381 337 // We are going to aggregate errors for all these statements 382 338 SemanticErrorException errors; … … 386 342 387 343 // get the stmts/decls that will need to be spliced in 388 auto decls_before = __pass::declsToAddBefore( visitor. core, 0);389 auto decls_after = __pass::declsToAddAfter ( visitor. core, 0);344 auto decls_before = __pass::declsToAddBefore( visitor.pass, 0); 345 auto decls_after = __pass::declsToAddAfter ( visitor.pass, 0); 390 346 391 347 // update pass statitistics … … 407 363 } 408 364 catch( SemanticErrorException &e ) { 409 if (__pass::on_error (visitor.core, *i, 0)) 410 errors.append( e ); 365 errors.append( e ); 411 366 } 412 367 … … 416 371 pass_visitor_stats.depth--; 417 372 if ( !errors.isEmpty() ) { throw errors; } 418 }419 420 template< typename core_t >421 inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & visitor ) {422 return ast::accept_all( unit.decls, visitor );423 373 } 424 374 … … 442 392 //-------------------------------------------------------------------------- 443 393 // ObjectDecl 444 template< typename core_t >445 const ast::DeclWithType * ast::Pass< core_t >::visit( const ast::ObjectDecl * node ) {394 template< typename pass_t > 395 const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::ObjectDecl * node ) { 446 396 VISIT_START( node ); 447 397 … … 456 406 ) 457 407 458 __pass::symtab::addId( core, 0, node );408 __pass::symtab::addId( pass, 0, node ); 459 409 460 410 VISIT_END( DeclWithType, node ); … … 463 413 //-------------------------------------------------------------------------- 464 414 // FunctionDecl 465 template< typename core_t >466 const ast::DeclWithType * ast::Pass< core_t >::visit( const ast::FunctionDecl * node ) {467 VISIT_START( node ); 468 469 __pass::symtab::addId( core, 0, node );415 template< typename pass_t > 416 const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::FunctionDecl * node ) { 417 VISIT_START( node ); 418 419 __pass::symtab::addId( pass, 0, node ); 470 420 471 421 VISIT(maybe_accept( node, &FunctionDecl::withExprs );) … … 475 425 // shadow with exprs and not the other way around. 476 426 guard_symtab guard { *this }; 477 __pass::symtab::addWith( core, 0, node->withExprs, node );427 __pass::symtab::addWith( pass, 0, node->withExprs, node ); 478 428 { 479 429 guard_symtab guard { *this }; 480 430 // implicit add __func__ identifier as specified in the C manual 6.4.2.2 481 static ast:: ptr< ast::ObjectDecl > func{ new ast::ObjectDecl{482 CodeLocation{}, "__func__",483 new ast::ArrayType {484 new ast::BasicType { ast::BasicType::Char, ast::CV::Const },431 static ast::ObjectDecl func( 432 node->location, "__func__", 433 new ast::ArrayType( 434 new ast::BasicType( ast::BasicType::Char, ast::CV::Qualifiers( ast::CV::Const ) ), 485 435 nullptr, VariableLen, DynamicDim 486 }487 } };488 __pass::symtab::addId( core, 0,func );436 ) 437 ); 438 __pass::symtab::addId( pass, 0, &func ); 489 439 VISIT( 490 // parameter declarations 491 maybe_accept( node, &FunctionDecl::params ); 492 maybe_accept( node, &FunctionDecl::returns ); 493 // type params and assertions 494 maybe_accept( node, &FunctionDecl::type_params ); 495 maybe_accept( node, &FunctionDecl::assertions ); 496 // First remember that we are now within a function. 440 maybe_accept( node, &FunctionDecl::type ); 441 // function body needs to have the same scope as parameters - CompoundStmt will not enter 442 // a new scope if inFunction is true 497 443 ValueGuard< bool > oldInFunction( inFunction ); 498 444 inFunction = true; 499 // The function body needs to have the same scope as parameters.500 // A CompoundStmt will not enter a new scope if atFunctionTop is true.501 ValueGuard< bool > oldAtFunctionTop( atFunctionTop );502 atFunctionTop = true;503 445 maybe_accept( node, &FunctionDecl::stmts ); 504 446 maybe_accept( node, &FunctionDecl::attributes ); … … 512 454 //-------------------------------------------------------------------------- 513 455 // StructDecl 514 template< typename core_t >515 const ast::Decl * ast::Pass< core_t >::visit( const ast::StructDecl * node ) {456 template< typename pass_t > 457 const ast::Decl * ast::Pass< pass_t >::visit( const ast::StructDecl * node ) { 516 458 VISIT_START( node ); 517 459 518 460 // make up a forward declaration and add it before processing the members 519 461 // needs to be on the heap because addStruct saves the pointer 520 __pass::symtab::addStructFwd( core, 0, node );462 __pass::symtab::addStructFwd( pass, 0, node ); 521 463 522 464 VISIT({ … … 527 469 528 470 // this addition replaces the forward declaration 529 __pass::symtab::addStruct( core, 0, node );471 __pass::symtab::addStruct( pass, 0, node ); 530 472 531 473 VISIT_END( Decl, node ); … … 534 476 //-------------------------------------------------------------------------- 535 477 // UnionDecl 536 template< typename core_t >537 const ast::Decl * ast::Pass< core_t >::visit( const ast::UnionDecl * node ) {478 template< typename pass_t > 479 const ast::Decl * ast::Pass< pass_t >::visit( const ast::UnionDecl * node ) { 538 480 VISIT_START( node ); 539 481 540 482 // make up a forward declaration and add it before processing the members 541 __pass::symtab::addUnionFwd( core, 0, node );483 __pass::symtab::addUnionFwd( pass, 0, node ); 542 484 543 485 VISIT({ … … 547 489 }) 548 490 549 __pass::symtab::addUnion( core, 0, node );491 __pass::symtab::addUnion( pass, 0, node ); 550 492 551 493 VISIT_END( Decl, node ); … … 554 496 //-------------------------------------------------------------------------- 555 497 // EnumDecl 556 template< typename core_t >557 const ast::Decl * ast::Pass< core_t >::visit( const ast::EnumDecl * node ) {558 VISIT_START( node ); 559 560 __pass::symtab::addEnum( core, 0, node );498 template< typename pass_t > 499 const ast::Decl * ast::Pass< pass_t >::visit( const ast::EnumDecl * node ) { 500 VISIT_START( node ); 501 502 __pass::symtab::addEnum( pass, 0, node ); 561 503 562 504 VISIT( … … 571 513 //-------------------------------------------------------------------------- 572 514 // TraitDecl 573 template< typename core_t >574 const ast::Decl * ast::Pass< core_t >::visit( const ast::TraitDecl * node ) {515 template< typename pass_t > 516 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TraitDecl * node ) { 575 517 VISIT_START( node ); 576 518 … … 581 523 }) 582 524 583 __pass::symtab::addTrait( core, 0, node );525 __pass::symtab::addTrait( pass, 0, node ); 584 526 585 527 VISIT_END( Decl, node ); … … 588 530 //-------------------------------------------------------------------------- 589 531 // TypeDecl 590 template< typename core_t >591 const ast::Decl * ast::Pass< core_t >::visit( const ast::TypeDecl * node ) {532 template< typename pass_t > 533 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TypeDecl * node ) { 592 534 VISIT_START( node ); 593 535 594 536 VISIT({ 595 537 guard_symtab guard { *this }; 538 maybe_accept( node, &TypeDecl::params ); 596 539 maybe_accept( node, &TypeDecl::base ); 597 540 }) … … 600 543 // note that assertions come after the type is added to the symtab, since they are not part of the type proper 601 544 // and may depend on the type itself 602 __pass::symtab::addType( core, 0, node );545 __pass::symtab::addType( pass, 0, node ); 603 546 604 547 VISIT( … … 616 559 //-------------------------------------------------------------------------- 617 560 // TypedefDecl 618 template< typename core_t >619 const ast::Decl * ast::Pass< core_t >::visit( const ast::TypedefDecl * node ) {561 template< typename pass_t > 562 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TypedefDecl * node ) { 620 563 VISIT_START( node ); 621 564 622 565 VISIT({ 623 566 guard_symtab guard { *this }; 567 maybe_accept( node, &TypedefDecl::params ); 624 568 maybe_accept( node, &TypedefDecl::base ); 625 569 }) 626 570 627 __pass::symtab::addType( core, 0, node );571 __pass::symtab::addType( pass, 0, node ); 628 572 629 573 VISIT( maybe_accept( node, &TypedefDecl::assertions ); ) … … 634 578 //-------------------------------------------------------------------------- 635 579 // AsmDecl 636 template< typename core_t >637 const ast::AsmDecl * ast::Pass< core_t >::visit( const ast::AsmDecl * node ) {580 template< typename pass_t > 581 const ast::AsmDecl * ast::Pass< pass_t >::visit( const ast::AsmDecl * node ) { 638 582 VISIT_START( node ); 639 583 … … 647 591 //-------------------------------------------------------------------------- 648 592 // StaticAssertDecl 649 template< typename core_t >650 const ast::StaticAssertDecl * ast::Pass< core_t >::visit( const ast::StaticAssertDecl * node ) {593 template< typename pass_t > 594 const ast::StaticAssertDecl * ast::Pass< pass_t >::visit( const ast::StaticAssertDecl * node ) { 651 595 VISIT_START( node ); 652 596 … … 661 605 //-------------------------------------------------------------------------- 662 606 // CompoundStmt 663 template< typename core_t > 664 const ast::CompoundStmt * ast::Pass< core_t >::visit( const ast::CompoundStmt * node ) { 665 VISIT_START( node ); 666 VISIT( 667 // Do not enter (or leave) a new scope if atFunctionTop. Remember to save the result. 668 auto guard1 = makeFuncGuard( [this, enterScope = !this->atFunctionTop]() { 669 if ( enterScope ) { 670 __pass::symtab::enter(core, 0); 671 __pass::scope::enter(core, 0); 672 } 673 }, [this, leaveScope = !this->atFunctionTop]() { 674 if ( leaveScope ) { 675 __pass::symtab::leave(core, 0); 676 __pass::scope::leave(core, 0); 677 } 607 template< typename pass_t > 608 const ast::CompoundStmt * ast::Pass< pass_t >::visit( const ast::CompoundStmt * node ) { 609 VISIT_START( node ); 610 VISIT({ 611 // do not enter a new scope if inFunction is true - needs to check old state before the assignment 612 auto guard1 = makeFuncGuard( [this, inFunction = this->inFunction]() { 613 if ( ! inFunction ) __pass::symtab::enter(pass, 0); 614 }, [this, inFunction = this->inFunction]() { 615 if ( ! inFunction ) __pass::symtab::leave(pass, 0); 678 616 }); 679 ValueGuard< bool > guard2( atFunctionTop ); 680 atFunctionTop = false; 617 ValueGuard< bool > guard2( inFunction ); 681 618 guard_scope guard3 { *this }; 619 inFunction = false; 682 620 maybe_accept( node, &CompoundStmt::kids ); 683 )621 }) 684 622 VISIT_END( CompoundStmt, node ); 685 623 } … … 687 625 //-------------------------------------------------------------------------- 688 626 // ExprStmt 689 template< typename core_t >690 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ExprStmt * node ) {627 template< typename pass_t > 628 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ExprStmt * node ) { 691 629 VISIT_START( node ); 692 630 … … 700 638 //-------------------------------------------------------------------------- 701 639 // AsmStmt 702 template< typename core_t >703 const ast::Stmt * ast::Pass< core_t >::visit( const ast::AsmStmt * node ) {640 template< typename pass_t > 641 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::AsmStmt * node ) { 704 642 VISIT_START( node ) 705 643 … … 716 654 //-------------------------------------------------------------------------- 717 655 // DirectiveStmt 718 template< typename core_t >719 const ast::Stmt * ast::Pass< core_t >::visit( const ast::DirectiveStmt * node ) {656 template< typename pass_t > 657 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::DirectiveStmt * node ) { 720 658 VISIT_START( node ) 721 659 … … 725 663 //-------------------------------------------------------------------------- 726 664 // IfStmt 727 template< typename core_t >728 const ast::Stmt * ast::Pass< core_t >::visit( const ast::IfStmt * node ) {665 template< typename pass_t > 666 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::IfStmt * node ) { 729 667 VISIT_START( node ); 730 668 … … 734 672 maybe_accept( node, &IfStmt::inits ); 735 673 maybe_accept( node, &IfStmt::cond ); 736 maybe_accept _as_compound( node, &IfStmt::thenPart );737 maybe_accept _as_compound( node, &IfStmt::elsePart );674 maybe_accept( node, &IfStmt::thenPart ); 675 maybe_accept( node, &IfStmt::elsePart ); 738 676 }) 739 677 … … 743 681 //-------------------------------------------------------------------------- 744 682 // WhileStmt 745 template< typename core_t >746 const ast::Stmt * ast::Pass< core_t >::visit( const ast::WhileStmt * node ) {683 template< typename pass_t > 684 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::WhileStmt * node ) { 747 685 VISIT_START( node ); 748 686 … … 752 690 maybe_accept( node, &WhileStmt::inits ); 753 691 maybe_accept( node, &WhileStmt::cond ); 754 maybe_accept _as_compound( node, &WhileStmt::body );692 maybe_accept( node, &WhileStmt::body ); 755 693 }) 756 694 … … 760 698 //-------------------------------------------------------------------------- 761 699 // ForStmt 762 template< typename core_t >763 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ForStmt * node ) {700 template< typename pass_t > 701 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ForStmt * node ) { 764 702 VISIT_START( node ); 765 703 … … 767 705 // for statements introduce a level of scope (for the initialization) 768 706 guard_symtab guard { *this }; 769 // xxx - old ast does not create WithStmtsToAdd scope for loop inits. should revisit this later.770 707 maybe_accept( node, &ForStmt::inits ); 771 708 maybe_accept( node, &ForStmt::cond ); 772 709 maybe_accept( node, &ForStmt::inc ); 773 maybe_accept _as_compound( node, &ForStmt::body );710 maybe_accept( node, &ForStmt::body ); 774 711 }) 775 712 … … 779 716 //-------------------------------------------------------------------------- 780 717 // SwitchStmt 781 template< typename core_t >782 const ast::Stmt * ast::Pass< core_t >::visit( const ast::SwitchStmt * node ) {718 template< typename pass_t > 719 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::SwitchStmt * node ) { 783 720 VISIT_START( node ); 784 721 … … 793 730 //-------------------------------------------------------------------------- 794 731 // CaseStmt 795 template< typename core_t >796 const ast::Stmt * ast::Pass< core_t >::visit( const ast::CaseStmt * node ) {732 template< typename pass_t > 733 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::CaseStmt * node ) { 797 734 VISIT_START( node ); 798 735 … … 807 744 //-------------------------------------------------------------------------- 808 745 // BranchStmt 809 template< typename core_t >810 const ast::Stmt * ast::Pass< core_t >::visit( const ast::BranchStmt * node ) {746 template< typename pass_t > 747 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::BranchStmt * node ) { 811 748 VISIT_START( node ); 812 749 VISIT_END( Stmt, node ); … … 815 752 //-------------------------------------------------------------------------- 816 753 // ReturnStmt 817 template< typename core_t >818 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ReturnStmt * node ) {754 template< typename pass_t > 755 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ReturnStmt * node ) { 819 756 VISIT_START( node ); 820 757 … … 828 765 //-------------------------------------------------------------------------- 829 766 // ThrowStmt 830 template< typename core_t >831 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ThrowStmt * node ) {767 template< typename pass_t > 768 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ThrowStmt * node ) { 832 769 VISIT_START( node ); 833 770 … … 842 779 //-------------------------------------------------------------------------- 843 780 // TryStmt 844 template< typename core_t >845 const ast::Stmt * ast::Pass< core_t >::visit( const ast::TryStmt * node ) {781 template< typename pass_t > 782 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::TryStmt * node ) { 846 783 VISIT_START( node ); 847 784 … … 857 794 //-------------------------------------------------------------------------- 858 795 // CatchStmt 859 template< typename core_t >860 const ast::Stmt * ast::Pass< core_t >::visit( const ast::CatchStmt * node ) {796 template< typename pass_t > 797 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::CatchStmt * node ) { 861 798 VISIT_START( node ); 862 799 … … 866 803 maybe_accept( node, &CatchStmt::decl ); 867 804 maybe_accept( node, &CatchStmt::cond ); 868 maybe_accept _as_compound( node, &CatchStmt::body );805 maybe_accept( node, &CatchStmt::body ); 869 806 }) 870 807 … … 874 811 //-------------------------------------------------------------------------- 875 812 // FinallyStmt 876 template< typename core_t >877 const ast::Stmt * ast::Pass< core_t >::visit( const ast::FinallyStmt * node ) {813 template< typename pass_t > 814 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::FinallyStmt * node ) { 878 815 VISIT_START( node ); 879 816 … … 886 823 887 824 //-------------------------------------------------------------------------- 888 // FinallyStmt889 template< typename core_t >890 const ast::Stmt * ast::Pass< core_t >::visit( const ast::SuspendStmt * node ) {891 VISIT_START( node );892 893 VISIT(894 maybe_accept( node, &SuspendStmt::then );895 )896 897 VISIT_END( Stmt, node );898 }899 900 //--------------------------------------------------------------------------901 825 // WaitForStmt 902 template< typename core_t >903 const ast::Stmt * ast::Pass< core_t >::visit( const ast::WaitForStmt * node ) {826 template< typename pass_t > 827 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::WaitForStmt * node ) { 904 828 VISIT_START( node ); 905 829 // for( auto & clause : node->clauses ) { … … 938 862 939 863 if(mutated) { 940 auto n = __pass::mutate<core_t>(node);864 auto n = mutate(node); 941 865 n->clauses = std::move( new_clauses ); 942 866 node = n; … … 948 872 auto nval = call_accept( node->field ); \ 949 873 if(nval != node->field ) { \ 950 auto nparent = __pass::mutate<core_t>(node); \874 auto nparent = mutate(node); \ 951 875 nparent->field = nval; \ 952 876 node = nparent; \ … … 969 893 //-------------------------------------------------------------------------- 970 894 // WithStmt 971 template< typename core_t >972 const ast::Decl * ast::Pass< core_t >::visit( const ast::WithStmt * node ) {895 template< typename pass_t > 896 const ast::Decl * ast::Pass< pass_t >::visit( const ast::WithStmt * node ) { 973 897 VISIT_START( node ); 974 898 … … 978 902 // catch statements introduce a level of scope (for the caught exception) 979 903 guard_symtab guard { *this }; 980 __pass::symtab::addWith( core, 0, node->exprs, node );904 __pass::symtab::addWith( pass, 0, node->exprs, node ); 981 905 maybe_accept( node, &WithStmt::stmt ); 982 906 } … … 987 911 //-------------------------------------------------------------------------- 988 912 // NullStmt 989 template< typename core_t >990 const ast::NullStmt * ast::Pass< core_t >::visit( const ast::NullStmt * node ) {913 template< typename pass_t > 914 const ast::NullStmt * ast::Pass< pass_t >::visit( const ast::NullStmt * node ) { 991 915 VISIT_START( node ); 992 916 VISIT_END( NullStmt, node ); … … 995 919 //-------------------------------------------------------------------------- 996 920 // DeclStmt 997 template< typename core_t >998 const ast::Stmt * ast::Pass< core_t >::visit( const ast::DeclStmt * node ) {921 template< typename pass_t > 922 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::DeclStmt * node ) { 999 923 VISIT_START( node ); 1000 924 … … 1008 932 //-------------------------------------------------------------------------- 1009 933 // ImplicitCtorDtorStmt 1010 template< typename core_t >1011 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ImplicitCtorDtorStmt * node ) {934 template< typename pass_t > 935 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ImplicitCtorDtorStmt * node ) { 1012 936 VISIT_START( node ); 1013 937 1014 938 // For now this isn't visited, it is unclear if this causes problem 1015 939 // if all tests are known to pass, remove this code 1016 VISIT(1017 maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );1018 )940 // VISIT( 941 // maybe_accept( node, &ImplicitCtorDtorStmt::callStmt ); 942 // ) 1019 943 1020 944 VISIT_END( Stmt, node ); … … 1023 947 //-------------------------------------------------------------------------- 1024 948 // ApplicationExpr 1025 template< typename core_t >1026 const ast::Expr * ast::Pass< core_t >::visit( const ast::ApplicationExpr * node ) {949 template< typename pass_t > 950 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ApplicationExpr * node ) { 1027 951 VISIT_START( node ); 1028 952 … … 1041 965 //-------------------------------------------------------------------------- 1042 966 // UntypedExpr 1043 template< typename core_t >1044 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedExpr * node ) {967 template< typename pass_t > 968 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedExpr * node ) { 1045 969 VISIT_START( node ); 1046 970 … … 1059 983 //-------------------------------------------------------------------------- 1060 984 // NameExpr 1061 template< typename core_t >1062 const ast::Expr * ast::Pass< core_t >::visit( const ast::NameExpr * node ) {985 template< typename pass_t > 986 const ast::Expr * ast::Pass< pass_t >::visit( const ast::NameExpr * node ) { 1063 987 VISIT_START( node ); 1064 988 … … 1073 997 //-------------------------------------------------------------------------- 1074 998 // CastExpr 1075 template< typename core_t >1076 const ast::Expr * ast::Pass< core_t >::visit( const ast::CastExpr * node ) {999 template< typename pass_t > 1000 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CastExpr * node ) { 1077 1001 VISIT_START( node ); 1078 1002 … … 1089 1013 //-------------------------------------------------------------------------- 1090 1014 // KeywordCastExpr 1091 template< typename core_t >1092 const ast::Expr * ast::Pass< core_t >::visit( const ast::KeywordCastExpr * node ) {1015 template< typename pass_t > 1016 const ast::Expr * ast::Pass< pass_t >::visit( const ast::KeywordCastExpr * node ) { 1093 1017 VISIT_START( node ); 1094 1018 … … 1105 1029 //-------------------------------------------------------------------------- 1106 1030 // VirtualCastExpr 1107 template< typename core_t >1108 const ast::Expr * ast::Pass< core_t >::visit( const ast::VirtualCastExpr * node ) {1031 template< typename pass_t > 1032 const ast::Expr * ast::Pass< pass_t >::visit( const ast::VirtualCastExpr * node ) { 1109 1033 VISIT_START( node ); 1110 1034 … … 1121 1045 //-------------------------------------------------------------------------- 1122 1046 // AddressExpr 1123 template< typename core_t >1124 const ast::Expr * ast::Pass< core_t >::visit( const ast::AddressExpr * node ) {1047 template< typename pass_t > 1048 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AddressExpr * node ) { 1125 1049 VISIT_START( node ); 1126 1050 … … 1137 1061 //-------------------------------------------------------------------------- 1138 1062 // LabelAddressExpr 1139 template< typename core_t >1140 const ast::Expr * ast::Pass< core_t >::visit( const ast::LabelAddressExpr * node ) {1063 template< typename pass_t > 1064 const ast::Expr * ast::Pass< pass_t >::visit( const ast::LabelAddressExpr * node ) { 1141 1065 VISIT_START( node ); 1142 1066 … … 1151 1075 //-------------------------------------------------------------------------- 1152 1076 // UntypedMemberExpr 1153 template< typename core_t >1154 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedMemberExpr * node ) {1077 template< typename pass_t > 1078 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedMemberExpr * node ) { 1155 1079 VISIT_START( node ); 1156 1080 … … 1168 1092 //-------------------------------------------------------------------------- 1169 1093 // MemberExpr 1170 template< typename core_t >1171 const ast::Expr * ast::Pass< core_t >::visit( const ast::MemberExpr * node ) {1094 template< typename pass_t > 1095 const ast::Expr * ast::Pass< pass_t >::visit( const ast::MemberExpr * node ) { 1172 1096 VISIT_START( node ); 1173 1097 … … 1184 1108 //-------------------------------------------------------------------------- 1185 1109 // VariableExpr 1186 template< typename core_t >1187 const ast::Expr * ast::Pass< core_t >::visit( const ast::VariableExpr * node ) {1110 template< typename pass_t > 1111 const ast::Expr * ast::Pass< pass_t >::visit( const ast::VariableExpr * node ) { 1188 1112 VISIT_START( node ); 1189 1113 … … 1198 1122 //-------------------------------------------------------------------------- 1199 1123 // ConstantExpr 1200 template< typename core_t >1201 const ast::Expr * ast::Pass< core_t >::visit( const ast::ConstantExpr * node ) {1124 template< typename pass_t > 1125 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConstantExpr * node ) { 1202 1126 VISIT_START( node ); 1203 1127 … … 1212 1136 //-------------------------------------------------------------------------- 1213 1137 // SizeofExpr 1214 template< typename core_t >1215 const ast::Expr * ast::Pass< core_t >::visit( const ast::SizeofExpr * node ) {1138 template< typename pass_t > 1139 const ast::Expr * ast::Pass< pass_t >::visit( const ast::SizeofExpr * node ) { 1216 1140 VISIT_START( node ); 1217 1141 … … 1232 1156 //-------------------------------------------------------------------------- 1233 1157 // AlignofExpr 1234 template< typename core_t >1235 const ast::Expr * ast::Pass< core_t >::visit( const ast::AlignofExpr * node ) {1158 template< typename pass_t > 1159 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AlignofExpr * node ) { 1236 1160 VISIT_START( node ); 1237 1161 … … 1252 1176 //-------------------------------------------------------------------------- 1253 1177 // UntypedOffsetofExpr 1254 template< typename core_t >1255 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedOffsetofExpr * node ) {1178 template< typename pass_t > 1179 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedOffsetofExpr * node ) { 1256 1180 VISIT_START( node ); 1257 1181 … … 1268 1192 //-------------------------------------------------------------------------- 1269 1193 // OffsetofExpr 1270 template< typename core_t >1271 const ast::Expr * ast::Pass< core_t >::visit( const ast::OffsetofExpr * node ) {1194 template< typename pass_t > 1195 const ast::Expr * ast::Pass< pass_t >::visit( const ast::OffsetofExpr * node ) { 1272 1196 VISIT_START( node ); 1273 1197 … … 1284 1208 //-------------------------------------------------------------------------- 1285 1209 // OffsetPackExpr 1286 template< typename core_t >1287 const ast::Expr * ast::Pass< core_t >::visit( const ast::OffsetPackExpr * node ) {1210 template< typename pass_t > 1211 const ast::Expr * ast::Pass< pass_t >::visit( const ast::OffsetPackExpr * node ) { 1288 1212 VISIT_START( node ); 1289 1213 … … 1300 1224 //-------------------------------------------------------------------------- 1301 1225 // LogicalExpr 1302 template< typename core_t >1303 const ast::Expr * ast::Pass< core_t >::visit( const ast::LogicalExpr * node ) {1226 template< typename pass_t > 1227 const ast::Expr * ast::Pass< pass_t >::visit( const ast::LogicalExpr * node ) { 1304 1228 VISIT_START( node ); 1305 1229 … … 1317 1241 //-------------------------------------------------------------------------- 1318 1242 // ConditionalExpr 1319 template< typename core_t >1320 const ast::Expr * ast::Pass< core_t >::visit( const ast::ConditionalExpr * node ) {1243 template< typename pass_t > 1244 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConditionalExpr * node ) { 1321 1245 VISIT_START( node ); 1322 1246 … … 1335 1259 //-------------------------------------------------------------------------- 1336 1260 // CommaExpr 1337 template< typename core_t >1338 const ast::Expr * ast::Pass< core_t >::visit( const ast::CommaExpr * node ) {1261 template< typename pass_t > 1262 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CommaExpr * node ) { 1339 1263 VISIT_START( node ); 1340 1264 … … 1352 1276 //-------------------------------------------------------------------------- 1353 1277 // TypeExpr 1354 template< typename core_t >1355 const ast::Expr * ast::Pass< core_t >::visit( const ast::TypeExpr * node ) {1278 template< typename pass_t > 1279 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TypeExpr * node ) { 1356 1280 VISIT_START( node ); 1357 1281 … … 1368 1292 //-------------------------------------------------------------------------- 1369 1293 // AsmExpr 1370 template< typename core_t >1371 const ast::Expr * ast::Pass< core_t >::visit( const ast::AsmExpr * node ) {1294 template< typename pass_t > 1295 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AsmExpr * node ) { 1372 1296 VISIT_START( node ); 1373 1297 … … 1385 1309 //-------------------------------------------------------------------------- 1386 1310 // ImplicitCopyCtorExpr 1387 template< typename core_t >1388 const ast::Expr * ast::Pass< core_t >::visit( const ast::ImplicitCopyCtorExpr * node ) {1311 template< typename pass_t > 1312 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ImplicitCopyCtorExpr * node ) { 1389 1313 VISIT_START( node ); 1390 1314 … … 1401 1325 //-------------------------------------------------------------------------- 1402 1326 // ConstructorExpr 1403 template< typename core_t >1404 const ast::Expr * ast::Pass< core_t >::visit( const ast::ConstructorExpr * node ) {1327 template< typename pass_t > 1328 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConstructorExpr * node ) { 1405 1329 VISIT_START( node ); 1406 1330 … … 1417 1341 //-------------------------------------------------------------------------- 1418 1342 // CompoundLiteralExpr 1419 template< typename core_t >1420 const ast::Expr * ast::Pass< core_t >::visit( const ast::CompoundLiteralExpr * node ) {1343 template< typename pass_t > 1344 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CompoundLiteralExpr * node ) { 1421 1345 VISIT_START( node ); 1422 1346 … … 1433 1357 //-------------------------------------------------------------------------- 1434 1358 // RangeExpr 1435 template< typename core_t >1436 const ast::Expr * ast::Pass< core_t >::visit( const ast::RangeExpr * node ) {1359 template< typename pass_t > 1360 const ast::Expr * ast::Pass< pass_t >::visit( const ast::RangeExpr * node ) { 1437 1361 VISIT_START( node ); 1438 1362 … … 1450 1374 //-------------------------------------------------------------------------- 1451 1375 // UntypedTupleExpr 1452 template< typename core_t >1453 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedTupleExpr * node ) {1376 template< typename pass_t > 1377 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedTupleExpr * node ) { 1454 1378 VISIT_START( node ); 1455 1379 … … 1466 1390 //-------------------------------------------------------------------------- 1467 1391 // TupleExpr 1468 template< typename core_t >1469 const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleExpr * node ) {1392 template< typename pass_t > 1393 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleExpr * node ) { 1470 1394 VISIT_START( node ); 1471 1395 … … 1482 1406 //-------------------------------------------------------------------------- 1483 1407 // TupleIndexExpr 1484 template< typename core_t >1485 const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleIndexExpr * node ) {1408 template< typename pass_t > 1409 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleIndexExpr * node ) { 1486 1410 VISIT_START( node ); 1487 1411 … … 1498 1422 //-------------------------------------------------------------------------- 1499 1423 // TupleAssignExpr 1500 template< typename core_t >1501 const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleAssignExpr * node ) {1424 template< typename pass_t > 1425 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleAssignExpr * node ) { 1502 1426 VISIT_START( node ); 1503 1427 … … 1514 1438 //-------------------------------------------------------------------------- 1515 1439 // StmtExpr 1516 template< typename core_t >1517 const ast::Expr * ast::Pass< core_t >::visit( const ast::StmtExpr * node ) {1440 template< typename pass_t > 1441 const ast::Expr * ast::Pass< pass_t >::visit( const ast::StmtExpr * node ) { 1518 1442 VISIT_START( node ); 1519 1443 1520 1444 VISIT(// don't want statements from outer CompoundStmts to be added to this StmtExpr 1521 1445 // get the stmts that will need to be spliced in 1522 auto stmts_before = __pass::stmtsToAddBefore( core, 0);1523 auto stmts_after = __pass::stmtsToAddAfter ( core, 0);1446 auto stmts_before = __pass::stmtsToAddBefore( pass, 0); 1447 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0); 1524 1448 1525 1449 // These may be modified by subnode but most be restored once we exit this statemnet. 1526 ValueGuardPtr< const ast::TypeSubstitution * > __old_env( __pass:: typeSubs( core, 0) );1450 ValueGuardPtr< const ast::TypeSubstitution * > __old_env( __pass::env( pass, 0) ); 1527 1451 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) >::type > __old_decls_before( stmts_before ); 1528 1452 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) >::type > __old_decls_after ( stmts_after ); … … 1542 1466 //-------------------------------------------------------------------------- 1543 1467 // UniqueExpr 1544 template< typename core_t >1545 const ast::Expr * ast::Pass< core_t >::visit( const ast::UniqueExpr * node ) {1468 template< typename pass_t > 1469 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UniqueExpr * node ) { 1546 1470 VISIT_START( node ); 1547 1471 … … 1558 1482 //-------------------------------------------------------------------------- 1559 1483 // UntypedInitExpr 1560 template< typename core_t >1561 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedInitExpr * node ) {1484 template< typename pass_t > 1485 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedInitExpr * node ) { 1562 1486 VISIT_START( node ); 1563 1487 … … 1575 1499 //-------------------------------------------------------------------------- 1576 1500 // InitExpr 1577 template< typename core_t >1578 const ast::Expr * ast::Pass< core_t >::visit( const ast::InitExpr * node ) {1501 template< typename pass_t > 1502 const ast::Expr * ast::Pass< pass_t >::visit( const ast::InitExpr * node ) { 1579 1503 VISIT_START( node ); 1580 1504 … … 1592 1516 //-------------------------------------------------------------------------- 1593 1517 // DeletedExpr 1594 template< typename core_t >1595 const ast::Expr * ast::Pass< core_t >::visit( const ast::DeletedExpr * node ) {1518 template< typename pass_t > 1519 const ast::Expr * ast::Pass< pass_t >::visit( const ast::DeletedExpr * node ) { 1596 1520 VISIT_START( node ); 1597 1521 … … 1609 1533 //-------------------------------------------------------------------------- 1610 1534 // DefaultArgExpr 1611 template< typename core_t >1612 const ast::Expr * ast::Pass< core_t >::visit( const ast::DefaultArgExpr * node ) {1535 template< typename pass_t > 1536 const ast::Expr * ast::Pass< pass_t >::visit( const ast::DefaultArgExpr * node ) { 1613 1537 VISIT_START( node ); 1614 1538 … … 1625 1549 //-------------------------------------------------------------------------- 1626 1550 // GenericExpr 1627 template< typename core_t >1628 const ast::Expr * ast::Pass< core_t >::visit( const ast::GenericExpr * node ) {1551 template< typename pass_t > 1552 const ast::Expr * ast::Pass< pass_t >::visit( const ast::GenericExpr * node ) { 1629 1553 VISIT_START( node ); 1630 1554 … … 1654 1578 1655 1579 if(mutated) { 1656 auto n = __pass::mutate<core_t>(node);1580 auto n = mutate(node); 1657 1581 n->associations = std::move( new_kids ); 1658 1582 node = n; … … 1665 1589 //-------------------------------------------------------------------------- 1666 1590 // VoidType 1667 template< typename core_t >1668 const ast::Type * ast::Pass< core_t >::visit( const ast::VoidType * node ) {1591 template< typename pass_t > 1592 const ast::Type * ast::Pass< pass_t >::visit( const ast::VoidType * node ) { 1669 1593 VISIT_START( node ); 1670 1594 … … 1674 1598 //-------------------------------------------------------------------------- 1675 1599 // BasicType 1676 template< typename core_t >1677 const ast::Type * ast::Pass< core_t >::visit( const ast::BasicType * node ) {1600 template< typename pass_t > 1601 const ast::Type * ast::Pass< pass_t >::visit( const ast::BasicType * node ) { 1678 1602 VISIT_START( node ); 1679 1603 … … 1683 1607 //-------------------------------------------------------------------------- 1684 1608 // PointerType 1685 template< typename core_t >1686 const ast::Type * ast::Pass< core_t >::visit( const ast::PointerType * node ) {1609 template< typename pass_t > 1610 const ast::Type * ast::Pass< pass_t >::visit( const ast::PointerType * node ) { 1687 1611 VISIT_START( node ); 1688 1612 … … 1697 1621 //-------------------------------------------------------------------------- 1698 1622 // ArrayType 1699 template< typename core_t >1700 const ast::Type * ast::Pass< core_t >::visit( const ast::ArrayType * node ) {1623 template< typename pass_t > 1624 const ast::Type * ast::Pass< pass_t >::visit( const ast::ArrayType * node ) { 1701 1625 VISIT_START( node ); 1702 1626 … … 1711 1635 //-------------------------------------------------------------------------- 1712 1636 // ReferenceType 1713 template< typename core_t >1714 const ast::Type * ast::Pass< core_t >::visit( const ast::ReferenceType * node ) {1637 template< typename pass_t > 1638 const ast::Type * ast::Pass< pass_t >::visit( const ast::ReferenceType * node ) { 1715 1639 VISIT_START( node ); 1716 1640 … … 1724 1648 //-------------------------------------------------------------------------- 1725 1649 // QualifiedType 1726 template< typename core_t >1727 const ast::Type * ast::Pass< core_t >::visit( const ast::QualifiedType * node ) {1650 template< typename pass_t > 1651 const ast::Type * ast::Pass< pass_t >::visit( const ast::QualifiedType * node ) { 1728 1652 VISIT_START( node ); 1729 1653 … … 1738 1662 //-------------------------------------------------------------------------- 1739 1663 // FunctionType 1740 template< typename core_t > 1741 const ast::Type * ast::Pass< core_t >::visit( const ast::FunctionType * node ) { 1742 VISIT_START( node ); 1743 1744 VISIT({ 1745 // guard_forall_subs forall_guard { *this, node }; 1746 // mutate_forall( node ); 1747 maybe_accept( node, &FunctionType::assertions ); 1664 template< typename pass_t > 1665 const ast::Type * ast::Pass< pass_t >::visit( const ast::FunctionType * node ) { 1666 VISIT_START( node ); 1667 1668 VISIT( 1669 maybe_accept( node, &FunctionType::forall ); 1748 1670 maybe_accept( node, &FunctionType::returns ); 1749 1671 maybe_accept( node, &FunctionType::params ); 1750 })1672 ) 1751 1673 1752 1674 VISIT_END( Type, node ); … … 1755 1677 //-------------------------------------------------------------------------- 1756 1678 // StructInstType 1757 template< typename core_t >1758 const ast::Type * ast::Pass< core_t >::visit( const ast::StructInstType * node ) {1759 VISIT_START( node ); 1760 1761 __pass::symtab::addStruct( core, 0, node->name );1679 template< typename pass_t > 1680 const ast::Type * ast::Pass< pass_t >::visit( const ast::StructInstType * node ) { 1681 VISIT_START( node ); 1682 1683 __pass::symtab::addStruct( pass, 0, node->name ); 1762 1684 1763 1685 VISIT({ 1764 1686 guard_symtab guard { *this }; 1687 maybe_accept( node, &StructInstType::forall ); 1765 1688 maybe_accept( node, &StructInstType::params ); 1766 1689 }) … … 1771 1694 //-------------------------------------------------------------------------- 1772 1695 // UnionInstType 1773 template< typename core_t >1774 const ast::Type * ast::Pass< core_t >::visit( const ast::UnionInstType * node ) {1775 VISIT_START( node ); 1776 1777 __pass::symtab::add Union( core, 0, node->name );1778 1779 VISIT({1696 template< typename pass_t > 1697 const ast::Type * ast::Pass< pass_t >::visit( const ast::UnionInstType * node ) { 1698 VISIT_START( node ); 1699 1700 __pass::symtab::addStruct( pass, 0, node->name ); 1701 1702 { 1780 1703 guard_symtab guard { *this }; 1704 maybe_accept( node, &UnionInstType::forall ); 1781 1705 maybe_accept( node, &UnionInstType::params ); 1782 } )1706 } 1783 1707 1784 1708 VISIT_END( Type, node ); … … 1787 1711 //-------------------------------------------------------------------------- 1788 1712 // EnumInstType 1789 template< typename core_t > 1790 const ast::Type * ast::Pass< core_t >::visit( const ast::EnumInstType * node ) { 1791 VISIT_START( node ); 1792 1793 VISIT({ 1713 template< typename pass_t > 1714 const ast::Type * ast::Pass< pass_t >::visit( const ast::EnumInstType * node ) { 1715 VISIT_START( node ); 1716 1717 VISIT( 1718 maybe_accept( node, &EnumInstType::forall ); 1794 1719 maybe_accept( node, &EnumInstType::params ); 1795 })1720 ) 1796 1721 1797 1722 VISIT_END( Type, node ); … … 1800 1725 //-------------------------------------------------------------------------- 1801 1726 // TraitInstType 1802 template< typename core_t > 1803 const ast::Type * ast::Pass< core_t >::visit( const ast::TraitInstType * node ) { 1804 VISIT_START( node ); 1805 1806 VISIT({ 1727 template< typename pass_t > 1728 const ast::Type * ast::Pass< pass_t >::visit( const ast::TraitInstType * node ) { 1729 VISIT_START( node ); 1730 1731 VISIT( 1732 maybe_accept( node, &TraitInstType::forall ); 1807 1733 maybe_accept( node, &TraitInstType::params ); 1808 })1734 ) 1809 1735 1810 1736 VISIT_END( Type, node ); … … 1813 1739 //-------------------------------------------------------------------------- 1814 1740 // TypeInstType 1815 template< typename core_t > 1816 const ast::Type * ast::Pass< core_t >::visit( const ast::TypeInstType * node ) { 1817 VISIT_START( node ); 1818 1819 VISIT( 1820 { 1821 maybe_accept( node, &TypeInstType::params ); 1822 } 1823 // ensure that base re-bound if doing substitution 1824 __pass::forall::replace( core, 0, node ); 1741 template< typename pass_t > 1742 const ast::Type * ast::Pass< pass_t >::visit( const ast::TypeInstType * node ) { 1743 VISIT_START( node ); 1744 1745 VISIT( 1746 maybe_accept( node, &TypeInstType::forall ); 1747 maybe_accept( node, &TypeInstType::params ); 1825 1748 ) 1826 1749 … … 1830 1753 //-------------------------------------------------------------------------- 1831 1754 // TupleType 1832 template< typename core_t >1833 const ast::Type * ast::Pass< core_t >::visit( const ast::TupleType * node ) {1755 template< typename pass_t > 1756 const ast::Type * ast::Pass< pass_t >::visit( const ast::TupleType * node ) { 1834 1757 VISIT_START( node ); 1835 1758 … … 1844 1767 //-------------------------------------------------------------------------- 1845 1768 // TypeofType 1846 template< typename core_t >1847 const ast::Type * ast::Pass< core_t >::visit( const ast::TypeofType * node ) {1769 template< typename pass_t > 1770 const ast::Type * ast::Pass< pass_t >::visit( const ast::TypeofType * node ) { 1848 1771 VISIT_START( node ); 1849 1772 … … 1857 1780 //-------------------------------------------------------------------------- 1858 1781 // VarArgsType 1859 template< typename core_t >1860 const ast::Type * ast::Pass< core_t >::visit( const ast::VarArgsType * node ) {1782 template< typename pass_t > 1783 const ast::Type * ast::Pass< pass_t >::visit( const ast::VarArgsType * node ) { 1861 1784 VISIT_START( node ); 1862 1785 … … 1866 1789 //-------------------------------------------------------------------------- 1867 1790 // ZeroType 1868 template< typename core_t >1869 const ast::Type * ast::Pass< core_t >::visit( const ast::ZeroType * node ) {1791 template< typename pass_t > 1792 const ast::Type * ast::Pass< pass_t >::visit( const ast::ZeroType * node ) { 1870 1793 VISIT_START( node ); 1871 1794 … … 1875 1798 //-------------------------------------------------------------------------- 1876 1799 // OneType 1877 template< typename core_t >1878 const ast::Type * ast::Pass< core_t >::visit( const ast::OneType * node ) {1800 template< typename pass_t > 1801 const ast::Type * ast::Pass< pass_t >::visit( const ast::OneType * node ) { 1879 1802 VISIT_START( node ); 1880 1803 … … 1884 1807 //-------------------------------------------------------------------------- 1885 1808 // GlobalScopeType 1886 template< typename core_t >1887 const ast::Type * ast::Pass< core_t >::visit( const ast::GlobalScopeType * node ) {1809 template< typename pass_t > 1810 const ast::Type * ast::Pass< pass_t >::visit( const ast::GlobalScopeType * node ) { 1888 1811 VISIT_START( node ); 1889 1812 … … 1894 1817 //-------------------------------------------------------------------------- 1895 1818 // Designation 1896 template< typename core_t >1897 const ast::Designation * ast::Pass< core_t >::visit( const ast::Designation * node ) {1819 template< typename pass_t > 1820 const ast::Designation * ast::Pass< pass_t >::visit( const ast::Designation * node ) { 1898 1821 VISIT_START( node ); 1899 1822 … … 1905 1828 //-------------------------------------------------------------------------- 1906 1829 // SingleInit 1907 template< typename core_t >1908 const ast::Init * ast::Pass< core_t >::visit( const ast::SingleInit * node ) {1830 template< typename pass_t > 1831 const ast::Init * ast::Pass< pass_t >::visit( const ast::SingleInit * node ) { 1909 1832 VISIT_START( node ); 1910 1833 … … 1918 1841 //-------------------------------------------------------------------------- 1919 1842 // ListInit 1920 template< typename core_t >1921 const ast::Init * ast::Pass< core_t >::visit( const ast::ListInit * node ) {1843 template< typename pass_t > 1844 const ast::Init * ast::Pass< pass_t >::visit( const ast::ListInit * node ) { 1922 1845 VISIT_START( node ); 1923 1846 … … 1932 1855 //-------------------------------------------------------------------------- 1933 1856 // ConstructorInit 1934 template< typename core_t >1935 const ast::Init * ast::Pass< core_t >::visit( const ast::ConstructorInit * node ) {1857 template< typename pass_t > 1858 const ast::Init * ast::Pass< pass_t >::visit( const ast::ConstructorInit * node ) { 1936 1859 VISIT_START( node ); 1937 1860 … … 1947 1870 //-------------------------------------------------------------------------- 1948 1871 // Attribute 1949 template< typename core_t >1950 const ast::Attribute * ast::Pass< core_t >::visit( const ast::Attribute * node ) {1872 template< typename pass_t > 1873 const ast::Attribute * ast::Pass< pass_t >::visit( const ast::Attribute * node ) { 1951 1874 VISIT_START( node ); 1952 1875 … … 1960 1883 //-------------------------------------------------------------------------- 1961 1884 // TypeSubstitution 1962 template< typename core_t >1963 const ast::TypeSubstitution * ast::Pass< core_t >::visit( const ast::TypeSubstitution * node ) {1885 template< typename pass_t > 1886 const ast::TypeSubstitution * ast::Pass< pass_t >::visit( const ast::TypeSubstitution * node ) { 1964 1887 VISIT_START( node ); 1965 1888 … … 1967 1890 { 1968 1891 bool mutated = false; 1969 std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;1892 std::unordered_map< std::string, ast::ptr< ast::Type > > new_map; 1970 1893 for ( const auto & p : node->typeEnv ) { 1971 1894 guard_symtab guard { *this }; 1972 1895 auto new_node = p.second->accept( *this ); 1973 if (new_node != p.second) mutated = true;1896 if (new_node != p.second) mutated = false; 1974 1897 new_map.insert({ p.first, new_node }); 1975 1898 } 1976 1899 if (mutated) { 1977 auto new_node = __pass::mutate<core_t>( node );1900 auto new_node = mutate( node ); 1978 1901 new_node->typeEnv.swap( new_map ); 1979 1902 node = new_node; 1980 1903 } 1981 1904 } 1905 1906 { 1907 bool mutated = false; 1908 std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map; 1909 for ( const auto & p : node->varEnv ) { 1910 guard_symtab guard { *this }; 1911 auto new_node = p.second->accept( *this ); 1912 if (new_node != p.second) mutated = false; 1913 new_map.insert({ p.first, new_node }); 1914 } 1915 if (mutated) { 1916 auto new_node = mutate( node ); 1917 new_node->varEnv.swap( new_map ); 1918 node = new_node; 1919 } 1920 } 1982 1921 ) 1983 1922 -
src/AST/Pass.proto.hpp
reef8dfb rbdfc032 17 17 // IWYU pragma: private, include "Pass.hpp" 18 18 19 #include "Common/Stats/Heap.h"20 21 19 namespace ast { 22 template<typename core_t>20 template<typename pass_type> 23 21 class Pass; 24 25 struct TranslationUnit;26 27 struct PureVisitor;28 22 29 23 namespace __pass { … … 88 82 }; 89 83 90 std::stack< cleanup_t , std::vector<cleanup_t>> cleanups;84 std::stack< cleanup_t > cleanups; 91 85 }; 92 86 … … 117 111 /// "Short hand" to check if this is a valid previsit function 118 112 /// Mostly used to make the static_assert look (and print) prettier 119 template<typename core_t, typename node_t>113 template<typename pass_t, typename node_t> 120 114 struct is_valid_previsit { 121 using ret_t = decltype( (( core_t*)nullptr)->previsit( (const node_t *)nullptr ) );115 using ret_t = decltype( ((pass_t*)nullptr)->previsit( (const node_t *)nullptr ) ); 122 116 123 117 static constexpr bool value = std::is_void< ret_t >::value || … … 133 127 template<> 134 128 struct __assign<true> { 135 template<typename core_t, typename node_t>136 static inline void result( core_t & core, const node_t * & node ) {137 core.previsit( node );129 template<typename pass_t, typename node_t> 130 static inline void result( pass_t & pass, const node_t * & node ) { 131 pass.previsit( node ); 138 132 } 139 133 }; … … 141 135 template<> 142 136 struct __assign<false> { 143 template<typename core_t, typename node_t>144 static inline void result( core_t & core, const node_t * & node ) {145 node = core.previsit( node );137 template<typename pass_t, typename node_t> 138 static inline void result( pass_t & pass, const node_t * & node ) { 139 node = pass.previsit( node ); 146 140 assertf(node, "Previsit must not return NULL"); 147 141 } … … 156 150 template<> 157 151 struct __return<true> { 158 template<typename core_t, typename node_t>159 static inline const node_t * result( core_t & core, const node_t * & node ) {160 core.postvisit( node );152 template<typename pass_t, typename node_t> 153 static inline const node_t * result( pass_t & pass, const node_t * & node ) { 154 pass.postvisit( node ); 161 155 return node; 162 156 } … … 165 159 template<> 166 160 struct __return<false> { 167 template<typename core_t, typename node_t>168 static inline auto result( core_t & core, const node_t * & node ) {169 return core.postvisit( node );161 template<typename pass_t, typename node_t> 162 static inline auto result( pass_t & pass, const node_t * & node ) { 163 return pass.postvisit( node ); 170 164 } 171 165 }; … … 186 180 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 187 181 // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call 188 template<typename core_t, typename node_t>189 static inline auto previsit( core_t & core, const node_t * & node, int ) -> decltype( core.previsit( node ), void() ) {182 template<typename pass_t, typename node_t> 183 static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) { 190 184 static_assert( 191 is_valid_previsit< core_t, node_t>::value,185 is_valid_previsit<pass_t, node_t>::value, 192 186 "Previsit may not change the type of the node. It must return its paremeter or void." 193 187 ); … … 195 189 __assign< 196 190 std::is_void< 197 decltype( core.previsit( node ) )191 decltype( pass.previsit( node ) ) 198 192 >::value 199 >::result( core, node );193 >::result( pass, node ); 200 194 } 201 195 202 template<typename core_t, typename node_t>203 static inline auto previsit( core_t &, const node_t *, long ) {}196 template<typename pass_t, typename node_t> 197 static inline auto previsit( pass_t &, const node_t *, long ) {} 204 198 205 199 // PostVisit : never mutates the passed pointer but may return a different node 206 template<typename core_t, typename node_t>207 static inline auto postvisit( core_t & core, const node_t * node, int ) ->208 decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) )200 template<typename pass_t, typename node_t> 201 static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> 202 decltype( pass.postvisit( node ), node->accept( *(Visitor*)nullptr ) ) 209 203 { 210 204 return __return< 211 205 std::is_void< 212 decltype( core.postvisit( node ) )206 decltype( pass.postvisit( node ) ) 213 207 >::value 214 >::result( core, node );208 >::result( pass, node ); 215 209 } 216 210 217 template<typename core_t, typename node_t>218 static inline const node_t * postvisit( core_t &, const node_t * node, long ) { return node; }211 template<typename pass_t, typename node_t> 212 static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; } 219 213 220 214 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- … … 231 225 // The type is not strictly enforced but does match the accessory 232 226 #define FIELD_PTR( name, default_type ) \ 233 template< typename core_t > \234 static inline auto name( core_t & core, int ) -> decltype( &core.name ) { return &core.name; } \227 template< typename pass_t > \ 228 static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \ 235 229 \ 236 template< typename core_t > \237 static inline default_type * name( core_t &, long ) { return nullptr; }230 template< typename pass_t > \ 231 static inline default_type * name( pass_t &, long ) { return nullptr; } 238 232 239 233 // List of fields and their expected types 240 FIELD_PTR( typeSubs, const ast::TypeSubstitution * )234 FIELD_PTR( env, const ast::TypeSubstitution * ) 241 235 FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > ) 242 236 FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > ) … … 245 239 FIELD_PTR( visit_children, __pass::bool_ref ) 246 240 FIELD_PTR( at_cleanup, __pass::at_cleanup_t ) 247 FIELD_PTR( visitor, ast::Pass< core_t> * const )241 FIELD_PTR( visitor, ast::Pass<pass_t> * const ) 248 242 249 243 // Remove the macro to make sure we don't clash 250 244 #undef FIELD_PTR 251 252 template< typename core_t >253 static inline auto beginTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {254 // Stats::Heap::stacktrace_push(core_t::traceId);255 }256 257 template< typename core_t >258 static inline auto endTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {259 // Stats::Heap::stacktrace_pop();260 }261 262 template< typename core_t >263 static void beginTrace(core_t &, long) {}264 265 template< typename core_t >266 static void endTrace(core_t &, long) {}267 268 // Allows visitor to handle an error on top-level declarations, and possibly suppress the error.269 // If onError() returns false, the error will be ignored. By default, it returns true.270 271 template< typename core_t >272 static bool on_error (core_t &, ptr<Decl> &, long) { return true; }273 274 template< typename core_t >275 static auto on_error (core_t & core, ptr<Decl> & decl, int) -> decltype(core.on_error(decl)) {276 return core.on_error(decl);277 }278 245 279 246 // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement. … … 281 248 // detect it using the same strategy 282 249 namespace scope { 283 template<typename core_t>284 static inline auto enter( core_t & core, int ) -> decltype( core.beginScope(), void() ) {285 core.beginScope();286 } 287 288 template<typename core_t>289 static inline void enter( core_t &, long ) {}290 291 template<typename core_t>292 static inline auto leave( core_t & core, int ) -> decltype( core.endScope(), void() ) {293 core.endScope();294 } 295 296 template<typename core_t>297 static inline void leave( core_t &, long ) {}298 } // namespace scope299 300 // Certain passes desire an up to date symbol table automatically250 template<typename pass_t> 251 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) { 252 pass.beginScope(); 253 } 254 255 template<typename pass_t> 256 static inline void enter( pass_t &, long ) {} 257 258 template<typename pass_t> 259 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) { 260 pass.endScope(); 261 } 262 263 template<typename pass_t> 264 static inline void leave( pass_t &, long ) {} 265 }; 266 267 // Finally certain pass desire an up to date symbol table automatically 301 268 // detect the presence of a member name `symtab` and call all the members appropriately 302 269 namespace symtab { 303 270 // Some simple scoping rules 304 template<typename core_t>305 static inline auto enter( core_t & core, int ) -> decltype( core.symtab, void() ) {306 core.symtab.enterScope();307 } 308 309 template<typename core_t>310 static inline auto enter( core_t &, long ) {}311 312 template<typename core_t>313 static inline auto leave( core_t & core, int ) -> decltype( core.symtab, void() ) {314 core.symtab.leaveScope();315 } 316 317 template<typename core_t>318 static inline auto leave( core_t &, long ) {}271 template<typename pass_t> 272 static inline auto enter( pass_t & pass, int ) -> decltype( pass.symtab.enterScope(), void() ) { 273 pass.symtab.enterScope(); 274 } 275 276 template<typename pass_t> 277 static inline auto enter( pass_t &, long ) {} 278 279 template<typename pass_t> 280 static inline auto leave( pass_t & pass, int ) -> decltype( pass.symtab.leaveScope(), void() ) { 281 pass.symtab.leaveScope(); 282 } 283 284 template<typename pass_t> 285 static inline auto leave( pass_t &, long ) {} 319 286 320 287 // The symbol table has 2 kind of functions mostly, 1 argument and 2 arguments 321 288 // Create macro to condense these common patterns 322 289 #define SYMTAB_FUNC1( func, type ) \ 323 template<typename core_t> \324 static inline auto func( core_t & core, int, type arg ) -> decltype( core.symtab.func( arg ), void() ) {\325 core.symtab.func( arg ); \290 template<typename pass_t> \ 291 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.symtab.func( arg ), void() ) {\ 292 pass.symtab.func( arg ); \ 326 293 } \ 327 294 \ 328 template<typename core_t> \329 static inline void func( core_t &, long, type ) {}295 template<typename pass_t> \ 296 static inline void func( pass_t &, long, type ) {} 330 297 331 298 #define SYMTAB_FUNC2( func, type1, type2 ) \ 332 template<typename core_t> \333 static inline auto func( core_t & core, int, type1 arg1, type2 arg2 ) -> decltype( core.symtab.func( arg1, arg2 ), void () ) {\334 core.symtab.func( arg1, arg2 ); \299 template<typename pass_t> \ 300 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.symtab.func( arg1, arg2 ), void () ) {\ 301 pass.symtab.func( arg1, arg2 ); \ 335 302 } \ 336 303 \ 337 template<typename core_t> \338 static inline void func( core_t &, long, type1, type2 ) {}304 template<typename pass_t> \ 305 static inline void func( pass_t &, long, type1, type2 ) {} 339 306 340 307 SYMTAB_FUNC1( addId , const DeclWithType * ); … … 344 311 SYMTAB_FUNC1( addUnion , const UnionDecl * ); 345 312 SYMTAB_FUNC1( addTrait , const TraitDecl * ); 346 SYMTAB_FUNC2( addWith , const std::vector< ptr<Expr> > &, const Decl* );313 SYMTAB_FUNC2( addWith , const std::vector< ptr<Expr> > &, const Node * ); 347 314 348 315 // A few extra functions have more complicated behaviour, they are hand written 349 template<typename core_t>350 static inline auto addStructFwd( core_t & core, int, const ast::StructDecl * decl ) -> decltype( core.symtab.addStruct( decl ), void() ) {316 template<typename pass_t> 317 static inline auto addStructFwd( pass_t & pass, int, const ast::StructDecl * decl ) -> decltype( pass.symtab.addStruct( decl ), void() ) { 351 318 ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name ); 352 319 fwd->params = decl->params; 353 core.symtab.addStruct( fwd );354 } 355 356 template<typename core_t>357 static inline void addStructFwd( core_t &, long, const ast::StructDecl * ) {}358 359 template<typename core_t>360 static inline auto addUnionFwd( core_t & core, int, const ast::UnionDecl * decl ) -> decltype( core.symtab.addUnion( decl ), void() ) {320 pass.symtab.addStruct( fwd ); 321 } 322 323 template<typename pass_t> 324 static inline void addStructFwd( pass_t &, long, const ast::StructDecl * ) {} 325 326 template<typename pass_t> 327 static inline auto addUnionFwd( pass_t & pass, int, const ast::UnionDecl * decl ) -> decltype( pass.symtab.addUnion( decl ), void() ) { 361 328 UnionDecl * fwd = new UnionDecl( decl->location, decl->name ); 362 329 fwd->params = decl->params; 363 core.symtab.addUnion( fwd );364 } 365 366 template<typename core_t>367 static inline void addUnionFwd( core_t &, long, const ast::UnionDecl * ) {}368 369 template<typename core_t>370 static inline auto addStruct( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addStruct( str ), void() ) {371 if ( ! core.symtab.lookupStruct( str ) ) {372 core.symtab.addStruct( str );373 } 374 } 375 376 template<typename core_t>377 static inline void addStruct( core_t &, long, const std::string & ) {}378 379 template<typename core_t>380 static inline auto addUnion( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addUnion( str ), void() ) {381 if ( ! core.symtab.lookupUnion( str ) ) {382 core.symtab.addUnion( str );383 } 384 } 385 386 template<typename core_t>387 static inline void addUnion( core_t &, long, const std::string & ) {}330 pass.symtab.addUnion( fwd ); 331 } 332 333 template<typename pass_t> 334 static inline void addUnionFwd( pass_t &, long, const ast::UnionDecl * ) {} 335 336 template<typename pass_t> 337 static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addStruct( str ), void() ) { 338 if ( ! pass.symtab.lookupStruct( str ) ) { 339 pass.symtab.addStruct( str ); 340 } 341 } 342 343 template<typename pass_t> 344 static inline void addStruct( pass_t &, long, const std::string & ) {} 345 346 template<typename pass_t> 347 static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addUnion( str ), void() ) { 348 if ( ! pass.symtab.lookupUnion( str ) ) { 349 pass.symtab.addUnion( str ); 350 } 351 } 352 353 template<typename pass_t> 354 static inline void addUnion( pass_t &, long, const std::string & ) {} 388 355 389 356 #undef SYMTAB_FUNC1 390 357 #undef SYMTAB_FUNC2 391 } // namespace symtab 392 393 // Some passes need to mutate TypeDecl and properly update their pointing TypeInstType. 394 // Detect the presence of a member name `subs` and call all members appropriately 395 namespace forall { 396 // Some simple scoping rules 397 template<typename core_t> 398 static inline auto enter( core_t & core, int, const ast::FunctionType * type ) 399 -> decltype( core.subs, void() ) { 400 if ( ! type->forall.empty() ) core.subs.beginScope(); 401 } 402 403 template<typename core_t> 404 static inline auto enter( core_t &, long, const ast::FunctionType * ) {} 405 406 template<typename core_t> 407 static inline auto leave( core_t & core, int, const ast::FunctionType * type ) 408 -> decltype( core.subs, void() ) { 409 if ( ! type->forall.empty() ) { core.subs.endScope(); } 410 } 411 412 template<typename core_t> 413 static inline auto leave( core_t &, long, const ast::FunctionType * ) {} 414 415 // Replaces a TypeInstType's base TypeDecl according to the table 416 template<typename core_t> 417 static inline auto replace( core_t & core, int, const ast::TypeInstType *& inst ) 418 -> decltype( core.subs, void() ) { 419 inst = ast::mutate_field( 420 inst, &ast::TypeInstType::base, core.subs.replace( inst->base ) ); 421 } 422 423 template<typename core_t> 424 static inline auto replace( core_t &, long, const ast::TypeInstType *& ) {} 425 426 } // namespace forall 427 428 template<typename core_t> 429 static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) { 430 return core.result(); 431 } 432 433 template<typename core_t> 434 static inline auto get_result( core_t & core, int ) -> decltype( core.result ) { 435 return core.result; 436 } 437 438 template<typename core_t> 439 static inline void get_result( core_t &, long ) {} 440 } // namespace __pass 441 } // namespace ast 358 }; 359 }; 360 }; -
src/AST/Print.cpp
reef8dfb rbdfc032 21 21 #include "Type.hpp" 22 22 #include "TypeSubstitution.hpp" 23 #include "CompilationState.h"24 23 25 24 #include "Common/utility.h" // for group_iterate … … 30 29 31 30 template <typename C, typename... T> 32 constexpr array<C,sizeof...(T)> make_array(T&&... values) 31 constexpr auto make_array(T&&... values) -> 32 array<C,sizeof...(T)> 33 33 { 34 34 return array<C,sizeof...(T)>{ … … 129 129 130 130 void print( const ast::Expr::InferUnion & inferred, unsigned level = 0 ) { 131 if (inferred.data.resnSlots && !inferred.data.resnSlots->empty()) { 132 os << indent << "with " << inferred.data.resnSlots->size() 131 switch ( inferred.mode ) { 132 case ast::Expr::InferUnion::Empty: return; 133 case ast::Expr::InferUnion::Slots: { 134 os << indent << "with " << inferred.data.resnSlots.size() 133 135 << " pending inference slots" << endl; 134 } 135 if (inferred.data.inferParams && !inferred.data.inferParams->empty()) { 136 return; 137 } 138 case ast::Expr::InferUnion::Params: { 136 139 os << indent << "with inferred parameters " << level << ":" << endl; 137 140 ++indent; 138 for ( const auto & i : *inferred.data.inferParams ) {141 for ( const auto & i : inferred.data.inferParams ) { 139 142 os << indent; 140 short_print( i.second.declptr);143 short_print( Decl::fromId( i.second.decl ) ); 141 144 os << endl; 142 145 print( i.second.expr->inferred, level+1 ); 143 146 } 144 147 --indent; 145 } 146 } 147 148 void print( const ast::FunctionType::ForallList & forall ) { 148 return; 149 } 150 } 151 } 152 153 void print( const ast::ParameterizedType::ForallList & forall ) { 149 154 if ( forall.empty() ) return; 150 155 os << "forall" << endl; 151 156 ++indent; 152 157 printAll( forall ); 153 os << indent;154 --indent;155 }156 157 void print( const ast::FunctionType::AssertionList & assts ) {158 if (assts.empty()) return;159 os << "with assertions" << endl;160 ++indent;161 printAll(assts);162 158 os << indent; 163 159 --indent; … … 214 210 215 211 void preprint( const ast::NamedTypeDecl * node ) { 216 if ( ! node->name.empty() ) { 217 os << node->name << ": "; 218 } 212 if ( ! node->name.empty() ) os << node->name << ": "; 219 213 220 214 if ( ! short_mode && node->linkage != Linkage::Cforall ) { … … 232 226 } 233 227 234 if ( ! node->assertions.empty() ) { 228 if ( ! node->params.empty() ) { 229 os << endl << indent << "... with parameters" << endl; 230 ++indent; 231 printAll( node->params ); 232 --indent; 233 } 234 235 if ( ! short_mode && ! node->assertions.empty() ) { 235 236 os << endl << indent << "... with assertions" << endl; 236 237 ++indent; … … 243 244 print( node->inferred ); 244 245 245 if ( node->result ) {246 os << endl << indent << "... with resolved type:" << endl;247 ++indent;248 os << indent;249 node->result->accept( *this );250 --indent;251 }252 253 246 if ( node->env ) { 254 247 os << endl << indent << "... with environment:" << endl; … … 267 260 } 268 261 269 void preprint( const ast:: FunctionType * node ) {262 void preprint( const ast::ParameterizedType * node ) { 270 263 print( node->forall ); 271 print( node->assertions );272 264 print( node->qualifiers ); 273 265 } 274 266 275 void preprint( const ast::BaseInstType * node ) { 267 void preprint( const ast::ReferenceToType * node ) { 268 print( node->forall ); 276 269 print( node->attributes ); 277 270 print( node->qualifiers ); … … 685 678 } 686 679 687 virtual const ast::Stmt * visit( const ast::SuspendStmt * node ) override final {688 os << "Suspend Statement";689 switch (node->type) {690 case ast::SuspendStmt::None : os << " with implicit target"; break;691 case ast::SuspendStmt::Generator: os << " for generator"; break;692 case ast::SuspendStmt::Coroutine: os << " for coroutine"; break;693 }694 os << endl;695 696 ++indent;697 if(node->then) {698 os << indent << " with post statement :" << endl;699 safe_print( node->then );700 }701 ++indent;702 703 return node;704 }705 706 680 virtual const ast::Stmt * visit( const ast::WaitForStmt * node ) override final { 707 681 os << "Waitfor Statement" << endl; … … 849 823 virtual const ast::Expr * visit( const ast::CastExpr * node ) override final { 850 824 ++indent; 851 os << (node->isGenerated ? "Generated" : "Explicit") << " Cast of:" << endl << indent;825 os << (node->isGenerated ? "Generated" : "Explicit") << " cast of:" << endl << indent; 852 826 safe_print( node->arg ); 853 827 os << endl << indent-1 << "... to:"; … … 1384 1358 virtual const ast::Type * visit( const ast::TypeInstType * node ) override final { 1385 1359 preprint( node ); 1386 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->typeString(); 1387 os << "instance of type " << _name 1360 os << "instance of type " << node->name 1388 1361 << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)"; 1389 1362 print( node->params ); … … 1511 1484 os << indent << "Types:" << endl; 1512 1485 for ( const auto& i : *node ) { 1513 os << indent+1 << i.first .typeString()<< " -> ";1486 os << indent+1 << i.first << " -> "; 1514 1487 indent += 2; 1515 1488 safe_print( i.second ); 1489 indent -= 2; 1490 os << endl; 1491 } 1492 os << indent << "Non-types:" << endl; 1493 for ( auto i = node->beginVar(); i != node->endVar(); ++i ) { 1494 os << indent+1 << i->first << " -> "; 1495 indent += 2; 1496 safe_print( i->second ); 1516 1497 indent -= 2; 1517 1498 os << endl; -
src/AST/Stmt.hpp
reef8dfb rbdfc032 27 27 28 28 // Must be included in *all* AST classes; should be #undef'd at the end of the file 29 #define MUTATE_FRIEND \ 30 template<typename node_t> friend node_t * mutate(const node_t * node); \ 31 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 29 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node); 32 30 33 31 namespace ast { … … 344 342 }; 345 343 346 /// Suspend statement347 class SuspendStmt final : public Stmt {348 public:349 ptr<CompoundStmt> then;350 enum Type { None, Coroutine, Generator } type = None;351 352 SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, std::vector<Label> && labels = {} )353 : Stmt(loc, std::move(labels)), then(then), type(type) {}354 355 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }356 private:357 SuspendStmt * clone() const override { return new SuspendStmt{ *this }; }358 MUTATE_FRIEND359 };360 361 344 /// Wait for concurrency statement `when (...) waitfor (... , ...) ... timeout(...) ... else ...` 362 345 class WaitForStmt final : public Stmt { … … 414 397 class ImplicitCtorDtorStmt final : public Stmt { 415 398 public: 416 ptr<Stmt> callStmt;399 readonly<Stmt> callStmt; 417 400 418 401 ImplicitCtorDtorStmt( const CodeLocation & loc, const Stmt * callStmt, -
src/AST/SymbolTable.cpp
reef8dfb rbdfc032 95 95 } 96 96 97 SymbolTable::SpecialFunctionKind SymbolTable::getSpecialFunctionKind(const std::string & name) {98 if (name == "?{}") return CTOR;99 if (name == "^?{}") return DTOR;100 if (name == "?=?") return ASSIGN;101 return NUMBER_OF_KINDS;102 }103 104 97 std::vector<SymbolTable::IdData> SymbolTable::lookupId( const std::string &id ) const { 105 static Stats::Counters::CounterGroup * name_lookup_stats = Stats::Counters::build<Stats::Counters::CounterGroup>("Name Lookup Stats");106 static std::map<std::string, Stats::Counters::SimpleCounter *> lookups_by_name;107 static std::map<std::string, Stats::Counters::SimpleCounter *> candidates_by_name;108 109 SpecialFunctionKind kind = getSpecialFunctionKind(id);110 if (kind != NUMBER_OF_KINDS) return specialLookupId(kind);111 112 98 ++*stats().lookup_calls; 113 99 if ( ! idTable ) return {}; … … 121 107 out.push_back( decl.second ); 122 108 } 123 124 if (Stats::Counters::enabled) {125 if (! lookups_by_name.count(id)) {126 // leaks some strings, but it is because Counters do not hold them127 auto lookupCounterName = new std::string(id + "%count");128 auto candidatesCounterName = new std::string(id + "%candidate");129 lookups_by_name.emplace(id, new Stats::Counters::SimpleCounter(lookupCounterName->c_str(), name_lookup_stats));130 candidates_by_name.emplace(id, new Stats::Counters::SimpleCounter(candidatesCounterName->c_str(), name_lookup_stats));131 }132 (*lookups_by_name[id]) ++;133 *candidates_by_name[id] += out.size();134 }135 136 return out;137 }138 139 std::vector<SymbolTable::IdData> SymbolTable::specialLookupId( SymbolTable::SpecialFunctionKind kind, const std::string & otypeKey ) const {140 static Stats::Counters::CounterGroup * special_stats = Stats::Counters::build<Stats::Counters::CounterGroup>("Special Lookups");141 static Stats::Counters::SimpleCounter * stat_counts[3] = {142 Stats::Counters::build<Stats::Counters::SimpleCounter>("constructor - count", special_stats),143 Stats::Counters::build<Stats::Counters::SimpleCounter>("destructor - count", special_stats),144 Stats::Counters::build<Stats::Counters::SimpleCounter>("assignment - count", special_stats)145 };146 147 static Stats::Counters::SimpleCounter * stat_candidates[3] = {148 Stats::Counters::build<Stats::Counters::SimpleCounter>("constructor - candidates", special_stats),149 Stats::Counters::build<Stats::Counters::SimpleCounter>("destructor - candidates", special_stats),150 Stats::Counters::build<Stats::Counters::SimpleCounter>("assignment - candidates", special_stats)151 };152 153 static Stats::Counters::SimpleCounter * num_lookup_with_key154 = Stats::Counters::build<Stats::Counters::SimpleCounter>("keyed lookups", special_stats);155 static Stats::Counters::SimpleCounter * num_lookup_without_key156 = Stats::Counters::build<Stats::Counters::SimpleCounter>("unkeyed lookups", special_stats);157 158 assert (kind != NUMBER_OF_KINDS);159 ++*stats().lookup_calls;160 if ( ! specialFunctionTable[kind] ) return {};161 162 std::vector<IdData> out;163 164 if (otypeKey.empty()) { // returns everything165 ++*num_lookup_without_key;166 for (auto & table : *specialFunctionTable[kind]) {167 for (auto & decl : *table.second) {168 out.push_back(decl.second);169 }170 }171 }172 else {173 ++*num_lookup_with_key;174 ++*stats().map_lookups;175 auto decls = specialFunctionTable[kind]->find(otypeKey);176 if (decls == specialFunctionTable[kind]->end()) return {};177 178 for (auto decl : *(decls->second)) {179 out.push_back(decl.second);180 }181 }182 183 ++*stat_counts[kind];184 *stat_candidates[kind] += out.size();185 186 109 return out; 187 110 } … … 390 313 if ( ! expr->result ) continue; 391 314 const Type * resTy = expr->result->stripReferences(); 392 auto aggrType = dynamic_cast< const BaseInstType * >( resTy );315 auto aggrType = dynamic_cast< const ReferenceToType * >( resTy ); 393 316 assertf( aggrType, "WithStmt expr has non-aggregate type: %s", 394 317 toString( expr->result ).c_str() ); … … 412 335 } 413 336 414 415 void SymbolTable::addFunction( const FunctionDecl * func ) { 416 for (auto & td : func->type_params) { 417 addType(td); 418 } 419 for (auto & asst : func->assertions) { 420 addId(asst); 421 } 422 // addTypes( func->type->forall ); 423 addIds( func->returns ); 424 addIds( func->params ); 425 } 426 337 void SymbolTable::addFunctionType( const FunctionType * ftype ) { 338 addTypes( ftype->forall ); 339 addIds( ftype->returns ); 340 addIds( ftype->params ); 341 } 427 342 428 343 void SymbolTable::lazyInitScope() { … … 449 364 namespace { 450 365 /// gets the base type of the first parameter; decl must be a ctor/dtor/assignment function 451 std::string getOtypeKey( const Function Type * ftype, bool stripParams = true) {452 const auto & params = f type->params;366 std::string getOtypeKey( const FunctionDecl * function ) { 367 const auto & params = function->type->params; 453 368 assert( ! params.empty() ); 454 369 // use base type of pointer, so that qualifiers on the pointer type aren't considered. 455 const Type * base = InitTweak::getPointerBase( params.front() );370 const Type * base = InitTweak::getPointerBase( params.front()->get_type() ); 456 371 assert( base ); 457 if (stripParams) { 458 if (dynamic_cast<const PointerType *>(base)) return Mangle::Encoding::pointer; 459 return Mangle::mangle( base, Mangle::Type | Mangle::NoGenericParams ); 460 } 461 else 462 return Mangle::mangle( base ); 372 return Mangle::mangle( base ); 463 373 } 464 374 … … 468 378 const DeclWithType * decl, const std::string & otypeKey ) { 469 379 auto func = dynamic_cast< const FunctionDecl * >( decl ); 470 if ( ! func || otypeKey != getOtypeKey( func ->type, false) ) return nullptr;380 if ( ! func || otypeKey != getOtypeKey( func ) ) return nullptr; 471 381 return func; 472 382 } … … 493 403 bool dataIsUserDefinedFunc = ! function->linkage.is_overrideable; 494 404 bool dataIsCopyFunc = InitTweak::isCopyFunction( function ); 495 std::string dataOtypeKey = getOtypeKey( function ->type, false ); // requires exact match to override autogen405 std::string dataOtypeKey = getOtypeKey( function ); 496 406 497 407 if ( dataIsUserDefinedFunc && dataIsCopyFunc ) { … … 665 575 const DeclWithType * decl, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr, 666 576 const Decl * deleter ) { 667 SpecialFunctionKind kind = getSpecialFunctionKind(decl->name);668 if (kind == NUMBER_OF_KINDS) { // not a special decl669 addId(decl, decl->name, idTable, handleConflicts, baseExpr, deleter);670 }671 else {672 std::string key;673 if (auto func = dynamic_cast<const FunctionDecl *>(decl)) {674 key = getOtypeKey(func->type);675 }676 else if (auto obj = dynamic_cast<const ObjectDecl *>(decl)) {677 key = getOtypeKey(obj->type.strict_as<PointerType>()->base.strict_as<FunctionType>());678 }679 else {680 assertf(false, "special decl with non-function type");681 }682 addId(decl, key, specialFunctionTable[kind], handleConflicts, baseExpr, deleter);683 }684 }685 686 void SymbolTable::addId(687 const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & table, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr,688 const Decl * deleter ) {689 577 ++*stats().add_calls; 690 578 const std::string &name = decl->name; … … 717 605 // ensure tables exist and add identifier 718 606 MangleTable::Ptr mangleTable; 719 if ( ! table ) {720 table = IdTable::new_ptr();607 if ( ! idTable ) { 608 idTable = IdTable::new_ptr(); 721 609 mangleTable = MangleTable::new_ptr(); 722 610 } else { 723 611 ++*stats().map_lookups; 724 auto decls = table->find( lookupKey);725 if ( decls == table->end() ) {612 auto decls = idTable->find( name ); 613 if ( decls == idTable->end() ) { 726 614 mangleTable = MangleTable::new_ptr(); 727 615 } else { … … 738 626 lazyInitScope(); 739 627 *stats().map_mutations += 2; 740 table = table->set(741 lookupKey,628 idTable = idTable->set( 629 name, 742 630 mangleTable->set( 743 631 mangleName, … … 754 642 IdData data{ decl, baseExpr, deleter, scope }; 755 643 // Ensure that auto-generated ctor/dtor/assignment are deleted if necessary 756 if (table != idTable) { // adding to special table 757 if ( ! removeSpecialOverrides( data, mangleTable ) ) return; 758 } 644 if ( ! removeSpecialOverrides( data, mangleTable ) ) return; 759 645 *stats().map_mutations += 2; 760 table = table->set( lookupKey, mangleTable->set( mangleName, std::move(data) ) );646 idTable = idTable->set( name, mangleTable->set( mangleName, std::move(data) ) ); 761 647 } 762 648 … … 768 654 if ( dwt->name == "" ) { 769 655 const Type * t = dwt->get_type()->stripReferences(); 770 if ( auto rty = dynamic_cast<const BaseInstType *>( t ) ) {656 if ( auto rty = dynamic_cast<const ReferenceToType *>( t ) ) { 771 657 if ( ! dynamic_cast<const StructInstType *>(rty) 772 658 && ! dynamic_cast<const UnionInstType *>(rty) ) continue; -
src/AST/SymbolTable.hpp
reef8dfb rbdfc032 33 33 class SymbolTable final : public std::enable_shared_from_this<ast::SymbolTable> { 34 34 public: 35 /// special functions stored in dedicated tables, with different lookup keys36 enum SpecialFunctionKind {CTOR, DTOR, ASSIGN, NUMBER_OF_KINDS};37 static SpecialFunctionKind getSpecialFunctionKind(const std::string & name);38 39 35 /// Stored information about a declaration 40 36 struct IdData { … … 81 77 UnionTable::Ptr unionTable; ///< union namespace 82 78 TraitTable::Ptr traitTable; ///< trait namespace 83 IdTable::Ptr specialFunctionTable[NUMBER_OF_KINDS];84 85 // using SpecialFuncTable = PersistentMap< std::string, IdTable::Ptr >; // fname (ctor/dtor/assign) - otypekey86 // SpecialFuncTable::Ptr specialFuncTable;87 79 88 80 using Ptr = std::shared_ptr<const SymbolTable>; … … 103 95 /// Gets all declarations with the given ID 104 96 std::vector<IdData> lookupId( const std::string &id ) const; 105 /// Gets special functions associated with a type; if no key is given, returns everything106 std::vector<IdData> specialLookupId( SpecialFunctionKind kind, const std::string & otypeKey = "" ) const;107 97 /// Gets the top-most type declaration with the given ID 108 98 const NamedTypeDecl * lookupType( const std::string &id ) const; … … 155 145 156 146 /// convenience function for adding all of the declarations in a function type to the indexer 157 void addFunction ( const FunctionDecl *);147 void addFunctionType( const FunctionType * ftype ); 158 148 159 149 private: … … 196 186 const Decl * deleter = nullptr ); 197 187 198 /// common code for addId when special decls are placed into separate tables199 void addId(200 const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & idTable, OnConflict handleConflicts,201 const Expr * baseExpr = nullptr, const Decl * deleter = nullptr);202 203 188 /// adds all of the members of the Aggregate (addWith helper) 204 189 void addMembers( const AggregateDecl * aggr, const Expr * expr, OnConflict handleConflicts ); -
src/AST/Type.cpp
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Mon May 13 15:00:00 2019 11 // Last Modified By : Andrew Beach12 // Last Modified On : Thu Jul 23 14:16:00 202013 // Update Count : 511 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Dec 15 16:56:28 2019 13 // Update Count : 4 14 14 // 15 15 … … 22 22 #include "Decl.hpp" 23 23 #include "Init.hpp" 24 #include "Common/utility.h" // for copy, move25 24 #include "InitTweak/InitTweak.h" // for getPointerBase 26 25 #include "Tuples/Tuples.h" // for isTtype … … 92 91 93 92 // --- FunctionType 93 94 94 namespace { 95 bool containsTtype( const std::vector<ptr< Type>> & l ) {95 bool containsTtype( const std::vector<ptr<DeclWithType>> & l ) { 96 96 if ( ! l.empty() ) { 97 return Tuples::isTtype( l.back() );97 return Tuples::isTtype( l.back()->get_type() ); 98 98 } 99 99 return false; … … 105 105 } 106 106 107 std::vector<readonly<Decl>> BaseInstType::lookup( const std::string& name ) const { 107 // --- ReferenceToType 108 std::vector<readonly<Decl>> ReferenceToType::lookup( const std::string& name ) const { 108 109 assertf( aggr(), "Must have aggregate to perform lookup" ); 109 110 … … 115 116 } 116 117 117 // --- S ueInstType (StructInstType, UnionInstType, EnumInstType)118 // --- StructInstType 118 119 119 template<typename decl_t> 120 SueInstType<decl_t>::SueInstType( 121 const decl_t * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as ) 122 : BaseInstType( b->name, q, move(as) ), base( b ) {} 120 StructInstType::StructInstType( const StructDecl * b, CV::Qualifiers q, 121 std::vector<ptr<Attribute>>&& as ) 122 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {} 123 123 124 template<typename decl_t> 125 SueInstType<decl_t>::SueInstType( 126 const base_type * b, std::vector<ptr<Expr>> && params, 127 CV::Qualifiers q, std::vector<ptr<Attribute>> && as ) 128 : BaseInstType( b->name, std::move(params), q, std::move(as) ), base( b ) {} 124 bool StructInstType::isComplete() const { return base ? base->body : false; } 129 125 130 template<typename decl_t> 131 bool SueInstType<decl_t>::isComplete() const { 132 return base ? base->body : false; 133 } 126 // --- UnionInstType 134 127 135 template class SueInstType<StructDecl>; 136 template class SueInstType<UnionDecl>; 137 template class SueInstType<EnumDecl>; 128 UnionInstType::UnionInstType( const UnionDecl * b, CV::Qualifiers q, 129 std::vector<ptr<Attribute>>&& as ) 130 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {} 131 132 bool UnionInstType::isComplete() const { return base ? base->body : false; } 133 134 // --- EnumInstType 135 136 EnumInstType::EnumInstType( const EnumDecl * b, CV::Qualifiers q, 137 std::vector<ptr<Attribute>>&& as ) 138 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {} 139 140 bool EnumInstType::isComplete() const { return base ? base->body : false; } 138 141 139 142 // --- TraitInstType 140 143 141 TraitInstType::TraitInstType( 142 const TraitDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as ) 143 : BaseInstType( b->name, q, move(as) ), base( b ) {} 144 TraitInstType::TraitInstType( const TraitDecl * b, CV::Qualifiers q, 145 std::vector<ptr<Attribute>>&& as ) 146 : ReferenceToType( b->name, q, std::move(as) ), base( b ) {} 147 148 // --- TypeInstType 144 149 145 150 void TypeInstType::set_base( const TypeDecl * b ) { … … 153 158 154 159 TupleType::TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q ) 155 : Type( q ), types( move(ts) ), members() {160 : Type( q ), types( std::move(ts) ), members() { 156 161 // This constructor is awkward. `TupleType` needs to contain objects so that members can be 157 162 // named, but members without initializer nodes end up getting constructors, which breaks … … 168 173 for ( const Type * ty : types ) { 169 174 members.emplace_back( new ObjectDecl{ 170 CodeLocation{}, "", ty, new ListInit( CodeLocation{}, {}, {}, NoConstruct ),175 CodeLocation{}, "", ty, new ListInit( CodeLocation{}, {}, {}, MaybeConstruct ), 171 176 Storage::Classes{}, Linkage::Cforall } ); 172 177 } 173 }174 175 bool isUnboundType(const Type * type) {176 if (auto typeInst = dynamic_cast<const TypeInstType *>(type)) {177 // xxx - look for a type name produced by renameTyVars.178 179 // TODO: once TypeInstType representation is updated, it should properly check180 // if the context id is filled. this is a temporary hack for now181 return typeInst->formal_usage > 0;182 }183 return false;184 178 } 185 179 -
src/AST/Type.hpp
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Thu May 9 10:00:00 2019 11 // Last Modified By : Andrew Beach12 // Last Modified On : Thu Jul 23 14:15:00 202013 // Update Count : 611 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Dec 11 21:56:46 2019 13 // Update Count : 5 14 14 // 15 15 … … 29 29 30 30 // Must be included in *all* AST classes; should be #undef'd at the end of the file 31 #define MUTATE_FRIEND \ 32 template<typename node_t> friend node_t * mutate(const node_t * node); \ 33 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 31 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node); 34 32 35 33 namespace ast { 36 37 template< typename T > class Pass;38 34 39 35 class Type : public Node { … … 48 44 bool is_volatile() const { return qualifiers.is_volatile; } 49 45 bool is_restrict() const { return qualifiers.is_restrict; } 46 bool is_lvalue() const { return qualifiers.is_lvalue; } 50 47 bool is_mutex() const { return qualifiers.is_mutex; } 51 48 bool is_atomic() const { return qualifiers.is_atomic; } … … 54 51 Type * set_volatile( bool v ) { qualifiers.is_volatile = v; return this; } 55 52 Type * set_restrict( bool v ) { qualifiers.is_restrict = v; return this; } 53 Type * set_lvalue( bool v ) { qualifiers.is_lvalue = v; return this; } 56 54 Type * set_mutex( bool v ) { qualifiers.is_mutex = v; return this; } 57 55 Type * set_atomic( bool v ) { qualifiers.is_atomic = v; return this; } … … 165 163 static const char *typeNames[]; 166 164 167 BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 165 BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 168 166 : Type(q, std::move(as)), kind(k) {} 169 167 … … 265 263 }; 266 264 265 /// Base type for potentially forall-qualified types 266 class ParameterizedType : public Type { 267 public: 268 using ForallList = std::vector<ptr<TypeDecl>>; 269 270 ForallList forall; 271 272 ParameterizedType( ForallList&& fs = {}, CV::Qualifiers q = {}, 273 std::vector<ptr<Attribute>> && as = {} ) 274 : Type(q, std::move(as)), forall(std::move(fs)) {} 275 276 ParameterizedType( CV::Qualifiers q, std::vector<ptr<Attribute>> && as = {} ) 277 : Type(q, std::move(as)), forall() {} 278 279 private: 280 virtual ParameterizedType * clone() const override = 0; 281 MUTATE_FRIEND 282 }; 283 267 284 /// Function variable arguments flag 268 285 enum ArgumentFlag { FixedArgs, VariableArgs }; 269 286 270 287 /// Type of a function `[R1, R2](*)(P1, P2, P3)` 271 class FunctionType final : public Type { 272 public: 273 using ForallList = std::vector<ptr<TypeInstType>>; 274 using AssertionList = std::vector<ptr<VariableExpr>>; 275 ForallList forall; 276 AssertionList assertions; 277 278 std::vector<ptr<Type>> returns; 279 std::vector<ptr<Type>> params; 288 class FunctionType final : public ParameterizedType { 289 public: 290 std::vector<ptr<DeclWithType>> returns; 291 std::vector<ptr<DeclWithType>> params; 280 292 281 293 /// Does the function accept a variable number of arguments following the arguments specified … … 287 299 288 300 FunctionType( ArgumentFlag va = FixedArgs, CV::Qualifiers q = {} ) 289 : Type(q), returns(), params(), isVarArgs(va) {} 290 291 FunctionType( const FunctionType & o ) = default; 301 : ParameterizedType(q), returns(), params(), isVarArgs(va) {} 292 302 293 303 /// true if either the parameters or return values contain a tttype … … 303 313 304 314 /// base class for types that refer to types declared elsewhere (aggregates and typedefs) 305 class BaseInstType : publicType {315 class ReferenceToType : public ParameterizedType { 306 316 public: 307 317 std::vector<ptr<Expr>> params; … … 309 319 bool hoistType = false; 310 320 311 BaseInstType( 312 const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 313 : Type(q, std::move(as)), params(), name(n) {} 314 315 BaseInstType( 316 const std::string& n, std::vector<ptr<Expr>> && params, 317 CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 318 : Type(q, std::move(as)), params(std::move(params)), name(n) {} 319 320 BaseInstType( const BaseInstType & o ) = default; 321 ReferenceToType( const std::string& n, CV::Qualifiers q = {}, 322 std::vector<ptr<Attribute>> && as = {} ) 323 : ParameterizedType(q, std::move(as)), params(), name(n) {} 321 324 322 325 /// Gets aggregate declaration this type refers to … … 326 329 327 330 private: 328 virtual BaseInstType * clone() const override = 0; 329 MUTATE_FRIEND 330 }; 331 332 // Common implementation for the SUE instance types. Not to be used directly. 333 template<typename decl_t> 334 class SueInstType final : public BaseInstType { 335 public: 336 using base_type = decl_t; 337 readonly<decl_t> base; 338 339 SueInstType( 340 const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 341 : BaseInstType( n, q, std::move(as) ), base() {} 342 343 SueInstType( 344 const base_type * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ); 345 346 SueInstType( 347 const base_type * b, std::vector<ptr<Expr>> && params, 348 CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ); 331 virtual ReferenceToType * clone() const override = 0; 332 MUTATE_FRIEND 333 }; 334 335 /// instance of struct type 336 class StructInstType final : public ReferenceToType { 337 public: 338 readonly<StructDecl> base; 339 340 StructInstType( const std::string& n, CV::Qualifiers q = {}, 341 std::vector<ptr<Attribute>> && as = {} ) 342 : ReferenceToType( n, q, std::move(as) ), base() {} 343 StructInstType( const StructDecl * b, CV::Qualifiers q = {}, 344 std::vector<ptr<Attribute>> && as = {} ); 349 345 350 346 bool isComplete() const override; 351 347 352 const decl_t * aggr() const override { return base; } 353 354 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 355 private: 356 SueInstType<decl_t> * clone() const override { return new SueInstType<decl_t>{ *this }; } 357 MUTATE_FRIEND 358 }; 359 360 /// An instance of a struct type. 361 using StructInstType = SueInstType<StructDecl>; 362 363 /// An instance of a union type. 364 using UnionInstType = SueInstType<UnionDecl>; 365 366 /// An instance of an enum type. 367 using EnumInstType = SueInstType<EnumDecl>; 368 369 /// An instance of a trait type. 370 class TraitInstType final : public BaseInstType { 348 const StructDecl * aggr() const override { return base; } 349 350 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 351 private: 352 StructInstType * clone() const override { return new StructInstType{ *this }; } 353 MUTATE_FRIEND 354 }; 355 356 /// instance of union type 357 class UnionInstType final : public ReferenceToType { 358 public: 359 readonly<UnionDecl> base; 360 361 UnionInstType( const std::string& n, CV::Qualifiers q = {}, 362 std::vector<ptr<Attribute>> && as = {} ) 363 : ReferenceToType( n, q, std::move(as) ), base() {} 364 UnionInstType( const UnionDecl * b, CV::Qualifiers q = {}, 365 std::vector<ptr<Attribute>> && as = {} ); 366 367 bool isComplete() const override; 368 369 const UnionDecl * aggr() const override { return base; } 370 371 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 372 private: 373 UnionInstType * clone() const override { return new UnionInstType{ *this }; } 374 MUTATE_FRIEND 375 }; 376 377 /// instance of enum type 378 class EnumInstType final : public ReferenceToType { 379 public: 380 readonly<EnumDecl> base; 381 382 EnumInstType( const std::string& n, CV::Qualifiers q = {}, 383 std::vector<ptr<Attribute>> && as = {} ) 384 : ReferenceToType( n, q, std::move(as) ), base() {} 385 EnumInstType( const EnumDecl * b, CV::Qualifiers q = {}, 386 std::vector<ptr<Attribute>> && as = {} ); 387 388 bool isComplete() const override; 389 390 const EnumDecl * aggr() const override { return base; } 391 392 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 393 private: 394 EnumInstType * clone() const override { return new EnumInstType{ *this }; } 395 MUTATE_FRIEND 396 }; 397 398 /// instance of trait type 399 class TraitInstType final : public ReferenceToType { 371 400 public: 372 401 readonly<TraitDecl> base; 373 402 374 TraitInstType( 375 const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 376 : BaseInstType( n, q, std::move(as) ), base() {} 377 378 TraitInstType( 379 const TraitDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ); 403 TraitInstType( const std::string& n, CV::Qualifiers q = {}, 404 std::vector<ptr<Attribute>> && as = {} ) 405 : ReferenceToType( n, q, std::move(as) ), base() {} 406 TraitInstType( const TraitDecl * b, CV::Qualifiers q = {}, 407 std::vector<ptr<Attribute>> && as = {} ); 380 408 381 409 // not meaningful for TraitInstType … … 391 419 392 420 /// instance of named type alias (typedef or variable) 393 class TypeInstType final : public BaseInstType {421 class TypeInstType final : public ReferenceToType { 394 422 public: 395 423 readonly<TypeDecl> base; 396 // previously from renameTyVars; now directly use integer fields instead of synthesized strings397 // a nonzero value of formal_usage indicates a formal type (only used in function type)398 // a zero value of formal_usage indicates an actual type (referenced inside body of parametric structs and functions)399 424 TypeDecl::Kind kind; 400 int formal_usage = 0; 401 int expr_id = 0; 402 403 // compact representation used for map lookups. 404 struct TypeEnvKey { 405 const TypeDecl * base; 406 int formal_usage; 407 int expr_id; 408 409 TypeEnvKey() = default; 410 TypeEnvKey(const TypeDecl * base, int formal_usage = 0, int expr_id = 0): base(base), formal_usage(formal_usage), expr_id(expr_id) {} 411 TypeEnvKey(const TypeInstType & inst): base(inst.base), formal_usage(inst.formal_usage), expr_id(inst.expr_id) {} 412 std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; } 413 bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 414 415 }; 416 417 bool operator==(const TypeInstType & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 418 419 TypeInstType( 420 const std::string& n, const TypeDecl * b, CV::Qualifiers q = {}, 421 std::vector<ptr<Attribute>> && as = {} ) 422 : BaseInstType( n, q, std::move(as) ), base( b ), kind( b->kind ) {} 425 426 TypeInstType( const std::string& n, const TypeDecl * b, CV::Qualifiers q = {}, 427 std::vector<ptr<Attribute>> && as = {} ) 428 : ReferenceToType( n, q, std::move(as) ), base( b ), kind( b->kind ) {} 423 429 TypeInstType( const std::string& n, TypeDecl::Kind k, CV::Qualifiers q = {}, 424 430 std::vector<ptr<Attribute>> && as = {} ) 425 : BaseInstType( n, q, std::move(as) ), base(), kind( k ) {} 426 427 TypeInstType( const TypeInstType & o ) = default; 428 429 TypeInstType( const TypeEnvKey & key ) 430 : BaseInstType(key.base->name), base(key.base), kind(key.base->kind), formal_usage(key.formal_usage), expr_id(key.expr_id) {} 431 : ReferenceToType( n, q, std::move(as) ), base(), kind( k ) {} 431 432 432 433 /// sets `base`, updating `kind` correctly … … 439 440 440 441 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 441 442 std::string typeString() const {443 if (formal_usage > 0) return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + name;444 else return name;445 }446 442 private: 447 443 TypeInstType * clone() const override { return new TypeInstType{ *this }; } … … 535 531 }; 536 532 537 bool isUnboundType(const Type * type);538 539 }540 541 namespace std {542 template<>543 struct hash<typename ast::TypeInstType::TypeEnvKey> {544 size_t operator() (const ast::TypeInstType::TypeEnvKey & x) const {545 const size_t p = 1000007;546 size_t res = reinterpret_cast<size_t>(x.base);547 res = p * res + x.formal_usage;548 res = p * res + x.expr_id;549 return res;550 }551 };552 533 } 553 534 -
src/AST/TypeEnvironment.cpp
reef8dfb rbdfc032 34 34 #include "ResolvExpr/Unify.h" // for unifyInexact 35 35 #include "Tuples/Tuples.h" // for isTtype 36 #include "CompilationState.h"37 36 38 37 using ResolvExpr::WidenMode; … … 52 51 for ( const auto & i : open ) { 53 52 if ( first ) { first = false; } else { out << ' '; } 54 out << i.first .typeString()<< "(" << i.second << ")";53 out << i.first << "(" << i.second << ")"; 55 54 } 56 55 } 57 56 58 57 void print( std::ostream & out, const EqvClass & clz, Indenter indent ) { 59 out << "("; 60 bool first = true; 61 for(const auto & var : clz.vars) { 62 if(first) first = false; 63 else out << " "; 64 65 if( deterministic_output ) out << "[unbound]"; 66 else out << "_" << var.formal_usage << "_" << var.expr_id << "_"; 67 68 out << var.base->name; 69 } 58 out << "( "; 59 std::copy( clz.vars.begin(), clz.vars.end(), std::ostream_iterator< std::string >( out, " " ) ); 70 60 out << ")"; 71 61 72 62 if ( clz.bound ) { 73 63 out << " -> "; … … 82 72 } 83 73 84 const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey& var ) const {74 const EqvClass * TypeEnvironment::lookup( const std::string & var ) const { 85 75 for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) { 86 76 if ( i->vars.find( var ) != i->vars.end() ) return &*i; … … 102 92 } 103 93 } 104 94 105 95 i = next; // go to next node even if this removed 106 96 } … … 108 98 } 109 99 110 void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) {111 for ( auto &tyDecl : tyDecls ) {100 void TypeEnvironment::add( const ParameterizedType::ForallList & tyDecls ) { 101 for ( const TypeDecl * tyDecl : tyDecls ) { 112 102 env.emplace_back( tyDecl ); 113 103 } … … 122 112 void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const { 123 113 for ( const auto & clz : env ) { 124 TypeInstType::TypeEnvKey clzRep; 125 bool first = true; 114 std::string clzRep; 126 115 for ( const auto & var : clz.vars ) { 127 116 if ( clz.bound ) { 128 117 sub.add( var, clz.bound ); 129 } else if ( first) {118 } else if ( clzRep.empty() ) { 130 119 clzRep = var; 131 first = false;132 120 } else { 133 sub.add( var, new TypeInstType{ clzRep } );121 sub.add( var, new TypeInstType{ clzRep, clz.data.kind } ); 134 122 } 135 123 } … … 146 134 struct Occurs : public ast::WithVisitorRef<Occurs> { 147 135 bool result; 148 std:: unordered_set< TypeInstType::TypeEnvKey> vars;136 std::set< std::string > vars; 149 137 const TypeEnvironment & tenv; 150 138 151 Occurs( const TypeInstType::TypeEnvKey& var, const TypeEnvironment & env )139 Occurs( const std::string & var, const TypeEnvironment & env ) 152 140 : result( false ), vars(), tenv( env ) { 153 141 if ( const EqvClass * clz = tenv.lookup( var ) ) { … … 159 147 160 148 void previsit( const TypeInstType * typeInst ) { 161 if ( vars.count( *typeInst) ) {149 if ( vars.count( typeInst->name ) ) { 162 150 result = true; 163 } else if ( const EqvClass * clz = tenv.lookup( *typeInst) ) {151 } else if ( const EqvClass * clz = tenv.lookup( typeInst->name ) ) { 164 152 if ( clz->bound ) { 165 153 clz->bound->accept( *visitor ); … … 170 158 171 159 /// true if `var` occurs in `ty` under `env` 172 bool occurs( const Type * ty, const TypeInstType::TypeEnvKey& var, const TypeEnvironment & env ) {160 bool occurs( const Type * ty, const std::string & var, const TypeEnvironment & env ) { 173 161 Pass<Occurs> occur{ var, env }; 174 162 maybe_accept( ty, occur ); 175 return occur. core.result;176 } 177 } 178 179 bool TypeEnvironment::combine( 163 return occur.pass.result; 164 } 165 } 166 167 bool TypeEnvironment::combine( 180 168 const TypeEnvironment & o, OpenVarSet & open, const SymbolTable & symtab ) { 181 169 // short-circuit easy cases … … 211 199 auto st = internal_lookup( *vt ); 212 200 if ( st == env.end() ) { 213 // unbound, safe to add if occurs 201 // unbound, safe to add if occurs 214 202 if ( r.bound && occurs( r.bound, *vt, *this ) ) return false; 215 203 r.vars.emplace( *vt ); … … 278 266 } 279 267 280 bool TypeEnvironment::bindVar( 281 const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 282 AssertionSet & need, AssertionSet & have, const OpenVarSet & open, WidenMode widen, 283 const SymbolTable & symtab 268 bool TypeEnvironment::bindVar( 269 const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 270 AssertionSet & need, AssertionSet & have, const OpenVarSet & open, WidenMode widen, 271 const SymbolTable & symtab 284 272 ) { 285 273 // remove references from bound type, so that type variables can only bind to value types 286 274 ptr<Type> target = bindTo->stripReferences(); 287 auto tyvar = open.find( *typeInst);275 auto tyvar = open.find( typeInst->name ); 288 276 assert( tyvar != open.end() ); 289 277 if ( ! tyVarCompatible( tyvar->second, target ) ) return false; 290 if ( occurs( target, *typeInst, *this ) ) return false;291 292 auto it = internal_lookup( *typeInst);278 if ( occurs( target, typeInst->name, *this ) ) return false; 279 280 auto it = internal_lookup( typeInst->name ); 293 281 if ( it != env.end() ) { 294 282 if ( it->bound ) { … … 298 286 ptr<Type> newType = it->bound; 299 287 reset_qualifiers( newType, typeInst->qualifiers ); 300 if ( unifyInexact( 301 newType, target, *this, need, have, open, 288 if ( unifyInexact( 289 newType, target, *this, need, have, open, 302 290 widen & WidenMode{ it->allowWidening, true }, symtab, common ) ) { 303 291 if ( common ) { … … 312 300 } 313 301 } else { 314 env.emplace_back( 315 *typeInst, target, widen.first && widen.second, data );302 env.emplace_back( 303 typeInst->name, target, widen.first && widen.second, data ); 316 304 } 317 305 return true; 318 306 } 319 307 320 bool TypeEnvironment::bindVarToVar( 321 const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 322 AssertionSet & need, AssertionSet & have, const OpenVarSet & open, 323 WidenMode widen, const SymbolTable & symtab 308 bool TypeEnvironment::bindVarToVar( 309 const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 310 AssertionSet & need, AssertionSet & have, const OpenVarSet & open, 311 WidenMode widen, const SymbolTable & symtab 324 312 ) { 325 auto c1 = internal_lookup( *var1);326 auto c2 = internal_lookup( *var2);327 313 auto c1 = internal_lookup( var1->name ); 314 auto c2 = internal_lookup( var2->name ); 315 328 316 // exit early if variables already bound together 329 317 if ( c1 != env.end() && c1 == c2 ) { … … 338 326 if ( c1 != env.end() ) { 339 327 if ( c1->bound ) { 340 if ( occurs( c1->bound, *var2, *this ) ) return false;328 if ( occurs( c1->bound, var2->name, *this ) ) return false; 341 329 type1 = c1->bound; 342 330 } … … 345 333 if ( c2 != env.end() ) { 346 334 if ( c2->bound ) { 347 if ( occurs( c2->bound, *var1, *this ) ) return false;335 if ( occurs( c2->bound, var1->name, *this ) ) return false; 348 336 type2 = c2->bound; 349 337 } … … 383 371 } else if ( c1 != env.end() ) { 384 372 // var2 unbound, add to env[c1] 385 c1->vars.emplace( *var2);373 c1->vars.emplace( var2->name ); 386 374 c1->allowWidening = widen1; 387 375 c1->data.isComplete |= data.isComplete; 388 376 } else if ( c2 != env.end() ) { 389 377 // var1 unbound, add to env[c2] 390 c2->vars.emplace( *var1);378 c2->vars.emplace( var1->name ); 391 379 c2->allowWidening = widen2; 392 380 c2->data.isComplete |= data.isComplete; 393 381 } else { 394 382 // neither var bound, create new class 395 env.emplace_back( *var1, *var2, widen1 && widen2, data );383 env.emplace_back( var1->name, var2->name, widen1 && widen2, data ); 396 384 } 397 385 … … 408 396 } 409 397 410 bool TypeEnvironment::mergeBound( 398 bool TypeEnvironment::mergeBound( 411 399 EqvClass & to, const EqvClass & from, OpenVarSet & open, const SymbolTable & symtab ) { 412 400 if ( from.bound ) { … … 418 406 AssertionSet need, have; 419 407 420 if ( unifyInexact( 408 if ( unifyInexact( 421 409 toType, fromType, *this, need, have, open, widen, symtab, common ) ) { 422 410 // unifies, set common type if necessary … … 436 424 } 437 425 438 bool TypeEnvironment::mergeClasses( 426 bool TypeEnvironment::mergeClasses( 439 427 ClassList::iterator to, ClassList::iterator from, OpenVarSet & open, const SymbolTable & symtab 440 428 ) { … … 457 445 } 458 446 459 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey& var ) {447 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string & var ) { 460 448 for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) { 461 449 if ( i->vars.count( var ) ) return i; -
src/AST/TypeEnvironment.hpp
reef8dfb rbdfc032 37 37 /// Adding this comparison operator significantly improves assertion satisfaction run time for 38 38 /// some cases. The current satisfaction algorithm's speed partially depends on the order of 39 /// assertions. Assertions which have fewer possible matches should appear before assertions 40 /// which have more possible matches. This seems to imply that this could be further improved 41 /// by providing an indexer as an additional argument and ordering based on the number of 39 /// assertions. Assertions which have fewer possible matches should appear before assertions 40 /// which have more possible matches. This seems to imply that this could be further improved 41 /// by providing an indexer as an additional argument and ordering based on the number of 42 42 /// matches of the same kind (object, function) for the names of the declarations. 43 43 /// 44 /// I've seen a TU go from 54 minutes to 1 minute 34 seconds with the addition of this 44 /// I've seen a TU go from 54 minutes to 1 minute 34 seconds with the addition of this 45 45 /// comparator. 46 46 /// 47 /// Note: since this compares pointers for position, minor changes in the source file that 48 /// affect memory layout can alter compilation time in unpredictable ways. For example, the 49 /// placement of a line directive can reorder type pointers with respect to each other so that 50 /// assertions are seen in different orders, causing a potentially different number of 51 /// unification calls when resolving assertions. I've seen a TU go from 36 seconds to 27 52 /// seconds by reordering line directives alone, so it would be nice to fix this comparison so 53 /// that assertions compare more consistently. I've tried to modify this to compare on mangle 54 /// name instead of type as the second comparator, but this causes some assertions to never be 47 /// Note: since this compares pointers for position, minor changes in the source file that 48 /// affect memory layout can alter compilation time in unpredictable ways. For example, the 49 /// placement of a line directive can reorder type pointers with respect to each other so that 50 /// assertions are seen in different orders, causing a potentially different number of 51 /// unification calls when resolving assertions. I've seen a TU go from 36 seconds to 27 52 /// seconds by reordering line directives alone, so it would be nice to fix this comparison so 53 /// that assertions compare more consistently. I've tried to modify this to compare on mangle 54 /// name instead of type as the second comparator, but this causes some assertions to never be 55 55 /// recorded. More investigation is needed. 56 56 struct AssertCompare { 57 bool operator()( const VariableExpr * d1, const VariableExpr* d2 ) const {58 int cmp = d1-> var->name.compare( d2->var->name );59 return cmp < 0 || ( cmp == 0 && d1-> result < d2->result);57 bool operator()( const DeclWithType * d1, const DeclWithType * d2 ) const { 58 int cmp = d1->name.compare( d2->name ); 59 return cmp < 0 || ( cmp == 0 && d1->get_type() < d2->get_type() ); 60 60 } 61 61 }; … … 70 70 71 71 /// Set of assertions pending satisfaction 72 using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >;72 using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >; 73 73 74 74 /// Set of open variables 75 using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, TypeDecl::Data >;75 using OpenVarSet = std::unordered_map< std::string, TypeDecl::Data >; 76 76 77 77 /// Merges one set of open vars into another … … 86 86 void print( std::ostream &, const OpenVarSet &, Indenter indent = {} ); 87 87 88 /// Represents an equivalence class of bound type variables, optionally with the concrete type 88 /// Represents an equivalence class of bound type variables, optionally with the concrete type 89 89 /// they bind to. 90 90 struct EqvClass { 91 std:: unordered_set< TypeInstType::TypeEnvKey> vars;91 std::set< std::string > vars; 92 92 ptr<Type> bound; 93 93 bool allowWidening; … … 95 95 96 96 EqvClass() : vars(), bound(), allowWidening( true ), data() {} 97 97 98 98 /// Copy-with-bound constructor 99 EqvClass( const EqvClass & o, const Type * b ) 99 EqvClass( const EqvClass & o, const Type * b ) 100 100 : vars( o.vars ), bound( b ), allowWidening( o.allowWidening ), data( o.data ) {} 101 101 102 102 /// Singleton class constructor from TypeDecl 103 EqvClass( const Type InstType * inst)104 : vars{ *inst }, bound(), allowWidening( true ), data( inst->base) {}103 EqvClass( const TypeDecl * decl ) 104 : vars{ decl->name }, bound(), allowWidening( true ), data( decl ) {} 105 105 106 106 /// Singleton class constructor from substitution 107 EqvClass( const TypeInstType::TypeEnvKey& v, const Type * b )107 EqvClass( const std::string & v, const Type * b ) 108 108 : vars{ v }, bound( b ), allowWidening( false ), data( TypeDecl::Dtype, false ) {} 109 109 110 110 /// Single-var constructor (strips qualifiers from bound type) 111 EqvClass( const TypeInstType::TypeEnvKey& v, const Type * b, bool w, const TypeDecl::Data & d )111 EqvClass( const std::string & v, const Type * b, bool w, const TypeDecl::Data & d ) 112 112 : vars{ v }, bound( b ), allowWidening( w ), data( d ) { 113 113 reset_qualifiers( bound ); … … 115 115 116 116 /// Double-var constructor 117 EqvClass( const TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey& u, bool w, const TypeDecl::Data & d )117 EqvClass( const std::string & v, const std::string & u, bool w, const TypeDecl::Data & d ) 118 118 : vars{ v, u }, bound(), allowWidening( w ), data( d ) {} 119 119 … … 131 131 public: 132 132 /// Finds the equivalence class containing a variable; nullptr for none such 133 const EqvClass * lookup( const TypeInstType::TypeEnvKey& var ) const;133 const EqvClass * lookup( const std::string & var ) const; 134 134 135 135 /// Add a new equivalence class for each type variable 136 void add( const FunctionType::ForallList & tyDecls );136 void add( const ParameterizedType::ForallList & tyDecls ); 137 137 138 138 /// Add a new equivalence class for each branch of the substitution, checking for conflicts … … 142 142 void writeToSubstitution( TypeSubstitution & sub ) const; 143 143 144 template< typename node_t >145 auto apply( node_t && type ) const {144 template< typename node_t, enum Node::ref_type ref_t > 145 int apply( ptr_base< node_t, ref_t > & type ) const { 146 146 TypeSubstitution sub; 147 147 writeToSubstitution( sub ); 148 return sub.apply( std::forward<node_t>(type));149 } 150 151 template< typename node_t >152 auto applyFree( node_t && type ) const {148 return sub.apply( type ); 149 } 150 151 template< typename node_t, enum Node::ref_type ref_t > 152 int applyFree( ptr_base< node_t, ref_t > & type ) const { 153 153 TypeSubstitution sub; 154 154 writeToSubstitution( sub ); 155 return sub.applyFree( std::forward<node_t>(type));155 return sub.applyFree( type ); 156 156 } 157 157 … … 172 172 void addActual( const TypeEnvironment & actualEnv, OpenVarSet & openVars ); 173 173 174 /// Binds the type class represented by `typeInst` to the type `bindTo`; will add the class if 174 /// Binds the type class represented by `typeInst` to the type `bindTo`; will add the class if 175 175 /// needed. Returns false on failure. 176 bool bindVar( 177 const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 178 AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 176 bool bindVar( 177 const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 178 AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 179 179 ResolvExpr::WidenMode widen, const SymbolTable & symtab ); 180 181 /// Binds the type classes represented by `var1` and `var2` together; will add one or both 180 181 /// Binds the type classes represented by `var1` and `var2` together; will add one or both 182 182 /// classes if needed. Returns false on failure. 183 bool bindVarToVar( 184 const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 185 AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 183 bool bindVarToVar( 184 const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 185 AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 186 186 ResolvExpr::WidenMode widen, const SymbolTable & symtab ); 187 187 … … 198 198 199 199 /// Unifies the type bound of `to` with the type bound of `from`, returning false if fails 200 bool mergeBound( 200 bool mergeBound( 201 201 EqvClass & to, const EqvClass & from, OpenVarSet & openVars, const SymbolTable & symtab ); 202 202 203 203 /// Merges two type classes from local environment, returning false if fails 204 bool mergeClasses( 205 ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars, 204 bool mergeClasses( 205 ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars, 206 206 const SymbolTable & symtab ); 207 207 208 208 /// Private lookup API; returns array index of string, or env.size() for not found 209 ClassList::iterator internal_lookup( const TypeInstType::TypeEnvKey& );209 ClassList::iterator internal_lookup( const std::string & ); 210 210 }; 211 211 -
src/AST/TypeSubstitution.cpp
reef8dfb rbdfc032 19 19 namespace ast { 20 20 21 22 // size_t TypeSubstitution::Substituter::traceId = Stats::Heap::new_stacktrace_id("TypeSubstitution");23 24 21 TypeSubstitution::TypeSubstitution() { 25 22 } … … 39 36 void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) { 40 37 dest.typeEnv.clear(); 38 dest.varEnv.clear(); 41 39 dest.add( src ); 42 40 } … … 46 44 typeEnv[ i->first ] = i->second; 47 45 } // for 48 } 49 50 void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) { 51 typeEnv[ *formalType ] = actualType; 52 } 53 54 void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) { 55 typeEnv[ key ] = actualType; 56 } 57 58 void TypeSubstitution::remove( const TypeInstType * formalType ) { 59 TypeEnvType::iterator i = typeEnv.find( *formalType ); 46 for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) { 47 varEnv[ i->first ] = i->second; 48 } // for 49 } 50 51 void TypeSubstitution::add( std::string formalType, const Type *actualType ) { 52 typeEnv[ formalType ] = actualType; 53 } 54 55 void TypeSubstitution::addVar( std::string formalExpr, const Expr *actualExpr ) { 56 varEnv[ formalExpr ] = actualExpr; 57 } 58 59 void TypeSubstitution::remove( std::string formalType ) { 60 TypeEnvType::iterator i = typeEnv.find( formalType ); 60 61 if ( i != typeEnv.end() ) { 61 typeEnv.erase( *formalType );62 } // if 63 } 64 65 const Type *TypeSubstitution::lookup( const TypeInstType *formalType ) const {66 TypeEnvType::const_iterator i = typeEnv.find( *formalType );62 typeEnv.erase( formalType ); 63 } // if 64 } 65 66 const Type *TypeSubstitution::lookup( std::string formalType ) const { 67 TypeEnvType::const_iterator i = typeEnv.find( formalType ); 67 68 68 69 // break on not in substitution set … … 71 72 // attempt to transitively follow TypeInstType links. 72 73 while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) { 74 const std::string& typeName = actualType->name; 75 73 76 // break cycles in the transitive follow 74 if ( *formalType == *actualType ) break;77 if ( formalType == typeName ) break; 75 78 76 79 // Look for the type this maps to, returning previous mapping if none-such 77 i = typeEnv.find( *actualType );80 i = typeEnv.find( typeName ); 78 81 if ( i == typeEnv.end() ) return actualType; 79 82 } … … 84 87 85 88 bool TypeSubstitution::empty() const { 86 return typeEnv.empty() ;89 return typeEnv.empty() && varEnv.empty(); 87 90 } 88 91 89 92 namespace { 90 93 struct EnvTrimmer { 91 const TypeSubstitution *env;94 ptr<TypeSubstitution> env; 92 95 TypeSubstitution * newEnv; 93 96 EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){} 94 void previsit( FunctionType * ftype) {97 void previsit( TypeDecl * tyDecl ) { 95 98 // transfer known bindings for seen type variables 96 for (auto & formal : ftype->forall) { 97 if ( const Type * t = env->lookup( formal ) ) { 98 newEnv->add( formal, t ); 99 } 99 if ( const Type * t = env->lookup( tyDecl->name ) ) { 100 newEnv->add( tyDecl->name, t ); 100 101 } 101 102 } … … 107 108 if ( env ) { 108 109 TypeSubstitution * newEnv = new TypeSubstitution(); 110 #if TIME_TO_CONVERT_PASSES 109 111 Pass<EnvTrimmer> trimmer( env, newEnv ); 110 112 expr->accept( trimmer ); 113 #else 114 (void)expr; 115 (void)env; 116 #endif 111 117 return newEnv; 112 118 } … … 115 121 116 122 void TypeSubstitution::normalize() { 117 Pass<Substituter> sub( *this, true ); 123 #if TIME_TO_CONVERT_PASSES 124 PassVisitor<Substituter> sub( *this, true ); 118 125 do { 119 sub. core.subCount = 0;120 sub. core.freeOnly = true;126 sub.pass.subCount = 0; 127 sub.pass.freeOnly = true; 121 128 for ( TypeEnvType::iterator i = typeEnv.begin(); i != typeEnv.end(); ++i ) { 122 i->second = i->second->accept ( sub );129 i->second = i->second->acceptMutator( sub ); 123 130 } 124 } while ( sub.core.subCount ); 125 } 126 127 const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) { 128 BoundVarsType::const_iterator bound = boundVars.find( *inst ); 131 } while ( sub.pass.subCount ); 132 #endif 133 } 134 135 #if TIME_TO_CONVERT_PASSES 136 137 Type * TypeSubstitution::Substituter::postmutate( TypeInstType *inst ) { 138 BoundVarsType::const_iterator bound = boundVars.find( inst->name ); 129 139 if ( bound != boundVars.end() ) return inst; 130 140 131 TypeEnvType::const_iterator i = sub.typeEnv.find( *inst);141 TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name ); 132 142 if ( i == sub.typeEnv.end() ) { 133 143 return inst; … … 136 146 // Note: this does not prevent cycles in the general case, so it may be necessary to do something more sophisticated here. 137 147 // TODO: investigate preventing type variables from being bound to themselves in the first place. 138 if ( constTypeInstType * replacement = i->second.as<TypeInstType>() ) {139 if ( *inst == *replacement) {148 if ( TypeInstType * replacement = i->second.as<TypeInstType>() ) { 149 if ( inst->name == replacement->name ) { 140 150 return inst; 141 151 } … … 143 153 // std::cerr << "found " << inst->name << ", replacing with " << i->second << std::endl; 144 154 subCount++; 145 ptr<Type> newType = i->second; // force clone if needed 146 add_qualifiers( newType, inst->qualifiers ); 147 // Note: need to recursively apply substitution to the new type because normalize does not 148 // substitute bound vars, but bound vars must be substituted when not in freeOnly mode. 149 newType = newType->accept( *visitor ); 150 return newType.release(); 151 } // if 152 } 153 154 void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) { 155 Type * newtype = i->second->clone(); 156 newtype->get_qualifiers() |= inst->get_qualifiers(); 157 delete inst; 158 // Note: need to recursively apply substitution to the new type because normalize does not substitute bound vars, but bound vars must be substituted when not in freeOnly mode. 159 return newtype->acceptMutator( *visitor ); 160 } // if 161 } 162 163 Expression * TypeSubstitution::Substituter::postmutate( NameExpr * nameExpr ) { 164 VarEnvType::const_iterator i = sub.varEnv.find( nameExpr->name ); 165 if ( i == sub.varEnv.end() ) { 166 return nameExpr; 167 } else { 168 subCount++; 169 delete nameExpr; 170 return i->second->clone(); 171 } // if 172 } 173 174 void TypeSubstitution::Substituter::premutate( Type * type ) { 155 175 GuardValue( boundVars ); 156 176 // bind type variables from forall-qualifiers 157 177 if ( freeOnly ) { 158 for ( auto & tyvar : ptype->forall) {159 boundVars.insert( *tyvar);178 for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) { 179 boundVars.insert( (*tyvar)->name ); 160 180 } // for 161 181 } // if 162 182 } 163 183 164 /* 165 void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType* type ) {184 template< typename TypeClass > 185 void TypeSubstitution::Substituter::handleAggregateType( TypeClass * type ) { 166 186 GuardValue( boundVars ); 167 187 // bind type variables from forall-qualifiers 168 188 if ( freeOnly ) { 189 for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) { 190 boundVars.insert( (*tyvar)->name ); 191 } // for 169 192 // bind type variables from generic type instantiations 170 if ( auto decl = type->aggr() ) { 171 if ( ! type->params.empty() ) { 172 for ( const TypeDecl * tyvar : decl->params ) { 173 boundVars.insert( *tyvar ); 174 } // for 175 } // if 176 } 177 } // if 178 } 179 180 void TypeSubstitution::Substituter::previsit( const StructInstType * aggregateUseType ) { 193 std::list< TypeDecl* > *baseParameters = type->get_baseParameters(); 194 if ( baseParameters && ! type->parameters.empty() ) { 195 for ( std::list< TypeDecl* >::const_iterator tyvar = baseParameters->begin(); tyvar != baseParameters->end(); ++tyvar ) { 196 boundVars.insert( (*tyvar)->name ); 197 } // for 198 } // if 199 } // if 200 } 201 202 void TypeSubstitution::Substituter::premutate( StructInstType * aggregateUseType ) { 181 203 handleAggregateType( aggregateUseType ); 182 204 } 183 205 184 void TypeSubstitution::Substituter::pre visit( constUnionInstType *aggregateUseType ) {206 void TypeSubstitution::Substituter::premutate( UnionInstType *aggregateUseType ) { 185 207 handleAggregateType( aggregateUseType ); 186 208 } 187 */ 209 210 #endif 188 211 189 212 } // namespace ast -
src/AST/TypeSubstitution.hpp
reef8dfb rbdfc032 44 44 TypeSubstitution &operator=( const TypeSubstitution &other ); 45 45 46 template< typename SynTreeClass > 47 struct ApplyResult { 48 ast::ptr<SynTreeClass> node; 49 int count; 50 }; 51 52 template< typename SynTreeClass > ApplyResult<SynTreeClass> apply( const SynTreeClass * input ) const; 53 template< typename SynTreeClass > ApplyResult<SynTreeClass> applyFree( const SynTreeClass * input ) const; 46 template< typename SynTreeClass > int apply( const SynTreeClass *& input ) const; 47 template< typename SynTreeClass > int applyFree( const SynTreeClass *& input ) const; 54 48 55 49 template< typename node_t, enum Node::ref_type ref_t > 56 50 int apply( ptr_base< node_t, ref_t > & input ) const { 57 51 const node_t * p = input.get(); 58 autoret = apply(p);59 input = ret.node;60 return ret .count;52 int ret = apply(p); 53 input = p; 54 return ret; 61 55 } 62 56 … … 64 58 int applyFree( ptr_base< node_t, ref_t > & input ) const { 65 59 const node_t * p = input.get(); 66 autoret = applyFree(p);67 input = ret.node;68 return ret .count;60 int ret = applyFree(p); 61 input = p; 62 return ret; 69 63 } 70 64 71 void add( const TypeInstType * formalType, const Type *actualType ); 72 void add( const TypeInstType::TypeEnvKey & key, const Type *actualType ); 65 void add( std::string formalType, const Type *actualType ); 73 66 void add( const TypeSubstitution &other ); 74 void remove( const TypeInstType *formalType );75 const Type *lookup( const TypeInstType *formalType ) const;67 void remove( std::string formalType ); 68 const Type *lookup( std::string formalType ) const; 76 69 bool empty() const; 70 71 void addVar( std::string formalExpr, const Expr *actualExpr ); 77 72 78 73 template< typename FormalIterator, typename ActualIterator > … … 97 92 void initialize( const TypeSubstitution &src, TypeSubstitution &dest ); 98 93 99 template<typename core_t>94 template<typename pass_type> 100 95 friend class Pass; 101 96 102 typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType; 97 typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType; 98 typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType; 103 99 TypeEnvType typeEnv; 100 VarEnvType varEnv; 104 101 105 102 public: … … 110 107 auto end() const -> decltype( typeEnv. end() ) { return typeEnv. end(); } 111 108 109 auto beginVar() -> decltype( varEnv.begin() ) { return varEnv.begin(); } 110 auto endVar() -> decltype( varEnv. end() ) { return varEnv. end(); } 111 auto beginVar() const -> decltype( varEnv.begin() ) { return varEnv.begin(); } 112 auto endVar() const -> decltype( varEnv. end() ) { return varEnv. end(); } 112 113 }; 113 114 114 // this is the only place where type parameters outside a function formal may be substituted.115 115 template< typename FormalIterator, typename ActualIterator > 116 116 void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) { … … 123 123 if ( const TypeExpr *actual = actualIt->template as<TypeExpr>() ) { 124 124 if ( formal->name != "" ) { 125 typeEnv[ formal ] = actual->type;125 typeEnv[ formal->name ] = actual->type; 126 126 } // if 127 127 } else { … … 129 129 } // if 130 130 } else { 131 131 // TODO: type check the formal and actual parameters 132 if ( (*formalIt)->name != "" ) { 133 varEnv[ (*formalIt)->name ] = *actualIt; 134 } // if 132 135 } // if 133 136 } // for 134 137 } 135 136 137 138 138 139 template< typename FormalIterator, typename ActualIterator > … … 141 142 } 142 143 143 144 144 } // namespace ast 145 145 … … 147 147 // PassVisitor are defined before PassVisitor implementation accesses TypeSubstitution internals. 148 148 #include "Pass.hpp" 149 #include "Copy.hpp"150 149 151 150 namespace ast { 152 151 153 152 // definitition must happen after PassVisitor is included so that WithGuards can be used 154 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter>, public PureVisitor { 155 static size_t traceId; 153 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter> { 156 154 157 155 Substituter( const TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {} 158 156 159 const Type * postvisit( const TypeInstType * aggregateUseType ); 157 #if TIME_TO_CONVERT_PASSES 158 159 Type * postmutate( TypeInstType * aggregateUseType ); 160 Expression * postmutate( NameExpr * nameExpr ); 160 161 161 162 /// Records type variable bindings from forall-statements 162 void pre visit( const FunctionType * type );163 void premutate( Type * type ); 163 164 /// Records type variable bindings from forall-statements and instantiations of generic types 164 // void handleAggregateType( const BaseInstType * type ); 165 166 // void previsit( const StructInstType * aggregateUseType ); 167 // void previsit( const UnionInstType * aggregateUseType ); 165 template< typename TypeClass > void handleAggregateType( TypeClass * type ); 166 167 void premutate( StructInstType * aggregateUseType ); 168 void premutate( UnionInstType * aggregateUseType ); 169 170 #endif 168 171 169 172 const TypeSubstitution & sub; 170 173 int subCount = 0; 171 174 bool freeOnly; 172 typedef std::unordered_set< TypeInstType::TypeEnvKey> BoundVarsType;175 typedef std::unordered_set< std::string > BoundVarsType; 173 176 BoundVarsType boundVars; 174 177 … … 176 179 177 180 template< typename SynTreeClass > 178 TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::apply( const SynTreeClass *input ) const {181 int TypeSubstitution::apply( const SynTreeClass *& input ) const { 179 182 assert( input ); 180 183 Pass<Substituter> sub( *this, false ); 181 184 input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) ); 182 return { input, sub.core.subCount }; 185 /// std::cerr << "substitution result is: "; 186 /// newType->print( std::cerr ); 187 /// std::cerr << std::endl; 188 return sub.pass.subCount; 183 189 } 184 190 185 191 template< typename SynTreeClass > 186 TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::applyFree( const SynTreeClass *input ) const {192 int TypeSubstitution::applyFree( const SynTreeClass *& input ) const { 187 193 assert( input ); 188 194 Pass<Substituter> sub( *this, true ); 189 195 input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) ); 190 return { input, sub.core.subCount }; 196 /// std::cerr << "substitution result is: "; 197 /// newType->print( std::cerr ); 198 /// std::cerr << std::endl; 199 return sub.pass.subCount; 191 200 } 192 201 -
src/AST/Visitor.hpp
reef8dfb rbdfc032 47 47 virtual const ast::Stmt * visit( const ast::CatchStmt * ) = 0; 48 48 virtual const ast::Stmt * visit( const ast::FinallyStmt * ) = 0; 49 virtual const ast::Stmt * visit( const ast::SuspendStmt * ) = 0;50 49 virtual const ast::Stmt * visit( const ast::WaitForStmt * ) = 0; 51 50 virtual const ast::Decl * visit( const ast::WithStmt * ) = 0; -
src/AST/module.mk
reef8dfb rbdfc032 17 17 SRC_AST = \ 18 18 AST/AssertAcyclic.cpp \ 19 AST/AssertAcyclic.hpp \20 19 AST/Attribute.cpp \ 21 AST/Attribute.hpp \22 AST/Bitfield.hpp \23 AST/Chain.hpp \24 20 AST/Convert.cpp \ 25 AST/Convert.hpp \26 AST/Copy.hpp \27 AST/CVQualifiers.hpp \28 21 AST/Decl.cpp \ 29 AST/Decl.hpp \30 22 AST/DeclReplacer.cpp \ 31 AST/DeclReplacer.hpp \32 AST/Eval.hpp \33 23 AST/Expr.cpp \ 34 AST/Expr.hpp \35 AST/FunctionSpec.hpp \36 AST/Fwd.hpp \37 24 AST/GenericSubstitution.cpp \ 38 AST/GenericSubstitution.hpp \39 25 AST/Init.cpp \ 40 AST/Init.hpp \41 AST/Label.hpp \42 26 AST/LinkageSpec.cpp \ 43 AST/LinkageSpec.hpp \44 27 AST/Node.cpp \ 45 AST/Node.hpp \46 AST/ParseNode.hpp \47 28 AST/Pass.cpp \ 48 AST/Pass.hpp \49 AST/Pass.impl.hpp \50 AST/Pass.proto.hpp \51 29 AST/Print.cpp \ 52 AST/Print.hpp \53 30 AST/Stmt.cpp \ 54 AST/Stmt.hpp \55 AST/StorageClasses.hpp \56 31 AST/SymbolTable.cpp \ 57 AST/SymbolTable.hpp \58 AST/TranslationUnit.hpp \59 32 AST/Type.cpp \ 60 AST/Type.hpp \61 33 AST/TypeEnvironment.cpp \ 62 AST/TypeEnvironment.hpp \ 63 AST/TypeSubstitution.cpp \ 64 AST/TypeSubstitution.hpp \ 65 AST/Visitor.hpp 34 AST/TypeSubstitution.cpp 66 35 67 36 SRC += $(SRC_AST) -
src/AST/porting.md
reef8dfb rbdfc032 30 30 * Base nodes now override `const Node * accept( Visitor & v ) const = 0` with, e.g. `const Stmt * accept( Visitor & v ) const override = 0` 31 31 * `PassVisitor` is replaced with `ast::Pass` 32 * Most one shot uses can use `ast::Pass::run` and `ast::Pass::read`.33 34 `WithConstTypeSubstitution`35 * `env` => `typeSubs`36 32 37 33 ## Structural Changes ## … … 51 47 template<typename node_t> 52 48 friend node_t * mutate(const node_t * node); 53 template<typename node_t>54 friend node_t * shallowCopy(const node_t * node);55 or equilant.56 * You should use the `mutate` function where possible as it avoids extra copies.57 * If you must copy use `shallowCopy` or `deepCopy` as required.58 49 59 50 All leaves of the `Node` inheritance tree are now declared `final` … … 150 141 * allows `newObject` as just default settings 151 142 152 `FunctionDecl`153 * `params` and `returns` added.154 * Contain the declarations of the parameters and return variables.155 * Types should match (even be shared with) the fields of `type`.156 157 143 `NamedTypeDecl` 158 144 * `parameters` => `params` … … 163 149 `AggregateDecl` 164 150 * `parameters` => `params` 165 166 `StructDecl`167 * `makeInst` replaced by better constructor on `StructInstType`.168 151 169 152 `Expr` … … 257 240 * **TODO** move `kind`, `typeNames` into code generator 258 241 259 `ReferenceToType` => `BaseInstType`242 `ReferenceToType` 260 243 * deleted `get_baseParameters()` from children 261 244 * replace with `aggr() ? aggr()->params : nullptr` … … 273 256 * `returnVals` => `returns` 274 257 * `parameters` => `params` 275 * Both now just point at types.276 258 * `bool isVarArgs;` => `enum ArgumentFlag { FixedArgs, VariableArgs }; ArgumentFlag isVarArgs;` 277 278 `SueInstType`279 * Template class, with specializations and using to implement some other types:280 * `StructInstType`, `UnionInstType` & `EnumInstType`281 259 282 260 `TypeInstType` -
src/CodeGen/CodeGenerator.cc
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 08:32:48 202013 // Update Count : 5 3212 // Last Modified On : Fri Dec 13 23:13:28 2019 13 // Update Count : 508 14 14 // 15 15 #include "CodeGenerator.h" … … 39 39 int CodeGenerator::tabsize = 4; 40 40 41 // The kinds of statements that would ideally be followed by whitespace.41 // the kinds of statements that would ideally be followed by whitespace 42 42 bool wantSpacing( Statement * stmt) { 43 43 return dynamic_cast< IfStmt * >( stmt ) || dynamic_cast< CompoundStmt * >( stmt ) || … … 78 78 } 79 79 80 // Using updateLocation at the beginning of a node and endl within a node should become the method of formating. 80 /* Using updateLocation at the beginning of a node and endl 81 * within a node should become the method of formating. 82 */ 81 83 void CodeGenerator::updateLocation( CodeLocation const & to ) { 82 84 // skip if linemarks shouldn't appear or if codelocation is unset … … 93 95 } else { 94 96 output << "\n# " << to.first_line << " \"" << to.filename 95 << "\"\n" << indent;97 << "\"\n" << indent; 96 98 currentLocation = to; 97 99 } … … 120 122 // GCC builtins should always be printed unmangled 121 123 if ( options.pretty || decl->linkage.is_gcc_builtin ) return decl->name; 122 if ( LinkageSpec::isMangled(decl->linkage) &&decl->mangleName != "" ) {124 if ( decl->mangleName != "" ) { 123 125 // need to incorporate scope level in order to differentiate names for destructors 124 126 return decl->get_scopedMangleName(); … … 129 131 130 132 void CodeGenerator::genAttributes( list< Attribute * > & attributes ) { 131 if ( attributes.empty() ) return;133 if ( attributes.empty() ) return; 132 134 output << "__attribute__ (("; 133 135 for ( list< Attribute * >::iterator attr( attributes.begin() );; ) { … … 138 140 output << ")"; 139 141 } // if 140 if ( ++attr == attributes.end() ) break;142 if ( ++attr == attributes.end() ) break; 141 143 output << ","; // separator 142 144 } // for … … 163 165 previsit( (BaseSyntaxNode *)node ); 164 166 GuardAction( [this, node](){ 165 if ( options.printExprTypes && node->result ) {166 output << " /* " << genType( node->result, "", options ) << " */ ";167 }168 } );167 if ( options.printExprTypes && node->result ) { 168 output << " /* " << genType( node->result, "", options ) << " */ "; 169 } 170 } ); 169 171 } 170 172 … … 397 399 extension( applicationExpr ); 398 400 if ( VariableExpr * varExpr = dynamic_cast< VariableExpr* >( applicationExpr->get_function() ) ) { 399 const OperatorInfo *opInfo;400 if ( varExpr->get_var()->get_linkage() == LinkageSpec::Intrinsic && ( opInfo = operatorLookup( varExpr->get_var()->get_name() )) ) {401 OperatorInfo opInfo; 402 if ( varExpr->get_var()->get_linkage() == LinkageSpec::Intrinsic && operatorLookup( varExpr->get_var()->get_name(), opInfo ) ) { 401 403 std::list< Expression* >::iterator arg = applicationExpr->get_args().begin(); 402 switch ( opInfo ->type ) {404 switch ( opInfo.type ) { 403 405 case OT_INDEX: 404 406 assert( applicationExpr->get_args().size() == 2 ); … … 421 423 output << "("; 422 424 (*arg++)->accept( *visitor ); 423 output << ") /* " << opInfo ->inputName << " */";425 output << ") /* " << opInfo.inputName << " */"; 424 426 } else if ( applicationExpr->get_args().size() == 2 ) { 425 427 // intrinsic two parameter constructors are essentially bitwise assignment 426 428 output << "("; 427 429 (*arg++)->accept( *visitor ); 428 output << opInfo ->symbol;430 output << opInfo.symbol; 429 431 (*arg)->accept( *visitor ); 430 output << ") /* " << opInfo ->inputName << " */";432 output << ") /* " << opInfo.inputName << " */"; 431 433 } else { 432 434 // no constructors with 0 or more than 2 parameters … … 439 441 assert( applicationExpr->get_args().size() == 1 ); 440 442 output << "("; 441 output << opInfo ->symbol;443 output << opInfo.symbol; 442 444 (*arg)->accept( *visitor ); 443 445 output << ")"; … … 448 450 assert( applicationExpr->get_args().size() == 1 ); 449 451 (*arg)->accept( *visitor ); 450 output << opInfo ->symbol;452 output << opInfo.symbol; 451 453 break; 452 454 … … 457 459 output << "("; 458 460 (*arg++)->accept( *visitor ); 459 output << opInfo ->symbol;461 output << opInfo.symbol; 460 462 (*arg)->accept( *visitor ); 461 463 output << ")"; … … 484 486 extension( untypedExpr ); 485 487 if ( NameExpr * nameExpr = dynamic_cast< NameExpr* >( untypedExpr->function ) ) { 486 const OperatorInfo * opInfo = operatorLookup( nameExpr->name );487 if ( op Info) {488 OperatorInfo opInfo; 489 if ( operatorLookup( nameExpr->name, opInfo ) ) { 488 490 std::list< Expression* >::iterator arg = untypedExpr->args.begin(); 489 switch ( opInfo ->type ) {491 switch ( opInfo.type ) { 490 492 case OT_INDEX: 491 493 assert( untypedExpr->args.size() == 2 ); … … 506 508 output << "("; 507 509 (*arg++)->accept( *visitor ); 508 output << ") /* " << opInfo ->inputName << " */";510 output << ") /* " << opInfo.inputName << " */"; 509 511 } else if ( untypedExpr->get_args().size() == 2 ) { 510 512 // intrinsic two parameter constructors are essentially bitwise assignment 511 513 output << "("; 512 514 (*arg++)->accept( *visitor ); 513 output << opInfo ->symbol;515 output << opInfo.symbol; 514 516 (*arg)->accept( *visitor ); 515 output << ") /* " << opInfo ->inputName << " */";517 output << ") /* " << opInfo.inputName << " */"; 516 518 } else { 517 519 // no constructors with 0 or more than 2 parameters … … 519 521 output << "("; 520 522 (*arg++)->accept( *visitor ); 521 output << opInfo ->symbol << "{ ";523 output << opInfo.symbol << "{ "; 522 524 genCommaList( arg, untypedExpr->args.end() ); 523 output << "}) /* " << opInfo ->inputName << " */";525 output << "}) /* " << opInfo.inputName << " */"; 524 526 } // if 525 527 break; … … 530 532 assert( untypedExpr->args.size() == 1 ); 531 533 output << "("; 532 output << opInfo ->symbol;534 output << opInfo.symbol; 533 535 (*arg)->accept( *visitor ); 534 536 output << ")"; … … 539 541 assert( untypedExpr->args.size() == 1 ); 540 542 (*arg)->accept( *visitor ); 541 output << opInfo ->symbol;543 output << opInfo.symbol; 542 544 break; 543 545 … … 547 549 output << "("; 548 550 (*arg++)->accept( *visitor ); 549 output << opInfo ->symbol;551 output << opInfo.symbol; 550 552 (*arg)->accept( *visitor ); 551 553 output << ")"; … … 579 581 void CodeGenerator::postvisit( NameExpr * nameExpr ) { 580 582 extension( nameExpr ); 581 const OperatorInfo * opInfo = operatorLookup( nameExpr->name );582 if ( op Info) {583 if ( opInfo ->type == OT_CONSTANT ) {584 output << opInfo ->symbol;583 OperatorInfo opInfo; 584 if ( operatorLookup( nameExpr->name, opInfo ) ) { 585 if ( opInfo.type == OT_CONSTANT ) { 586 output << opInfo.symbol; 585 587 } else { 586 output << opInfo ->outputName;588 output << opInfo.outputName; 587 589 } 588 590 } else { … … 652 654 void CodeGenerator::postvisit( VariableExpr * variableExpr ) { 653 655 extension( variableExpr ); 654 const OperatorInfo *opInfo;655 if ( variableExpr->get_var()->get_linkage() == LinkageSpec::Intrinsic && (opInfo = operatorLookup( variableExpr->get_var()->get_name() )) && opInfo->type == OT_CONSTANT ) {656 output << opInfo ->symbol;656 OperatorInfo opInfo; 657 if ( variableExpr->get_var()->get_linkage() == LinkageSpec::Intrinsic && operatorLookup( variableExpr->get_var()->get_name(), opInfo ) && opInfo.type == OT_CONSTANT ) { 658 output << opInfo.symbol; 657 659 } else { 658 660 output << mangleName( variableExpr->get_var() ); … … 1009 1011 case BranchStmt::FallThroughDefault: 1010 1012 assertf( ! options.genC, "fallthru should not reach code generation." ); 1011 output << "fallthru";1013 output << "fallthru"; 1012 1014 break; 1013 1015 } // switch … … 1033 1035 1034 1036 output << ((throwStmt->get_kind() == ThrowStmt::Terminate) ? 1035 "throw" : "throwResume");1037 "throw" : "throwResume"); 1036 1038 if (throwStmt->get_expr()) { 1037 1039 output << " "; … … 1048 1050 1049 1051 output << ((stmt->get_kind() == CatchStmt::Terminate) ? 1050 "catch" : "catchResume");1052 "catch" : "catchResume"); 1051 1053 output << "( "; 1052 1054 stmt->decl->accept( *visitor ); … … 1185 1187 1186 1188 std::string genName( DeclarationWithType * decl ) { 1187 const OperatorInfo * opInfo = operatorLookup( decl->get_name() );1188 if ( op Info) {1189 return opInfo ->outputName;1189 CodeGen::OperatorInfo opInfo; 1190 if ( operatorLookup( decl->get_name(), opInfo ) ) { 1191 return opInfo.outputName; 1190 1192 } else { 1191 1193 return decl->get_name(); -
src/CodeGen/CodeGenerator.h
reef8dfb rbdfc032 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sun Feb 16 03:58:31 202013 // Update Count : 6211 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Apr 30 12:01:00 2019 13 // Update Count : 57 14 14 // 15 15 … … 29 29 namespace CodeGen { 30 30 struct CodeGenerator : public WithShortCircuiting, public WithGuards, public WithVisitorRef<CodeGenerator> { 31 static int tabsize;31 static int tabsize; 32 32 33 33 CodeGenerator( std::ostream &os, bool pretty = false, bool genC = false, bool lineMarks = false, bool printExprTypes = false ); … … 104 104 void postvisit( AsmStmt * ); 105 105 void postvisit( DirectiveStmt * ); 106 void postvisit( AsmDecl * ); // special: statement in declaration context106 void postvisit( AsmDecl * ); // special: statement in declaration context 107 107 void postvisit( IfStmt * ); 108 108 void postvisit( SwitchStmt * ); … … 147 147 LabelPrinter printLabels; 148 148 Options options; 149 public:149 public: 150 150 LineEnder endl; 151 private:151 private: 152 152 153 153 CodeLocation currentLocation; … … 162 162 template< class Iterator > 163 163 void CodeGenerator::genCommaList( Iterator begin, Iterator end ) { 164 if ( begin == end ) return;164 if ( begin == end ) return; 165 165 for ( ;; ) { 166 166 (*begin++)->accept( *visitor ); 167 if ( begin == end ) break;167 if ( begin == end ) break; 168 168 output << ", "; // separator 169 169 } // for -
src/CodeGen/FixMain.cc
reef8dfb rbdfc032 26 26 #include "SynTree/Declaration.h" // for FunctionDecl, operator<< 27 27 #include "SynTree/Type.h" // for FunctionType 28 #include "SymTab/Mangler.h"29 28 30 29 namespace CodeGen { … … 48 47 if( main_signature ) { 49 48 os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return "; 50 main_signature->mangleName = SymTab::Mangler::mangle(main_signature.get());51 49 52 50 os << main_signature->get_scopedMangleName() << "("; -
src/CodeGen/FixMain.h
reef8dfb rbdfc032 10 10 // Created On : Thr Jan 12 14:11:09 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 03:24:32 202013 // Update Count : 512 // Last Modified On : Fri Dec 13 23:12:21 2019 13 // Update Count : 3 14 14 // 15 15 … … 42 42 static std::unique_ptr<FunctionDecl> main_signature; 43 43 }; 44 } // namespace CodeGen44 }; -
src/CodeGen/FixNames.cc
reef8dfb rbdfc032 31 31 #include "SynTree/Type.h" // for Type, BasicType, Type::Qualifiers 32 32 #include "SynTree/Visitor.h" // for Visitor, acceptAll 33 #include "CompilationState.h"34 33 35 34 namespace CodeGen { … … 103 102 if ( dwt->get_name() != "" ) { 104 103 if ( LinkageSpec::isMangled( dwt->get_linkage() ) ) { 105 if (!useNewAST) { 106 dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) ); 107 } 104 dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) ); 108 105 dwt->set_scopeLevel( scopeLevel ); 109 106 } // if -
src/CodeGen/GenType.h
reef8dfb rbdfc032 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sun Feb 16 04:11:40 202013 // Update Count : 511 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Apr 30 11:47:00 2019 13 // Update Count : 3 14 14 // 15 15 … … 25 25 std::string genType( Type *type, const std::string &baseString, const Options &options ); 26 26 std::string genType( Type *type, const std::string &baseString, bool pretty = false, bool genC = false, bool lineMarks = false ); 27 std::string genPrettyType( Type * type, const std::string & baseString );27 std::string genPrettyType( Type * type, const std::string & baseString ); 28 28 } // namespace CodeGen 29 29 -
src/CodeGen/Generate.cc
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 03:01:51 202013 // Update Count : 912 // Last Modified On : Fri Dec 13 23:38:56 2019 13 // Update Count : 8 14 14 // 15 15 #include "Generate.h" … … 64 64 void generate( BaseSyntaxNode * node, std::ostream & os ) { 65 65 if ( Type * type = dynamic_cast< Type * >( node ) ) { 66 os << genPrettyType( type, "" );66 os << CodeGen::genPrettyType( type, "" ); 67 67 } else { 68 68 PassVisitor<CodeGenerator> cgv( os, true, false, false, false ); -
src/CodeGen/OperatorTable.cc
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 18 15:55:01 202013 // Update Count : 5512 // Last Modified On : Sat Jul 15 17:12:22 2017 13 // Update Count : 15 14 14 // 15 15 … … 17 17 #include <map> // for map, _Rb_tree_const_iterator, map<>::const_iterator 18 18 #include <utility> // for pair 19 using namespace std;20 19 21 20 #include "OperatorTable.h" … … 23 22 24 23 namespace CodeGen { 25 const OperatorInfo CodeGen::tableValues[] ={26 // inputName symbol outputName friendlyName type27 { "?[?]", "", "_operator_index", "Index", OT_INDEX},28 { "?{}", "=", "_constructor", "Constructor", OT_CTOR},29 { "^?{}", "", "_destructor", "Destructor", OT_DTOR},30 { "?()", "", "_operator_call", "Call Operator", OT_CALL},31 { "?++", "++", "_operator_postincr", "Postfix Increment", OT_POSTFIXASSIGN},32 { "?--", "--", "_operator_postdecr", "Postfix Decrement", OT_POSTFIXASSIGN},33 { "*?", "*", "_operator_deref", "Dereference", OT_PREFIX},34 { "+?", "+", "_operator_unaryplus", "Plus", OT_PREFIX},35 { "-?", "-", "_operator_unaryminus", "Minus", OT_PREFIX},36 { "~?", "~", "_operator_bitnot", "Bitwise Not", OT_PREFIX},37 { "!?", "!", "_operator_lognot", "Logical Not", OT_PREFIX},38 { "++?", "++", "_operator_preincr", "Prefix Increment", OT_PREFIXASSIGN},39 { "--?", "--", "_operator_predecr", "Prefix Decrement", OT_PREFIXASSIGN},40 { "?\\?", "\\", "_operator_exponential", "Exponentiation", OT_INFIX},41 { "?*?", "*", "_operator_multiply", "Multiplication", OT_INFIX},42 { "?/?", "/", "_operator_divide", "Division", OT_INFIX},43 { "?%?", "%", "_operator_modulus", "Modulo", OT_INFIX},44 { "?+?", "+", "_operator_add", "Addition", OT_INFIX},45 { "?-?", "-", "_operator_subtract", "Substraction", OT_INFIX},46 { "?<<?", "<<", "_operator_shiftleft", "Shift Left", OT_INFIX},47 { "?>>?", ">>", "_operator_shiftright", "Shift Right", OT_INFIX},48 { "?<?", "<", "_operator_less", "Less-than", OT_INFIX},49 { "?>?", ">", "_operator_greater", "Greater-than", OT_INFIX},50 { "?<=?", "<=", "_operator_lessequal", "Less-than-or-Equal", OT_INFIX},51 { "?>=?", ">=", "_operator_greaterequal", "Greater-than-or-Equal", OT_INFIX},52 { "?==?", "==", "_operator_equal", "Equality", OT_INFIX},53 { "?!=?", "!=", "_operator_notequal", "Not-Equal", OT_INFIX},54 { "?&?", "&", "_operator_bitand", "Bitwise And", OT_INFIX},55 { "?^?", "^", "_operator_bitxor", "Bitwise Xor", OT_INFIX},56 { "?|?", "|", "_operator_bitor", "Bitwise Or", OT_INFIX},57 { "?=?", "=", "_operator_assign", "Assignment", OT_INFIXASSIGN},58 { "?\\=?", "\\=", "_operator_expassign", "Exponentiation Assignment", OT_INFIXASSIGN},59 { "?*=?", "*=", "_operator_multassign", "Multiplication Assignment", OT_INFIXASSIGN},60 { "?/=?", "/=", "_operator_divassign", "Division Assignment", OT_INFIXASSIGN},61 { "?%=?", "%=", "_operator_modassign", "Modulo Assignment", OT_INFIXASSIGN},62 { "?+=?", "+=", "_operator_addassign", "Addition Assignment", OT_INFIXASSIGN},63 { "?-=?", "-=", "_operator_subassign", "Substrction Assignment", OT_INFIXASSIGN},64 { "?<<=?", "<<=", "_operator_shiftleftassign", "Shift Left Assignment", OT_INFIXASSIGN},65 { "?>>=?", ">>=", "_operator_shiftrightassign", "Shift Right Assignment", OT_INFIXASSIGN},66 { "?&=?", "&=", "_operator_bitandassign", "Bitwise And Assignment", OT_INFIXASSIGN},67 { "?^=?", "^=", "_operator_bitxorassign", "Bitwise Xor Assignment", OT_INFIXASSIGN},68 { "?|=?", "|=", "_operator_bitorassign", "Bitwise Or Assignment", OT_INFIXASSIGN},69 }; // tableValues24 namespace { 25 const OperatorInfo tableValues[] = { 26 { "?[?]", "", "_operator_index", OT_INDEX }, 27 { "?{}", "=", "_constructor", OT_CTOR }, 28 { "^?{}", "", "_destructor", OT_DTOR }, 29 { "?()", "", "_operator_call", OT_CALL }, 30 { "?++", "++", "_operator_postincr", OT_POSTFIXASSIGN }, 31 { "?--", "--", "_operator_postdecr", OT_POSTFIXASSIGN }, 32 { "*?", "*", "_operator_deref", OT_PREFIX }, 33 { "+?", "+", "_operator_unaryplus", OT_PREFIX }, 34 { "-?", "-", "_operator_unaryminus", OT_PREFIX }, 35 { "~?", "~", "_operator_bitnot", OT_PREFIX }, 36 { "!?", "!", "_operator_lognot", OT_PREFIX }, 37 { "++?", "++", "_operator_preincr", OT_PREFIXASSIGN }, 38 { "--?", "--", "_operator_predecr", OT_PREFIXASSIGN }, 39 { "?\\?", "\\", "_operator_exponential", OT_INFIX }, 40 { "?*?", "*", "_operator_multiply", OT_INFIX }, 41 { "?/?", "/", "_operator_divide", OT_INFIX }, 42 { "?%?", "%", "_operator_modulus", OT_INFIX }, 43 { "?+?", "+", "_operator_add", OT_INFIX }, 44 { "?-?", "-", "_operator_subtract", OT_INFIX }, 45 { "?<<?", "<<", "_operator_shiftleft", OT_INFIX }, 46 { "?>>?", ">>", "_operator_shiftright", OT_INFIX }, 47 { "?<?", "<", "_operator_less", OT_INFIX }, 48 { "?>?", ">", "_operator_greater", OT_INFIX }, 49 { "?<=?", "<=", "_operator_lessequal", OT_INFIX }, 50 { "?>=?", ">=", "_operator_greaterequal", OT_INFIX }, 51 { "?==?", "==", "_operator_equal", OT_INFIX }, 52 { "?!=?", "!=", "_operator_notequal", OT_INFIX }, 53 { "?&?", "&", "_operator_bitand", OT_INFIX }, 54 { "?^?", "^", "_operator_bitxor", OT_INFIX }, 55 { "?|?", "|", "_operator_bitor", OT_INFIX }, 56 { "?=?", "=", "_operator_assign", OT_INFIXASSIGN }, 57 { "?\\=?", "\\=", "_operator_expassign", OT_INFIXASSIGN }, 58 { "?*=?", "*=", "_operator_multassign", OT_INFIXASSIGN }, 59 { "?/=?", "/=", "_operator_divassign", OT_INFIXASSIGN }, 60 { "?%=?", "%=", "_operator_modassign", OT_INFIXASSIGN }, 61 { "?+=?", "+=", "_operator_addassign", OT_INFIXASSIGN }, 62 { "?-=?", "-=", "_operator_subassign", OT_INFIXASSIGN }, 63 { "?<<=?", "<<=", "_operator_shiftleftassign", OT_INFIXASSIGN }, 64 { "?>>=?", ">>=", "_operator_shiftrightassign", OT_INFIXASSIGN }, 65 { "?&=?", "&=", "_operator_bitandassign", OT_INFIXASSIGN }, 66 { "?^=?", "^=", "_operator_bitxorassign", OT_INFIXASSIGN }, 67 { "?|=?", "|=", "_operator_bitorassign", OT_INFIXASSIGN }, 68 }; 70 69 71 std::map< std::string, OperatorInfo > CodeGen::table;70 const int numOps = sizeof( tableValues ) / sizeof( OperatorInfo ); 72 71 73 CodeGen::CodeGen() { 74 enum { numOps = sizeof( tableValues ) / sizeof( OperatorInfo ) }; 75 for ( int i = 0; i < numOps; i += 1 ) { 76 table[ tableValues[i].inputName ] = tableValues[i]; 77 } // for 72 std::map< std::string, OperatorInfo > table; 73 74 void initialize() { 75 for ( int i = 0; i < numOps; ++i ) { 76 table[ tableValues[i].inputName ] = tableValues[i]; 77 } // for 78 } 79 } // namespace 80 81 bool operatorLookup( const std::string & funcName, OperatorInfo & info ) { 82 static bool init = false; 83 if ( ! init ) { 84 initialize(); 85 } // if 86 87 std::map< std::string, OperatorInfo >::const_iterator i = table.find( funcName ); 88 if ( i == table.end() ) { 89 if ( isPrefix( funcName, "?`" ) ) { 90 // handle literal suffixes, which are user-defined postfix operators 91 info.inputName = funcName; 92 info.symbol = funcName.substr(2); 93 info.outputName = toString( "__operator_literal_", info.symbol ); 94 info.type = OT_POSTFIX; 95 return true; 96 } 97 return false; 98 } else { 99 info = i->second; 100 return true; 101 } // if 78 102 } 79 103 80 const OperatorInfo * operatorLookup( const string & funcName ) { 81 if ( funcName.find_first_of( "?^*+-!", 0, 1 ) == string::npos ) return nullptr; // prefilter 82 const OperatorInfo * ret = &CodeGen::table.find( funcName )->second; // must be in the table 83 assert( ret ); 84 return ret; 104 bool isOperator( const std::string & funcName ) { 105 OperatorInfo info; 106 return operatorLookup( funcName, info ); 85 107 } 86 108 87 bool isOperator( const string & funcName ) { 88 return operatorLookup( funcName ) != nullptr; 89 } 90 91 string operatorFriendlyName( const string & funcName ) { 92 const OperatorInfo * info = operatorLookup( funcName ); 93 if ( info ) return info->friendlyName; 94 return ""; 95 } 96 97 bool isConstructor( const string & funcName ) { 98 const OperatorInfo * info = operatorLookup( funcName ); 99 if ( info ) return info->type == OT_CTOR; 109 /// determines if a given function name is one of the operator types between [begin, end) 110 template<typename Iterator> 111 bool isOperatorType( const std::string & funcName, Iterator begin, Iterator end ) { 112 OperatorInfo info; 113 if ( operatorLookup( funcName, info ) ) { 114 return std::find( begin, end, info.type ) != end; 115 } 100 116 return false; 101 117 } 102 118 103 bool isDestructor( const string & funcName ) { 104 const OperatorInfo * info = operatorLookup( funcName ); 105 if ( info ) return info->type == OT_DTOR; 106 return false; 119 bool isConstructor( const std::string & funcName ) { 120 static OperatorType types[] = { OT_CTOR }; 121 return isOperatorType( funcName, std::begin(types), std::end(types) ); 107 122 } 108 123 109 bool isCtorDtor( const string & funcName ) { 110 const OperatorInfo * info = operatorLookup( funcName ); 111 if ( info ) return info->type <= OT_CONSTRUCTOR; 112 return false; 124 bool isDestructor( const std::string & funcName ) { 125 static OperatorType types[] = { OT_DTOR }; 126 return isOperatorType( funcName, std::begin(types), std::end(types) ); 113 127 } 114 128 115 bool isAssignment( const string & funcName ) { 116 const OperatorInfo * info = operatorLookup( funcName ); 117 if ( info ) return info->type > OT_CONSTRUCTOR && info->type <= OT_ASSIGNMENT; 118 return false; 129 bool isAssignment( const std::string & funcName ) { 130 static OperatorType types[] = { OT_PREFIXASSIGN, OT_POSTFIXASSIGN, OT_INFIXASSIGN }; 131 return isOperatorType( funcName, std::begin(types), std::end(types) ); 119 132 } 120 133 121 bool isCtorDtorAssign( const string & funcName ) { 122 const OperatorInfo * info = operatorLookup( funcName ); 123 if ( info ) return info->type <= OT_ASSIGNMENT; 124 return false; 134 bool isCtorDtor( const std::string & funcName ) { 135 static OperatorType types[] = { OT_CTOR, OT_DTOR }; 136 return isOperatorType( funcName, std::begin(types), std::end(types) ); 125 137 } 126 138 127 CodeGen codegen; // initialize singleton package 139 bool isCtorDtorAssign( const std::string & funcName ) { 140 static OperatorType types[] = { OT_CTOR, OT_DTOR, OT_PREFIXASSIGN, OT_POSTFIXASSIGN, OT_INFIXASSIGN }; 141 return isOperatorType( funcName, std::begin(types), std::end(types) ); 142 } 128 143 } // namespace CodeGen 129 144 130 145 // Local Variables: // 131 146 // tab-width: 4 // 147 // mode: c++ // 148 // compile-command: "make install" // 132 149 // End: // -
src/CodeGen/OperatorTable.h
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 08:13:34 202013 // Update Count : 2612 // Last Modified On : Fri Jul 21 22:17:11 2017 13 // Update Count : 6 14 14 // 15 15 … … 17 17 18 18 #include <string> 19 #include <map>20 19 21 20 namespace CodeGen { 22 21 enum OperatorType { 22 OT_INDEX, 23 23 OT_CTOR, 24 24 OT_DTOR, 25 OT_CONSTRUCTOR = OT_DTOR, 25 OT_CALL, 26 OT_PREFIX, 27 OT_POSTFIX, 28 OT_INFIX, 26 29 OT_PREFIXASSIGN, 27 30 OT_POSTFIXASSIGN, 28 31 OT_INFIXASSIGN, 29 OT_ASSIGNMENT = OT_INFIXASSIGN,30 OT_CALL,31 OT_PREFIX,32 OT_INFIX,33 OT_POSTFIX,34 OT_INDEX,35 32 OT_LABELADDRESS, 36 33 OT_CONSTANT … … 41 38 std::string symbol; 42 39 std::string outputName; 43 std::string friendlyName;44 40 OperatorType type; 45 41 }; 46 42 47 class CodeGen {48 friend const OperatorInfo * operatorLookup( const std::string & funcName );49 50 static const OperatorInfo tableValues[];51 static std::map< std::string, OperatorInfo > table;52 public:53 CodeGen();54 }; // CodeGen55 56 43 bool isOperator( const std::string & funcName ); 57 const OperatorInfo * operatorLookup( const std::string & funcName ); 58 std::string operatorFriendlyName( const std::string & funcName ); 44 bool operatorLookup( const std::string & funcName, OperatorInfo & info ); 59 45 60 46 bool isConstructor( const std::string & ); -
src/CodeGen/Options.h
reef8dfb rbdfc032 9 9 // Author : Andrew Beach 10 10 // Created On : Tue Apr 30 11:36:00 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Feb 15 18:37:06 202013 // Update Count : 311 // Last Modified By : Andrew Beach 12 // Last Modified On : Thr May 2 10:45:00 2019 13 // Update Count : 2 14 14 // 15 15 16 16 #pragma once 17 17 18 struct Options { 19 // External Options: Same thoughout a pass. 20 bool pretty; 21 bool genC; 22 bool lineMarks; 23 bool printExprTypes; 18 namespace CodeGen { 19 struct Options { 20 // External Options: Same thoughout a pass. 21 bool pretty; 22 bool genC; 23 bool lineMarks; 24 bool printExprTypes; 24 25 25 // Internal Options: Changed on some recurisive calls.26 bool anonymousUnused = false;26 // Internal Options: Changed on some recurisive calls. 27 bool anonymousUnused = false; 27 28 28 Options(bool pretty, bool genC, bool lineMarks, bool printExprTypes) :29 pretty(pretty), genC(genC), lineMarks(lineMarks), printExprTypes(printExprTypes)29 Options(bool pretty, bool genC, bool lineMarks, bool printExprTypes) : 30 pretty(pretty), genC(genC), lineMarks(lineMarks), printExprTypes(printExprTypes) 30 31 {} 31 }; 32 }; 33 } // namespace CodeGen 32 34 33 35 // Local Variables: // -
src/CodeGen/module.mk
reef8dfb rbdfc032 20 20 SRC_CODEGEN = \ 21 21 CodeGen/CodeGenerator.cc \ 22 CodeGen/CodeGenerator.h \23 22 CodeGen/FixMain.cc \ 24 CodeGen/FixMain.h \25 23 CodeGen/GenType.cc \ 26 CodeGen/GenType.h \ 27 CodeGen/OperatorTable.cc \ 28 CodeGen/OperatorTable.h \ 29 CodeGen/Options.h 24 CodeGen/OperatorTable.cc 30 25 31 SRC += $(SRC_CODEGEN) CodeGen/Generate.cc CodeGen/ Generate.h CodeGen/FixNames.cc CodeGen/FixNames.h26 SRC += $(SRC_CODEGEN) CodeGen/Generate.cc CodeGen/FixNames.cc 32 27 SRCDEMANGLE += $(SRC_CODEGEN) -
src/CodeTools/ResolvProtoDump.cc
reef8dfb rbdfc032 9 9 // Author : Aaron Moss 10 10 // Created On : Tue Sep 11 09:04:00 2018 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Feb 15 13:50:11 202013 // Update Count : 311 // Last Modified By : Aaron Moss 12 // Last Modified On : Tue Sep 11 09:04:00 2018 13 // Update Count : 1 14 14 // 15 15 … … 182 182 183 183 // replace operator names 184 const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( name );185 if ( opInfo) {184 CodeGen::OperatorInfo info; 185 if ( CodeGen::operatorLookup( name, info ) ) { 186 186 ss << new_prefix(pre, ""); 187 op_name( opInfo->outputName, ss );187 op_name( info.outputName, ss ); 188 188 return; 189 189 } -
src/CodeTools/TrackLoc.cc
reef8dfb rbdfc032 10 10 // Created On : Tues May 2 15:46:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Nov 27 18:00:00 202013 // Update Count : 112 // Last Modified On : Wed May 3 14:43:00 2017 13 // Update Count : 0 14 14 // 15 15 … … 22 22 #include <string> // for operator<<, string 23 23 #include <typeindex> // for type_index 24 #include <vector> // for vector25 24 26 25 #include "Common/PassVisitor.h" // for PassVisitor … … 38 37 CodeLocation *lastNode; 39 38 40 std::stack< CodeLocation * , std::vector< CodeLocation * >> parents;39 std::stack< CodeLocation * > parents; 41 40 public: 42 41 LocationPrinter(size_t printLevel) : -
src/CodeTools/module.mk
reef8dfb rbdfc032 15 15 ############################################################################### 16 16 17 SRC += \ 18 CodeTools/DeclStats.cc \ 19 CodeTools/DeclStats.h \ 17 SRC += CodeTools/DeclStats.cc \ 20 18 CodeTools/ResolvProtoDump.cc \ 21 CodeTools/ResolvProtoDump.h \ 22 CodeTools/TrackLoc.cc \ 23 CodeTools/TrackLoc.h 19 CodeTools/TrackLoc.cc -
src/Common/CodeLocation.h
reef8dfb rbdfc032 42 42 } 43 43 44 bool startsBefore( CodeLocation const & other ) const { 45 if( filename < other.filename ) return true; 46 if( filename > other.filename ) return false; 47 48 if( first_line < other.first_line ) return true; 49 if( first_line > other.first_line ) return false; 50 51 if( last_line < other.last_line ) return true; 52 return false; 53 } 54 55 bool followedBy( CodeLocation const & other, int seperation ) const { 44 bool followedBy( CodeLocation const & other, int seperation ) { 56 45 return (first_line + seperation == other.first_line && 57 46 filename == other.filename); 58 47 } 59 48 60 bool operator==( CodeLocation const & other ) const{49 bool operator==( CodeLocation const & other ) { 61 50 return followedBy( other, 0 ); 62 51 } 63 52 64 bool operator!=( CodeLocation const & other ) const{53 bool operator!=( CodeLocation const & other ) { 65 54 return !(*this == other); 66 55 } -
src/Common/Eval.cc
reef8dfb rbdfc032 168 168 if (expr) { 169 169 expr->accept(ev); 170 return std::make_pair(ev. core.value, ev.core.valid);170 return std::make_pair(ev.pass.value, ev.pass.valid); 171 171 } else { 172 172 return std::make_pair(0, false); -
src/Common/PassVisitor.h
reef8dfb rbdfc032 110 110 virtual void visit( FinallyStmt * finallyStmt ) override final; 111 111 virtual void visit( const FinallyStmt * finallyStmt ) override final; 112 virtual void visit( SuspendStmt * suspendStmt ) override final;113 virtual void visit( const SuspendStmt * suspendStmt ) override final;114 112 virtual void visit( WaitForStmt * waitforStmt ) override final; 115 113 virtual void visit( const WaitForStmt * waitforStmt ) override final; … … 278 276 virtual Statement * mutate( CatchStmt * catchStmt ) override final; 279 277 virtual Statement * mutate( FinallyStmt * finallyStmt ) override final; 280 virtual Statement * mutate( SuspendStmt * suspendStmt ) override final;281 278 virtual Statement * mutate( WaitForStmt * waitforStmt ) override final; 282 279 virtual Declaration * mutate( WithStmt * withStmt ) override final; … … 354 351 virtual TypeSubstitution * mutate( TypeSubstitution * sub ) final; 355 352 356 bool isInFunction() const {357 return inFunction;358 }359 360 353 private: 361 354 bool inFunction = false; 362 bool atFunctionTop = false;363 355 364 356 template<typename pass_t> friend void acceptAll( std::list< Declaration* > &decls, PassVisitor< pass_t >& visitor ); … … 531 523 public: 532 524 PassVisitor<pass_type> * const visitor = nullptr; 533 534 bool isInFunction() const {535 return visitor->isInFunction();536 }537 525 }; 538 526 -
src/Common/PassVisitor.impl.h
reef8dfb rbdfc032 532 532 indexerAddId( &func ); 533 533 maybeAccept_impl( node->type, *this ); 534 // First remember that we are now within a function. 534 // function body needs to have the same scope as parameters - CompoundStmt will not enter 535 // a new scope if inFunction is true 535 536 ValueGuard< bool > oldInFunction( inFunction ); 536 537 inFunction = true; 537 // The function body needs to have the same scope as parameters.538 // A CompoundStmt will not enter a new scope if atFunctionTop is true.539 ValueGuard< bool > oldAtFunctionTop( atFunctionTop );540 atFunctionTop = true;541 538 maybeAccept_impl( node->statements, *this ); 542 539 maybeAccept_impl( node->attributes, *this ); … … 570 567 indexerAddId( &func ); 571 568 maybeAccept_impl( node->type, *this ); 572 // First remember that we are now within a function. 569 // function body needs to have the same scope as parameters - CompoundStmt will not enter 570 // a new scope if inFunction is true 573 571 ValueGuard< bool > oldInFunction( inFunction ); 574 572 inFunction = true; 575 // The function body needs to have the same scope as parameters.576 // A CompoundStmt will not enter a new scope if atFunctionTop is true.577 ValueGuard< bool > oldAtFunctionTop( atFunctionTop );578 atFunctionTop = true;579 573 maybeAccept_impl( node->statements, *this ); 580 574 maybeAccept_impl( node->attributes, *this ); … … 607 601 indexerAddId( &func ); 608 602 maybeMutate_impl( node->type, *this ); 609 // First remember that we are now within a function. 603 // function body needs to have the same scope as parameters - CompoundStmt will not enter 604 // a new scope if inFunction is true 610 605 ValueGuard< bool > oldInFunction( inFunction ); 611 606 inFunction = true; 612 // The function body needs to have the same scope as parameters.613 // A CompoundStmt will not enter a new scope if atFunctionTop is true.614 ValueGuard< bool > oldAtFunctionTop( atFunctionTop );615 atFunctionTop = true;616 607 maybeMutate_impl( node->statements, *this ); 617 608 maybeMutate_impl( node->attributes, *this ); … … 835 826 { 836 827 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 828 maybeAccept_impl( node->parameters, *this ); 837 829 maybeAccept_impl( node->base , *this ); 838 830 } … … 857 849 { 858 850 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 851 maybeAccept_impl( node->parameters, *this ); 859 852 maybeAccept_impl( node->base , *this ); 860 853 } … … 878 871 { 879 872 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 873 maybeMutate_impl( node->parameters, *this ); 880 874 maybeMutate_impl( node->base , *this ); 881 875 } … … 901 895 { 902 896 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 897 maybeAccept_impl( node->parameters, *this ); 903 898 maybeAccept_impl( node->base , *this ); 904 899 } … … 917 912 { 918 913 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 914 maybeAccept_impl( node->parameters, *this ); 919 915 maybeAccept_impl( node->base , *this ); 920 916 } … … 933 929 { 934 930 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 931 maybeMutate_impl( node->parameters, *this ); 935 932 maybeMutate_impl( node->base , *this ); 936 933 } … … 1010 1007 VISIT_START( node ); 1011 1008 { 1012 // Do not enter a new scope if atFunctionTop is true, don't leave one either.1013 ValueGuard< bool > old AtFunctionTop( atFunctionTop);1014 auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go) indexerScopeLeave(); } );1009 // do not enter a new scope if inFunction is true - needs to check old state before the assignment 1010 ValueGuard< bool > oldInFunction( inFunction ); 1011 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } ); 1015 1012 auto guard2 = makeFuncGuard( [this]() { call_beginScope(); }, [this]() { call_endScope(); } ); 1016 atFunctionTop= false;1013 inFunction = false; 1017 1014 visitStatementList( node->kids ); 1018 1015 } … … 1024 1021 VISIT_START( node ); 1025 1022 { 1026 // Do not enter a new scope if atFunctionTop is true, don't leave one either.1027 ValueGuard< bool > old AtFunctionTop( atFunctionTop);1028 auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go) indexerScopeLeave(); } );1023 // do not enter a new scope if inFunction is true - needs to check old state before the assignment 1024 ValueGuard< bool > oldInFunction( inFunction ); 1025 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } ); 1029 1026 auto guard2 = makeFuncGuard( [this]() { call_beginScope(); }, [this]() { call_endScope(); } ); 1030 atFunctionTop= false;1027 inFunction = false; 1031 1028 visitStatementList( node->kids ); 1032 1029 } … … 1038 1035 MUTATE_START( node ); 1039 1036 { 1040 // Do not enter a new scope if atFunctionTop is true, don't leave one either.1041 ValueGuard< bool > old AtFunctionTop( atFunctionTop);1042 auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go) indexerScopeLeave(); } );1037 // do not enter a new scope if inFunction is true - needs to check old state before the assignment 1038 ValueGuard< bool > oldInFunction( inFunction ); 1039 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } ); 1043 1040 auto guard2 = makeFuncGuard( [this]() { call_beginScope(); }, [this]() { call_endScope(); } ); 1044 atFunctionTop= false;1041 inFunction = false; 1045 1042 mutateStatementList( node->kids ); 1046 1043 } … … 1525 1522 1526 1523 //-------------------------------------------------------------------------- 1527 // SuspendStmt1528 template< typename pass_type >1529 void PassVisitor< pass_type >::visit( SuspendStmt * node ) {1530 VISIT_START( node );1531 1532 maybeAccept_impl( node->then , *this );1533 1534 VISIT_END( node );1535 }1536 1537 template< typename pass_type >1538 void PassVisitor< pass_type >::visit( const SuspendStmt * node ) {1539 VISIT_START( node );1540 1541 maybeAccept_impl( node->then , *this );1542 1543 VISIT_END( node );1544 }1545 1546 template< typename pass_type >1547 Statement * PassVisitor< pass_type >::mutate( SuspendStmt * node ) {1548 MUTATE_START( node );1549 1550 maybeMutate_impl( node->then , *this );1551 1552 MUTATE_END( Statement, node );1553 }1554 1555 //--------------------------------------------------------------------------1556 1524 // WaitForStmt 1557 1525 template< typename pass_type > … … 3334 3302 VISIT_START( node ); 3335 3303 3336 indexerAdd Union( node->name );3304 indexerAddStruct( node->name ); 3337 3305 3338 3306 { … … 3349 3317 VISIT_START( node ); 3350 3318 3351 indexerAdd Union( node->name );3319 indexerAddStruct( node->name ); 3352 3320 3353 3321 { … … 3364 3332 MUTATE_START( node ); 3365 3333 3366 indexerAdd Union( node->name );3334 indexerAddStruct( node->name ); 3367 3335 3368 3336 { -
src/Common/PassVisitor.proto.h
reef8dfb rbdfc032 38 38 }; 39 39 40 std::stack< cleanup_t , std::vector< cleanup_t >> cleanups;40 std::stack< cleanup_t > cleanups; 41 41 }; 42 42 -
src/Common/ScopedMap.h
reef8dfb rbdfc032 93 93 94 94 reference operator* () { return *it; } 95 pointer operator-> () const{ return it.operator->(); }95 pointer operator-> () { return it.operator->(); } 96 96 97 97 iterator& operator++ () { … … 249 249 250 250 /// Gets the note at the given scope 251 Note& getNote() { return scopes.back().note; }252 const Note& getNote() const { return scopes.back().note; }253 251 Note& getNote( size_type i ) { return scopes[i].note; } 254 252 const Note& getNote( size_type i ) const { return scopes[i].note; } -
src/Common/SemanticError.cc
reef8dfb rbdfc032 90 90 void SemanticErrorException::print() { 91 91 using std::to_string; 92 93 errors.sort([](const error & lhs, const error & rhs) -> bool {94 if(lhs.location.startsBefore(rhs.location)) return true;95 if(rhs.location.startsBefore(lhs.location)) return false;96 97 return lhs.description < rhs.description;98 });99 100 92 for( auto err : errors ) { 101 93 std::cerr << ErrorHelpers::bold() << err.location << ErrorHelpers::error_str() << ErrorHelpers::reset_font() << err.description << std::endl; -
src/Common/SemanticError.h
reef8dfb rbdfc032 49 49 struct WarningData { 50 50 const char * const name; 51 const char * const message; 51 52 const Severity default_severity; 52 const char * const message;53 53 }; 54 54 55 55 constexpr WarningData WarningFormats[] = { 56 {"self-assign" , Severity::Warn , "self assignment of expression: %s" }, 57 {"reference-conversion" , Severity::Warn , "rvalue to reference conversion of rvalue: %s" }, 58 {"qualifiers-zero_t-one_t", Severity::Warn , "questionable use of type qualifier %s with %s" }, 59 {"aggregate-forward-decl" , Severity::Warn , "forward declaration of nested aggregate: %s" }, 60 {"superfluous-decl" , Severity::Warn , "declaration does not allocate storage: %s" }, 61 {"gcc-attributes" , Severity::Warn , "invalid attribute: %s" }, 62 {"c++-like-copy" , Severity::Warn , "Constructor from reference is not a valid copy constructor" }, 56 {"self-assign" , "self assignment of expression: %s" , Severity::Warn}, 57 {"reference-conversion" , "rvalue to reference conversion of rvalue: %s" , Severity::Warn}, 58 {"qualifiers-zero_t-one_t", "questionable use of type qualifier %s with %s", Severity::Warn}, 59 {"aggregate-forward-decl" , "forward declaration of nested aggregate: %s" , Severity::Warn}, 60 {"superfluous-decl" , "declaration does not allocate storage: %s" , Severity::Warn}, 61 {"gcc-attributes" , "invalid attribute: %s" , Severity::Warn}, 63 62 }; 64 63 … … 70 69 SuperfluousDecl, 71 70 GccAttributes, 72 CppCopy,73 71 NUMBER_OF_WARNINGS, // This MUST be the last warning 74 72 }; -
src/Common/Stats/Heap.cc
reef8dfb rbdfc032 53 53 const size_t passes_size = sizeof(passes) / sizeof(passes[0]); 54 54 size_t passes_cnt = 1; 55 56 StatBlock stacktrace_stats[100];57 size_t stacktrace_stats_count = 0;58 bool stacktrace_stats_enabled = true;59 60 size_t trace[1000];61 const size_t stacktrace_max_depth = sizeof(trace) / sizeof(size_t);62 size_t stacktrace_depth;63 64 size_t new_stacktrace_id(const char * const name) {65 stacktrace_stats[stacktrace_stats_count].name = name;66 return stacktrace_stats_count++;67 }68 69 void stacktrace_push(size_t id) {70 ++stacktrace_depth;71 assertf(stacktrace_depth < stacktrace_max_depth, "Stack trace too deep: increase size of array in Heap.cc");72 trace[stacktrace_depth] = id;73 }74 75 void stacktrace_pop() {76 assertf(stacktrace_depth > 0, "Invalid stack tracing operation: trace is empty");77 --stacktrace_depth;78 }79 55 80 56 void newPass( const char * const name ) { … … 140 116 for(size_t i = 0; i < passes_cnt; i++) { 141 117 print(passes[i], nc, total_mallocs, total_frees, overall_peak); 142 }143 144 print('-', nct);145 std::cerr << std::setw(nc) << "Trace";146 std::cerr << " | Malloc Count | Free Count | Peak Allocs |" << std::endl;147 148 print('-', nct);149 for (size_t i = 0; i < stacktrace_stats_count; i++) {150 print(stacktrace_stats[i], nc, total_mallocs, total_frees, overall_peak);151 118 } 152 119 print('-', nct); … … 221 188 = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs); 222 189 } 223 224 if ( stacktrace_stats_enabled && stacktrace_depth > 0) {225 stacktrace_stats[trace[stacktrace_depth]].mallocs++;226 }227 190 return __malloc( size ); 228 191 } … … 233 196 passes[passes_cnt - 1].frees++; 234 197 passes[passes_cnt - 1].n_allocs--; 235 }236 if ( stacktrace_stats_enabled && stacktrace_depth > 0) {237 stacktrace_stats[trace[stacktrace_depth]].frees++;238 198 } 239 199 return __free( ptr ); … … 248 208 = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs); 249 209 } 250 if ( stacktrace_stats_enabled && stacktrace_depth > 0) {251 stacktrace_stats[trace[stacktrace_depth]].mallocs++;252 }253 210 return __calloc( nelem, size ); 254 211 } … … 261 218 passes[passes_cnt - 1].frees++; 262 219 } // if 263 if ( stacktrace_stats_enabled && stacktrace_depth > 0) {264 stacktrace_stats[trace[stacktrace_depth]].mallocs++;265 stacktrace_stats[trace[stacktrace_depth]].frees++;266 }267 220 return s; 268 221 } -
src/Common/Stats/Heap.h
reef8dfb rbdfc032 20 20 void newPass( const char * const name ); 21 21 void print(); 22 23 size_t new_stacktrace_id(const char * const name);24 void stacktrace_push(size_t id);25 void stacktrace_pop();26 22 } 27 23 } -
src/Common/Stats/Stats.cc
reef8dfb rbdfc032 35 35 } 36 36 37 namespace ResolveTime {38 bool enabled = false;39 }40 41 37 struct { 42 38 const char * const opt; … … 47 43 { "heap" , Heap::enabled }, 48 44 { "time" , Time::enabled }, 49 { "resolve" , ResolveTime::enabled },50 45 }; 51 46 -
src/Common/module.mk
reef8dfb rbdfc032 17 17 SRC_COMMON = \ 18 18 Common/Assert.cc \ 19 Common/CodeLocation.h \20 Common/CodeLocationTools.hpp \21 Common/CodeLocationTools.cpp \22 Common/CompilerError.h \23 Common/Debug.h \24 Common/ErrorObjects.h \25 19 Common/Eval.cc \ 26 Common/Examine.cc \27 Common/Examine.h \28 Common/FilterCombos.h \29 Common/Indenter.h \30 20 Common/PassVisitor.cc \ 31 Common/PassVisitor.h \32 Common/PassVisitor.impl.h \33 Common/PassVisitor.proto.h \34 Common/PersistentMap.h \35 Common/ScopedMap.h \36 21 Common/SemanticError.cc \ 37 Common/SemanticError.h \38 Common/Stats.h \39 Common/Stats/Base.h \40 22 Common/Stats/Counter.cc \ 41 Common/Stats/Counter.h \42 23 Common/Stats/Heap.cc \ 43 Common/Stats/Heap.h \44 Common/Stats/ResolveTime.cc \45 Common/Stats/ResolveTime.h \46 24 Common/Stats/Stats.cc \ 47 25 Common/Stats/Time.cc \ 48 Common/Stats/Time.h \ 49 Common/UnimplementedError.h \ 50 Common/UniqueName.cc \ 51 Common/UniqueName.h \ 52 Common/utility.h \ 53 Common/VectorMap.h 26 Common/UniqueName.cc 54 27 55 28 SRC += $(SRC_COMMON) Common/DebugMalloc.cc -
src/Common/utility.h
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 11 13:00:36 202013 // Update Count : 5012 // Last Modified On : Wed Jul 24 14:28:19 2019 13 // Update Count : 41 14 14 // 15 15 … … 29 29 #include <utility> 30 30 #include <vector> 31 #include <cstring> // memcmp32 31 33 32 #include "Common/Indenter.h" … … 265 264 } 266 265 267 // determines if pref is a prefix of str268 static inline bool isPrefix( const std::string & str, const std::string & pref , unsigned int start = 0) {266 /// determines if `pref` is a prefix of `str` 267 static inline bool isPrefix( const std::string & str, const std::string & pref ) { 269 268 if ( pref.size() > str.size() ) return false; 270 return 0 == memcmp( str.c_str() + start, pref.c_str(), pref.size() );271 // return prefix == full.substr(0, prefix.size()); // for future, requires c++17269 auto its = std::mismatch( pref.begin(), pref.end(), str.begin() ); 270 return its.first == pref.end(); 272 271 } 273 272 … … 360 359 reverse_iterate_t( T & ref ) : ref(ref) {} 361 360 362 // this does NOT work on const T!!! 363 // typedef typename T::reverse_iterator iterator; 364 auto begin() { return ref.rbegin(); } 365 auto end() { return ref.rend(); } 361 typedef typename T::reverse_iterator iterator; 362 iterator begin() { return ref.rbegin(); } 363 iterator end() { return ref.rend(); } 366 364 }; 367 365 -
src/CompilationState.cc
reef8dfb rbdfc032 14 14 // 15 15 16 #include "config.h"17 18 16 int 19 17 astp = false, … … 29 27 nopreludep = false, 30 28 genproto = false, 31 deterministic_output = false,32 useNewAST = CFA_USE_NEW_AST,33 29 nomainp = false, 34 30 parsep = false, -
src/CompilationState.h
reef8dfb rbdfc032 28 28 nopreludep, 29 29 genproto, 30 deterministic_output,31 useNewAST,32 30 nomainp, 33 31 parsep, -
src/Concurrency/Keywords.cc
reef8dfb rbdfc032 16 16 #include "Concurrency/Keywords.h" 17 17 18 #include <cassert> // for assert 19 #include <string> // for string, operator== 20 21 #include <iostream> 22 23 #include "Common/Examine.h" // for isMainFor 24 #include "Common/PassVisitor.h" // for PassVisitor 25 #include "Common/SemanticError.h" // for SemanticError 26 #include "Common/utility.h" // for deleteAll, map_range 27 #include "CodeGen/OperatorTable.h" // for isConstructor 28 #include "ControlStruct/LabelGenerator.h" // for LebelGenerator 29 #include "InitTweak/InitTweak.h" // for getPointerBase 30 #include "SynTree/LinkageSpec.h" // for Cforall 31 #include "SynTree/Constant.h" // for Constant 32 #include "SynTree/Declaration.h" // for StructDecl, FunctionDecl, ObjectDecl 33 #include "SynTree/Expression.h" // for VariableExpr, ConstantExpr, Untype... 34 #include "SynTree/Initializer.h" // for SingleInit, ListInit, Initializer ... 35 #include "SynTree/Label.h" // for Label 36 #include "SynTree/Statement.h" // for CompoundStmt, DeclStmt, ExprStmt 37 #include "SynTree/Type.h" // for StructInstType, Type, PointerType 38 #include "SynTree/Visitor.h" // for Visitor, acceptAll 39 #include "Virtual/Tables.h" 18 #include <cassert> // for assert 19 #include <string> // for string, operator== 20 21 #include "Common/PassVisitor.h" // for PassVisitor 22 #include "Common/SemanticError.h" // for SemanticError 23 #include "Common/utility.h" // for deleteAll, map_range 24 #include "CodeGen/OperatorTable.h" // for isConstructor 25 #include "InitTweak/InitTweak.h" // for getPointerBase 26 #include "SynTree/LinkageSpec.h" // for Cforall 27 #include "SynTree/Constant.h" // for Constant 28 #include "SynTree/Declaration.h" // for StructDecl, FunctionDecl, ObjectDecl 29 #include "SynTree/Expression.h" // for VariableExpr, ConstantExpr, Untype... 30 #include "SynTree/Initializer.h" // for SingleInit, ListInit, Initializer ... 31 #include "SynTree/Label.h" // for Label 32 #include "SynTree/Statement.h" // for CompoundStmt, DeclStmt, ExprStmt 33 #include "SynTree/Type.h" // for StructInstType, Type, PointerType 34 #include "SynTree/Visitor.h" // for Visitor, acceptAll 40 35 41 36 class Attribute; 42 37 43 38 namespace Concurrency { 44 inline static std::string getVTableName( std::string const & exception_name ) {45 return exception_name.empty() ? std::string() : Virtual::vtableTypeName(exception_name);46 }47 48 // Only detects threads constructed with the keyword thread.49 inline static bool isThread( DeclarationWithType * decl ) {50 Type * baseType = decl->get_type()->stripDeclarator();51 StructInstType * instType = dynamic_cast<StructInstType *>( baseType );52 if ( nullptr == instType ) { return false; }53 return instType->baseStruct->is_thread();54 }55 56 39 //============================================================================================= 57 40 // Pass declarations … … 70 53 public: 71 54 72 ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name, 73 std::string&& getter_name, std::string&& context_error, std::string&& exception_name, 74 bool needs_main, AggregateDecl::Aggregate cast_target ) : 75 type_name( type_name ), field_name( field_name ), getter_name( getter_name ), 76 context_error( context_error ), exception_name( exception_name ), 77 vtable_name( getVTableName( exception_name ) ), 78 needs_main( needs_main ), cast_target( cast_target ) {} 55 ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name, std::string&& getter_name, std::string&& context_error, bool needs_main, AggregateDecl::Aggregate cast_target ) : 56 type_name( type_name ), field_name( field_name ), getter_name( getter_name ), context_error( context_error ), needs_main( needs_main ), cast_target( cast_target ) {} 79 57 80 58 virtual ~ConcurrentSueKeyword() {} … … 84 62 85 63 void handle( StructDecl * ); 86 void addVtableForward( StructDecl * );87 64 FunctionDecl * forwardDeclare( StructDecl * ); 88 65 ObjectDecl * addField( StructDecl * ); … … 98 75 const std::string getter_name; 99 76 const std::string context_error; 100 const std::string exception_name;101 const std::string vtable_name;102 77 bool needs_main; 103 78 AggregateDecl::Aggregate cast_target; … … 105 80 StructDecl * type_decl = nullptr; 106 81 FunctionDecl * dtor_decl = nullptr; 107 StructDecl * except_decl = nullptr;108 StructDecl * vtable_decl = nullptr;109 82 }; 110 83 … … 115 88 // int data; int data; 116 89 // a_struct_t more_data; a_struct_t more_data; 117 // => $thread__thrd_d;90 // => thread_desc __thrd_d; 118 91 // }; }; 119 // static inline $thread* get_thread( MyThread * this ) { return &this->__thrd_d; }92 // static inline thread_desc * get_thread( MyThread * this ) { return &this->__thrd_d; } 120 93 // 121 94 class ThreadKeyword final : public ConcurrentSueKeyword { … … 123 96 124 97 ThreadKeyword() : ConcurrentSueKeyword( 125 " $thread",98 "thread_desc", 126 99 "__thrd", 127 100 "get_thread", 128 101 "thread keyword requires threads to be in scope, add #include <thread.hfa>\n", 129 "ThreadCancelled",130 102 true, 131 103 AggregateDecl::Thread … … 148 120 // int data; int data; 149 121 // a_struct_t more_data; a_struct_t more_data; 150 // => $coroutine__cor_d;122 // => coroutine_desc __cor_d; 151 123 // }; }; 152 // static inline $coroutine* get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }124 // static inline coroutine_desc * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; } 153 125 // 154 126 class CoroutineKeyword final : public ConcurrentSueKeyword { … … 156 128 157 129 CoroutineKeyword() : ConcurrentSueKeyword( 158 " $coroutine",130 "coroutine_desc", 159 131 "__cor", 160 132 "get_coroutine", 161 133 "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n", 162 "CoroutineCancelled",163 134 true, 164 135 AggregateDecl::Coroutine … … 175 146 } 176 147 }; 177 178 179 148 180 149 //----------------------------------------------------------------------------- … … 183 152 // int data; int data; 184 153 // a_struct_t more_data; a_struct_t more_data; 185 // => $monitor__mon_d;154 // => monitor_desc __mon_d; 186 155 // }; }; 187 // static inline $monitor* get_coroutine( MyMonitor * this ) { return &this->__cor_d; }156 // static inline monitor_desc * get_coroutine( MyMonitor * this ) { return &this->__cor_d; } 188 157 // 189 158 class MonitorKeyword final : public ConcurrentSueKeyword { … … 191 160 192 161 MonitorKeyword() : ConcurrentSueKeyword( 193 " $monitor",162 "monitor_desc", 194 163 "__mon", 195 164 "get_monitor", 196 165 "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n", 197 "",198 166 false, 199 167 AggregateDecl::Monitor … … 212 180 213 181 //----------------------------------------------------------------------------- 214 //Handles generator type declarations :215 // generator MyGenerator { struct MyGenerator {216 // int data; int data;217 // a_struct_t more_data; a_struct_t more_data;218 // => int __gen_next;219 // }; };220 //221 class GeneratorKeyword final : public ConcurrentSueKeyword {222 public:223 224 GeneratorKeyword() : ConcurrentSueKeyword(225 "$generator",226 "__generator_state",227 "get_generator",228 "Unable to find builtin type $generator\n",229 "",230 true,231 AggregateDecl::Generator232 )233 {}234 235 virtual ~GeneratorKeyword() {}236 237 virtual bool is_target( StructDecl * decl ) override final { return decl->is_generator(); }238 239 static void implement( std::list< Declaration * > & translationUnit ) {240 PassVisitor< GeneratorKeyword > impl;241 mutateAll( translationUnit, impl );242 }243 };244 245 246 //-----------------------------------------------------------------------------247 class SuspendKeyword final : public WithStmtsToAdd, public WithGuards {248 public:249 SuspendKeyword() = default;250 virtual ~SuspendKeyword() = default;251 252 void premutate( FunctionDecl * );253 DeclarationWithType * postmutate( FunctionDecl * );254 255 Statement * postmutate( SuspendStmt * );256 257 static void implement( std::list< Declaration * > & translationUnit ) {258 PassVisitor< SuspendKeyword > impl;259 mutateAll( translationUnit, impl );260 }261 262 private:263 bool is_real_suspend( FunctionDecl * );264 265 Statement * make_generator_suspend( SuspendStmt * );266 Statement * make_coroutine_suspend( SuspendStmt * );267 268 struct LabelPair {269 Label obj;270 int idx;271 };272 273 LabelPair make_label() {274 labels.push_back( gen.newLabel("generator") );275 return { labels.back(), int(labels.size()) };276 }277 278 DeclarationWithType * in_generator = nullptr;279 FunctionDecl * decl_suspend = nullptr;280 std::vector<Label> labels;281 ControlStruct::LabelGenerator & gen = *ControlStruct::LabelGenerator::getGenerator();282 };283 284 //-----------------------------------------------------------------------------285 182 //Handles mutex routines definitions : 286 183 // void foo( A * mutex a, B * mutex b, int i ) { void foo( A * a, B * b, int i ) { 287 // $monitor* __monitors[] = { get_monitor(a), get_monitor(b) };184 // monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) }; 288 185 // monitor_guard_t __guard = { __monitors, 2 }; 289 186 // /*Some code*/ => /*Some code*/ … … 298 195 std::list<DeclarationWithType*> findMutexArgs( FunctionDecl*, bool & first ); 299 196 void validate( DeclarationWithType * ); 300 void addDtorStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 301 void addStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 302 void addThreadDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ); 197 void addDtorStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 198 void addStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 303 199 304 200 static void implement( std::list< Declaration * > & translationUnit ) { … … 311 207 StructDecl* guard_decl = nullptr; 312 208 StructDecl* dtor_guard_decl = nullptr; 313 StructDecl* thread_guard_decl = nullptr;314 209 315 210 static std::unique_ptr< Type > generic_func; … … 326 221 //Handles mutex routines definitions : 327 222 // void foo( A * mutex a, B * mutex b, int i ) { void foo( A * a, B * b, int i ) { 328 // $monitor* __monitors[] = { get_monitor(a), get_monitor(b) };223 // monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) }; 329 224 // monitor_guard_t __guard = { __monitors, 2 }; 330 225 // /*Some code*/ => /*Some code*/ … … 356 251 CoroutineKeyword ::implement( translationUnit ); 357 252 MonitorKeyword ::implement( translationUnit ); 358 GeneratorKeyword ::implement( translationUnit );359 SuspendKeyword ::implement( translationUnit );360 253 } 361 254 … … 390 283 handle( decl ); 391 284 } 392 else if ( !except_decl && exception_name == decl->name && decl->body ) {393 except_decl = decl;394 }395 else if ( !vtable_decl && vtable_name == decl->name && decl->body ) {396 vtable_decl = decl;397 }398 // Might be able to get ride of is target.399 assert( is_target(decl) == (cast_target == decl->kind) );400 285 return decl; 401 286 } 402 287 403 288 DeclarationWithType * ConcurrentSueKeyword::postmutate( FunctionDecl * decl ) { 404 if ( type_decl && isDestructorFor( decl, type_decl ) ) 405 dtor_decl = decl; 406 else if ( vtable_name.empty() ) 407 ; 408 else if( !decl->has_body() ) 409 ; 410 else if ( auto param = isMainFor( decl, cast_target ) ) { 411 // This should never trigger. 412 assert( vtable_decl ); 413 // Should be safe because of isMainFor. 414 StructInstType * struct_type = static_cast<StructInstType *>( 415 static_cast<ReferenceType *>( param->get_type() )->base ); 416 assert( struct_type ); 417 418 std::list< Expression * > poly_args = { new TypeExpr( struct_type->clone() ) }; 419 ObjectDecl * vtable_object = Virtual::makeVtableInstance( 420 vtable_decl->makeInst( poly_args ), struct_type, nullptr ); 421 declsToAddAfter.push_back( vtable_object ); 422 declsToAddAfter.push_back( Virtual::makeGetExceptionFunction( 423 vtable_object, except_decl->makeInst( std::move( poly_args ) ) 424 ) ); 425 } 426 289 if( !type_decl ) return decl; 290 if( !CodeGen::isDestructor( decl->name ) ) return decl; 291 292 auto params = decl->type->parameters; 293 if( params.size() != 1 ) return decl; 294 295 auto type = dynamic_cast<ReferenceType*>( params.front()->get_type() ); 296 if( !type ) return decl; 297 298 auto stype = dynamic_cast<StructInstType*>( type->base ); 299 if( !stype ) return decl; 300 if( stype->baseStruct != type_decl ) return decl; 301 302 if( !dtor_decl ) dtor_decl = decl; 427 303 return decl; 428 304 } … … 430 306 Expression * ConcurrentSueKeyword::postmutate( KeywordCastExpr * cast ) { 431 307 if ( cast_target == cast->target ) { 432 // convert (thread &)t to ( $thread&)*get_thread(t), etc.308 // convert (thread &)t to (thread_desc &)*get_thread(t), etc. 433 309 if( !type_decl ) SemanticError( cast, context_error ); 434 310 if( !dtor_decl ) SemanticError( cast, context_error ); … … 448 324 if( !dtor_decl ) SemanticError( decl, context_error ); 449 325 450 addVtableForward( decl );451 326 FunctionDecl * func = forwardDeclare( decl ); 452 327 ObjectDecl * field = addField( decl ); 453 328 addRoutines( field, func ); 454 }455 456 void ConcurrentSueKeyword::addVtableForward( StructDecl * decl ) {457 if ( vtable_decl ) {458 std::list< Expression * > poly_args = {459 new TypeExpr( new StructInstType( noQualifiers, decl ) ),460 };461 declsToAddBefore.push_back( Virtual::makeGetExceptionForward(462 vtable_decl->makeInst( poly_args ),463 except_decl->makeInst( poly_args )464 ) );465 declsToAddBefore.push_back( Virtual::makeVtableForward(466 vtable_decl->makeInst( move( poly_args ) ) ) );467 // Its only an error if we want a vtable and don't have one.468 } else if ( ! vtable_name.empty() ) {469 SemanticError( decl, context_error );470 }471 329 } 472 330 … … 519 377 get_type, 520 378 nullptr, 521 { new Attribute("const") },379 noAttributes, 522 380 Type::Inline 523 381 ); … … 576 434 new CastExpr( 577 435 new VariableExpr( func->get_functionType()->get_parameters().front() ), 578 func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone(), 579 false 436 func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone() 580 437 ) 581 438 ) … … 589 446 590 447 declsToAddAfter.push_back( get_decl ); 591 } 592 593 //============================================================================================= 594 // Suspend keyword implementation 595 //============================================================================================= 596 bool SuspendKeyword::is_real_suspend( FunctionDecl * func ) { 597 if(isMangled(func->linkage)) return false; // the real suspend isn't mangled 598 if(func->name != "__cfactx_suspend") return false; // the real suspend has a specific name 599 if(func->type->parameters.size() != 0) return false; // Too many parameters 600 if(func->type->returnVals.size() != 0) return false; // Too many return values 601 602 return true; 603 } 604 605 void SuspendKeyword::premutate( FunctionDecl * func ) { 606 GuardValue(in_generator); 607 in_generator = nullptr; 608 609 // Is this the real suspend? 610 if(is_real_suspend(func)) { 611 decl_suspend = decl_suspend ? decl_suspend : func; 612 return; 613 } 614 615 // Is this the main of a generator? 616 auto param = isMainFor( func, AggregateDecl::Aggregate::Generator ); 617 if(!param) return; 618 619 if(func->type->returnVals.size() != 0) SemanticError(func->location, "Generator main must return void"); 620 621 in_generator = param; 622 GuardValue(labels); 623 labels.clear(); 624 } 625 626 DeclarationWithType * SuspendKeyword::postmutate( FunctionDecl * func ) { 627 if( !func->statements ) return func; // Not the actual definition, don't do anything 628 if( !in_generator ) return func; // Not in a generator, don't do anything 629 if( labels.empty() ) return func; // Generator has no states, nothing to do, could throw a warning 630 631 // This is a generator main, we need to add the following code to the top 632 // static void * __generator_labels[] = {&&s0, &&s1, ...}; 633 // goto * __generator_labels[gen.__generator_state]; 634 const auto & loc = func->location; 635 636 const auto first_label = gen.newLabel("generator"); 637 638 // for each label add to declaration 639 std::list<Initializer*> inits = { new SingleInit( new LabelAddressExpr( first_label ) ) }; 640 for(const auto & label : labels) { 641 inits.push_back( 642 new SingleInit( 643 new LabelAddressExpr( label ) 644 ) 645 ); 646 } 647 auto init = new ListInit(std::move(inits), noDesignators, true); 648 labels.clear(); 649 650 // create decl 651 auto decl = new ObjectDecl( 652 "__generator_labels", 653 Type::StorageClasses( Type::Static ), 654 LinkageSpec::AutoGen, 655 nullptr, 656 new ArrayType( 657 Type::Qualifiers(), 658 new PointerType( 659 Type::Qualifiers(), 660 new VoidType( Type::Qualifiers() ) 661 ), 662 nullptr, 663 false, false 664 ), 665 init 666 ); 667 668 // create the goto 669 assert(in_generator); 670 671 auto go_decl = new ObjectDecl( 672 "__generator_label", 673 noStorageClasses, 674 LinkageSpec::AutoGen, 675 nullptr, 676 new PointerType( 677 Type::Qualifiers(), 678 new VoidType( Type::Qualifiers() ) 679 ), 680 new SingleInit( 681 new UntypedExpr( 682 new NameExpr("?[?]"), 683 { 684 new NameExpr("__generator_labels"), 685 new UntypedMemberExpr( 686 new NameExpr("__generator_state"), 687 new VariableExpr( in_generator ) 688 ) 689 } 690 ) 691 ) 692 ); 693 go_decl->location = loc; 694 695 auto go = new BranchStmt( 696 new VariableExpr( go_decl ), 697 BranchStmt::Goto 698 ); 699 go->location = loc; 700 go->computedTarget->location = loc; 701 702 auto noop = new NullStmt({ first_label }); 703 noop->location = loc; 704 705 // wrap everything in a nice compound 706 auto body = new CompoundStmt({ 707 new DeclStmt( decl ), 708 new DeclStmt( go_decl ), 709 go, 710 noop, 711 func->statements 712 }); 713 body->location = loc; 714 func->statements = body; 715 716 return func; 717 } 718 719 Statement * SuspendKeyword::postmutate( SuspendStmt * stmt ) { 720 SuspendStmt::Type type = stmt->type; 721 if(type == SuspendStmt::None) { 722 // This suspend has a implicit target, find it 723 type = in_generator ? SuspendStmt::Generator : SuspendStmt::Coroutine; 724 } 725 726 // Check that the target makes sense 727 if(!in_generator && type == SuspendStmt::Generator) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type."); 728 729 // Act appropriately 730 switch(type) { 731 case SuspendStmt::Generator: return make_generator_suspend(stmt); 732 case SuspendStmt::Coroutine: return make_coroutine_suspend(stmt); 733 default: abort(); 734 } 735 } 736 737 Statement * SuspendKeyword::make_generator_suspend( SuspendStmt * stmt ) { 738 assert(in_generator); 739 // Target code is : 740 // gen.__generator_state = X; 741 // { THEN } 742 // return; 743 // __gen_X:; 744 745 // Save the location and delete the old statement, we only need the location from this point on 746 auto loc = stmt->location; 747 748 // Build the label and get its index 749 auto label = make_label(); 750 751 // Create the context saving statement 752 auto save = new ExprStmt( new UntypedExpr( 753 new NameExpr( "?=?" ), 754 { 755 new UntypedMemberExpr( 756 new NameExpr("__generator_state"), 757 new VariableExpr( in_generator ) 758 ), 759 new ConstantExpr( 760 Constant::from_int( label.idx ) 761 ) 762 } 763 )); 764 assert(save->expr); 765 save->location = loc; 766 stmtsToAddBefore.push_back( save ); 767 768 // if we have a then add it here 769 auto then = stmt->then; 770 stmt->then = nullptr; 771 delete stmt; 772 if(then) stmtsToAddBefore.push_back( then ); 773 774 // Create the return statement 775 auto ret = new ReturnStmt( nullptr ); 776 ret->location = loc; 777 stmtsToAddBefore.push_back( ret ); 778 779 // Create the null statement with the created label 780 auto noop = new NullStmt({ label.obj }); 781 noop->location = loc; 782 783 // Return the null statement to take the place of the previous statement 784 return noop; 785 } 786 787 Statement * SuspendKeyword::make_coroutine_suspend( SuspendStmt * stmt ) { 788 if(stmt->then) SemanticError( stmt->location, "Compound statement following coroutines is not implemented."); 789 790 // Save the location and delete the old statement, we only need the location from this point on 791 auto loc = stmt->location; 792 delete stmt; 793 794 // Create the call expression 795 if(!decl_suspend) SemanticError( loc, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n"); 796 auto expr = new UntypedExpr( VariableExpr::functionPointer( decl_suspend ) ); 797 expr->location = loc; 798 799 // Change this statement into a regular expr 800 assert(expr); 801 auto nstmt = new ExprStmt( expr ); 802 nstmt->location = loc; 803 return nstmt; 804 } 805 448 449 // get_decl->fixUniqueId(); 450 } 806 451 807 452 //============================================================================================= … … 813 458 bool first = false; 814 459 std::list<DeclarationWithType*> mutexArgs = findMutexArgs( decl, first ); 815 bool constisDtor = CodeGen::isDestructor( decl->name );460 bool isDtor = CodeGen::isDestructor( decl->name ); 816 461 817 462 // Is this function relevant to monitors … … 861 506 862 507 // Instrument the body 863 if ( isDtor && isThread( mutexArgs.front() ) ) { 864 if( !thread_guard_decl ) { 865 SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" ); 866 } 867 addThreadDtorStatements( decl, body, mutexArgs ); 868 } 869 else if ( isDtor ) { 870 addDtorStatements( decl, body, mutexArgs ); 508 if( isDtor ) { 509 addDtorStatments( decl, body, mutexArgs ); 871 510 } 872 511 else { 873 addStat ements( decl, body, mutexArgs );512 addStatments( decl, body, mutexArgs ); 874 513 } 875 514 } … … 877 516 void MutexKeyword::postvisit(StructDecl* decl) { 878 517 879 if( decl->name == " $monitor" && decl->body ) {518 if( decl->name == "monitor_desc" && decl->body ) { 880 519 assert( !monitor_decl ); 881 520 monitor_decl = decl; … … 888 527 assert( !dtor_guard_decl ); 889 528 dtor_guard_decl = decl; 890 }891 else if( decl->name == "thread_dtor_guard_t" && decl->body ) {892 assert( !thread_guard_decl );893 thread_guard_decl = decl;894 529 } 895 530 } … … 930 565 } 931 566 932 void MutexKeyword::addDtorStat ements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {567 void MutexKeyword::addDtorStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) { 933 568 Type * arg_type = args.front()->get_type()->clone(); 934 569 arg_type->set_mutex( false ); … … 948 583 new SingleInit( new UntypedExpr( 949 584 new NameExpr( "get_monitor" ), 950 { new CastExpr( new VariableExpr( args.front() ), arg_type , false) }585 { new CastExpr( new VariableExpr( args.front() ), arg_type ) } 951 586 )) 952 587 ); … … 969 604 { 970 605 new SingleInit( new AddressExpr( new VariableExpr( monitors ) ) ), 971 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) ), 972 new SingleInit( new ConstantExpr( Constant::from_bool( false ) ) ) 606 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() ) ) 973 607 }, 974 608 noDesignators, … … 978 612 ); 979 613 980 //$monitor * __monitors[] = { get_monitor(a), get_monitor(b) }; 981 body->push_front( new DeclStmt( monitors ) ); 982 } 983 984 void MutexKeyword::addThreadDtorStatements( 985 FunctionDecl*, CompoundStmt * body, 986 const std::list<DeclarationWithType * > & args ) { 987 assert( args.size() == 1 ); 988 DeclarationWithType * arg = args.front(); 989 Type * arg_type = arg->get_type()->clone(); 990 assert( arg_type->get_mutex() ); 991 arg_type->set_mutex( false ); 992 993 // thread_dtor_guard_t __guard = { this, intptr( 0 ) }; 994 body->push_front( 995 new DeclStmt( new ObjectDecl( 996 "__guard", 997 noStorageClasses, 998 LinkageSpec::Cforall, 999 nullptr, 1000 new StructInstType( 1001 noQualifiers, 1002 thread_guard_decl 1003 ), 1004 new ListInit( 1005 { 1006 new SingleInit( new CastExpr( new VariableExpr( arg ), arg_type ) ), 1007 new SingleInit( new UntypedExpr( 1008 new NameExpr( "intptr" ), { 1009 new ConstantExpr( Constant::from_int( 0 ) ), 1010 } 1011 ) ), 1012 }, 1013 noDesignators, 1014 true 1015 ) 1016 )) 1017 ); 1018 } 1019 1020 void MutexKeyword::addStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) { 614 //monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) }; 615 body->push_front( new DeclStmt( monitors) ); 616 } 617 618 void MutexKeyword::addStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) { 1021 619 ObjectDecl * monitors = new ObjectDecl( 1022 620 "__monitors", … … 1043 641 return new SingleInit( new UntypedExpr( 1044 642 new NameExpr( "get_monitor" ), 1045 { new CastExpr( new VariableExpr( var ), type , false) }643 { new CastExpr( new VariableExpr( var ), type ) } 1046 644 ) ); 1047 645 }) … … 1067 665 new SingleInit( new VariableExpr( monitors ) ), 1068 666 new SingleInit( new ConstantExpr( Constant::from_ulong( args.size() ) ) ), 1069 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() , false) )667 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() ) ) 1070 668 }, 1071 669 noDesignators, … … 1075 673 ); 1076 674 1077 // $monitor* __monitors[] = { get_monitor(a), get_monitor(b) };675 //monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) }; 1078 676 body->push_front( new DeclStmt( monitors) ); 1079 677 } … … 1083 681 //============================================================================================= 1084 682 void ThreadStarter::previsit( StructDecl * decl ) { 1085 if( decl->name == " $thread" && decl->body ) {683 if( decl->name == "thread_desc" && decl->body ) { 1086 684 assert( !thread_decl ); 1087 685 thread_decl = decl; … … 1129 727 // tab-width: 4 // 1130 728 // End: // 1131 -
src/Concurrency/Waitfor.cc
reef8dfb rbdfc032 244 244 decl_mask = decl; 245 245 } 246 else if( decl->name == " $monitor" ) {246 else if( decl->name == "monitor_desc" ) { 247 247 assert( !decl_monitor ); 248 248 decl_monitor = decl; … … 384 384 decl_monitor 385 385 ) 386 ), 387 false 386 ) 388 387 ); 389 388 … … 409 408 new CompoundStmt({ 410 409 makeAccStatement( acceptables, index, "is_dtor", detectIsDtor( clause.target.function ) , indexer ), 411 makeAccStatement( acceptables, index, "func" , new CastExpr( clause.target.function, fptr_t , false ), indexer ),410 makeAccStatement( acceptables, index, "func" , new CastExpr( clause.target.function, fptr_t ) , indexer ), 412 411 makeAccStatement( acceptables, index, "data" , new VariableExpr( monitors ) , indexer ), 413 412 makeAccStatement( acceptables, index, "size" , new ConstantExpr( Constant::from_ulong( clause.target.arguments.size() ) ), indexer ), … … 532 531 decl_mask 533 532 ) 534 ), 535 false 533 ) 536 534 ), 537 535 timeout -
src/Concurrency/module.mk
reef8dfb rbdfc032 15 15 ############################################################################### 16 16 17 SRC += Concurrency/Keywords.cc Concurrency/ Keywords.h Concurrency/Waitfor.cc Concurrency/Waitfor.h17 SRC += Concurrency/Keywords.cc Concurrency/Waitfor.cc 18 18 SRCDEMANGLE += Concurrency/Keywords.cc 19 19 -
src/ControlStruct/ExceptTranslate.cc
reef8dfb rbdfc032 9 9 // Author : Andrew Beach 10 10 // Created On : Wed Jun 14 16:49:00 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : Wed Jun 24 11:18:00 202013 // Update Count : 1 711 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Dec 13 23:40:15 2019 13 // Update Count : 12 14 14 // 15 15 … … 64 64 } 65 65 66 class ThrowMutatorCore : public WithGuards { 67 ObjectDecl * terminate_handler_except; 68 enum Context { NoHandler, TerHandler, ResHandler } cur_context; 69 70 // The helper functions for code/syntree generation. 71 Statement * create_either_throw( 72 ThrowStmt * throwStmt, const char * throwFunc ); 73 Statement * create_terminate_rethrow( ThrowStmt * throwStmt ); 74 75 public: 76 ThrowMutatorCore() : 77 terminate_handler_except( nullptr ), 78 cur_context( NoHandler ) 79 {} 80 81 void premutate( CatchStmt *catchStmt ); 82 Statement * postmutate( ThrowStmt *throwStmt ); 83 }; 84 85 // ThrowStmt Mutation Helpers 86 87 Statement * ThrowMutatorCore::create_either_throw( 88 ThrowStmt * throwStmt, const char * throwFunc ) { 89 // `throwFunc`( `throwStmt->get_name()` ); 90 UntypedExpr * call = new UntypedExpr( new NameExpr( throwFunc ) ); 91 call->get_args().push_back( throwStmt->get_expr() ); 92 throwStmt->set_expr( nullptr ); 93 delete throwStmt; 94 return new ExprStmt( call ); 95 } 96 97 Statement * ThrowMutatorCore::create_terminate_rethrow( 98 ThrowStmt *throwStmt ) { 99 // { `terminate_handler_except` = 0p; __rethrow_terminate(); } 100 assert( nullptr == throwStmt->get_expr() ); 101 assert( terminate_handler_except ); 102 103 CompoundStmt * result = new CompoundStmt(); 104 result->labels = throwStmt->labels; 105 result->push_back( new ExprStmt( UntypedExpr::createAssign( 106 nameOf( terminate_handler_except ), 107 new ConstantExpr( Constant::null( 108 terminate_handler_except->get_type()->clone() 109 ) ) 110 ) ) ); 111 result->push_back( new ExprStmt( 112 new UntypedExpr( new NameExpr( "__cfaehm_rethrow_terminate" ) ) 113 ) ); 114 delete throwStmt; 115 return result; 116 } 117 118 // Visiting/Mutating Functions 119 120 void ThrowMutatorCore::premutate( CatchStmt *catchStmt ) { 121 // Validate the statement's form. 122 ObjectDecl * decl = dynamic_cast<ObjectDecl *>( catchStmt->get_decl() ); 123 // Also checking the type would be nice. 124 if ( !decl || !dynamic_cast<PointerType *>( decl->type ) ) { 125 std::string kind = (CatchStmt::Terminate == catchStmt->kind) ? "catch" : "catchResume"; 126 SemanticError( catchStmt->location, kind + " must have pointer to an exception type" ); 127 } 128 129 // Track the handler context. 130 GuardValue( cur_context ); 131 if ( CatchStmt::Terminate == catchStmt->get_kind() ) { 132 cur_context = TerHandler; 133 134 GuardValue( terminate_handler_except ); 135 terminate_handler_except = decl; 136 } else { 137 cur_context = ResHandler; 138 } 139 } 140 141 Statement * ThrowMutatorCore::postmutate( ThrowStmt *throwStmt ) { 142 // Ignoring throwStmt->get_target() for now. 143 if ( ThrowStmt::Terminate == throwStmt->get_kind() ) { 144 if ( throwStmt->get_expr() ) { 145 return create_either_throw( throwStmt, "$throw" ); 146 } else if ( TerHandler == cur_context ) { 147 return create_terminate_rethrow( throwStmt ); 148 } else { 149 abort("Invalid throw in %s at %i\n", 150 throwStmt->location.filename.c_str(), 151 throwStmt->location.first_line); 152 } 153 } else { 154 if ( throwStmt->get_expr() ) { 155 return create_either_throw( throwStmt, "$throwResume" ); 156 } else if ( ResHandler == cur_context ) { 157 // This has to be handled later. 158 return throwStmt; 159 } else { 160 abort("Invalid throwResume in %s at %i\n", 161 throwStmt->location.filename.c_str(), 162 throwStmt->location.first_line); 163 } 164 } 165 } 166 167 class TryMutatorCore { 66 class ExceptionMutatorCore : public WithGuards { 67 enum Context { NoHandler, TerHandler, ResHandler }; 68 69 // Also need to handle goto, break & continue. 70 // They need to be cut off in a ResHandler, until we enter another 71 // loop, switch or the goto stays within the function. 72 73 Context cur_context; 74 75 // The current (innermost) termination handler exception declaration. 76 ObjectDecl * handler_except_decl; 77 168 78 // The built in types used in translation. 169 79 StructDecl * except_decl; … … 172 82 173 83 // The many helper functions for code/syntree generation. 84 Statement * create_given_throw( 85 const char * throwFunc, ThrowStmt * throwStmt ); 86 Statement * create_terminate_throw( ThrowStmt * throwStmt ); 87 Statement * create_terminate_rethrow( ThrowStmt * throwStmt ); 88 Statement * create_resume_throw( ThrowStmt * throwStmt ); 89 Statement * create_resume_rethrow( ThrowStmt * throwStmt ); 174 90 CompoundStmt * take_try_block( TryStmt * tryStmt ); 175 91 FunctionDecl * create_try_wrapper( CompoundStmt * body ); … … 185 101 FunctionDecl * create_finally_wrapper( TryStmt * tryStmt ); 186 102 ObjectDecl * create_finally_hook( FunctionDecl * finally_wrapper ); 187 Statement * create_resume_rethrow( ThrowStmt * throwStmt );188 103 189 104 // Types used in translation, make sure to use clone. … … 206 121 207 122 public: 208 TryMutatorCore() : 123 ExceptionMutatorCore() : 124 cur_context( NoHandler ), 125 handler_except_decl( nullptr ), 209 126 except_decl( nullptr ), node_decl( nullptr ), hook_decl( nullptr ), 210 127 try_func_t( noQualifiers, false ), … … 215 132 {} 216 133 134 void premutate( CatchStmt *catchStmt ); 217 135 void premutate( StructDecl *structDecl ); 136 Statement * postmutate( ThrowStmt *throwStmt ); 218 137 Statement * postmutate( TryStmt *tryStmt ); 219 Statement * postmutate( ThrowStmt *throwStmt );220 138 }; 221 139 222 void TryMutatorCore::init_func_types() {140 void ExceptionMutatorCore::init_func_types() { 223 141 assert( except_decl ); 224 142 … … 278 196 } 279 197 198 // ThrowStmt Mutation Helpers 199 200 Statement * ExceptionMutatorCore::create_given_throw( 201 const char * throwFunc, ThrowStmt * throwStmt ) { 202 // `throwFunc`( `throwStmt->get_name` ); 203 UntypedExpr * call = new UntypedExpr( new NameExpr( throwFunc ) ); 204 call->get_args().push_back( throwStmt->get_expr() ); 205 throwStmt->set_expr( nullptr ); 206 delete throwStmt; 207 return new ExprStmt( call ); 208 } 209 210 Statement * ExceptionMutatorCore::create_terminate_throw( 211 ThrowStmt *throwStmt ) { 212 // __throw_terminate( `throwStmt->get_name()` ); } 213 return create_given_throw( "__cfaabi_ehm__throw_terminate", throwStmt ); 214 } 215 216 Statement * ExceptionMutatorCore::create_terminate_rethrow( 217 ThrowStmt *throwStmt ) { 218 // { `handler_except_decl` = NULL; __rethrow_terminate(); } 219 assert( nullptr == throwStmt->get_expr() ); 220 assert( handler_except_decl ); 221 222 CompoundStmt * result = new CompoundStmt(); 223 result->labels = throwStmt->labels; 224 result->push_back( new ExprStmt( UntypedExpr::createAssign( 225 nameOf( handler_except_decl ), 226 new ConstantExpr( Constant::null( 227 new PointerType( 228 noQualifiers, 229 handler_except_decl->get_type()->clone() 230 ) 231 ) ) 232 ) ) ); 233 result->push_back( new ExprStmt( 234 new UntypedExpr( new NameExpr( "__cfaabi_ehm__rethrow_terminate" ) ) 235 ) ); 236 delete throwStmt; 237 return result; 238 } 239 240 Statement * ExceptionMutatorCore::create_resume_throw( 241 ThrowStmt *throwStmt ) { 242 // __throw_resume( `throwStmt->get_name` ); 243 return create_given_throw( "__cfaabi_ehm__throw_resume", throwStmt ); 244 } 245 246 Statement * ExceptionMutatorCore::create_resume_rethrow( 247 ThrowStmt *throwStmt ) { 248 // return false; 249 Statement * result = new ReturnStmt( 250 new ConstantExpr( Constant::from_bool( false ) ) 251 ); 252 result->labels = throwStmt->labels; 253 delete throwStmt; 254 return result; 255 } 256 280 257 // TryStmt Mutation Helpers 281 258 282 CompoundStmt * TryMutatorCore::take_try_block( TryStmt *tryStmt ) {259 CompoundStmt * ExceptionMutatorCore::take_try_block( TryStmt *tryStmt ) { 283 260 CompoundStmt * block = tryStmt->get_block(); 284 261 tryStmt->set_block( nullptr ); … … 286 263 } 287 264 288 FunctionDecl * TryMutatorCore::create_try_wrapper(265 FunctionDecl * ExceptionMutatorCore::create_try_wrapper( 289 266 CompoundStmt *body ) { 290 267 … … 293 270 } 294 271 295 FunctionDecl * TryMutatorCore::create_terminate_catch(272 FunctionDecl * ExceptionMutatorCore::create_terminate_catch( 296 273 CatchList &handlers ) { 297 274 std::list<CaseStmt *> handler_wrappers; … … 332 309 local_except->get_attributes().push_back( new Attribute( 333 310 "cleanup", 334 { new NameExpr( "__cfa ehm_cleanup_terminate" ) }311 { new NameExpr( "__cfaabi_ehm__cleanup_terminate" ) } 335 312 ) ); 336 313 … … 373 350 // Create a single check from a moddified handler. 374 351 // except_obj is referenced, modded_handler will be freed. 375 CompoundStmt * TryMutatorCore::create_single_matcher(352 CompoundStmt * ExceptionMutatorCore::create_single_matcher( 376 353 DeclarationWithType * except_obj, CatchStmt * modded_handler ) { 377 354 // { … … 411 388 } 412 389 413 FunctionDecl * TryMutatorCore::create_terminate_match(390 FunctionDecl * ExceptionMutatorCore::create_terminate_match( 414 391 CatchList &handlers ) { 415 392 // int match(exception * except) { … … 448 425 } 449 426 450 CompoundStmt * TryMutatorCore::create_terminate_caller(427 CompoundStmt * ExceptionMutatorCore::create_terminate_caller( 451 428 FunctionDecl * try_wrapper, 452 429 FunctionDecl * terminate_catch, 453 430 FunctionDecl * terminate_match ) { 454 // { __cfa ehm_try_terminate(`try`, `catch`, `match`); }431 // { __cfaabi_ehm__try_terminate(`try`, `catch`, `match`); } 455 432 456 433 UntypedExpr * caller = new UntypedExpr( new NameExpr( 457 "__cfa ehm_try_terminate" ) );434 "__cfaabi_ehm__try_terminate" ) ); 458 435 std::list<Expression *>& args = caller->get_args(); 459 436 args.push_back( nameOf( try_wrapper ) ); … … 466 443 } 467 444 468 FunctionDecl * TryMutatorCore::create_resume_handler(445 FunctionDecl * ExceptionMutatorCore::create_resume_handler( 469 446 CatchList &handlers ) { 470 447 // bool handle(exception * except) { … … 503 480 } 504 481 505 CompoundStmt * TryMutatorCore::create_resume_wrapper(482 CompoundStmt * ExceptionMutatorCore::create_resume_wrapper( 506 483 Statement * wraps, 507 484 FunctionDecl * resume_handler ) { … … 509 486 510 487 // struct __try_resume_node __resume_node 511 // __attribute__((cleanup( __cfa ehm_try_resume_cleanup )));488 // __attribute__((cleanup( __cfaabi_ehm__try_resume_cleanup ))); 512 489 // ** unwinding of the stack here could cause problems ** 513 490 // ** however I don't think that can happen currently ** 514 // __cfa ehm_try_resume_setup( &__resume_node, resume_handler );491 // __cfaabi_ehm__try_resume_setup( &__resume_node, resume_handler ); 515 492 516 493 std::list< Attribute * > attributes; … … 518 495 std::list< Expression * > attr_params; 519 496 attr_params.push_back( new NameExpr( 520 "__cfa ehm_try_resume_cleanup" ) );497 "__cfaabi_ehm__try_resume_cleanup" ) ); 521 498 attributes.push_back( new Attribute( "cleanup", attr_params ) ); 522 499 } … … 537 514 538 515 UntypedExpr *setup = new UntypedExpr( new NameExpr( 539 "__cfa ehm_try_resume_setup" ) );516 "__cfaabi_ehm__try_resume_setup" ) ); 540 517 setup->get_args().push_back( new AddressExpr( nameOf( obj ) ) ); 541 518 setup->get_args().push_back( nameOf( resume_handler ) ); … … 547 524 } 548 525 549 FunctionDecl * TryMutatorCore::create_finally_wrapper(526 FunctionDecl * ExceptionMutatorCore::create_finally_wrapper( 550 527 TryStmt * tryStmt ) { 551 // void finally() { `finally->block`}528 // void finally() { <finally code> } 552 529 FinallyStmt * finally = tryStmt->get_finally(); 553 530 CompoundStmt * body = finally->get_block(); … … 560 537 } 561 538 562 ObjectDecl * TryMutatorCore::create_finally_hook(539 ObjectDecl * ExceptionMutatorCore::create_finally_hook( 563 540 FunctionDecl * finally_wrapper ) { 564 // struct __cfa ehm_cleanup_hook __finally_hook565 // __attribute__((cleanup( `finally_wrapper`)));541 // struct __cfaabi_ehm__cleanup_hook __finally_hook 542 // __attribute__((cleanup( finally_wrapper ))); 566 543 567 544 // Make Cleanup Attribute. … … 587 564 } 588 565 589 Statement * TryMutatorCore::create_resume_rethrow( ThrowStmt *throwStmt ) {590 // return false;591 Statement * result = new ReturnStmt(592 new ConstantExpr( Constant::from_bool( false ) )593 );594 result->labels = throwStmt->labels;595 delete throwStmt;596 return result;597 }598 599 566 // Visiting/Mutating Functions 600 void TryMutatorCore::premutate( StructDecl *structDecl ) { 567 void ExceptionMutatorCore::premutate( CatchStmt *catchStmt ) { 568 // Validate the Statement's form. 569 ObjectDecl * decl = 570 dynamic_cast<ObjectDecl *>( catchStmt->get_decl() ); 571 if ( decl && true /* check decl->get_type() */ ) { 572 // Pass. 573 } else if ( CatchStmt::Terminate == catchStmt->get_kind() ) { 574 SemanticError(catchStmt->location, "catch must have exception type"); 575 } else { 576 SemanticError(catchStmt->location, "catchResume must have exception type"); 577 } 578 579 // Track the handler context. 580 GuardValue( cur_context ); 581 if ( CatchStmt::Terminate == catchStmt->get_kind() ) { 582 cur_context = TerHandler; 583 584 GuardValue( handler_except_decl ); 585 handler_except_decl = decl; 586 } else { 587 cur_context = ResHandler; 588 } 589 } 590 591 void ExceptionMutatorCore::premutate( StructDecl *structDecl ) { 601 592 if ( !structDecl->has_body() ) { 602 593 // Skip children? 603 594 return; 604 } else if ( structDecl->get_name() == "__cfa ehm_base_exception_t" ) {595 } else if ( structDecl->get_name() == "__cfaabi_ehm__base_exception_t" ) { 605 596 assert( nullptr == except_decl ); 606 597 except_decl = structDecl; 607 598 init_func_types(); 608 } else if ( structDecl->get_name() == "__cfa ehm_try_resume_node" ) {599 } else if ( structDecl->get_name() == "__cfaabi_ehm__try_resume_node" ) { 609 600 assert( nullptr == node_decl ); 610 601 node_decl = structDecl; 611 } else if ( structDecl->get_name() == "__cfa ehm_cleanup_hook" ) {602 } else if ( structDecl->get_name() == "__cfaabi_ehm__cleanup_hook" ) { 612 603 assert( nullptr == hook_decl ); 613 604 hook_decl = structDecl; 614 605 } 615 } 616 617 Statement * TryMutatorCore::postmutate( TryStmt *tryStmt ) { 606 // Later we might get the exception type as well. 607 } 608 609 Statement * ExceptionMutatorCore::postmutate( ThrowStmt *throwStmt ) { 610 assert( except_decl ); 611 612 // Ignoring throwStmt->get_target() for now. 613 if ( ThrowStmt::Terminate == throwStmt->get_kind() ) { 614 if ( throwStmt->get_expr() ) { 615 return create_terminate_throw( throwStmt ); 616 } else if ( TerHandler == cur_context ) { 617 return create_terminate_rethrow( throwStmt ); 618 } else { 619 abort("Invalid throw in %s at %i\n", 620 throwStmt->location.filename.c_str(), 621 throwStmt->location.first_line); 622 } 623 } else { 624 if ( throwStmt->get_expr() ) { 625 return create_resume_throw( throwStmt ); 626 } else if ( ResHandler == cur_context ) { 627 return create_resume_rethrow( throwStmt ); 628 } else { 629 abort("Invalid throwResume in %s at %i\n", 630 throwStmt->location.filename.c_str(), 631 throwStmt->location.first_line); 632 } 633 } 634 } 635 636 Statement * ExceptionMutatorCore::postmutate( TryStmt *tryStmt ) { 618 637 assert( except_decl ); 619 638 assert( node_decl ); … … 669 688 } 670 689 671 Statement * TryMutatorCore::postmutate( ThrowStmt *throwStmt ) { 672 // Only valid `throwResume;` statements should remain. (2/3 checks) 673 assert( ThrowStmt::Resume == throwStmt->kind && ! throwStmt->expr ); 674 return create_resume_rethrow( throwStmt ); 675 } 676 677 void translateThrows( std::list< Declaration *> & translationUnit ) { 678 PassVisitor<ThrowMutatorCore> translator; 690 void translateEHM( std::list< Declaration *> & translationUnit ) { 691 PassVisitor<ExceptionMutatorCore> translator; 679 692 mutateAll( translationUnit, translator ); 680 693 } 681 682 void translateTries( std::list< Declaration *> & translationUnit ) {683 PassVisitor<TryMutatorCore> translator;684 mutateAll( translationUnit, translator );685 }686 694 } -
src/ControlStruct/ExceptTranslate.h
reef8dfb rbdfc032 9 9 // Author : Andrew Beach 10 10 // Created On : Tus Jun 06 10:13:00 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : Tus May 19 11:47:00 202013 // Update Count : 511 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Jul 22 09:19:23 2017 13 // Update Count : 4 14 14 // 15 15 … … 21 21 22 22 namespace ControlStruct { 23 void translateThrows( std::list< Declaration *> & translationUnit ); 24 /* Replaces all throw & throwResume statements with function calls. 25 * These still need to be resolved, so call this before the reslover. 26 */ 27 28 void translateTries( std::list< Declaration *> & translationUnit ); 29 /* Replaces all try blocks (and their many clauses) with function definitions and calls. 30 * This uses the exception built-ins to produce typed output and should take place after 31 * the resolver. It also produces virtual casts and should happen before they are expanded. 32 */ 23 void translateEHM( std::list< Declaration *> & translationUnit ); 24 // Converts exception handling structures into their underlying C code. Translation does use the exception 25 // handling header, make sure it is visible wherever translation occurs. 33 26 } 34 27 -
src/ControlStruct/Mutate.cc
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 03:22:07 202013 // Update Count : 1012 // Last Modified On : Thu Aug 4 11:39:08 2016 13 // Update Count : 9 14 14 // 15 15 … … 37 37 mutateAll( translationUnit, formut ); 38 38 } 39 } // namespace Co ntrolStruct39 } // namespace CodeGen 40 40 41 41 // Local Variables: // -
src/ControlStruct/module.mk
reef8dfb rbdfc032 17 17 SRC_CONTROLSTRUCT = \ 18 18 ControlStruct/ForExprMutator.cc \ 19 ControlStruct/ForExprMutator.h \20 19 ControlStruct/LabelFixer.cc \ 21 ControlStruct/LabelFixer.h \22 20 ControlStruct/LabelGenerator.cc \ 23 ControlStruct/LabelGenerator.h \24 21 ControlStruct/MLEMutator.cc \ 25 ControlStruct/MLEMutator.h \ 26 ControlStruct/Mutate.cc \ 27 ControlStruct/Mutate.h 22 ControlStruct/Mutate.cc 28 23 29 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h24 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc 30 25 SRCDEMANGLE += $(SRC_CONTROLSTRUCT) 31 26 -
src/GenPoly/GenPoly.cc
reef8dfb rbdfc032 46 46 } 47 47 48 bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const ast::TypeSubstitution * env) {49 for (auto ¶m : params) {50 auto paramType = param.strict_as<ast::TypeExpr>();51 if (isPolyType(paramType->type, env)) return true;52 }53 return false;54 }55 56 48 /// Checks a parameter list for polymorphic parameters from tyVars; will substitute according to env if present 57 49 bool hasPolyParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) { … … 64 56 } 65 57 66 bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {67 for (auto ¶m : params) {68 auto paramType = param.strict_as<ast::TypeExpr>();69 if (isPolyType(paramType->type, tyVars, env)) return true;70 }71 return false;72 }73 74 58 /// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present 75 59 bool hasDynParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) { … … 108 92 Type *newType = env->lookup( typeInst->get_name() ); 109 93 if ( newType ) return newType; 110 }111 return type;112 }113 114 const ast::Type * replaceTypeInst(const ast::Type * type, const ast::TypeSubstitution * env) {115 if (!env) return type;116 if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) {117 auto newType = env->lookup(typeInst);118 if (newType) return newType;119 94 } 120 95 return type; … … 136 111 } 137 112 138 const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env) {139 type = replaceTypeInst( type, env );140 141 if ( dynamic_cast< const ast::TypeInstType * >( type ) ) {142 return type;143 } else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {144 return isPolyType( arrayType->base, env );145 } else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {146 if ( hasPolyParams( structType->params, env ) ) return type;147 } else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {148 if ( hasPolyParams( unionType->params, env ) ) return type;149 }150 return 0;151 }152 153 113 Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env ) { 154 114 type = replaceTypeInst( type, env ); … … 166 126 } 167 127 return 0; 168 }169 170 const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {171 type = replaceTypeInst( type, env );172 173 if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) {174 return tyVars.find(typeInst->typeString()) != tyVars.end() ? type : nullptr;175 } else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {176 return isPolyType( arrayType->base, env );177 } else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {178 if ( hasPolyParams( structType->params, env ) ) return type;179 } else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {180 if ( hasPolyParams( unionType->params, env ) ) return type;181 }182 return nullptr;183 128 } 184 129 … … 504 449 } 505 450 506 namespace {507 // temporary hack to avoid re-implementing anything related to TyVarMap508 // does this work? these two structs have identical definitions.509 inline TypeDecl::Data convData(const ast::TypeDecl::Data & data) {510 return *reinterpret_cast<const TypeDecl::Data *>(&data);511 }512 }513 514 451 bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ) { 515 452 // is parameter is not polymorphic, don't need to box … … 522 459 } 523 460 524 bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env) {525 // is parameter is not polymorphic, don't need to box526 if ( ! isPolyType( param, exprTyVars ) ) return false;527 ast::ptr<ast::Type> newType = arg;528 if ( env ) env->apply( newType );529 // if the argument's type is polymorphic, we don't need to box again!530 return ! isPolyType( newType );531 }532 533 461 bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env ) { 534 462 FunctionType * function = getFunctionType( appExpr->function->result ); … … 539 467 } 540 468 541 bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env) {542 const ast::FunctionType * function = getFunctionType(appExpr->func->result);543 assertf( function, "ApplicationExpr has non-function type: %s", toString( appExpr->func->result ).c_str() );544 TyVarMap exprTyVars(TypeDecl::Data{});545 makeTyVarMap(function, exprTyVars);546 return needsBoxing(param, arg, exprTyVars, env);547 548 }549 550 469 void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) { 551 470 tyVarMap.insert( tyVar->name, TypeDecl::Data{ tyVar } ); 552 }553 554 void addToTyVarMap( const ast::TypeInstType * tyVar, TyVarMap & tyVarMap) {555 tyVarMap.insert(tyVar->typeString(), convData(ast::TypeDecl::Data{tyVar->base}));556 471 } 557 472 … … 563 478 if ( PointerType *pointer = dynamic_cast< PointerType* >( type ) ) { 564 479 makeTyVarMap( pointer->get_base(), tyVarMap ); 565 }566 }567 568 void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap) {569 if (auto ptype = dynamic_cast<const ast::FunctionType *>(type)) {570 for (auto & tyVar : ptype->forall) {571 assert (tyVar);572 addToTyVarMap(tyVar, tyVarMap);573 }574 }575 if (auto pointer = dynamic_cast<const ast::PointerType *>(type)) {576 makeTyVarMap(pointer->base, tyVarMap);577 480 } 578 481 } -
src/GenPoly/GenPoly.h
reef8dfb rbdfc032 26 26 27 27 namespace GenPoly { 28 typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap; 28 29 29 typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap;30 30 /// Replaces a TypeInstType by its referrent in the environment, if applicable 31 31 Type* replaceTypeInst( Type* type, const TypeSubstitution* env ); … … 33 33 /// returns polymorphic type if is polymorphic type, NULL otherwise; will look up substitution in env if provided 34 34 Type *isPolyType( Type *type, const TypeSubstitution *env = 0 ); 35 const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env = nullptr);36 35 37 36 /// returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided 38 37 Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env = 0 ); 39 const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env = nullptr);40 38 41 39 /// returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided … … 86 84 /// true if arg requires boxing given exprTyVars 87 85 bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ); 88 bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env);89 86 90 87 /// true if arg requires boxing in the call to appExpr 91 88 bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env ); 92 bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env);93 89 94 90 /// Adds the type variable `tyVar` to `tyVarMap` … … 97 93 /// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `tyVarMap` 98 94 void makeTyVarMap( Type *type, TyVarMap &tyVarMap ); 99 void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap);100 95 101 96 /// Prints type variable map -
src/GenPoly/InstantiateGeneric.cc
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Thu Aug 04 18:33:00 2016 11 // Last Modified By : A ndrew Beach12 // Last Modified On : Wed Jul 16 10:17:00 202013 // Update Count : 211 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Thu Aug 04 18:33:00 2016 13 // Update Count : 1 14 14 // 15 15 #include "InstantiateGeneric.h" … … 172 172 InstantiationMap< AggregateDecl, AggregateDecl > instantiations; 173 173 /// Set of types which are dtype-only generic (and therefore have static layout) 174 std::set<AggregateDecl *> dtypeStatics;174 ScopedSet< AggregateDecl* > dtypeStatics; 175 175 /// Namer for concrete types 176 176 UniqueName typeNamer; … … 297 297 } 298 298 299 template< typename AggrInst >300 static AggrInst * asForward( AggrInst * decl ) {301 if ( !decl->body ) {302 return nullptr;303 }304 decl = decl->clone();305 decl->body = false;306 deleteAll( decl->members );307 decl->members.clear();308 return decl;309 }310 311 299 void GenericInstantiator::stripDtypeParams( AggregateDecl *base, std::list< TypeDecl* >& baseParams, const std::list< TypeExpr* >& typeSubs ) { 312 300 substituteMembers( base->get_members(), baseParams, typeSubs ); … … 385 373 concDecl->set_body( inst->get_baseStruct()->has_body() ); 386 374 substituteMembers( inst->get_baseStruct()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() ); 387 // Forward declare before recursion. (TODO: Only when needed, #199.) 388 insert( inst, typeSubs, concDecl ); 389 if ( StructDecl *forwardDecl = asForward( concDecl ) ) { 390 declsToAddBefore.push_back( forwardDecl ); 391 } 375 insert( inst, typeSubs, concDecl ); // must insert before recursion 392 376 concDecl->acceptMutator( *visitor ); // recursively instantiate members 393 377 declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first … … 439 423 concDecl->set_body( inst->get_baseUnion()->has_body() ); 440 424 substituteMembers( inst->get_baseUnion()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() ); 441 // Forward declare before recursion. (TODO: Only when needed, #199.) 442 insert( inst, typeSubs, concDecl ); 443 if ( UnionDecl *forwardDecl = asForward( concDecl ) ) { 444 declsToAddBefore.push_back( forwardDecl ); 445 } 425 insert( inst, typeSubs, concDecl ); // must insert before recursion 446 426 concDecl->acceptMutator( *visitor ); // recursively instantiate members 447 427 declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first … … 505 485 void GenericInstantiator::beginScope() { 506 486 instantiations.beginScope(); 487 dtypeStatics.beginScope(); 507 488 } 508 489 509 490 void GenericInstantiator::endScope() { 510 491 instantiations.endScope(); 492 dtypeStatics.endScope(); 511 493 } 512 494 -
src/GenPoly/Specialize.cc
reef8dfb rbdfc032 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 : Thr Jul 2 17:42:00 202013 // Update Count : 3 311 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Dec 13 23:40:49 2019 13 // Update Count : 32 14 14 // 15 15 … … 42 42 43 43 namespace GenPoly { 44 struct Specialize final : public WithConstTypeSubstitution, 45 public WithDeclsToAdd, public WithVisitorRef<Specialize> { 44 struct Specialize final : public WithConstTypeSubstitution, public WithStmtsToAdd, public WithVisitorRef<Specialize> { 46 45 Expression * postmutate( ApplicationExpr *applicationExpr ); 47 46 Expression * postmutate( CastExpr *castExpr ); … … 218 217 thunkFunc->get_attributes().push_back( new Attribute( "unused" ) ); 219 218 220 // Thunks at the global level must be static to avoid collisions between files.221 // (Conversly thunks inside a function must be unique and not static.)222 thunkFunc->storageClasses.is_static = !isInFunction();223 224 219 // thread thunk parameters into call to actual function, naming thunk parameters as we go 225 220 UniqueName paramNamer( paramPrefix ); … … 253 248 } // if 254 249 255 // Handle any specializations that may still be present. 256 { 257 std::string oldParamPrefix = paramPrefix; 258 paramPrefix += "p"; 259 std::list< Declaration * > oldDecls; 260 oldDecls.splice( oldDecls.end(), declsToAddBefore ); 261 262 appExpr->acceptMutator( *visitor ); 263 // Write recursive specializations into the thunk body. 264 for ( Declaration * decl : declsToAddBefore ) { 265 thunkFunc->statements->kids.push_back( new DeclStmt( decl ) ); 266 } 267 268 declsToAddBefore = std::move( oldDecls ); 269 paramPrefix = oldParamPrefix; 270 } 250 // handle any specializations that may still be present 251 std::string oldParamPrefix = paramPrefix; 252 paramPrefix += "p"; 253 // save stmtsToAddBefore in oldStmts 254 std::list< Statement* > oldStmts; 255 oldStmts.splice( oldStmts.end(), stmtsToAddBefore ); 256 appExpr->acceptMutator( *visitor ); 257 paramPrefix = oldParamPrefix; 258 // write any statements added for recursive specializations into the thunk body 259 thunkFunc->statements->kids.splice( thunkFunc->statements->kids.end(), stmtsToAddBefore ); 260 // restore oldStmts into stmtsToAddBefore 261 stmtsToAddBefore.splice( stmtsToAddBefore.end(), oldStmts ); 271 262 272 263 // add return (or valueless expression) to the thunk … … 279 270 thunkFunc->statements->kids.push_back( appStmt ); 280 271 281 // Add the thunk definition (converted to DeclStmt if appproprate).282 declsToAddBefore.push_back( thunkFunc);272 // add thunk definition to queue of statements to add 273 stmtsToAddBefore.push_back( new DeclStmt( thunkFunc ) ); 283 274 // return address of thunk function as replacement expression 284 275 return new AddressExpr( new VariableExpr( thunkFunc ) ); -
src/GenPoly/module.mk
reef8dfb rbdfc032 16 16 17 17 SRC += GenPoly/Box.cc \ 18 GenPoly/Box.h \ 19 GenPoly/ErasableScopedMap.h \ 18 GenPoly/GenPoly.cc \ 19 GenPoly/ScrubTyVars.cc \ 20 GenPoly/Lvalue.cc \ 21 GenPoly/Specialize.cc \ 20 22 GenPoly/FindFunction.cc \ 21 GenPoly/FindFunction.h \ 22 GenPoly/GenPoly.cc \ 23 GenPoly/GenPoly.h \ 24 GenPoly/InstantiateGeneric.cc \ 25 GenPoly/InstantiateGeneric.h \ 26 GenPoly/Lvalue.cc \ 27 GenPoly/Lvalue.h \ 28 GenPoly/ScopedSet.h \ 29 GenPoly/ScrubTyVars.cc \ 30 GenPoly/ScrubTyVars.h \ 31 GenPoly/Specialize.cc \ 32 GenPoly/Specialize.h 23 GenPoly/InstantiateGeneric.cc 33 24 34 SRCDEMANGLE += GenPoly/GenPoly.cc GenPoly/ GenPoly.h GenPoly/Lvalue.cc GenPoly/Lvalue.h25 SRCDEMANGLE += GenPoly/GenPoly.cc GenPoly/Lvalue.cc 35 26 -
src/InitTweak/FixGlobalInit.cc
reef8dfb rbdfc032 34 34 #include "SynTree/Visitor.h" // for acceptAll, Visitor 35 35 36 #include "AST/Expr.hpp"37 #include "AST/Node.hpp"38 #include "AST/Pass.hpp"39 40 36 namespace InitTweak { 41 37 class GlobalFixer : public WithShortCircuiting { … … 54 50 FunctionDecl * initFunction; 55 51 FunctionDecl * destroyFunction; 56 };57 58 class GlobalFixer_new : public ast::WithShortCircuiting {59 public:60 void previsit (const ast::ObjectDecl *);61 void previsit (const ast::FunctionDecl *) { visit_children = false; }62 void previsit (const ast::StructDecl *) { visit_children = false; }63 void previsit (const ast::UnionDecl *) { visit_children = false; }64 void previsit (const ast::EnumDecl *) { visit_children = false; }65 void previsit (const ast::TraitDecl *) { visit_children = false; }66 void previsit (const ast::TypeDecl *) { visit_children = false; }67 68 std::list< ast::ptr<ast::Stmt> > initStmts;69 std::list< ast::ptr<ast::Stmt> > destroyStmts;70 52 }; 71 53 … … 109 91 } 110 92 111 void fixGlobalInit(ast::TranslationUnit & translationUnit, bool inLibrary) {112 ast::Pass<GlobalFixer_new> fixer;113 accept_all(translationUnit, fixer);114 115 if ( !fixer.core.initStmts.empty() ) {116 std::vector<ast::ptr<ast::Expr>> ctorParams;117 if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int({}, 200));118 auto initFunction = new ast::FunctionDecl({}, "__global_init__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.initStmts)),119 ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("constructor", std::move(ctorParams))});120 121 translationUnit.decls.emplace_back( initFunction );122 } // if123 124 if ( !fixer.core.destroyStmts.empty() ) {125 std::vector<ast::ptr<ast::Expr>> dtorParams;126 if (inLibrary) dtorParams.emplace_back(ast::ConstantExpr::from_int({}, 200));127 auto destroyFunction = new ast::FunctionDecl({}, "__global_destroy__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.destroyStmts)),128 ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("destructor", std::move(dtorParams))});129 130 translationUnit.decls.emplace_back(destroyFunction);131 } // if132 }133 134 93 void GlobalFixer::previsit( ObjectDecl *objDecl ) { 135 94 std::list< Statement * > & initStatements = initFunction->get_statements()->get_kids(); … … 153 112 } // if 154 113 if ( Statement * ctor = ctorInit->ctor ) { 155 addDataSectonAttribute( objDecl );156 114 initStatements.push_back( ctor ); 157 115 objDecl->init = nullptr; … … 165 123 } // if 166 124 delete ctorInit; 167 } // if168 }169 170 void GlobalFixer_new::previsit(const ast::ObjectDecl * objDecl) {171 auto mutDecl = mutate(objDecl);172 assertf(mutDecl == objDecl, "Global object decl must be unique");173 if ( auto ctorInit = objDecl->init.as<ast::ConstructorInit>() ) {174 // a decision should have been made by the resolver, so ctor and init are not both non-NULL175 assert( ! ctorInit->ctor || ! ctorInit->init );176 177 const ast::Stmt * dtor = ctorInit->dtor;178 if ( dtor && ! isIntrinsicSingleArgCallStmt( dtor ) ) {179 // don't need to call intrinsic dtor, because it does nothing, but180 // non-intrinsic dtors must be called181 destroyStmts.push_front( dtor );182 // ctorInit->dtor = nullptr;183 } // if184 if ( const ast::Stmt * ctor = ctorInit->ctor ) {185 addDataSectionAttribute(mutDecl);186 initStmts.push_back( ctor );187 mutDecl->init = nullptr;188 // ctorInit->ctor = nullptr;189 } else if ( const ast::Init * init = ctorInit->init ) {190 mutDecl->init = init;191 // ctorInit->init = nullptr;192 } else {193 // no constructor and no initializer, which is okay194 mutDecl->init = nullptr;195 } // if196 // delete ctorInit;197 125 } // if 198 126 } -
src/InitTweak/FixGlobalInit.h
reef8dfb rbdfc032 19 19 #include <string> // for string 20 20 21 #include <AST/Fwd.hpp>22 23 24 21 class Declaration; 25 22 … … 29 26 /// function is for library code. 30 27 void fixGlobalInit( std::list< Declaration * > & translationUnit, bool inLibrary ); 31 void fixGlobalInit( ast::TranslationUnit & translationUnit, bool inLibrary );32 28 } // namespace 33 29 -
src/InitTweak/FixInit.cc
reef8dfb rbdfc032 10 10 // Created On : Wed Jan 13 16:29:30 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 04:17:07 202013 // Update Count : 8212 // Last Modified On : Fri Dec 13 23:41:27 2019 13 // Update Count : 77 14 14 // 15 15 #include "FixInit.h" … … 219 219 }; 220 220 221 struct SplitExpressions : public WithShortCircuiting, /*public WithTypeSubstitution, */public WithStmtsToAdd {221 struct SplitExpressions : public WithShortCircuiting, public WithTypeSubstitution, public WithStmtsToAdd { 222 222 /// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places. 223 223 static void split( std::list< Declaration * > &translationUnit ); … … 745 745 } 746 746 747 // to prevent warnings ( '_unq0'may be used uninitialized in this function),747 // to prevent warnings (ā_unq0ā may be used uninitialized in this function), 748 748 // insert an appropriate zero initializer for UniqueExpr temporaries. 749 749 Initializer * makeInit( Type * t ) { … … 802 802 if ( Statement * ctor = ctorInit->get_ctor() ) { 803 803 if ( objDecl->get_storageClasses().is_static ) { 804 805 // The ojbect needs to go in the data section, regardless of dtor complexity below.806 // The attribute works, and is meant to apply, both for leaving the static local alone,807 // and for hoisting it out as a static global.808 addDataSectonAttribute( objDecl );809 810 804 // originally wanted to take advantage of gcc nested functions, but 811 805 // we get memory errors with this approach. To remedy this, the static -
src/InitTweak/FixInit.h
reef8dfb rbdfc032 10 10 // Created On : Wed Jan 13 16:29:30 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : S un Feb 16 07:54:50 202013 // Update Count : 812 // Last Modified On : Sat Jul 22 09:31:06 2017 13 // Update Count : 6 14 14 // 15 15 … … 20 20 21 21 class Declaration; 22 namespace ast {23 struct TranslationUnit;24 }25 22 26 23 namespace InitTweak { 27 /// replace constructor initializers with expression statements and unwrap basic C-style initializers 24 /// replace constructor initializers with expression statements 25 /// and unwrap basic C-style initializers 28 26 void fix( std::list< Declaration * > & translationUnit, bool inLibrary ); 29 30 void fix( ast::TranslationUnit & translationUnit, bool inLibrary);31 27 } // namespace 32 28 -
src/InitTweak/GenInit.cc
reef8dfb rbdfc032 26 26 #include "AST/Node.hpp" 27 27 #include "AST/Stmt.hpp" 28 #include "CompilationState.h"29 28 #include "CodeGen/OperatorTable.h" 30 29 #include "Common/PassVisitor.h" // for PassVisitor, WithGuards, WithShort... … … 122 121 }; 123 122 124 struct HoistArrayDimension_NoResolve final : public WithDeclsToAdd, public WithShortCircuiting, public WithGuards {125 /// hoist dimension from array types in object declaration so that it uses a single126 /// const variable of type size_t, so that side effecting array dimensions are only127 /// computed once.128 static void hoistArrayDimension( std::list< Declaration * > & translationUnit );129 130 void premutate( ObjectDecl * objectDecl );131 DeclarationWithType * postmutate( ObjectDecl * objectDecl );132 void premutate( FunctionDecl *functionDecl );133 // should not traverse into any of these declarations to find objects134 // that need to be constructed or destructed135 void premutate( AggregateDecl * ) { visit_children = false; }136 void premutate( NamedTypeDecl * ) { visit_children = false; }137 void premutate( FunctionType * ) { visit_children = false; }138 139 void hoist( Type * type );140 141 Type::StorageClasses storageClasses;142 bool inFunction = false;143 };144 145 123 void genInit( std::list< Declaration * > & translationUnit ) { 146 if (!useNewAST) {147 HoistArrayDimension::hoistArrayDimension( translationUnit );148 }149 else {150 HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit );151 }152 124 fixReturnStatements( translationUnit ); 153 154 if (!useNewAST) { 155 CtorDtor::generateCtorDtor( translationUnit ); 156 } 125 HoistArrayDimension::hoistArrayDimension( translationUnit ); 126 CtorDtor::generateCtorDtor( translationUnit ); 157 127 } 158 128 … … 226 196 arrayType->isVarLen = ! isConstExpr( arrayType->dimension ); 227 197 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects. 228 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve229 // still try to detect constant expressions230 198 if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return; 231 199 … … 242 210 243 211 void HoistArrayDimension::premutate( FunctionDecl * ) { 244 GuardValue( inFunction );245 inFunction = true;246 }247 248 // precompute array dimension expression, because constructor generation may duplicate it,249 // which would be incorrect if it is a side-effecting computation.250 void HoistArrayDimension_NoResolve::hoistArrayDimension( std::list< Declaration * > & translationUnit ) {251 PassVisitor<HoistArrayDimension_NoResolve> hoister;252 mutateAll( translationUnit, hoister );253 }254 255 void HoistArrayDimension_NoResolve::premutate( ObjectDecl * objectDecl ) {256 GuardValue( storageClasses );257 storageClasses = objectDecl->get_storageClasses();258 }259 260 DeclarationWithType * HoistArrayDimension_NoResolve::postmutate( ObjectDecl * objectDecl ) {261 hoist( objectDecl->get_type() );262 return objectDecl;263 }264 265 void HoistArrayDimension_NoResolve::hoist( Type * type ) {266 // if in function, generate const size_t var267 static UniqueName dimensionName( "_array_dim" );268 269 // C doesn't allow variable sized arrays at global scope or for static variables, so don't hoist dimension.270 if ( ! inFunction ) return;271 if ( storageClasses.is_static ) return;272 273 if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) {274 if ( ! arrayType->get_dimension() ) return; // xxx - recursive call to hoist?275 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.276 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve277 // still try to detect constant expressions278 if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return;279 280 ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) );281 arrayDimension->get_type()->set_const( true );282 283 arrayType->set_dimension( new VariableExpr( arrayDimension ) );284 declsToAddBefore.push_back( arrayDimension );285 286 hoist( arrayType->get_base() );287 return;288 }289 }290 291 void HoistArrayDimension_NoResolve::premutate( FunctionDecl * ) {292 212 GuardValue( inFunction ); 293 213 inFunction = true; … … 325 245 } 326 246 327 // why is this not just on FunctionDecl?328 247 void ManagedTypes::handleDWT( DeclarationWithType * dwt ) { 329 248 // if this function is a user-defined constructor or destructor, mark down the type as "managed" … … 356 275 void ManagedTypes::endScope() { managedTypes.endScope(); } 357 276 358 bool ManagedTypes_new::isManaged( const ast::Type * type ) const {359 // references are never constructed360 if ( dynamic_cast< const ast::ReferenceType * >( type ) ) return false;361 if ( auto tupleType = dynamic_cast< const ast::TupleType * > ( type ) ) {362 // tuple is also managed if any of its components are managed363 for (auto & component : tupleType->types) {364 if (isManaged(component)) return true;365 }366 }367 // need to clear and reset qualifiers when determining if a type is managed368 // ValueGuard< Type::Qualifiers > qualifiers( type->get_qualifiers() );369 auto tmp = shallowCopy(type);370 tmp->qualifiers = {};371 // delete tmp at return372 ast::ptr<ast::Type> guard = tmp;373 // a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable)374 return managedTypes.find( Mangle::mangle( tmp, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ) != managedTypes.end() || GenPoly::isPolyType( tmp );375 }376 377 bool ManagedTypes_new::isManaged( const ast::ObjectDecl * objDecl ) const {378 const ast::Type * type = objDecl->type;379 while ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {380 // must always construct VLAs with an initializer, since this is an error in C381 if ( at->isVarLen && objDecl->init ) return true;382 type = at->base;383 }384 return isManaged( type );385 }386 387 void ManagedTypes_new::handleDWT( const ast::DeclWithType * dwt ) {388 // if this function is a user-defined constructor or destructor, mark down the type as "managed"389 if ( ! dwt->linkage.is_overrideable && CodeGen::isCtorDtor( dwt->name ) ) {390 auto & params = GenPoly::getFunctionType( dwt->get_type())->params;391 assert( ! params.empty() );392 // Type * type = InitTweak::getPointerBase( params.front() );393 // assert( type );394 managedTypes.insert( Mangle::mangle( params.front(), {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );395 }396 }397 398 void ManagedTypes_new::handleStruct( const ast::StructDecl * aggregateDecl ) {399 // don't construct members, but need to take note if there is a managed member,400 // because that means that this type is also managed401 for ( auto & member : aggregateDecl->members ) {402 if ( auto field = member.as<ast::ObjectDecl>() ) {403 if ( isManaged( field ) ) {404 // generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that405 // polymorphic constructors make generic types managed types406 ast::StructInstType inst( aggregateDecl );407 managedTypes.insert( Mangle::mangle( &inst, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );408 break;409 }410 }411 }412 }413 414 void ManagedTypes_new::beginScope() { managedTypes.beginScope(); }415 void ManagedTypes_new::endScope() { managedTypes.endScope(); }416 417 277 ImplicitCtorDtorStmt * genCtorDtor( const std::string & fname, ObjectDecl * objDecl, Expression * arg ) { 418 278 // call into genImplicitCall from Autogen.h to generate calls to ctor/dtor … … 423 283 assert( stmts.size() <= 1 ); 424 284 return stmts.size() == 1 ? strict_dynamic_cast< ImplicitCtorDtorStmt * >( stmts.front() ) : nullptr; 425 426 }427 428 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg) {429 assertf(objDecl, "genCtorDtor passed null objDecl");430 InitExpander_new srcParam(arg);431 return SymTab::genImplicitCall(srcParam, new ast::VariableExpr(loc, objDecl), loc, fname, objDecl);432 285 } 433 286 … … 510 363 // constructable object 511 364 InitExpander_new srcParam{ objDecl->init }, nullParam{ (const ast::Init *)nullptr }; 512 ast::ptr< ast::Expr > dstParam = new ast::VariableExpr(loc, objDecl);513 365 514 366 ast::ptr< ast::Stmt > ctor = SymTab::genImplicitCall( 515 srcParam, dstParam, loc, "?{}", objDecl );367 srcParam, new ast::VariableExpr{ loc, objDecl }, loc, "?{}", objDecl ); 516 368 ast::ptr< ast::Stmt > dtor = SymTab::genImplicitCall( 517 nullParam, dstParam, loc, "^?{}", objDecl,369 nullParam, new ast::VariableExpr{ loc, objDecl }, loc, "^?{}", objDecl, 518 370 SymTab::LoopBackward ); 519 371 -
src/InitTweak/GenInit.h
reef8dfb rbdfc032 33 33 /// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument 34 34 ImplicitCtorDtorStmt * genCtorDtor( const std::string & fname, ObjectDecl * objDecl, Expression * arg = nullptr ); 35 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr);36 35 37 36 /// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer … … 52 51 GenPoly::ScopedSet< std::string > managedTypes; 53 52 }; 54 55 class ManagedTypes_new {56 public:57 bool isManaged( const ast::ObjectDecl * objDecl ) const ; // determine if object is managed58 bool isManaged( const ast::Type * type ) const; // determine if type is managed59 60 void handleDWT( const ast::DeclWithType * dwt ); // add type to managed if ctor/dtor61 void handleStruct( const ast::StructDecl * aggregateDecl ); // add type to managed if child is managed62 63 void beginScope();64 void endScope();65 private:66 GenPoly::ScopedSet< std::string > managedTypes;67 };68 53 } // namespace 69 54 -
src/InitTweak/InitTweak.cc
reef8dfb rbdfc032 87 87 }; 88 88 89 struct HasDesignations_new : public ast::WithShortCircuiting {90 bool result = false;91 92 void previsit( const ast::Node * ) {93 // short circuit if we already know there are designations94 if ( result ) visit_children = false;95 }96 97 void previsit( const ast::Designation * des ) {98 // short circuit if we already know there are designations99 if ( result ) visit_children = false;100 else if ( ! des->designators.empty() ) {101 result = true;102 visit_children = false;103 }104 }105 };106 107 struct InitDepthChecker_new : public ast::WithGuards {108 bool result = true;109 const ast::Type * type;110 int curDepth = 0, maxDepth = 0;111 InitDepthChecker_new( const ast::Type * type ) : type( type ) {112 const ast::Type * t = type;113 while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) {114 maxDepth++;115 t = at->base;116 }117 maxDepth++;118 }119 void previsit( ListInit * ) {120 curDepth++;121 GuardAction( [this]() { curDepth--; } );122 if ( curDepth > maxDepth ) result = false;123 }124 };125 126 89 struct InitFlattener_old : public WithShortCircuiting { 127 90 void previsit( SingleInit * singleInit ) { … … 161 124 } 162 125 163 bool isDesignated( const ast::Init * init ) {164 ast::Pass<HasDesignations_new> finder;165 maybe_accept( init, finder );166 return finder.core.result;167 }168 169 bool checkInitDepth( const ast::ObjectDecl * objDecl ) {170 ast::Pass<InitDepthChecker_new> checker( objDecl->type );171 maybe_accept( objDecl->init.get(), checker );172 return checker.core.result;173 }174 175 126 std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) { 176 127 ast::Pass< InitFlattener_new > flattener; 177 128 maybe_accept( init, flattener ); 178 return std::move( flattener. core.argList );129 return std::move( flattener.pass.argList ); 179 130 } 180 131 … … 407 358 if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) { 408 359 for ( const ast::Init * init : *listInit ) { 409 buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );360 buildCallExpr( callExpr, index, dimension, init, out ); 410 361 } 411 362 } else { 412 buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );363 buildCallExpr( callExpr, index, dimension, init, out ); 413 364 } 414 365 } else { … … 547 498 } 548 499 549 const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {550 assertf( func, "getParamThis: nullptr ftype" );551 auto & params = func->params;552 assertf( ! params.empty(), "getParamThis: ftype with 0 parameters: %s", toString( func ).c_str());553 return params.front().strict_as<ast::ObjectDecl>();554 }555 556 500 bool tryConstruct( DeclarationWithType * dwt ) { 557 501 ObjectDecl * objDecl = dynamic_cast< ObjectDecl * >( dwt ); … … 567 511 } 568 512 569 bool tryConstruct( const ast::DeclWithType * dwt ) {570 auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );571 if ( ! objDecl ) return false;572 return (objDecl->init == nullptr ||573 ( objDecl->init != nullptr && objDecl->init->maybeConstructed ))574 && ! objDecl->storage.is_extern575 && isConstructable( objDecl->type );576 }577 578 bool isConstructable( const ast::Type * type ) {579 return ! dynamic_cast< const ast::VarArgsType * >( type ) && ! dynamic_cast< const ast::ReferenceType * >( type )580 && ! dynamic_cast< const ast::FunctionType * >( type ) && ! Tuples::isTtype( type );581 }582 583 513 struct CallFinder_old { 584 514 CallFinder_old( const std::list< std::string > & names ) : names( names ) {} … … 606 536 607 537 struct CallFinder_new final { 608 std::vector< const ast::Expr *> matches;538 std::vector< ast::ptr< ast::Expr > > matches; 609 539 const std::vector< std::string > names; 610 540 … … 628 558 } 629 559 630 std::vector< const ast::Expr *> collectCtorDtorCalls( const ast::Stmt * stmt ) {560 std::vector< ast::ptr< ast::Expr > > collectCtorDtorCalls( const ast::Stmt * stmt ) { 631 561 ast::Pass< CallFinder_new > finder{ std::vector< std::string >{ "?{}", "^?{}" } }; 632 562 maybe_accept( stmt, finder ); 633 return std::move( finder. core.matches );563 return std::move( finder.pass.matches ); 634 564 } 635 565 … … 766 696 template <typename Predicate> 767 697 bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) { 768 std::vector< const ast::Expr *> callExprs = collectCtorDtorCalls( stmt );698 std::vector< ast::ptr< ast::Expr > > callExprs = collectCtorDtorCalls( stmt ); 769 699 return std::all_of( callExprs.begin(), callExprs.end(), pred ); 770 700 } … … 1009 939 } 1010 940 1011 // looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards1012 // following passes may accidentally resolve this expression if returned as untyped...1013 ast::Expr * createBitwiseAssignment (const ast::Expr * dst, const ast::Expr * src) {1014 static ast::ptr<ast::FunctionDecl> assign = nullptr;1015 if (!assign) {1016 auto td = new ast::TypeDecl({}, "T", {}, nullptr, ast::TypeDecl::Dtype, true);1017 assign = new ast::FunctionDecl({}, "?=?", {},1018 { new ast::ObjectDecl({}, "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),1019 new ast::ObjectDecl({}, "_src", new ast::TypeInstType("T", td))},1020 { new ast::ObjectDecl({}, "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);1021 }1022 if (dst->result.as<ast::ReferenceType>()) {1023 for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {1024 dst = new ast::AddressExpr(dst);1025 }1026 }1027 else {1028 dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));1029 }1030 if (src->result.as<ast::ReferenceType>()) {1031 for (int depth = src->result->referenceDepth(); depth > 0; depth--) {1032 src = new ast::AddressExpr(src);1033 }1034 }1035 return new ast::ApplicationExpr(dst->location, ast::VariableExpr::functionPointer(dst->location, assign), {dst, src});1036 }1037 1038 941 struct ConstExprChecker : public WithShortCircuiting { 1039 942 // most expressions are not const expr … … 1076 979 }; 1077 980 1078 struct ConstExprChecker_new : public ast::WithShortCircuiting {1079 // most expressions are not const expr1080 void previsit( const ast::Expr * ) { result = false; visit_children = false; }1081 1082 void previsit( const ast::AddressExpr *addressExpr ) {1083 visit_children = false;1084 const ast::Expr * arg = addressExpr->arg;1085 1086 // address of a variable or member expression is constexpr1087 if ( ! dynamic_cast< const ast::NameExpr * >( arg )1088 && ! dynamic_cast< const ast::VariableExpr * >( arg )1089 && ! dynamic_cast< const ast::MemberExpr * >( arg )1090 && ! dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;1091 }1092 1093 // these expressions may be const expr, depending on their children1094 void previsit( const ast::SizeofExpr * ) {}1095 void previsit( const ast::AlignofExpr * ) {}1096 void previsit( const ast::UntypedOffsetofExpr * ) {}1097 void previsit( const ast::OffsetofExpr * ) {}1098 void previsit( const ast::OffsetPackExpr * ) {}1099 void previsit( const ast::CommaExpr * ) {}1100 void previsit( const ast::LogicalExpr * ) {}1101 void previsit( const ast::ConditionalExpr * ) {}1102 void previsit( const ast::CastExpr * ) {}1103 void previsit( const ast::ConstantExpr * ) {}1104 1105 void previsit( const ast::VariableExpr * varExpr ) {1106 visit_children = false;1107 1108 if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {1109 long long int value;1110 if ( inst->base->valueOf( varExpr->var, value ) ) {1111 // enumerators are const expr1112 return;1113 }1114 }1115 result = false;1116 }1117 1118 bool result = true;1119 };1120 1121 981 bool isConstExpr( Expression * expr ) { 1122 982 if ( expr ) { … … 1138 998 } 1139 999 1140 bool isConstExpr( const ast::Expr * expr ) {1141 if ( expr ) {1142 ast::Pass<ConstExprChecker_new> checker;1143 expr->accept( checker );1144 return checker.core.result;1145 }1146 return true;1147 }1148 1149 bool isConstExpr( const ast::Init * init ) {1150 if ( init ) {1151 ast::Pass<ConstExprChecker_new> checker;1152 init->accept( checker );1153 return checker.core.result;1154 } // if1155 // for all intents and purposes, no initializer means const expr1156 return true;1157 }1158 1159 1000 bool isConstructor( const std::string & str ) { return str == "?{}"; } 1160 1001 bool isDestructor( const std::string & str ) { return str == "^?{}"; } … … 1185 1026 if ( ftype->params.size() != 2 ) return false; 1186 1027 1187 const ast::Type * t1 = getPointerBase( ftype->params.front() );1028 const ast::Type * t1 = getPointerBase( ftype->params.front()->get_type() ); 1188 1029 if ( ! t1 ) return false; 1189 const ast::Type * t2 = ftype->params.back() ;1030 const ast::Type * t2 = ftype->params.back()->get_type(); 1190 1031 1191 1032 return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} ); … … 1214 1055 return isCopyFunction( decl, "?{}" ); 1215 1056 } 1216 1217 void addDataSectonAttribute( ObjectDecl * objDecl ) {1218 Type *strLitT = new PointerType( Type::Qualifiers( ),1219 new BasicType( Type::Qualifiers( ), BasicType::Char ) );1220 std::list< Expression * > attr_params;1221 attr_params.push_back(1222 new ConstantExpr( Constant( strLitT, "\".data#\"", std::nullopt ) ) );1223 objDecl->attributes.push_back(new Attribute("section", attr_params));1224 }1225 1226 void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {1227 auto strLitT = new ast::PointerType(new ast::BasicType(ast::BasicType::Char));1228 objDecl->attributes.push_back(new ast::Attribute("section", {new ast::ConstantExpr(objDecl->location, strLitT, "\".data#\"", std::nullopt)}));1229 }1230 1231 1057 } -
src/InitTweak/InitTweak.h
reef8dfb rbdfc032 38 38 /// returns the first parameter of a constructor/destructor/assignment function 39 39 ObjectDecl * getParamThis( FunctionType * ftype ); 40 const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func);41 40 42 41 /// generate a bitwise assignment operation. 43 42 ApplicationExpr * createBitwiseAssignment( Expression * dst, Expression * src ); 44 45 ast::Expr * createBitwiseAssignment( const ast::Expr * dst, const ast::Expr * src);46 43 47 44 /// transform Initializer into an argument list that can be passed to a call expression … … 51 48 /// True if the resolver should try to construct dwt 52 49 bool tryConstruct( DeclarationWithType * dwt ); 53 bool tryConstruct( const ast::DeclWithType * dwt );54 50 55 51 /// True if the type can have a user-defined constructor 56 52 bool isConstructable( Type * t ); 57 bool isConstructable( const ast::Type * t );58 53 59 54 /// True if the Initializer contains designations 60 55 bool isDesignated( Initializer * init ); 61 bool isDesignated( const ast::Init * init );62 56 63 57 /// True if the ObjectDecl's Initializer nesting level is not deeper than the depth of its 64 58 /// type, where the depth of its type is the number of nested ArrayTypes + 1 65 59 bool checkInitDepth( ObjectDecl * objDecl ); 66 bool checkInitDepth( const ast::ObjectDecl * objDecl );67 60 68 61 /// returns the declaration of the function called by the expr (must be ApplicationExpr or UntypedExpr) … … 86 79 /// get all Ctor/Dtor call expressions from a Statement 87 80 void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches ); 88 std::vector< const ast::Expr *> collectCtorDtorCalls( const ast::Stmt * stmt );81 std::vector< ast::ptr< ast::Expr > > collectCtorDtorCalls( const ast::Stmt * stmt ); 89 82 90 83 /// get the Ctor/Dtor call expression from a Statement that looks like a generated ctor/dtor call … … 109 102 bool isConstExpr( Expression * expr ); 110 103 bool isConstExpr( Initializer * init ); 111 112 bool isConstExpr( const ast::Expr * expr );113 bool isConstExpr( const ast::Init * init );114 115 /// Modifies objDecl to have:116 /// __attribute__((section (".data#")))117 /// which makes gcc put the declared variable in the data section,118 /// which is helpful for global constants on newer gcc versions,119 /// so that CFA's generated initialization won't segfault when writing it via a const cast.120 /// The trailing # is an injected assembly comment, to suppress the "a" in121 /// .section .data,"a"122 /// .section .data#,"a"123 /// to avoid assembler warning "ignoring changed section attributes for .data"124 void addDataSectonAttribute( ObjectDecl * objDecl );125 126 void addDataSectionAttribute( ast::ObjectDecl * objDecl );127 104 128 105 class InitExpander_old { -
src/InitTweak/module.mk
reef8dfb rbdfc032 15 15 ############################################################################### 16 16 17 SRC += \ 17 SRC += InitTweak/GenInit.cc \ 18 InitTweak/FixInit.cc \ 18 19 InitTweak/FixGlobalInit.cc \ 19 InitTweak/FixGlobalInit.h \ 20 InitTweak/FixInit.cc \ 21 InitTweak/FixInit.h \ 22 InitTweak/GenInit.cc \ 23 InitTweak/GenInit.h \ 24 InitTweak/InitTweak.cc \ 25 InitTweak/InitTweak.h \ 26 InitTweak/FixInitNew.cpp 20 InitTweak/InitTweak.cc 27 21 28 SRCDEMANGLE += \ 29 InitTweak/GenInit.cc \ 30 InitTweak/GenInit.h \ 31 InitTweak/InitTweak.cc \ 32 InitTweak/InitTweak.h 22 SRCDEMANGLE += InitTweak/GenInit.cc \ 23 InitTweak/InitTweak.cc 33 24 -
src/MakeLibCfa.cc
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 10:33:33 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 03:49:49 202013 // Update Count : 4 512 // Last Modified On : Fri Dec 13 23:41:40 2019 13 // Update Count : 42 14 14 // 15 15 … … 96 96 97 97 FunctionDecl *funcDecl = origFuncDecl->clone(); 98 const CodeGen::OperatorInfo *opInfo;99 opInfo = CodeGen::operatorLookup( funcDecl->get_name());100 assert( opInfo);98 CodeGen::OperatorInfo opInfo; 99 bool lookResult = CodeGen::operatorLookup( funcDecl->get_name(), opInfo ); 100 assert( lookResult ); 101 101 assert( ! funcDecl->get_statements() ); 102 102 // build a recursive call - this is okay, as the call will actually be codegen'd using operator syntax … … 120 120 121 121 Statement * stmt = nullptr; 122 switch ( opInfo ->type ) {122 switch ( opInfo.type ) { 123 123 case CodeGen::OT_INDEX: 124 124 case CodeGen::OT_CALL: -
src/Makefile.am
reef8dfb rbdfc032 20 20 21 21 SRC = main.cc \ 22 CompilationState.cc \23 CompilationState.h \24 22 MakeLibCfa.cc \ 25 MakeLibCfa.h 23 CompilationState.cc 26 24 27 25 SRCDEMANGLE = CompilationState.cc … … 68 66 ___driver_cfa_cpp_SOURCES = $(SRC) 69 67 ___driver_cfa_cpp_LDADD = -ldl $(LIBPROFILER) $(LIBTCMALLOC) 70 EXTRA_DIST = include/cassert include/optional BasicTypes-gen.cc71 68 72 69 AM_CXXFLAGS = @HOST_FLAGS@ -Wno-deprecated -Wall -Wextra -DDEBUG_ALL -I./Parser -I$(srcdir)/Parser -I$(srcdir)/include -DYY_NO_INPUT -O3 -g -std=c++14 $(TCMALLOCFLAG) -
src/Parser/DeclarationNode.cc
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 12:34:05 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Oct 8 08:03:38 202013 // Update Count : 113 512 // Last Modified On : Mon Dec 16 15:32:22 2019 13 // Update Count : 1133 14 14 // 15 15 … … 1016 1016 if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) { 1017 1017 dwt->location = cur->location; 1018 * out++ = dwt;1018 * out++ = dwt; 1019 1019 } else if ( StructDecl * agg = dynamic_cast< StructDecl * >( decl ) ) { 1020 1020 // e.g., int foo(struct S) {} … … 1022 1022 auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr ); 1023 1023 obj->location = cur->location; 1024 * out++ = obj;1024 * out++ = obj; 1025 1025 delete agg; 1026 1026 } else if ( UnionDecl * agg = dynamic_cast< UnionDecl * >( decl ) ) { … … 1029 1029 auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr ); 1030 1030 obj->location = cur->location; 1031 * out++ = obj;1031 * out++ = obj; 1032 1032 } else if ( EnumDecl * agg = dynamic_cast< EnumDecl * >( decl ) ) { 1033 1033 // e.g., int foo(enum E) {} … … 1035 1035 auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr ); 1036 1036 obj->location = cur->location; 1037 * out++ = obj;1037 * out++ = obj; 1038 1038 } // if 1039 1039 } catch( SemanticErrorException & e ) { … … 1115 1115 // SUE's cannot have function specifiers, either 1116 1116 // 1117 // inl ine _Noreturn struct S { ... }; // disallowed1118 // inl ine _Noreturn enum E { ... }; // disallowed1117 // inlne _Noreturn struct S { ... }; // disallowed 1118 // inlne _Noreturn enum E { ... }; // disallowed 1119 1119 if ( funcSpecs.any() ) { 1120 1120 SemanticError( this, "invalid function specifier for " ); -
src/Parser/ExpressionNode.cc
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 13:17:07 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Aug 20 14:01:46 202013 // Update Count : 107612 // Last Modified On : Wed Dec 18 21:14:58 2019 13 // Update Count : 981 14 14 // 15 15 … … 65 65 66 66 void lnthSuffix( string & str, int & type, int & ltype ) { 67 // 'u' can appear before or after length suffix68 67 string::size_type posn = str.find_last_of( "lL" ); 69 68 70 69 if ( posn == string::npos ) return; // no suffix 71 size_t end = str.length() - 1; 72 if ( posn == end ) { type = 3; return; } // no length after 'l' => long 73 70 if ( posn == str.length() - 1 ) { type = 3; return; } // no length => long 71 74 72 string::size_type next = posn + 1; // advance to length 75 73 if ( str[next] == '3' ) { // 32 … … 86 84 } // if 87 85 } // if 88 89 char fix = '\0'; 90 if ( str[end] == 'u' || str[end] == 'U' ) fix = str[end]; // ends with 'uU' ? 91 str.erase( posn ); // remove length suffix and possibly uU 92 if ( type == 5 ) { // L128 does not need uU 93 end = str.length() - 1; 94 if ( str[end] == 'u' || str[end] == 'U' ) str.erase( end ); // ends with 'uU' ? remove 95 } else if ( fix != '\0' ) str += fix; // put 'uU' back if removed 86 // remove "lL" for these cases because it may not imply long 87 str.erase( posn ); // remove length 96 88 } // lnthSuffix 97 89 … … 116 108 } // valueToType 117 109 118 static void scanbin( string & str, unsigned long long int & v ) {119 v = 0;120 size_t last = str.length() - 1; // last subscript of constant121 for ( unsigned int i = 2;; ) { // ignore prefix122 if ( str[i] == '1' ) v |= 1;123 i += 1;124 if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break;125 v <<= 1;126 } // for127 } // scanbin128 129 110 Expression * build_constantInteger( string & str ) { 130 111 static const BasicType::Kind kind[2][6] = { 131 112 // short (h) must be before char (hh) because shorter type has the longer suffix 132 { BasicType::ShortSignedInt, BasicType::SignedChar, BasicType::SignedInt, BasicType::LongSignedInt, BasicType::LongLongSignedInt, /* BasicType::SignedInt128 */ BasicType::LongLongSignedInt, },133 { BasicType::ShortUnsignedInt, BasicType::UnsignedChar, BasicType::UnsignedInt, BasicType::LongUnsignedInt, BasicType::LongLongUnsignedInt, /* BasicType::UnsignedInt128 */ BasicType::LongLongUnsignedInt, },113 { BasicType::ShortSignedInt, BasicType::SignedChar, BasicType::SignedInt, BasicType::LongSignedInt, BasicType::LongLongSignedInt, BasicType::SignedInt128, }, 114 { BasicType::ShortUnsignedInt, BasicType::UnsignedChar, BasicType::UnsignedInt, BasicType::LongUnsignedInt, BasicType::LongLongUnsignedInt, BasicType::UnsignedInt128, }, 134 115 }; 135 116 … … 139 120 }; // lnthsInt 140 121 141 string str2( "0x0" ); 142 unsigned long long int v, v2 = 0; // converted integral value 143 Expression * ret, * ret2; 122 unsigned long long int v; // converted integral value 123 size_t last = str.length() - 1; // last subscript of constant 124 Expression * ret; 125 //string fred( str ); 144 126 145 127 int type = -1; // 0 => short, 1 => char, 2 => int, 3 => long int, 4 => long long int, 5 => int128 … … 157 139 } // if 158 140 159 string::size_type posn;160 161 // 'u' can appear before or after length suffix162 if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true;163 164 if ( isdigit( str[str.length() - 1] ) ) { // no suffix ?165 lnthSuffix( str, type, ltype ); // could have length suffix166 } else {167 // At least one digit in integer constant, so safe to backup while looking for suffix.168 169 posn = str.find_last_of( "pP" ); // pointer value170 if ( posn != string::npos ) { ltype = 5; str.erase( posn, 1 ); goto FINI; }171 172 posn = str.find_last_of( "zZ" ); // size_t173 if ( posn != string::npos ) { Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 ); goto FINI; }174 175 posn = str.rfind( "hh" ); // char176 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; }177 178 posn = str.rfind( "HH" ); // char179 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; }180 181 posn = str.find_last_of( "hH" ); // short182 if ( posn != string::npos ) { type = 0; str.erase( posn, 1 ); goto FINI; }183 184 posn = str.find_last_of( "nN" ); // int (natural number)185 if ( posn != string::npos ) { type = 2; str.erase( posn, 1 ); goto FINI; }186 187 if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) { type = 4; goto FINI; }188 189 lnthSuffix( str, type, ltype ); // must be after check for "ll"190 FINI: ;191 } // if192 193 141 // Cannot be just "0"/"1"; sscanf stops at the suffix, if any; value goes over the wall => always generate 194 142 195 #if ! defined(__SIZEOF_INT128__)196 if ( type == 5 ) SemanticError( yylloc, "int128 constant is not supported on this target " + str );197 #endif // ! __SIZEOF_INT128__198 199 143 if ( str[0] == '0' ) { // radix character ? 200 144 dec = false; 201 145 if ( checkX( str[1] ) ) { // hex constant ? 202 if ( type < 5 ) { // not L128 ? 203 sscanf( (char *)str.c_str(), "%llx", &v ); 204 #if defined(__SIZEOF_INT128__) 205 } else { // hex int128 constant 206 unsigned int len = str.length(); 207 if ( len > (2 + 16 + 16) ) SemanticError( yylloc, "128-bit hexadecimal constant to large " + str ); 208 if ( len <= (2 + 16) ) goto FHEX1; // hex digits < 2^64 209 str2 = "0x" + str.substr( len - 16 ); 210 sscanf( (char *)str2.c_str(), "%llx", &v2 ); 211 str = str.substr( 0, len - 16 ); 212 FHEX1: ; 213 sscanf( (char *)str.c_str(), "%llx", &v ); 214 #endif // __SIZEOF_INT128__ 215 } // if 146 sscanf( (char *)str.c_str(), "%llx", &v ); 216 147 //printf( "%llx %llu\n", v, v ); 217 148 } else if ( checkB( str[1] ) ) { // binary constant ? 218 #if defined(__SIZEOF_INT128__) 219 unsigned int len = str.length(); 220 if ( type == 5 && len > 2 + 64 ) { 221 if ( len > 2 + 64 + 64 ) SemanticError( yylloc, "128-bit binary constant to large " + str ); 222 str2 = "0b" + str.substr( len - 64 ); 223 str = str.substr( 0, len - 64 ); 224 scanbin( str2, v2 ); 225 } // if 226 #endif // __SIZEOF_INT128__ 227 scanbin( str, v ); 149 v = 0; // compute value 150 for ( unsigned int i = 2;; ) { // ignore prefix 151 if ( str[i] == '1' ) v |= 1; 152 i += 1; 153 if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break; 154 v <<= 1; 155 } // for 228 156 //printf( "%#llx %llu\n", v, v ); 229 157 } else { // octal constant 230 if ( type < 5 ) { // not L128 ? 231 sscanf( (char *)str.c_str(), "%llo", &v ); 232 #if defined(__SIZEOF_INT128__) 233 } else { // octal int128 constant 234 unsigned int len = str.length(); 235 if ( len > 1 + 43 || (len == 1 + 43 && str[0] > '3') ) SemanticError( yylloc, "128-bit octal constant to large " + str ); 236 char buf[32]; 237 if ( len <= 1 + 21 ) { // value < 21 octal digitis 238 sscanf( (char *)str.c_str(), "%llo", &v ); 239 } else { 240 sscanf( &str[len - 21], "%llo", &v ); 241 __int128 val = v; // accumulate bits 242 str[len - 21] ='\0'; // shorten string 243 sscanf( &str[len == 43 ? 1 : 0], "%llo", &v ); 244 val |= (__int128)v << 63; // store bits 245 if ( len == 1 + 43 ) { // most significant 2 bits ? 246 str[2] = '\0'; // shorten string 247 sscanf( &str[1], "%llo", &v ); // process most significant 2 bits 248 val |= (__int128)v << 126; // store bits 249 } // if 250 v = val >> 64; v2 = (uint64_t)val; // replace octal constant with 2 hex constants 251 sprintf( buf, "%#llx", v2 ); 252 str2 = buf; 253 } // if 254 sprintf( buf, "%#llx", v ); 255 str = buf; 256 #endif // __SIZEOF_INT128__ 257 } // if 158 sscanf( (char *)str.c_str(), "%llo", &v ); 258 159 //printf( "%#llo %llu\n", v, v ); 259 160 } // if 260 161 } else { // decimal constant ? 261 if ( type < 5 ) { // not L128 ? 262 sscanf( (char *)str.c_str(), "%llu", &v ); 263 #if defined(__SIZEOF_INT128__) 264 } else { // decimal int128 constant 265 #define P10_UINT64 10'000'000'000'000'000'000ULL // 19 zeroes 266 unsigned int len = str.length(); 267 if ( str.length() == 39 && str > (Unsigned ? "340282366920938463463374607431768211455" : "170141183460469231731687303715884105727") ) 268 SemanticError( yylloc, "128-bit decimal constant to large " + str ); 269 char buf[32]; 270 if ( len <= 19 ) { // value < 19 decimal digitis 271 sscanf( (char *)str.c_str(), "%llu", &v ); 272 } else { 273 sscanf( &str[len - 19], "%llu", &v ); 274 __int128 val = v; // accumulate bits 275 str[len - 19] ='\0'; // shorten string 276 sscanf( &str[len == 39 ? 1 : 0], "%llu", &v ); 277 val += (__int128)v * (__int128)P10_UINT64; // store bits 278 if ( len == 39 ) { // most significant 2 bits ? 279 str[1] = '\0'; // shorten string 280 sscanf( &str[0], "%llu", &v ); // process most significant 2 bits 281 val += (__int128)v * (__int128)P10_UINT64 * (__int128)P10_UINT64; // store bits 282 } // if 283 v = val >> 64; v2 = (uint64_t)val; // replace decimal constant with 2 hex constants 284 sprintf( buf, "%#llx", v2 ); 285 str2 = buf; 286 } // if 287 sprintf( buf, "%#llx", v ); 288 str = buf; 289 #endif // __SIZEOF_INT128__ 290 } // if 162 sscanf( (char *)str.c_str(), "%llu", &v ); 291 163 //printf( "%llu\n", v ); 292 164 } // if 293 165 294 if ( type == -1 ) { // no suffix => determine type from value size 295 valueToType( v, dec, type, Unsigned ); 296 } // if 297 /* printf( "%s %llo %s %llo\n", str.c_str(), v, str2.c_str(), v2 ); */ 166 string::size_type posn; 167 168 if ( isdigit( str[last] ) ) { // no suffix ? 169 lnthSuffix( str, type, ltype ); // could have length suffix 170 if ( type == -1 ) { // no suffix 171 valueToType( v, dec, type, Unsigned ); 172 } // if 173 } else { 174 // At least one digit in integer constant, so safe to backup while looking for suffix. 175 176 posn = str.find_last_of( "pP" ); 177 if ( posn != string::npos ) { valueToType( v, dec, type, Unsigned ); ltype = 5; str.erase( posn, 1 ); goto FINI; } 178 179 posn = str.find_last_of( "zZ" ); 180 if ( posn != string::npos ) { Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 ); goto FINI; } 181 182 // 'u' can appear before or after length suffix 183 if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true; 184 185 posn = str.rfind( "hh" ); 186 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; } 187 188 posn = str.rfind( "HH" ); 189 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; } 190 191 posn = str.find_last_of( "hH" ); 192 if ( posn != string::npos ) { type = 0; str.erase( posn, 1 ); goto FINI; } 193 194 posn = str.find_last_of( "nN" ); 195 if ( posn != string::npos ) { type = 2; str.erase( posn, 1 ); goto FINI; } 196 197 if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) { type = 4; goto FINI; } 198 199 lnthSuffix( str, type, ltype ); // must be after check for "ll" 200 if ( type == -1 ) { // only 'u' suffix ? 201 valueToType( v, dec, type, Unsigned ); 202 } // if 203 FINI: ; 204 } // if 298 205 299 206 //if ( !( 0 <= type && type <= 6 ) ) { printf( "%s %lu %d %s\n", fred.c_str(), fred.length(), type, str.c_str() ); } … … 307 214 } else if ( ltype != -1 ) { // explicit length ? 308 215 if ( ltype == 6 ) { // int128, (int128)constant 309 // ret = new CastExpr( ret, new BasicType( Type::Qualifiers(), kind[Unsigned][type] ), false ); 310 ret2 = new ConstantExpr( Constant( new BasicType( noQualifiers, BasicType::LongLongSignedInt ), str2, v2 ) ); 311 ret = build_compoundLiteral( DeclarationNode::newBasicType( DeclarationNode::Int128 )->addType( DeclarationNode::newSignedNess( DeclarationNode::Unsigned ) ), 312 new InitializerNode( (InitializerNode *)(new InitializerNode( new ExpressionNode( v2 == 0 ? ret2 : ret ) ))->set_last( new InitializerNode( new ExpressionNode( v2 == 0 ? ret : ret2 ) ) ), true ) ); 216 ret = new CastExpr( ret, new BasicType( Type::Qualifiers(), kind[Unsigned][type] ), false ); 313 217 } else { // explicit length, (length_type)constant 314 218 ret = new CastExpr( ret, new TypeInstType( Type::Qualifiers(), lnthsInt[Unsigned][ltype], false ), false ); … … 438 342 if ( str[1] == '8' ) goto Default; // utf-8 characters => array of char 439 343 // lookup type of associated typedef 440 strtype = new TypeInstType( Type::Qualifiers( ), "char16_t", false );344 strtype = new TypeInstType( Type::Qualifiers( Type::Const ), "char16_t", false ); 441 345 break; 442 346 case 'U': 443 strtype = new TypeInstType( Type::Qualifiers( ), "char32_t", false );347 strtype = new TypeInstType( Type::Qualifiers( Type::Const ), "char32_t", false ); 444 348 break; 445 349 case 'L': 446 strtype = new TypeInstType( Type::Qualifiers( ), "wchar_t", false );350 strtype = new TypeInstType( Type::Qualifiers( Type::Const ), "wchar_t", false ); 447 351 break; 448 352 Default: // char default string type 449 353 default: 450 strtype = new BasicType( Type::Qualifiers( ), BasicType::Char );354 strtype = new BasicType( Type::Qualifiers( Type::Const ), BasicType::Char ); 451 355 } // switch 452 356 ArrayType * at = new ArrayType( noQualifiers, strtype, -
src/Parser/ParseNode.h
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 13:28:16 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Oct 24 03:53:54 202013 // Update Count : 8 9512 // Last Modified On : Mon Dec 16 07:46:01 2019 13 // Update Count : 888 14 14 // 15 15 … … 37 37 class Attribute; 38 38 class Declaration; 39 structDeclarationNode;39 class DeclarationNode; 40 40 class DeclarationWithType; 41 41 class ExpressionNode; 42 42 class Initializer; 43 structStatementNode;43 class StatementNode; 44 44 45 45 //############################################################################## … … 86 86 class InitializerNode : public ParseNode { 87 87 public: 88 InitializerNode( ExpressionNode *, bool aggrp = false, ExpressionNode * des = nullptr );88 InitializerNode( ExpressionNode *, bool aggrp = false, ExpressionNode * des = nullptr ); 89 89 InitializerNode( InitializerNode *, bool aggrp = false, ExpressionNode * des = nullptr ); 90 90 InitializerNode( bool isDelete ); … … 205 205 struct TypeData; 206 206 207 struct DeclarationNode : public ParseNode { 207 class DeclarationNode : public ParseNode { 208 public: 208 209 // These enumerations must harmonize with their names in DeclarationNode.cc. 209 210 enum BasicType { Void, Bool, Char, Int, Int128, … … 303 304 bool get_inLine() const { return inLine; } 304 305 DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; } 305 306 public: 306 307 DeclarationNode * get_last() { return (DeclarationNode *)ParseNode::get_last(); } 307 308 … … 359 360 //############################################################################## 360 361 361 struct StatementNode final : public ParseNode { 362 class StatementNode final : public ParseNode { 363 public: 362 364 StatementNode() { stmt = nullptr; } 363 365 StatementNode( Statement * stmt ) : stmt( stmt ) {} … … 380 382 os << stmt.get() << std::endl; 381 383 } 382 384 private: 383 385 std::unique_ptr<Statement> stmt; 384 386 }; // StatementNode … … 424 426 Statement * build_finally( StatementNode * stmt ); 425 427 Statement * build_compound( StatementNode * first ); 426 StatementNode * maybe_build_compound( StatementNode * first );427 428 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output = nullptr, ExpressionNode * input = nullptr, ExpressionNode * clobber = nullptr, LabelNode * gotolabels = nullptr ); 428 429 Statement * build_directive( std::string * directive ); 429 SuspendStmt * build_suspend( StatementNode *, SuspendStmt::Type = SuspendStmt::None);430 430 WaitForStmt * build_waitfor( ExpressionNode * target, StatementNode * stmt, ExpressionNode * when ); 431 431 WaitForStmt * build_waitfor( ExpressionNode * target, StatementNode * stmt, ExpressionNode * when, WaitForStmt * existing ); … … 449 449 * out++ = result; 450 450 } else { 451 SemanticError( cur->location, "type specifier declaration in forall clause is currently unimplemented.");451 assertf(false, "buildList unknown type"); 452 452 } // if 453 453 } catch( SemanticErrorException & e ) { -
src/Parser/ParserTypes.h
reef8dfb rbdfc032 10 10 // Created On : Sat Sep 22 08:58:10 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 15 11:04:40 202013 // Update Count : 35 112 // Last Modified On : Sat Jul 22 09:33:28 2017 13 // Update Count : 350 14 14 // 15 15 … … 27 27 // current location in the input 28 28 extern int yylineno; 29 extern char * yyfilename;29 extern char *yyfilename; 30 30 31 31 struct Location { 32 char * file;32 char *file; 33 33 int line; 34 34 }; // Location 35 35 36 36 struct Token { 37 std::string * str; // must be pointer as used in union37 std::string *str; // must be pointer as used in union 38 38 Location loc; 39 39 -
src/Parser/StatementNode.cc
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 14:59:41 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Oct 24 04:20:55 202013 // Update Count : 3 8312 // Last Modified On : Sat Aug 4 09:39:25 2018 13 // Update Count : 363 14 14 // 15 15 … … 249 249 } // build_finally 250 250 251 SuspendStmt * build_suspend( StatementNode * then, SuspendStmt::Type type ) {252 auto node = new SuspendStmt();253 254 node->type = type;255 256 std::list< Statement * > stmts;257 buildMoveList< Statement, StatementNode >( then, stmts );258 if(!stmts.empty()) {259 assert( stmts.size() == 1 );260 node->then = dynamic_cast< CompoundStmt * >( stmts.front() );261 }262 263 return node;264 }265 266 251 WaitForStmt * build_waitfor( ExpressionNode * targetExpr, StatementNode * stmt, ExpressionNode * when ) { 267 252 auto node = new WaitForStmt(); … … 345 330 } // build_compound 346 331 347 // A single statement in a control structure is always converted to a compound statement so subsequent generated code348 // can be placed within this compound statement. Otherwise, code generation has to constantly check for a single349 // statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a350 // conical form for code generation.351 StatementNode * maybe_build_compound( StatementNode * first ) {352 // Optimization: if the control-structure statement is a compound statement, do not wrap it.353 // e.g., if (...) {...} do not wrap the existing compound statement.354 if ( ! dynamic_cast<CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr355 CompoundStmt * cs = new CompoundStmt();356 buildMoveList( first, cs->get_kids() );357 return new StatementNode( cs );358 } // if359 return first;360 } // maybe_build_compound361 362 332 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) { 363 333 std::list< Expression * > out, in; -
src/Parser/TypeData.cc
reef8dfb rbdfc032 769 769 case AggregateDecl::Struct: 770 770 case AggregateDecl::Coroutine: 771 case AggregateDecl::Generator:772 771 case AggregateDecl::Monitor: 773 772 case AggregateDecl::Thread: … … 900 899 ret = new TypeDecl( name, scs, typebuild( td->base ), TypeDecl::Dtype, true ); 901 900 } // if 901 buildList( td->symbolic.params, ret->get_parameters() ); 902 902 buildList( td->symbolic.assertions, ret->get_assertions() ); 903 903 ret->base->attributes.splice( ret->base->attributes.end(), attributes ); -
src/Parser/TypedefTable.cc
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 15:20:13 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 15 08:06:36 202013 // Update Count : 25 912 // Last Modified On : Wed Jul 25 15:32:35 2018 13 // Update Count : 258 14 14 // 15 15 … … 47 47 } // TypedefTable::~TypedefTable 48 48 49 bool TypedefTable::exists( const string & identifier ) const{49 bool TypedefTable::exists( const string & identifier ) { 50 50 return kindTable.find( identifier ) != kindTable.end(); 51 51 } // TypedefTable::exists 52 52 53 bool TypedefTable::existsCurr( const string & identifier ) const{53 bool TypedefTable::existsCurr( const string & identifier ) { 54 54 return kindTable.findAt( kindTable.currentScope() - 1, identifier ) != kindTable.end(); 55 55 } // TypedefTable::exists -
src/Parser/TypedefTable.h
reef8dfb rbdfc032 10 10 // Created On : Sat May 16 15:24:36 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 15 08:06:37 202013 // Update Count : 11 712 // Last Modified On : Wed Jul 25 15:33:55 2018 13 // Update Count : 114 14 14 // 15 15 … … 30 30 ~TypedefTable(); 31 31 32 bool exists( const std::string & identifier ) const;33 bool existsCurr( const std::string & identifier ) const;32 bool exists( const std::string & identifier ); 33 bool existsCurr( const std::string & identifier ); 34 34 int isKind( const std::string & identifier ) const; 35 35 void makeTypedef( const std::string & name, int kind = TYPEDEFname ); -
src/Parser/lex.ll
reef8dfb rbdfc032 10 10 * Created On : Sat Sep 22 08:58:10 2001 11 11 * Last Modified By : Peter A. Buhr 12 * Last Modified On : Tue Oct 6 18:15:41202013 * Update Count : 7 4312 * Last Modified On : Sat Feb 1 07:16:44 2020 13 * Update Count : 724 14 14 */ 15 15 … … 43 43 #include "TypedefTable.h" 44 44 45 string * build_postfix_name( string * name );46 47 45 char *yyfilename; 48 46 string *strtext; // accumulate parts of character and string constant value … … 62 60 #define IDENTIFIER_RETURN() RETURN_VAL( typedefTable.isKind( yytext ) ) 63 61 64 #ifdef HAVE_KEYWORDS_FLOATXX // GCC >= 7 => keyword, otherwise typedef62 #ifdef HAVE_KEYWORDS_FLOATXX // GCC >= 7 => keyword, otherwise typedef 65 63 #define FLOATXX(v) KEYWORD_RETURN(v); 66 64 #else 67 #define FLOATXX(v) IDENTIFIER_RETURN(); 65 #define FLOATXX(v) IDENTIFIER_RETURN(); 68 66 #endif // HAVE_KEYWORDS_FLOATXX 69 67 … … 292 290 __restrict__ { KEYWORD_RETURN(RESTRICT); } // GCC 293 291 return { KEYWORD_RETURN(RETURN); } 294 /* resume { KEYWORD_RETURN(RESUME); } // CFA */292 /* resume { KEYWORD_RETURN(RESUME); } // CFA */ 295 293 short { KEYWORD_RETURN(SHORT); } 296 294 signed { KEYWORD_RETURN(SIGNED); } … … 301 299 _Static_assert { KEYWORD_RETURN(STATICASSERT); } // C11 302 300 struct { KEYWORD_RETURN(STRUCT); } 303 suspend { KEYWORD_RETURN(SUSPEND); } // CFA 301 /* suspend { KEYWORD_RETURN(SUSPEND); } // CFA */ 304 302 switch { KEYWORD_RETURN(SWITCH); } 305 303 thread { KEYWORD_RETURN(THREAD); } // C11 … … 332 330 /* identifier */ 333 331 {identifier} { IDENTIFIER_RETURN(); } 334 "``"{identifier} {// CFA335 yytext[yyleng ] = '\0'; yytext += 2;// SKULLDUGGERY: remove backquotes (ok to shorten?)332 "``"{identifier}"``" { // CFA 333 yytext[yyleng - 2] = '\0'; yytext += 2; // SKULLDUGGERY: remove backquotes (ok to shorten?) 336 334 IDENTIFIER_RETURN(); 337 335 } … … 434 432 "?"({op_unary_pre_post}|"()"|"[?]"|"{}") { IDENTIFIER_RETURN(); } 435 433 "^?{}" { IDENTIFIER_RETURN(); } 436 "?`"{identifier} { // postfix operator 437 yylval.tok.str = new string( &yytext[2] ); // remove ?` 438 yylval.tok.str = build_postfix_name( yylval.tok.str ); // add prefix 439 RETURN_LOCN( typedefTable.isKind( *yylval.tok.str ) ); 440 } 434 "?`"{identifier} { IDENTIFIER_RETURN(); } // postfix operator 441 435 "?"{op_binary_over}"?" { IDENTIFIER_RETURN(); } // binary 442 436 /* -
src/Parser/module.mk
reef8dfb rbdfc032 17 17 BUILT_SOURCES = Parser/parser.hh 18 18 19 AM_YFLAGS = -d -t -v -Wno-yacc19 AM_YFLAGS = -d -t -v 20 20 21 21 SRC += \ … … 23 23 Parser/ExpressionNode.cc \ 24 24 Parser/InitializerNode.cc \ 25 Parser/lex.ll \26 25 Parser/ParseNode.cc \ 27 Parser/ParseNode.h \28 Parser/parser.yy \29 Parser/ParserTypes.h \30 Parser/parserutility.cc \31 Parser/parserutility.h \32 26 Parser/StatementNode.cc \ 33 27 Parser/TypeData.cc \ 34 Parser/TypeData.h \35 28 Parser/TypedefTable.cc \ 36 Parser/TypedefTable.h 29 Parser/lex.ll \ 30 Parser/parser.yy \ 31 Parser/parserutility.cc 37 32 38 33 MOSTLYCLEANFILES += Parser/lex.cc Parser/parser.cc Parser/parser.hh Parser/parser.output -
src/Parser/parser.yy
reef8dfb rbdfc032 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Oct 24 08:21:14202013 // Update Count : 4 62412 // Last Modified On : Sat Feb 1 10:04:40 2020 13 // Update Count : 4440 14 14 // 15 15 … … 166 166 } // rebindForall 167 167 168 string * build_postfix_name( string * name ) { 169 *name = string("__postfix_func_") + *name; 170 return name; 168 NameExpr * build_postfix_name( const string * name ) { 169 NameExpr * new_name = build_varref( new string( "?`" + *name ) ); 170 delete name; 171 return new_name; 171 172 } // build_postfix_name 172 173 … … 204 205 return forCtrl( type, new string( identifier->name ), start, compop, comp, inc ); 205 206 } else { 206 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed ." ); return nullptr;207 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed" ); return nullptr; 207 208 } // if 208 209 } else { 209 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed ." ); return nullptr;210 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed" ); return nullptr; 210 211 } // if 211 212 } // forCtrl … … 278 279 %token OTYPE FTYPE DTYPE TTYPE TRAIT // CFA 279 280 %token SIZEOF OFFSETOF 280 // %token RESUME // CFA 281 %token SUSPEND // CFA 281 // %token SUSPEND RESUME // CFA 282 282 %token ATTRIBUTE EXTENSION // GCC 283 283 %token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN … … 329 329 %type<en> conditional_expression constant_expression assignment_expression assignment_expression_opt 330 330 %type<en> comma_expression comma_expression_opt 331 %type<en> argument_expression_list _optargument_expression default_initialize_opt331 %type<en> argument_expression_list argument_expression default_initialize_opt 332 332 %type<ifctl> if_control_expression 333 333 %type<fctl> for_control_expression for_control_expression_list … … 370 370 %type<decl> assertion assertion_list assertion_list_opt 371 371 372 %type<en> bit_subrange_size_opt bit_subrange_size372 %type<en> bit_subrange_size_opt bit_subrange_size 373 373 374 374 %type<decl> basic_declaration_specifier basic_type_name basic_type_specifier direct_type indirect_type … … 624 624 // equivalent to the old x[i,j]. 625 625 { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, $3 ) ); } 626 | postfix_expression '{' argument_expression_list _opt'}' // CFA, constructor call626 | postfix_expression '{' argument_expression_list '}' // CFA, constructor call 627 627 { 628 628 Token fn; … … 630 630 $$ = new ExpressionNode( new ConstructorExpr( build_func( new ExpressionNode( build_varref( fn ) ), (ExpressionNode *)( $1 )->set_last( $3 ) ) ) ); 631 631 } 632 | postfix_expression '(' argument_expression_list _opt')'632 | postfix_expression '(' argument_expression_list ')' 633 633 { $$ = new ExpressionNode( build_func( $1, $3 ) ); } 634 634 | postfix_expression '`' identifier // CFA, postfix call 635 { $$ = new ExpressionNode( build_func( new ExpressionNode( build_ varref( build_postfix_name( $3 )) ), $1 ) ); }635 { $$ = new ExpressionNode( build_func( new ExpressionNode( build_postfix_name( $3 ) ), $1 ) ); } 636 636 | constant '`' identifier // CFA, postfix call 637 { $$ = new ExpressionNode( build_func( new ExpressionNode( build_ varref( build_postfix_name( $3 )) ), $1 ) ); }637 { $$ = new ExpressionNode( build_func( new ExpressionNode( build_postfix_name( $3 ) ), $1 ) ); } 638 638 | string_literal '`' identifier // CFA, postfix call 639 { $$ = new ExpressionNode( build_func( new ExpressionNode( build_ varref( build_postfix_name( $3 )) ), new ExpressionNode( $1 ) ) ); }639 { $$ = new ExpressionNode( build_func( new ExpressionNode( build_postfix_name( $3 ) ), new ExpressionNode( $1 ) ) ); } 640 640 | postfix_expression '.' identifier 641 641 { $$ = new ExpressionNode( build_fieldSel( $1, build_varref( $3 ) ) ); } … … 662 662 | '(' type_no_function ')' '@' '{' initializer_list_opt comma_opt '}' // CFA, explicit C compound-literal 663 663 { $$ = new ExpressionNode( build_compoundLiteral( $2, (new InitializerNode( $6, true ))->set_maybeConstructed( false ) ) ); } 664 | '^' primary_expression '{' argument_expression_list _opt'}' // CFA, destructor call664 | '^' primary_expression '{' argument_expression_list '}' // CFA, destructor call 665 665 { 666 666 Token fn; … … 670 670 ; 671 671 672 argument_expression_list _opt:672 argument_expression_list: 673 673 // empty 674 674 { $$ = nullptr; } 675 675 | argument_expression 676 | argument_expression_list _opt',' argument_expression676 | argument_expression_list ',' argument_expression 677 677 { $$ = (ExpressionNode *)($1->set_last( $3 )); } 678 678 ; … … 793 793 | '(' aggregate_control '&' ')' cast_expression // CFA 794 794 { $$ = new ExpressionNode( build_keyword_cast( $2, $5 ) ); } 795 // VIRTUAL cannot be opt because of look ahead issues 795 796 | '(' VIRTUAL ')' cast_expression // CFA 796 797 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression >( $4 ), maybeMoveBuildType( nullptr ) ) ); } … … 918 919 conditional_expression 919 920 | unary_expression assignment_operator assignment_expression 920 { 921 // if ( $2 == OperKinds::AtAssn ) { 922 // SemanticError( yylloc, "C @= assignment is currently unimplemented." ); $$ = nullptr; 923 // } else { 924 $$ = new ExpressionNode( build_binary_val( $2, $1, $3 ) ); 925 // } // if 926 } 921 { $$ = new ExpressionNode( build_binary_val( $2, $1, $3 ) ); } 927 922 | unary_expression '=' '{' initializer_list_opt comma_opt '}' 928 923 { SemanticError( yylloc, "Initializer assignment is currently unimplemented." ); $$ = nullptr; } … … 965 960 966 961 tuple_expression_list: 967 assignment_expression 968 | '@' // CFA 969 { SemanticError( yylloc, "Eliding tuple element with '@' is currently unimplemented." ); $$ = nullptr; } 970 | tuple_expression_list ',' assignment_expression 962 assignment_expression_opt 963 | tuple_expression_list ',' assignment_expression_opt 971 964 { $$ = (ExpressionNode *)($1->set_last( $3 )); } 972 | tuple_expression_list ',' '@'973 { SemanticError( yylloc, "Eliding tuple element with '@' is currently unimplemented." ); $$ = nullptr; }974 965 ; 975 966 … … 1080 1071 IF '(' if_control_expression ')' statement %prec THEN 1081 1072 // explicitly deal with the shift/reduce conflict on if/else 1082 { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), nullptr ) ); }1073 { $$ = new StatementNode( build_if( $3, $5, nullptr ) ); } 1083 1074 | IF '(' if_control_expression ')' statement ELSE statement 1084 { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), maybe_build_compound( $7 )) ); }1075 { $$ = new StatementNode( build_if( $3, $5, $7 ) ); } 1085 1076 ; 1086 1077 … … 1130 1121 1131 1122 case_clause: // CFA 1132 case_label_list statement { $$ = $1->append_last_case( maybe_build_compound( $2) ); }1123 case_label_list statement { $$ = $1->append_last_case( new StatementNode( build_compound( $2 ) ) ); } 1133 1124 ; 1134 1125 … … 1148 1139 iteration_statement: 1149 1140 WHILE '(' push if_control_expression ')' statement pop 1150 { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 )) ); }1141 { $$ = new StatementNode( build_while( $4, $6 ) ); } 1151 1142 | WHILE '(' ')' statement // CFA => while ( 1 ) 1152 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 )) ); }1143 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), $4 ) ); } 1153 1144 | DO statement WHILE '(' comma_expression ')' ';' 1154 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 )) ); }1145 { $$ = new StatementNode( build_do_while( $5, $2 ) ); } 1155 1146 | DO statement WHILE '(' ')' ';' // CFA => do while( 1 ) 1156 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 )) ); }1147 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), $2 ) ); } 1157 1148 | FOR '(' push for_control_expression_list ')' statement pop 1158 { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 )) ); }1149 { $$ = new StatementNode( build_for( $4, $6 ) ); } 1159 1150 | FOR '(' ')' statement // CFA => for ( ;; ) 1160 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 )) ); }1151 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), $4 ) ); } 1161 1152 ; 1162 1153 … … 1195 1186 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1196 1187 OperKinds::LThan, $1->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } 1197 | '=' comma_expression // CFA1188 | '=' comma_expression // CFA 1198 1189 { $$ = forCtrl( $2, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1199 1190 OperKinds::LEThan, $2->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } … … 1202 1193 | comma_expression inclexcl comma_expression '~' comma_expression // CFA 1203 1194 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), $1->clone(), $2, $3, $5 ); } 1204 | comma_expression ';' // CFA1205 { $$ = forCtrl( new ExpressionNode( build_constantInteger( *new string( "0u" ) ) ), $1, nullptr, OperKinds::LThan, nullptr, nullptr ); }1206 1195 | comma_expression ';' comma_expression // CFA 1207 1196 { $$ = forCtrl( $3, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1208 1197 OperKinds::LThan, $3->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } 1209 | comma_expression ';' '=' comma_expression // CFA1198 | comma_expression ';' '=' comma_expression // CFA 1210 1199 { $$ = forCtrl( $4, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1211 1200 OperKinds::LEThan, $4->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } … … 1271 1260 | RETURN '{' initializer_list_opt comma_opt '}' ';' 1272 1261 { SemanticError( yylloc, "Initializer return is currently unimplemented." ); $$ = nullptr; } 1273 | SUSPEND ';' 1274 { $$ = new StatementNode( build_suspend( nullptr ) ); } 1275 | SUSPEND compound_statement 1276 { $$ = new StatementNode( build_suspend( $2 ) ); } 1277 | SUSPEND COROUTINE ';' 1278 { $$ = new StatementNode( build_suspend( nullptr, SuspendStmt::Coroutine ) ); } 1279 | SUSPEND COROUTINE compound_statement 1280 { $$ = new StatementNode( build_suspend( $3, SuspendStmt::Coroutine ) ); } 1281 | SUSPEND GENERATOR ';' 1282 { $$ = new StatementNode( build_suspend( nullptr, SuspendStmt::Generator ) ); } 1283 | SUSPEND GENERATOR compound_statement 1284 { $$ = new StatementNode( build_suspend( $3, SuspendStmt::Generator ) ); } 1262 // | SUSPEND ';' 1263 // { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; } 1264 // | SUSPEND compound_statement ';' 1265 // { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; } 1285 1266 | THROW assignment_expression_opt ';' // handles rethrow 1286 1267 { $$ = new StatementNode( build_throw( $2 ) ); } … … 1305 1286 // If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex". 1306 1287 mutex_statement: 1307 MUTEX '(' argument_expression_list _opt')' statement1288 MUTEX '(' argument_expression_list ')' statement 1308 1289 { SemanticError( yylloc, "Mutex statement is currently unimplemented." ); $$ = nullptr; } 1309 1290 ; … … 1322 1303 WAITFOR '(' cast_expression ')' 1323 1304 { $$ = $3; } 1324 // | WAITFOR '(' cast_expression ',' argument_expression_list _opt')'1305 // | WAITFOR '(' cast_expression ',' argument_expression_list ')' 1325 1306 // { $$ = (ExpressionNode *)$3->set_last( $5 ); } 1326 | WAITFOR '(' cast_expression_list ':' argument_expression_list _opt')'1307 | WAITFOR '(' cast_expression_list ':' argument_expression_list ')' 1327 1308 { $$ = (ExpressionNode *)($3->set_last( $5 )); } 1328 1309 ; … … 1331 1312 cast_expression 1332 1313 | cast_expression_list ',' cast_expression 1333 // { $$ = (ExpressionNode *)($1->set_last( $3 )); } 1334 { SemanticError( yylloc, "List of mutex member is currently unimplemented." ); $$ = nullptr; } 1314 { $$ = (ExpressionNode *)($1->set_last( $3 )); } 1335 1315 ; 1336 1316 … … 1341 1321 waitfor_clause: 1342 1322 when_clause_opt waitfor statement %prec THEN 1343 { $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1 ); }1323 { $$ = build_waitfor( $2, $3, $1 ); } 1344 1324 | when_clause_opt waitfor statement WOR waitfor_clause 1345 { $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1, $5 ); }1325 { $$ = build_waitfor( $2, $3, $1, $5 ); } 1346 1326 | when_clause_opt timeout statement %prec THEN 1347 { $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1 ); }1327 { $$ = build_waitfor_timeout( $2, $3, $1 ); } 1348 1328 | when_clause_opt ELSE statement 1349 { $$ = build_waitfor_timeout( nullptr, maybe_build_compound( $3 ), $1 ); }1329 { $$ = build_waitfor_timeout( nullptr, $3, $1 ); } 1350 1330 // "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless) 1351 1331 | when_clause_opt timeout statement WOR ELSE statement 1352 1332 { SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; } 1353 1333 | when_clause_opt timeout statement WOR when_clause ELSE statement 1354 { $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1, maybe_build_compound( $7 ), $5 ); }1334 { $$ = build_waitfor_timeout( $2, $3, $1, $7, $5 ); } 1355 1335 ; 1356 1336 … … 1610 1590 // type_specifier can resolve to just TYPEDEFname (e.g., typedef int T; int f( T );). Therefore this must be 1611 1591 // flattened to allow lookahead to the '(' without having to reduce identifier_or_type_name. 1612 cfa_abstract_tuple identifier_or_type_name '(' push cfa_parameter_ellipsis_list_opt pop ')' attribute_list_opt1592 cfa_abstract_tuple identifier_or_type_name '(' push cfa_parameter_ellipsis_list_opt pop ')' 1613 1593 // To obtain LR(1 ), this rule must be factored out from function return type (see cfa_abstract_declarator). 1614 { $$ = DeclarationNode::newFunction( $2, $1, $5, 0 ) ->addQualifiers( $8 ); }1615 | cfa_function_return identifier_or_type_name '(' push cfa_parameter_ellipsis_list_opt pop ')' attribute_list_opt1616 { $$ = DeclarationNode::newFunction( $2, $1, $5, 0 ) ->addQualifiers( $8 ); }1594 { $$ = DeclarationNode::newFunction( $2, $1, $5, 0 ); } 1595 | cfa_function_return identifier_or_type_name '(' push cfa_parameter_ellipsis_list_opt pop ')' 1596 { $$ = DeclarationNode::newFunction( $2, $1, $5, 0 ); } 1617 1597 ; 1618 1598 … … 1675 1655 1676 1656 typedef_expression: 1677 // deprecatedGCC, naming expression type: typedef name = exp; gives a name to the type of an expression1657 // GCC, naming expression type: typedef name = exp; gives a name to the type of an expression 1678 1658 TYPEDEF identifier '=' assignment_expression 1679 1659 { 1680 SemanticError( yylloc, "Typedef expression is deprecated, use typeof(...) instead." ); $$ = nullptr; 1660 // $$ = DeclarationNode::newName( 0 ); // unimplemented 1661 SemanticError( yylloc, "Typedef expression is currently unimplemented." ); $$ = nullptr; 1681 1662 } 1682 1663 | typedef_expression pop ',' push identifier '=' assignment_expression 1683 1664 { 1684 SemanticError( yylloc, "Typedef expression is deprecated, use typeof(...) instead." ); $$ = nullptr; 1685 } 1686 ; 1665 // $$ = DeclarationNode::newName( 0 ); // unimplemented 1666 SemanticError( yylloc, "Typedef expression is currently unimplemented." ); $$ = nullptr; 1667 } 1668 ; 1669 1670 //c_declaration: 1671 // declaring_list pop ';' 1672 // | typedef_declaration pop ';' 1673 // | typedef_expression pop ';' // GCC, naming expression type 1674 // | sue_declaration_specifier pop ';' 1675 // ; 1676 // 1677 //declaring_list: 1678 // // A semantic check is required to ensure asm_name only appears on declarations with implicit or explicit static 1679 // // storage-class 1680 // declarator asm_name_opt initializer_opt 1681 // { 1682 // typedefTable.addToEnclosingScope( IDENTIFIER ); 1683 // $$ = ( $2->addType( $1 ))->addAsmName( $3 )->addInitializer( $4 ); 1684 // } 1685 // | declaring_list ',' attribute_list_opt declarator asm_name_opt initializer_opt 1686 // { 1687 // typedefTable.addToEnclosingScope( IDENTIFIER ); 1688 // $$ = $1->appendList( $1->cloneBaseType( $4->addAsmName( $5 )->addInitializer( $6 ) ) ); 1689 // } 1690 // ; 1687 1691 1688 1692 c_declaration: … … 1690 1694 { $$ = distAttr( $1, $2 ); } 1691 1695 | typedef_declaration 1692 | typedef_expression // deprecatedGCC, naming expression type1696 | typedef_expression // GCC, naming expression type 1693 1697 | sue_declaration_specifier 1694 1698 ; … … 2069 2073 { yyy = true; $$ = AggregateDecl::Union; } 2070 2074 | EXCEPTION // CFA 2071 // { yyy = true; $$ = AggregateDecl::Exception; } 2072 { SemanticError( yylloc, "exception aggregate is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; } 2075 { yyy = true; $$ = AggregateDecl::Exception; } 2073 2076 ; 2074 2077 2075 2078 aggregate_control: // CFA 2076 MONITOR 2077 { yyy = true; $$ = AggregateDecl::Monitor; } 2078 | MUTEX STRUCT 2079 { yyy = true; $$ = AggregateDecl::Monitor; } 2080 | GENERATOR 2081 { yyy = true; $$ = AggregateDecl::Generator; } 2082 | MUTEX GENERATOR 2083 { SemanticError( yylloc, "monitor generator is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; } 2079 GENERATOR 2080 { yyy = true; $$ = AggregateDecl::Coroutine; } 2084 2081 | COROUTINE 2085 2082 { yyy = true; $$ = AggregateDecl::Coroutine; } 2086 | M UTEX COROUTINE2087 { SemanticError( yylloc, "monitor coroutine is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; }2083 | MONITOR 2084 { yyy = true; $$ = AggregateDecl::Monitor; } 2088 2085 | THREAD 2089 2086 { yyy = true; $$ = AggregateDecl::Thread; } 2090 | MUTEX THREAD2091 { SemanticError( yylloc, "monitor thread is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; }2092 2087 ; 2093 2088 … … 2412 2407 // Overloading: function, data, and operator identifiers may be overloaded. 2413 2408 // 2414 // Type declarations: " otype" is used to generate new types for declaring objects. Similarly, "dtype" is used for object2409 // Type declarations: "type" is used to generate new types for declaring objects. Similarly, "dtype" is used for object 2415 2410 // and incomplete types, and "ftype" is used for function types. Type declarations with initializers provide 2416 2411 // definitions of new types. Type declarations with storage class "extern" provide opaque types. … … 2441 2436 type_class identifier_or_type_name 2442 2437 { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); } 2443 type_initializer_opt assertion_list_opt2438 type_initializer_opt assertion_list_opt 2444 2439 { $$ = DeclarationNode::newTypeParam( $1, $2 )->addTypeInitializer( $4 )->addAssertions( $5 ); } 2445 2440 | type_specifier identifier_parameter_declarator … … 2468 2463 assertion 2469 2464 | assertion_list assertion 2470 { $$ = $1 ->appendList( $2 ); }2465 { $$ = $1 ? $1->appendList( $2 ) : $2; } 2471 2466 ; 2472 2467 … … 2755 2750 | attr_name 2756 2751 { $$ = DeclarationNode::newAttribute( $1 ); } 2757 | attr_name '(' argument_expression_list _opt')'2752 | attr_name '(' argument_expression_list ')' 2758 2753 { $$ = DeclarationNode::newAttribute( $1, $3 ); } 2759 2754 ; -
src/ResolvExpr/AdjustExprType.cc
reef8dfb rbdfc032 100 100 101 101 namespace { 102 class AdjustExprType_new final : public ast::WithShortCircuiting { 102 struct AdjustExprType_new final : public ast::WithShortCircuiting { 103 const ast::TypeEnvironment & tenv; 103 104 const ast::SymbolTable & symtab; 104 public:105 const ast::TypeEnvironment & tenv;106 105 107 106 AdjustExprType_new( const ast::TypeEnvironment & e, const ast::SymbolTable & syms ) 108 : symtab( syms ), tenv( e) {}107 : tenv( e ), symtab( syms ) {} 109 108 110 void pre visit( const ast::VoidType * ) { visit_children = false; }111 void pre visit( const ast::BasicType * ) { visit_children = false; }112 void pre visit( const ast::PointerType * ) { visit_children = false; }113 void pre visit( const ast::ArrayType * ) { visit_children = false; }114 void pre visit( const ast::FunctionType * ) { visit_children = false; }115 void pre visit( const ast::StructInstType * ) { visit_children = false; }116 void pre visit( const ast::UnionInstType * ) { visit_children = false; }117 void pre visit( const ast::EnumInstType * ) { visit_children = false; }118 void pre visit( const ast::TraitInstType * ) { visit_children = false; }119 void pre visit( const ast::TypeInstType * ) { visit_children = false; }120 void pre visit( const ast::TupleType * ) { visit_children = false; }121 void pre visit( const ast::VarArgsType * ) { visit_children = false; }122 void pre visit( const ast::ZeroType * ) { visit_children = false; }123 void pre visit( const ast::OneType * ) { visit_children = false; }109 void premutate( const ast::VoidType * ) { visit_children = false; } 110 void premutate( const ast::BasicType * ) { visit_children = false; } 111 void premutate( const ast::PointerType * ) { visit_children = false; } 112 void premutate( const ast::ArrayType * ) { visit_children = false; } 113 void premutate( const ast::FunctionType * ) { visit_children = false; } 114 void premutate( const ast::StructInstType * ) { visit_children = false; } 115 void premutate( const ast::UnionInstType * ) { visit_children = false; } 116 void premutate( const ast::EnumInstType * ) { visit_children = false; } 117 void premutate( const ast::TraitInstType * ) { visit_children = false; } 118 void premutate( const ast::TypeInstType * ) { visit_children = false; } 119 void premutate( const ast::TupleType * ) { visit_children = false; } 120 void premutate( const ast::VarArgsType * ) { visit_children = false; } 121 void premutate( const ast::ZeroType * ) { visit_children = false; } 122 void premutate( const ast::OneType * ) { visit_children = false; } 124 123 125 const ast::Type * post visit( const ast::ArrayType * at ) {124 const ast::Type * postmutate( const ast::ArrayType * at ) { 126 125 return new ast::PointerType{ at->base, at->qualifiers }; 127 126 } 128 127 129 const ast::Type * post visit( const ast::FunctionType * ft ) {128 const ast::Type * postmutate( const ast::FunctionType * ft ) { 130 129 return new ast::PointerType{ ft }; 131 130 } 132 131 133 const ast::Type * post visit( const ast::TypeInstType * inst ) {132 const ast::Type * postmutate( const ast::TypeInstType * inst ) { 134 133 // replace known function-type-variables with pointer-to-function 135 if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst) ) {134 if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name ) ) { 136 135 if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) { 137 136 return new ast::PointerType{ inst }; -
src/ResolvExpr/AlternativeFinder.cc
reef8dfb rbdfc032 131 131 132 132 void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt ) { 133 std::vector<std::string> sorted; 134 sorted.reserve(list.size()); 135 for(const auto & c : list) { 136 std::stringstream ss; 137 c.print( ss, indentAmt ); 138 sorted.push_back(ss.str()); 139 } 140 141 std::sort(sorted.begin(), sorted.end()); 142 143 for ( const auto & s : sorted ) { 144 os << s << std::endl; 133 Indenter indent = { indentAmt }; 134 for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) { 135 i->print( os, indent ); 136 os << std::endl; 145 137 } 146 138 } … … 259 251 SemanticError( expr, "No reasonable alternatives for expression " ); 260 252 } 261 if ( mode. prune ) {253 if ( mode.satisfyAssns || mode.prune ) { 262 254 // trim candidates just to those where the assertions resolve 263 255 // - necessary pre-requisite to pruning … … 1224 1216 unify( castExpr->result, alt.expr->result, alt.env, needAssertions, 1225 1217 haveAssertions, openVars, indexer ); 1226 Cost thisCost = 1227 castExpr->isGenerated 1228 ? conversionCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), indexer, alt.env ) 1229 : castCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), indexer, alt.env ); 1218 Cost thisCost = castCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), 1219 indexer, alt.env ); 1230 1220 PRINT( 1231 1221 std::cerr << "working on cast with result: " << castExpr->result << std::endl; … … 1302 1292 1303 1293 try { 1304 // Attempt 1 : turn (thread&)X into ( $thread&)X.__thrd1294 // Attempt 1 : turn (thread&)X into (thread_desc&)X.__thrd 1305 1295 // Clone is purely for memory management 1306 1296 std::unique_ptr<Expression> tech1 { new UntypedMemberExpr(new NameExpr(castExpr->concrete_target.field), castExpr->arg->clone()) }; … … 1313 1303 } catch(SemanticErrorException & ) {} 1314 1304 1315 // Fallback : turn (thread&)X into ( $thread&)get_thread(X)1305 // Fallback : turn (thread&)X into (thread_desc&)get_thread(X) 1316 1306 std::unique_ptr<Expression> fallback { UntypedExpr::createDeref( new UntypedExpr(new NameExpr(castExpr->concrete_target.getter), { castExpr->arg->clone() })) }; 1317 1307 // don't prune here, since it's guaranteed all alternatives will have the same type … … 1708 1698 1709 1699 // unification run for side-effects 1710 bool canUnify = unify( toType, alt.expr->result, newEnv, need, have, openVars, indexer ); 1711 (void) canUnify; 1700 unify( toType, alt.expr->result, newEnv, need, have, openVars, indexer ); 1712 1701 // xxx - do some inspecting on this line... why isn't result bound to initAlt.type? 1713 1702 1714 Cost thisCost = c omputeConversionCost( alt.expr->result, toType, alt.expr->get_lvalue(),1703 Cost thisCost = castCost( alt.expr->result, toType, alt.expr->get_lvalue(), 1715 1704 indexer, newEnv ); 1716 1717 PRINT(1718 Cost legacyCost = castCost( alt.expr->result, toType, alt.expr->get_lvalue(),1719 indexer, newEnv );1720 std::cerr << "Considering initialization:";1721 std::cerr << std::endl << " FROM: "; alt.expr->result->print(std::cerr);1722 std::cerr << std::endl << " TO: "; toType ->print(std::cerr);1723 std::cerr << std::endl << " Unification " << (canUnify ? "succeeded" : "failed");1724 std::cerr << std::endl << " Legacy cost " << legacyCost;1725 std::cerr << std::endl << " New cost " << thisCost;1726 std::cerr << std::endl;1727 )1728 1729 1705 if ( thisCost != Cost::infinity ) { 1730 1706 // count one safe conversion for each value that is thrown away -
src/ResolvExpr/Candidate.cpp
reef8dfb rbdfc032 41 41 42 42 void print( std::ostream & os, const CandidateList & cands, Indenter indent ) { 43 std::vector<std::string> sorted; 44 sorted.reserve(cands.size()); 45 for(const auto & c : cands) { 46 std::stringstream ss; 47 print( ss, *c, indent ); 48 sorted.push_back(ss.str()); 49 } 50 51 std::sort(sorted.begin(), sorted.end()); 52 53 for ( const auto & s : sorted ) { 54 os << s << std::endl; 43 for ( const CandidateRef & cand : cands ) { 44 print( os, *cand, indent ); 45 os << std::endl; 55 46 } 56 47 } -
src/ResolvExpr/Candidate.hpp
reef8dfb rbdfc032 51 51 52 52 Candidate( const ast::Expr * x, const ast::TypeEnvironment & e ) 53 : expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() { 54 assert(x->result); 55 } 53 : expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() {} 56 54 57 55 Candidate( const Candidate & o, const ast::Expr * x, const Cost & addedCost = Cost::zero ) 58 56 : expr( x ), cost( o.cost + addedCost ), cvtCost( Cost::zero ), env( o.env ), open( o.open ), 59 need( o.need ) { 60 assert(x->result); 61 } 57 need( o.need ) {} 62 58 63 59 Candidate( 64 const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 60 const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 65 61 const ast::AssertionSet & n, const Cost & c, const Cost & cvt = Cost::zero ) 66 : expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) { 67 assert(x->result); 68 } 62 : expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) {} 69 63 70 64 Candidate( … … 72 66 ast::AssertionSet && n, const Cost & c, const Cost & cvt = Cost::zero ) 73 67 : expr( x ), cost( c ), cvtCost( cvt ), env( std::move( e ) ), open( std::move( o ) ), 74 need( n.begin(), n.end() ) { 75 assert(x->result); 76 } 68 need( n.begin(), n.end() ) {} 77 69 }; 78 70 -
src/ResolvExpr/CandidateFinder.cpp
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Wed Jun 5 14:30:00 2019 11 // Last Modified By : A ndrew Beach12 // Last Modified On : Tue Oct 1 14:55:00 201913 // Update Count : 211 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Wed Jun 5 14:30:00 2019 13 // Update Count : 1 14 14 // 15 15 … … 43 43 #include "SymTab/Validate.h" // for validateType 44 44 #include "Tuples/Tuples.h" // for handleTupleAssignment 45 #include "InitTweak/InitTweak.h" // for getPointerBase46 47 #include "Common/Stats/Counter.h"48 45 49 46 #define PRINT( text ) if ( resolvep ) { text } … … 57 54 return new ast::CastExpr{ expr, expr->result->stripReferences() }; 58 55 } 59 56 60 57 return expr; 61 58 } … … 64 61 UniqueId globalResnSlot = 0; 65 62 66 Cost computeConversionCost( 67 const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue,68 const ast:: SymbolTable & symtab, const ast::TypeEnvironment & env63 Cost computeConversionCost( 64 const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab, 65 const ast::TypeEnvironment & env 69 66 ) { 70 67 PRINT( … … 77 74 std::cerr << std::endl; 78 75 ) 79 Cost convCost = conversionCost( argType, paramType, argIsLvalue,symtab, env );76 Cost convCost = conversionCost( argType, paramType, symtab, env ); 80 77 PRINT( 81 78 std::cerr << std::endl << "cost is " << convCost << std::endl; … … 110 107 111 108 /// Computes conversion cost for a given expression to a given type 112 const ast::Expr * computeExpressionConversionCost( 113 const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost 109 const ast::Expr * computeExpressionConversionCost( 110 const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost 114 111 ) { 115 Cost convCost = computeConversionCost( 116 arg->result, paramType, arg->get_lvalue(), symtab, env ); 112 Cost convCost = computeConversionCost( arg->result, paramType, symtab, env ); 117 113 outCost += convCost; 118 114 119 // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires 120 // conversion. Ignore poly cost for now, since this requires resolution of the cast to 115 // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires 116 // conversion. Ignore poly cost for now, since this requires resolution of the cast to 121 117 // infer parameters and this does not currently work for the reason stated below 122 118 Cost tmpCost = convCost; … … 127 123 return new ast::CastExpr{ arg, newType }; 128 124 129 // xxx - *should* be able to resolve this cast, but at the moment pointers are not 130 // castable to zero_t, but are implicitly convertible. This is clearly inconsistent, 125 // xxx - *should* be able to resolve this cast, but at the moment pointers are not 126 // castable to zero_t, but are implicitly convertible. This is clearly inconsistent, 131 127 // once this is fixed it should be possible to resolve the cast. 132 // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable, 133 // but it shouldn't be because this makes the conversion from DT* to DT* since 128 // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable, 129 // but it shouldn't be because this makes the conversion from DT* to DT* since 134 130 // commontype(zero_t, DT*) is DT*, rather than nothing 135 131 136 132 // CandidateFinder finder{ symtab, env }; 137 133 // finder.find( arg, ResolvMode::withAdjustment() ); 138 // assertf( finder.candidates.size() > 0, 134 // assertf( finder.candidates.size() > 0, 139 135 // "Somehow castable expression failed to find alternatives." ); 140 // assertf( finder.candidates.size() == 1, 136 // assertf( finder.candidates.size() == 1, 141 137 // "Somehow got multiple alternatives for known cast expression." ); 142 138 // return finder.candidates.front()->expr; … … 147 143 148 144 /// Computes conversion cost for a given candidate 149 Cost computeApplicationConversionCost( 150 CandidateRef cand, const ast::SymbolTable & symtab 145 Cost computeApplicationConversionCost( 146 CandidateRef cand, const ast::SymbolTable & symtab 151 147 ) { 152 148 auto appExpr = cand->expr.strict_as< ast::ApplicationExpr >(); … … 171 167 if ( function->isVarArgs ) { 172 168 convCost.incUnsafe(); 173 PRINT( std::cerr << "end of params with varargs function: inc unsafe: " 169 PRINT( std::cerr << "end of params with varargs function: inc unsafe: " 174 170 << convCost << std::endl; ; ) 175 171 // convert reference-typed expressions into value-typed expressions 176 cand->expr = ast::mutate_field_index( 177 appExpr, &ast::ApplicationExpr::args, i, 172 cand->expr = ast::mutate_field_index( 173 appExpr, &ast::ApplicationExpr::args, i, 178 174 referenceToRvalueConversion( args[i], convCost ) ); 179 175 continue; … … 184 180 // Default arguments should be free - don't include conversion cost. 185 181 // Unwrap them here because they are not relevant to the rest of the system 186 cand->expr = ast::mutate_field_index( 182 cand->expr = ast::mutate_field_index( 187 183 appExpr, &ast::ApplicationExpr::args, i, def->expr ); 188 184 ++param; … … 191 187 192 188 // mark conversion cost and also specialization cost of param type 193 //const ast::Type * paramType = (*param)->get_type();194 cand->expr = ast::mutate_field_index( 195 appExpr, &ast::ApplicationExpr::args, i, 196 computeExpressionConversionCost( 197 args[i], *param, symtab, cand->env, convCost ) );198 convCost.decSpec( specCost( *param) );189 const ast::Type * paramType = (*param)->get_type(); 190 cand->expr = ast::mutate_field_index( 191 appExpr, &ast::ApplicationExpr::args, i, 192 computeExpressionConversionCost( 193 args[i], paramType, symtab, cand->env, convCost ) ); 194 convCost.decSpec( specCost( paramType ) ); 199 195 ++param; // can't be in for-loop update because of the continue 200 196 } … … 202 198 if ( param != params.end() ) return Cost::infinity; 203 199 204 // specialization cost of return types can't be accounted for directly, it disables 200 // specialization cost of return types can't be accounted for directly, it disables 205 201 // otherwise-identical calls, like this example based on auto-newline in the I/O lib: 206 202 // … … 212 208 // mark type variable and specialization cost of forall clause 213 209 convCost.incVar( function->forall.size() ); 214 convCost.decSpec( function->assertions.size() ); 210 for ( const ast::TypeDecl * td : function->forall ) { 211 convCost.decSpec( td->assertions.size() ); 212 } 215 213 216 214 return convCost; 217 215 } 218 216 219 void makeUnifiableVars( 220 const ast:: FunctionType * type, ast::OpenVarSet & unifiableVars,221 ast::AssertionSet & need 217 void makeUnifiableVars( 218 const ast::ParameterizedType * type, ast::OpenVarSet & unifiableVars, 219 ast::AssertionSet & need 222 220 ) { 223 for ( auto &tyvar : type->forall ) {224 unifiableVars[ *tyvar ] = ast::TypeDecl::Data{ tyvar->base};225 }226 for ( auto & assn : type->assertions ) {227 need[ assn ].isUsed = true;221 for ( const ast::TypeDecl * tyvar : type->forall ) { 222 unifiableVars[ tyvar->name ] = ast::TypeDecl::Data{ tyvar }; 223 for ( const ast::DeclWithType * assn : tyvar->assertions ) { 224 need[ assn ].isUsed = true; 225 } 228 226 } 229 227 } … … 256 254 257 255 ArgPack() 258 : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ), 256 : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ), 259 257 tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {} 260 258 259 ArgPack( 260 const ast::TypeEnvironment & env, const ast::AssertionSet & need, 261 const ast::AssertionSet & have, const ast::OpenVarSet & open ) 262 : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ), 263 open( open ), nextArg( 0 ), tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {} 264 261 265 ArgPack( 262 const ast::TypeEnvironment & env, const ast::AssertionSet & need, 263 const ast::AssertionSet & have, const ast::OpenVarSet & open ) 264 : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ), 265 open( open ), nextArg( 0 ), tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {} 266 267 ArgPack( 268 std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env, 269 ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open, 270 unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero, 266 std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env, 267 ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open, 268 unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero, 271 269 unsigned nextExpl = 0, unsigned explAlt = 0 ) 272 270 : parent(parent), expr( expr ), cost( cost ), env( move( env ) ), need( move( need ) ), 273 271 have( move( have ) ), open( move( open ) ), nextArg( nextArg ), tupleStart( tupleStart ), 274 272 nextExpl( nextExpl ), explAlt( explAlt ) {} 275 273 276 274 ArgPack( 277 const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need, 275 const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need, 278 276 ast::AssertionSet && have, ast::OpenVarSet && open, unsigned nextArg, Cost added ) 279 : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ), 280 need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ), 277 : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ), 278 need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ), 281 279 tupleStart( o.tupleStart ), nextExpl( 0 ), explAlt( 0 ) {} 282 280 283 281 /// true if this pack is in the middle of an exploded argument 284 282 bool hasExpl() const { return nextExpl > 0; } … … 288 286 return args[ nextArg-1 ][ explAlt ]; 289 287 } 290 288 291 289 /// Ends a tuple expression, consolidating the appropriate args 292 290 void endTuple( const std::vector< ArgPack > & packs ) { … … 309 307 310 308 /// Instantiates an argument to match a parameter, returns false if no matching results left 311 bool instantiateArgument( 312 const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args, 313 std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab, 314 unsigned nTuples = 0 309 bool instantiateArgument( 310 const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args, 311 std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab, 312 unsigned nTuples = 0 315 313 ) { 316 314 if ( auto tupleType = dynamic_cast< const ast::TupleType * >( paramType ) ) { … … 320 318 // xxx - dropping initializer changes behaviour from previous, but seems correct 321 319 // ^^^ need to handle the case where a tuple has a default argument 322 if ( ! instantiateArgument( 320 if ( ! instantiateArgument( 323 321 type, nullptr, args, results, genStart, symtab, nTuples ) ) return false; 324 322 nTuples = 0; … … 331 329 } else if ( const ast::TypeInstType * ttype = Tuples::isTtype( paramType ) ) { 332 330 // paramType is a ttype, consumes all remaining arguments 333 331 334 332 // completed tuples; will be spliced to end of results to finish 335 333 std::vector< ArgPack > finalResults{}; … … 344 342 for ( std::size_t i = genStart; i < genEnd; ++i ) { 345 343 unsigned nextArg = results[i].nextArg; 346 344 347 345 // use next element of exploded tuple if present 348 346 if ( results[i].hasExpl() ) { … … 354 352 results.emplace_back( 355 353 i, expl.exprs[ results[i].nextExpl ], copy( results[i].env ), 356 copy( results[i].need ), copy( results[i].have ), 354 copy( results[i].need ), copy( results[i].have ), 357 355 copy( results[i].open ), nextArg, nTuples, Cost::zero, nextExpl, 358 356 results[i].explAlt ); … … 372 370 // push empty tuple expression 373 371 newResult.parent = i; 374 newResult.expr = new ast::TupleExpr{ CodeLocation{}, {} }; 372 std::vector< ast::ptr< ast::Expr > > emptyList; 373 newResult.expr = 374 new ast::TupleExpr{ CodeLocation{}, move( emptyList ) }; 375 375 argType = newResult.expr->result; 376 376 } else { … … 400 400 401 401 // check unification for ttype before adding to final 402 if ( 403 unify( 402 if ( 403 unify( 404 404 ttype, argType, newResult.env, newResult.need, newResult.have, 405 newResult.open, symtab ) 405 newResult.open, symtab ) 406 406 ) { 407 407 finalResults.emplace_back( move( newResult ) ); … … 424 424 if ( expl.exprs.empty() ) { 425 425 results.emplace_back( 426 results[i], move( env ), copy( results[i].need ), 426 results[i], move( env ), copy( results[i].need ), 427 427 copy( results[i].have ), move( open ), nextArg + 1, expl.cost ); 428 428 429 429 continue; 430 430 } … … 432 432 // add new result 433 433 results.emplace_back( 434 i, expl.exprs.front(), move( env ), copy( results[i].need ), 435 copy( results[i].have ), move( open ), nextArg + 1, nTuples, 434 i, expl.exprs.front(), move( env ), copy( results[i].need ), 435 copy( results[i].have ), move( open ), nextArg + 1, nTuples, 436 436 expl.cost, expl.exprs.size() == 1 ? 0 : 1, j ); 437 437 } … … 479 479 480 480 results.emplace_back( 481 i, expr, move( env ), move( need ), move( have ), move( open ), nextArg, 481 i, expr, move( env ), move( need ), move( have ), move( open ), nextArg, 482 482 nTuples, Cost::zero, nextExpl, results[i].explAlt ); 483 483 } … … 495 495 if ( unify( paramType, cnst->result, env, need, have, open, symtab ) ) { 496 496 results.emplace_back( 497 i, new ast::DefaultArgExpr{ cnst->location, cnst }, move( env ), 497 i, new ast::DefaultArgExpr{ cnst->location, cnst }, move( env ), 498 498 move( need ), move( have ), move( open ), nextArg, nTuples ); 499 499 } … … 517 517 if ( expl.exprs.empty() ) { 518 518 results.emplace_back( 519 results[i], move( env ), move( need ), move( have ), move( open ), 519 results[i], move( env ), move( need ), move( have ), move( open ), 520 520 nextArg + 1, expl.cost ); 521 521 522 522 continue; 523 523 } … … 539 539 // add new result 540 540 results.emplace_back( 541 i, expr, move( env ), move( need ), move( have ), move( open ), 541 i, expr, move( env ), move( need ), move( have ), move( open ), 542 542 nextArg + 1, nTuples, expl.cost, expl.exprs.size() == 1 ? 0 : 1, j ); 543 543 } … … 548 548 genStart = genEnd; 549 549 550 return genEnd != results.size(); // were any new results added?550 return genEnd != results.size(); 551 551 } 552 552 553 553 /// Generate a cast expression from `arg` to `toType` 554 const ast::Expr * restructureCast( 554 const ast::Expr * restructureCast( 555 555 ast::ptr< ast::Expr > & arg, const ast::Type * toType, ast::GeneratedFlag isGenerated = ast::GeneratedCast 556 556 ) { 557 if ( 558 arg->result->size() > 1 559 && ! toType->isVoid() 560 && ! dynamic_cast< const ast::ReferenceType * >( toType ) 557 if ( 558 arg->result->size() > 1 559 && ! toType->isVoid() 560 && ! dynamic_cast< const ast::ReferenceType * >( toType ) 561 561 ) { 562 // Argument is a tuple and the target type is neither void nor a reference. Cast each 563 // member of the tuple to its corresponding target type, producing the tuple of those 564 // cast expressions. If there are more components of the tuple than components in the 565 // target type, then excess components do not come out in the result expression (but 562 // Argument is a tuple and the target type is neither void nor a reference. Cast each 563 // member of the tuple to its corresponding target type, producing the tuple of those 564 // cast expressions. If there are more components of the tuple than components in the 565 // target type, then excess components do not come out in the result expression (but 566 566 // UniqueExpr ensures that the side effects will still be produced) 567 567 if ( Tuples::maybeImpureIgnoreUnique( arg ) ) { 568 // expressions which may contain side effects require a single unique instance of 568 // expressions which may contain side effects require a single unique instance of 569 569 // the expression 570 570 arg = new ast::UniqueExpr{ arg->location, arg }; … … 574 574 // cast each component 575 575 ast::ptr< ast::Expr > idx = new ast::TupleIndexExpr{ arg->location, arg, i }; 576 components.emplace_back( 576 components.emplace_back( 577 577 restructureCast( idx, toType->getComponent( i ), isGenerated ) ); 578 578 } … … 594 594 595 595 /// Actually visits expressions to find their candidate interpretations 596 class Finder final : public ast::WithShortCircuiting { 596 struct Finder final : public ast::WithShortCircuiting { 597 CandidateFinder & selfFinder; 597 598 const ast::SymbolTable & symtab; 598 public:599 static size_t traceId;600 CandidateFinder & selfFinder;601 599 CandidateList & candidates; 602 600 const ast::TypeEnvironment & tenv; 603 601 ast::ptr< ast::Type > & targetType; 604 602 605 enum Errors {606 NotFound,607 NoMatch,608 ArgsToFew,609 ArgsToMany,610 RetsToFew,611 RetsToMany,612 NoReason613 };614 615 struct {616 Errors code = NotFound;617 } reason;618 619 603 Finder( CandidateFinder & f ) 620 : s ymtab( f.localSyms ), selfFinder( f ), candidates( f.candidates ), tenv( f.env ),604 : selfFinder( f ), symtab( f.symtab ), candidates( f.candidates ), tenv( f.env ), 621 605 targetType( f.targetType ) {} 622 606 623 607 void previsit( const ast::Node * ) { visit_children = false; } 624 608 … … 627 611 void addCandidate( Args &&... args ) { 628 612 candidates.emplace_back( new Candidate{ std::forward<Args>( args )... } ); 629 reason.code = NoReason;630 613 } 631 614 … … 656 639 657 640 /// Completes a function candidate with arguments located 658 void validateFunctionCandidate( 659 const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results, 660 CandidateList & out 641 void validateFunctionCandidate( 642 const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results, 643 CandidateList & out 661 644 ) { 662 ast::ApplicationExpr * appExpr = 645 ast::ApplicationExpr * appExpr = 663 646 new ast::ApplicationExpr{ func->expr->location, func->expr }; 664 647 // sum cost and accumulate arguments … … 674 657 appExpr->args = move( vargs ); 675 658 // build and validate new candidate 676 auto newCand = 659 auto newCand = 677 660 std::make_shared<Candidate>( appExpr, result.env, result.open, result.need, cost ); 678 661 PRINT( … … 686 669 /// Builds a list of candidates for a function, storing them in out 687 670 void makeFunctionCandidates( 688 const CandidateRef & func, const ast::FunctionType * funcType, 671 const CandidateRef & func, const ast::FunctionType * funcType, 689 672 const ExplodedArgs_new & args, CandidateList & out 690 673 ) { … … 693 676 ast::TypeEnvironment funcEnv{ func->env }; 694 677 makeUnifiableVars( funcType, funcOpen, funcNeed ); 695 // add all type variables as open variables now so that those not used in the 696 // parameterlist are still considered open678 // add all type variables as open variables now so that those not used in the parameter 679 // list are still considered open 697 680 funcEnv.add( funcType->forall ); 698 681 699 682 if ( targetType && ! targetType->isVoid() && ! funcType->returns.empty() ) { 700 683 // attempt to narrow based on expected target type 701 const ast::Type * returnType = funcType->returns.front() ;702 if ( ! unify( 703 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 684 const ast::Type * returnType = funcType->returns.front()->get_type(); 685 if ( ! unify( 686 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 704 687 ) { 705 688 // unification failed, do not pursue this candidate … … 713 696 std::size_t genStart = 0; 714 697 715 // xxx - how to handle default arg after change to ftype representation? 716 if (const ast::VariableExpr * varExpr = func->expr.as<ast::VariableExpr>()) { 717 if (const ast::FunctionDecl * funcDecl = varExpr->var.as<ast::FunctionDecl>()) { 718 // function may have default args only if directly calling by name 719 // must use types on candidate however, due to RenameVars substitution 720 auto nParams = funcType->params.size(); 721 722 for (size_t i=0; i<nParams; ++i) { 723 auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>(); 724 if (!instantiateArgument( 725 funcType->params[i], obj->init, args, results, genStart, symtab)) return; 726 } 727 goto endMatch; 728 } 729 } 730 for ( const auto & param : funcType->params ) { 731 // Try adding the arguments corresponding to the current parameter to the existing 698 for ( const ast::DeclWithType * param : funcType->params ) { 699 auto obj = strict_dynamic_cast< const ast::ObjectDecl * >( param ); 700 // Try adding the arguments corresponding to the current parameter to the existing 732 701 // matches 733 // no default args for indirect calls 734 if ( ! instantiateArgument( 735 param, nullptr, args, results, genStart, symtab ) ) return; 736 } 737 738 endMatch: 702 if ( ! instantiateArgument( 703 obj->type, obj->init, args, results, genStart, symtab ) ) return; 704 } 705 739 706 if ( funcType->isVarArgs ) { 740 707 // append any unused arguments to vararg pack … … 783 750 if ( expl.exprs.empty() ) { 784 751 results.emplace_back( 785 results[i], move( env ), copy( results[i].need ), 786 copy( results[i].have ), move( open ), nextArg + 1, 752 results[i], move( env ), copy( results[i].need ), 753 copy( results[i].have ), move( open ), nextArg + 1, 787 754 expl.cost ); 788 755 … … 793 760 results.emplace_back( 794 761 i, expl.exprs.front(), move( env ), copy( results[i].need ), 795 copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost, 762 copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost, 796 763 expl.exprs.size() == 1 ? 0 : 1, j ); 797 764 } … … 813 780 /// Adds implicit struct-conversions to the alternative list 814 781 void addAnonConversions( const CandidateRef & cand ) { 815 // adds anonymous member interpretations whenever an aggregate value type is seen. 816 // it's okay for the aggregate expression to have reference type -- cast it to the 782 // adds anonymous member interpretations whenever an aggregate value type is seen. 783 // it's okay for the aggregate expression to have reference type -- cast it to the 817 784 // base type to treat the aggregate as the referenced value 818 785 ast::ptr< ast::Expr > aggrExpr( cand->expr ); 819 786 ast::ptr< ast::Type > & aggrType = aggrExpr.get_and_mutate()->result; 820 787 cand->env.apply( aggrType ); 821 788 822 789 if ( aggrType.as< ast::ReferenceType >() ) { 823 790 aggrExpr = new ast::CastExpr{ aggrExpr, aggrType->stripReferences() }; … … 832 799 833 800 /// Adds aggregate member interpretations 834 void addAggMembers( 835 const ast:: BaseInstType * aggrInst, const ast::Expr * expr,836 const Candidate & cand, const Cost & addedCost, const std::string & name 801 void addAggMembers( 802 const ast::ReferenceToType * aggrInst, const ast::Expr * expr, 803 const Candidate & cand, const Cost & addedCost, const std::string & name 837 804 ) { 838 805 for ( const ast::Decl * decl : aggrInst->lookup( name ) ) { 839 806 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl ); 840 CandidateRef newCand = std::make_shared<Candidate>( 807 CandidateRef newCand = std::make_shared<Candidate>( 841 808 cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost ); 842 // add anonymous member interpretations whenever an aggregate value type is seen 809 // add anonymous member interpretations whenever an aggregate value type is seen 843 810 // as a member expression 844 811 addAnonConversions( newCand ); … … 848 815 849 816 /// Adds tuple member interpretations 850 void addTupleMembers( 851 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 852 const Cost & addedCost, const ast::Expr * member 817 void addTupleMembers( 818 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 819 const Cost & addedCost, const ast::Expr * member 853 820 ) { 854 821 if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) { 855 // get the value of the constant expression as an int, must be between 0 and the 822 // get the value of the constant expression as an int, must be between 0 and the 856 823 // length of the tuple to have meaning 857 824 long long val = constantExpr->intValue(); 858 825 if ( val >= 0 && (unsigned long long)val < tupleType->size() ) { 859 826 addCandidate( 860 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 827 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 861 828 addedCost ); 862 829 } … … 865 832 866 833 void postvisit( const ast::UntypedExpr * untypedExpr ) { 867 std::vector< CandidateFinder > argCandidates = 834 CandidateFinder funcFinder{ symtab, tenv }; 835 funcFinder.find( untypedExpr->func, ResolvMode::withAdjustment() ); 836 // short-circuit if no candidates 837 if ( funcFinder.candidates.empty() ) return; 838 839 std::vector< CandidateFinder > argCandidates = 868 840 selfFinder.findSubExprs( untypedExpr->args ); 869 841 870 842 // take care of possible tuple assignments 871 843 // if not tuple assignment, handled as normal function call 872 844 Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates ); 873 874 CandidateFinder funcFinder{ symtab, tenv };875 if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) {876 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);877 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {878 assertf(!argCandidates.empty(), "special function call without argument");879 for (auto & firstArgCand: argCandidates[0]) {880 ast::ptr<ast::Type> argType = firstArgCand->expr->result;881 firstArgCand->env.apply(argType);882 // strip references883 // xxx - is this correct?884 while (argType.as<ast::ReferenceType>()) argType = argType.as<ast::ReferenceType>()->base;885 886 // convert 1-tuple to plain type887 if (auto tuple = argType.as<ast::TupleType>()) {888 if (tuple->size() == 1) {889 argType = tuple->types[0];890 }891 }892 893 // if argType is an unbound type parameter, all special functions need to be searched.894 if (isUnboundType(argType)) {895 funcFinder.otypeKeys.clear();896 break;897 }898 899 if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);900 else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type));901 }902 }903 }904 // if candidates are already produced, do not fail905 // xxx - is it possible that handleTupleAssignment and main finder both produce candidates?906 // this means there exists ctor/assign functions with a tuple as first parameter.907 ResolvMode mode = {908 true, // adjust909 !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name910 selfFinder.candidates.empty() // failfast if other options are not found911 };912 funcFinder.find( untypedExpr->func, mode );913 // short-circuit if no candidates914 // if ( funcFinder.candidates.empty() ) return;915 916 reason.code = NoMatch;917 845 918 846 // find function operators … … 949 877 if ( auto function = pointer->base.as< ast::FunctionType >() ) { 950 878 CandidateRef newFunc{ new Candidate{ *func } }; 951 newFunc->expr = 879 newFunc->expr = 952 880 referenceToRvalueConversion( newFunc->expr, newFunc->cost ); 953 881 makeFunctionCandidates( newFunc, function, argExpansions, found ); 954 882 } 955 } else if ( 956 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 883 } else if ( 884 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 957 885 ) { 958 if ( const ast::EqvClass * clz = func->env.lookup( *inst) ) {886 if ( const ast::EqvClass * clz = func->env.lookup( inst->name ) ) { 959 887 if ( auto function = clz->bound.as< ast::FunctionType >() ) { 960 888 CandidateRef newFunc{ new Candidate{ *func } }; 961 newFunc->expr = 889 newFunc->expr = 962 890 referenceToRvalueConversion( newFunc->expr, newFunc->cost ); 963 891 makeFunctionCandidates( newFunc, function, argExpansions, found ); … … 973 901 std::vector< ExplodedArg > funcE; 974 902 funcE.reserve( funcFinder.candidates.size() ); 975 for ( const CandidateRef & func : funcFinder ) { 903 for ( const CandidateRef & func : funcFinder ) { 976 904 funcE.emplace_back( *func, symtab ); 977 905 } … … 985 913 if ( auto function = pointer->base.as< ast::FunctionType >() ) { 986 914 CandidateRef newOp{ new Candidate{ *op} }; 987 newOp->expr = 915 newOp->expr = 988 916 referenceToRvalueConversion( newOp->expr, newOp->cost ); 989 917 makeFunctionCandidates( newOp, function, argExpansions, found ); … … 994 922 } 995 923 996 // Implement SFINAE; resolution errors are only errors if there aren't any non-error 924 // Implement SFINAE; resolution errors are only errors if there aren't any non-error 997 925 // candidates 998 926 if ( found.empty() && ! errors.isEmpty() ) { throw errors; } … … 1006 934 auto pointer = appExpr->func->result.strict_as< ast::PointerType >(); 1007 935 auto function = pointer->base.strict_as< ast::FunctionType >(); 1008 936 1009 937 std::cerr << "Case +++++++++++++ " << appExpr->func << std::endl; 1010 938 std::cerr << "parameters are:" << std::endl; … … 1029 957 promoteCvtCost( winners ); 1030 958 1031 // function may return a struct/union value, in which case we need to add candidates 1032 // for implicit conversions to each of the anonymous members, which must happen after 959 // function may return a struct/union value, in which case we need to add candidates 960 // for implicit conversions to each of the anonymous members, which must happen after 1033 961 // `findMinCost`, since anon conversions are never the cheapest 1034 962 for ( const CandidateRef & c : winners ) { … … 1038 966 1039 967 if ( candidates.empty() && targetType && ! targetType->isVoid() ) { 1040 // If resolution is unsuccessful with a target type, try again without, since it 968 // If resolution is unsuccessful with a target type, try again without, since it 1041 969 // will sometimes succeed when it wouldn't with a target type binding. 1042 970 // For example: … … 1055 983 /// true if expression is an lvalue 1056 984 static bool isLvalue( const ast::Expr * x ) { 1057 return x->result && ( x-> get_lvalue() || x->result.as< ast::ReferenceType >() );985 return x->result && ( x->result->is_lvalue() || x->result.as< ast::ReferenceType >() ); 1058 986 } 1059 987 … … 1061 989 CandidateFinder finder{ symtab, tenv }; 1062 990 finder.find( addressExpr->arg ); 1063 1064 if( finder.candidates.empty() ) return;1065 1066 reason.code = NoMatch;1067 1068 991 for ( CandidateRef & r : finder.candidates ) { 1069 992 if ( ! isLvalue( r->expr ) ) continue; … … 1080 1003 assert( toType ); 1081 1004 toType = resolveTypeof( toType, symtab ); 1082 //toType = SymTab::validateType( castExpr->location, toType, symtab );1005 toType = SymTab::validateType( castExpr->location, toType, symtab ); 1083 1006 toType = adjustExprType( toType, tenv, symtab ); 1084 1007 1085 1008 CandidateFinder finder{ symtab, tenv, toType }; 1086 1009 finder.find( castExpr->arg, ResolvMode::withAdjustment() ); 1087 1088 if( !finder.candidates.empty() ) reason.code = NoMatch;1089 1010 1090 1011 CandidateList matches; … … 1095 1016 cand->env.extractOpenVars( open ); 1096 1017 1097 // It is possible that a cast can throw away some values in a multiply-valued 1098 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the 1099 // subexpression results that are cast directly. The candidate is invalid if it 1018 // It is possible that a cast can throw away some values in a multiply-valued 1019 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the 1020 // subexpression results that are cast directly. The candidate is invalid if it 1100 1021 // has fewer results than there are types to cast to. 1101 1022 int discardedValues = cand->expr->result->size() - toType->size(); … … 1104 1025 // unification run for side-effects 1105 1026 unify( toType, cand->expr->result, cand->env, need, have, open, symtab ); 1106 Cost thisCost = 1107 (castExpr->isGenerated == ast::GeneratedFlag::GeneratedCast) 1108 ? conversionCost( cand->expr->result, toType, cand->expr->get_lvalue(), symtab, cand->env ) 1109 : castCost( cand->expr->result, toType, cand->expr->get_lvalue(), symtab, cand->env ); 1110 1027 Cost thisCost = castCost( cand->expr->result, toType, symtab, cand->env ); 1111 1028 PRINT( 1112 1029 std::cerr << "working on cast with result: " << toType << std::endl; … … 1120 1037 // count one safe conversion for each value that is thrown away 1121 1038 thisCost.incSafe( discardedValues ); 1122 CandidateRef newCand = std::make_shared<Candidate>( 1123 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1124 copy( cand->env ), move( open ), move( need ), cand->cost, 1039 CandidateRef newCand = std::make_shared<Candidate>( 1040 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1041 copy( cand->env ), move( open ), move( need ), cand->cost, 1125 1042 cand->cost + thisCost ); 1126 1043 inferParameters( newCand, matches ); … … 1140 1057 finder.find( castExpr->arg, ResolvMode::withoutPrune() ); 1141 1058 for ( CandidateRef & r : finder.candidates ) { 1142 addCandidate( 1143 *r, 1059 addCandidate( 1060 *r, 1144 1061 new ast::VirtualCastExpr{ castExpr->location, r->expr, castExpr->result } ); 1145 1062 } 1146 }1147 1148 void postvisit( const ast::KeywordCastExpr * castExpr ) {1149 const auto & loc = castExpr->location;1150 assertf( castExpr->result, "Cast target should have been set in Validate." );1151 auto ref = castExpr->result.strict_as<ast::ReferenceType>();1152 auto inst = ref->base.strict_as<ast::StructInstType>();1153 auto target = inst->base.get();1154 1155 CandidateFinder finder{ symtab, tenv };1156 1157 auto pick_alternatives = [target, this](CandidateList & found, bool expect_ref) {1158 for(auto & cand : found) {1159 const ast::Type * expr = cand->expr->result.get();1160 if(expect_ref) {1161 auto res = dynamic_cast<const ast::ReferenceType*>(expr);1162 if(!res) { continue; }1163 expr = res->base.get();1164 }1165 1166 if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) {1167 auto td = cand->env.lookup(*insttype);1168 if(!td) { continue; }1169 expr = td->bound.get();1170 }1171 1172 if(auto base = dynamic_cast<const ast::StructInstType*>(expr)) {1173 if(base->base == target) {1174 candidates.push_back( std::move(cand) );1175 reason.code = NoReason;1176 }1177 }1178 }1179 };1180 1181 try {1182 // Attempt 1 : turn (thread&)X into ($thread&)X.__thrd1183 // Clone is purely for memory management1184 std::unique_ptr<const ast::Expr> tech1 { new ast::UntypedMemberExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.field), castExpr->arg) };1185 1186 // don't prune here, since it's guaranteed all alternatives will have the same type1187 finder.find( tech1.get(), ResolvMode::withoutPrune() );1188 pick_alternatives(finder.candidates, false);1189 1190 return;1191 } catch(SemanticErrorException & ) {}1192 1193 // Fallback : turn (thread&)X into ($thread&)get_thread(X)1194 std::unique_ptr<const ast::Expr> fallback { ast::UntypedExpr::createDeref(loc, new ast::UntypedExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.getter), { castExpr->arg })) };1195 // don't prune here, since it's guaranteed all alternatives will have the same type1196 finder.find( fallback.get(), ResolvMode::withoutPrune() );1197 1198 pick_alternatives(finder.candidates, true);1199 1200 // Whatever happens here, we have no more fallbacks1201 1063 } 1202 1064 … … 1205 1067 aggFinder.find( memberExpr->aggregate, ResolvMode::withAdjustment() ); 1206 1068 for ( CandidateRef & agg : aggFinder.candidates ) { 1207 // it's okay for the aggregate expression to have reference type -- cast it to the 1069 // it's okay for the aggregate expression to have reference type -- cast it to the 1208 1070 // base type to treat the aggregate as the referenced value 1209 1071 Cost addedCost = Cost::zero; … … 1212 1074 // find member of the given type 1213 1075 if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) { 1214 addAggMembers( 1076 addAggMembers( 1215 1077 structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) ); 1216 1078 } else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) { 1217 addAggMembers( 1079 addAggMembers( 1218 1080 unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) ); 1219 1081 } else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) { … … 1228 1090 1229 1091 void postvisit( const ast::NameExpr * nameExpr ) { 1230 std::vector< ast::SymbolTable::IdData > declList; 1231 if (!selfFinder.otypeKeys.empty()) { 1232 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name); 1233 assertf(kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS, "special lookup with non-special target: %s", nameExpr->name.c_str()); 1234 1235 for (auto & otypeKey: selfFinder.otypeKeys) { 1236 auto result = symtab.specialLookupId(kind, otypeKey); 1237 declList.insert(declList.end(), std::make_move_iterator(result.begin()), std::make_move_iterator(result.end())); 1238 } 1239 } 1240 else { 1241 declList = symtab.lookupId( nameExpr->name ); 1242 } 1092 std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( nameExpr->name ); 1243 1093 PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; ) 1244 1245 if( declList.empty() ) return;1246 1247 reason.code = NoMatch;1248 1249 1094 for ( auto & data : declList ) { 1250 1095 Cost cost = Cost::zero; … … 1252 1097 1253 1098 CandidateRef newCand = std::make_shared<Candidate>( 1254 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 1099 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 1255 1100 cost ); 1256 1101 PRINT( … … 1262 1107 std::cerr << std::endl; 1263 1108 ) 1264 newCand->expr = ast::mutate_field( 1265 newCand->expr.get(), &ast::Expr::result, 1109 newCand->expr = ast::mutate_field( 1110 newCand->expr.get(), &ast::Expr::result, 1266 1111 renameTyVars( newCand->expr->result ) ); 1267 // add anonymous member interpretations whenever an aggregate value type is seen 1112 // add anonymous member interpretations whenever an aggregate value type is seen 1268 1113 // as a name expression 1269 1114 addAnonConversions( newCand ); … … 1275 1120 // not sufficient to just pass `variableExpr` here, type might have changed since 1276 1121 // creation 1277 addCandidate( 1122 addCandidate( 1278 1123 new ast::VariableExpr{ variableExpr->location, variableExpr->var }, tenv ); 1279 1124 } … … 1285 1130 void postvisit( const ast::SizeofExpr * sizeofExpr ) { 1286 1131 if ( sizeofExpr->type ) { 1287 addCandidate( 1288 new ast::SizeofExpr{ 1289 sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 1132 addCandidate( 1133 new ast::SizeofExpr{ 1134 sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 1290 1135 tenv ); 1291 1136 } else { … … 1296 1141 CandidateList winners = findMinCost( finder.candidates ); 1297 1142 if ( winners.size() != 1 ) { 1298 SemanticError( 1143 SemanticError( 1299 1144 sizeofExpr->expr.get(), "Ambiguous expression in sizeof operand: " ); 1300 1145 } … … 1309 1154 void postvisit( const ast::AlignofExpr * alignofExpr ) { 1310 1155 if ( alignofExpr->type ) { 1311 addCandidate( 1312 new ast::AlignofExpr{ 1313 alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 1156 addCandidate( 1157 new ast::AlignofExpr{ 1158 alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 1314 1159 tenv ); 1315 1160 } else { … … 1320 1165 CandidateList winners = findMinCost( finder.candidates ); 1321 1166 if ( winners.size() != 1 ) { 1322 SemanticError( 1167 SemanticError( 1323 1168 alignofExpr->expr.get(), "Ambiguous expression in alignof operand: " ); 1324 1169 } … … 1327 1172 choice->expr = referenceToRvalueConversion( choice->expr, choice->cost ); 1328 1173 choice->cost = Cost::zero; 1329 addCandidate( 1174 addCandidate( 1330 1175 *choice, new ast::AlignofExpr{ alignofExpr->location, choice->expr } ); 1331 1176 } … … 1333 1178 1334 1179 void postvisit( const ast::UntypedOffsetofExpr * offsetofExpr ) { 1335 const ast:: BaseInstType * aggInst;1180 const ast::ReferenceToType * aggInst; 1336 1181 if (( aggInst = offsetofExpr->type.as< ast::StructInstType >() )) ; 1337 1182 else if (( aggInst = offsetofExpr->type.as< ast::UnionInstType >() )) ; … … 1340 1185 for ( const ast::Decl * member : aggInst->lookup( offsetofExpr->member ) ) { 1341 1186 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( member ); 1342 addCandidate( 1187 addCandidate( 1343 1188 new ast::OffsetofExpr{ offsetofExpr->location, aggInst, dwt }, tenv ); 1344 1189 } … … 1361 1206 finder2.find( logicalExpr->arg2, ResolvMode::withAdjustment() ); 1362 1207 if ( finder2.candidates.empty() ) return; 1363 1364 reason.code = NoMatch;1365 1208 1366 1209 for ( const CandidateRef & r1 : finder1.candidates ) { … … 1375 1218 1376 1219 addCandidate( 1377 new ast::LogicalExpr{ 1220 new ast::LogicalExpr{ 1378 1221 logicalExpr->location, r1->expr, r2->expr, logicalExpr->isAnd }, 1379 1222 move( env ), move( open ), move( need ), r1->cost + r2->cost ); … … 1397 1240 finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() ); 1398 1241 if ( finder3.candidates.empty() ) return; 1399 1400 reason.code = NoMatch;1401 1242 1402 1243 for ( const CandidateRef & r1 : finder1.candidates ) { … … 1415 1256 ast::AssertionSet have; 1416 1257 1417 // unify true and false results, then infer parameters to produce new 1258 // unify true and false results, then infer parameters to produce new 1418 1259 // candidates 1419 1260 ast::ptr< ast::Type > common; 1420 if ( 1421 unify( 1422 r2->expr->result, r3->expr->result, env, need, have, open, symtab, 1423 common ) 1261 if ( 1262 unify( 1263 r2->expr->result, r3->expr->result, env, need, have, open, symtab, 1264 common ) 1424 1265 ) { 1425 1266 // generate typed expression 1426 ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 1267 ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 1427 1268 conditionalExpr->location, r1->expr, r2->expr, r3->expr }; 1428 1269 newExpr->result = common ? common : r2->expr->result; 1429 1270 // convert both options to result type 1430 1271 Cost cost = r1->cost + r2->cost + r3->cost; 1431 newExpr->arg2 = computeExpressionConversionCost( 1272 newExpr->arg2 = computeExpressionConversionCost( 1432 1273 newExpr->arg2, newExpr->result, symtab, env, cost ); 1433 1274 newExpr->arg3 = computeExpressionConversionCost( … … 1446 1287 ast::TypeEnvironment env{ tenv }; 1447 1288 ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, symtab, env ); 1448 1289 1449 1290 CandidateFinder finder2{ symtab, env }; 1450 1291 finder2.find( commaExpr->arg2, ResolvMode::withAdjustment() ); … … 1476 1317 finder2.find( rangeExpr->high, ResolvMode::withAdjustment() ); 1477 1318 if ( finder2.candidates.empty() ) return; 1478 1479 reason.code = NoMatch;1480 1319 1481 1320 for ( const CandidateRef & r1 : finder1.candidates ) { … … 1491 1330 1492 1331 ast::ptr< ast::Type > common; 1493 if ( 1494 unify( 1495 r1->expr->result, r2->expr->result, env, need, have, open, symtab, 1496 common ) 1332 if ( 1333 unify( 1334 r1->expr->result, r2->expr->result, env, need, have, open, symtab, 1335 common ) 1497 1336 ) { 1498 1337 // generate new expression 1499 ast::RangeExpr * newExpr = 1338 ast::RangeExpr * newExpr = 1500 1339 new ast::RangeExpr{ rangeExpr->location, r1->expr, r2->expr }; 1501 1340 newExpr->result = common ? common : r1->expr->result; 1502 1341 // add candidate 1503 1342 CandidateRef newCand = std::make_shared<Candidate>( 1504 newExpr, move( env ), move( open ), move( need ), 1343 newExpr, move( env ), move( open ), move( need ), 1505 1344 r1->cost + r2->cost ); 1506 1345 inferParameters( newCand, candidates ); … … 1511 1350 1512 1351 void postvisit( const ast::UntypedTupleExpr * tupleExpr ) { 1513 std::vector< CandidateFinder > subCandidates = 1352 std::vector< CandidateFinder > subCandidates = 1514 1353 selfFinder.findSubExprs( tupleExpr->exprs ); 1515 1354 std::vector< CandidateList > possibilities; … … 1531 1370 1532 1371 addCandidate( 1533 new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 1372 new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 1534 1373 move( env ), move( open ), move( need ), sumCost( subs ) ); 1535 1374 } … … 1571 1410 // calculate target type 1572 1411 const ast::Type * toType = resolveTypeof( initAlt.type, symtab ); 1573 //toType = SymTab::validateType( initExpr->location, toType, symtab );1412 toType = SymTab::validateType( initExpr->location, toType, symtab ); 1574 1413 toType = adjustExprType( toType, tenv, symtab ); 1575 // The call to find must occur inside this loop, otherwise polymorphic return 1576 // types are not bound to the initialization type, since return type variables are 1577 // only open for the duration of resolving the UntypedExpr. 1414 // The call to find must occur inside this loop, otherwise polymorphic return 1415 // types are not bound to the initialization type, since return type variables are 1416 // only open for the duration of resolving the UntypedExpr. 1578 1417 CandidateFinder finder{ symtab, tenv, toType }; 1579 1418 finder.find( initExpr->expr, ResolvMode::withAdjustment() ); 1580 1419 for ( CandidateRef & cand : finder.candidates ) { 1581 if(reason.code == NotFound) reason.code = NoMatch;1582 1583 1420 ast::TypeEnvironment env{ cand->env }; 1584 1421 ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have; … … 1589 1426 ) 1590 1427 1591 // It is possible that a cast can throw away some values in a multiply-valued 1592 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of 1593 // the subexpression results that are cast directly. The candidate is invalid 1428 // It is possible that a cast can throw away some values in a multiply-valued 1429 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of 1430 // the subexpression results that are cast directly. The candidate is invalid 1594 1431 // if it has fewer results than there are types to cast to. 1595 1432 int discardedValues = cand->expr->result->size() - toType->size(); … … 1597 1434 1598 1435 // unification run for side-effects 1599 bool canUnify = unify( toType, cand->expr->result, env, need, have, open, symtab ); 1600 (void) canUnify; 1601 Cost thisCost = computeConversionCost( cand->expr->result, toType, cand->expr->get_lvalue(), 1602 symtab, env ); 1603 PRINT( 1604 Cost legacyCost = castCost( cand->expr->result, toType, cand->expr->get_lvalue(), 1605 symtab, env ); 1606 std::cerr << "Considering initialization:"; 1607 std::cerr << std::endl << " FROM: " << cand->expr->result << std::endl; 1608 std::cerr << std::endl << " TO: " << toType << std::endl; 1609 std::cerr << std::endl << " Unification " << (canUnify ? "succeeded" : "failed"); 1610 std::cerr << std::endl << " Legacy cost " << legacyCost; 1611 std::cerr << std::endl << " New cost " << thisCost; 1612 std::cerr << std::endl; 1613 ) 1436 unify( toType, cand->expr->result, env, need, have, open, symtab ); 1437 Cost thisCost = castCost( cand->expr->result, toType, symtab, env ); 1438 1614 1439 if ( thisCost != Cost::infinity ) { 1615 1440 // count one safe conversion for each value that is thrown away 1616 1441 thisCost.incSafe( discardedValues ); 1617 CandidateRef newCand = std::make_shared<Candidate>( 1618 new ast::InitExpr{ 1619 initExpr->location, restructureCast( cand->expr, toType ), 1620 initAlt.designation }, 1621 move(env), move( open ), move( need ), cand->cost, thisCost );1442 CandidateRef newCand = std::make_shared<Candidate>( 1443 new ast::InitExpr{ 1444 initExpr->location, restructureCast( cand->expr, toType ), 1445 initAlt.designation }, 1446 copy( cand->env ), move( open ), move( need ), cand->cost, thisCost ); 1622 1447 inferParameters( newCand, matches ); 1623 1448 } 1624 1449 } 1625 1626 1450 } 1627 1451 … … 1645 1469 }; 1646 1470 1647 // size_t Finder::traceId = Stats::Heap::new_stacktrace_id("Finder"); 1648 /// Prunes a list of candidates down to those that have the minimum conversion cost for a given 1471 /// Prunes a list of candidates down to those that have the minimum conversion cost for a given 1649 1472 /// return type. Skips ambiguous candidates. 1650 1651 } // anonymous namespace 1652 1653 bool CandidateFinder::pruneCandidates( CandidateList & candidates, CandidateList & out, std::vector<std::string> & errors ) { 1654 struct PruneStruct { 1655 CandidateRef candidate; 1656 bool ambiguous; 1657 1658 PruneStruct() = default; 1659 PruneStruct( const CandidateRef & c ) : candidate( c ), ambiguous( false ) {} 1660 }; 1661 1662 // find lowest-cost candidate for each type 1663 std::unordered_map< std::string, PruneStruct > selected; 1664 // attempt to skip satisfyAssertions on more expensive alternatives if better options have been found 1665 std::sort(candidates.begin(), candidates.end(), [](const CandidateRef & x, const CandidateRef & y){return x->cost < y->cost;}); 1666 for ( CandidateRef & candidate : candidates ) { 1667 std::string mangleName; 1668 { 1669 ast::ptr< ast::Type > newType = candidate->expr->result; 1670 assertf(candidate->expr->result, "Result of expression %p for candidate is null", candidate->expr.get()); 1671 candidate->env.apply( newType ); 1672 mangleName = Mangle::mangle( newType ); 1673 } 1674 1675 auto found = selected.find( mangleName ); 1676 if (found != selected.end() && found->second.candidate->cost < candidate->cost) { 1677 PRINT( 1678 std::cerr << "cost " << candidate->cost << " loses to " 1679 << found->second.candidate->cost << std::endl; 1680 ) 1681 continue; 1682 } 1683 1684 // xxx - when do satisfyAssertions produce more than 1 result? 1685 // this should only happen when initial result type contains 1686 // unbound type parameters, then it should never be pruned by 1687 // the previous step, since renameTyVars guarantees the mangled name 1688 // is unique. 1689 CandidateList satisfied; 1690 bool needRecomputeKey = false; 1691 if (candidate->need.empty()) { 1692 satisfied.emplace_back(candidate); 1693 } 1694 else { 1695 satisfyAssertions(candidate, localSyms, satisfied, errors); 1696 needRecomputeKey = true; 1697 } 1698 1699 for (auto & newCand : satisfied) { 1700 // recomputes type key, if satisfyAssertions changed it 1701 if (needRecomputeKey) 1473 CandidateList pruneCandidates( CandidateList & candidates ) { 1474 struct PruneStruct { 1475 CandidateRef candidate; 1476 bool ambiguous; 1477 1478 PruneStruct() = default; 1479 PruneStruct( const CandidateRef & c ) : candidate( c ), ambiguous( false ) {} 1480 }; 1481 1482 // find lowest-cost candidate for each type 1483 std::unordered_map< std::string, PruneStruct > selected; 1484 for ( CandidateRef & candidate : candidates ) { 1485 std::string mangleName; 1702 1486 { 1703 ast::ptr< ast::Type > newType = newCand->expr->result; 1704 assertf(newCand->expr->result, "Result of expression %p for candidate is null", newCand->expr.get()); 1705 newCand->env.apply( newType ); 1487 ast::ptr< ast::Type > newType = candidate->expr->result; 1488 candidate->env.apply( newType ); 1706 1489 mangleName = Mangle::mangle( newType ); 1707 1490 } 1491 1708 1492 auto found = selected.find( mangleName ); 1709 1493 if ( found != selected.end() ) { 1710 if ( newCand->cost < found->second.candidate->cost ) {1494 if ( candidate->cost < found->second.candidate->cost ) { 1711 1495 PRINT( 1712 std::cerr << "cost " << newCand->cost << " beats "1496 std::cerr << "cost " << candidate->cost << " beats " 1713 1497 << found->second.candidate->cost << std::endl; 1714 1498 ) 1715 1499 1716 found->second = PruneStruct{ newCand};1717 } else if ( newCand->cost == found->second.candidate->cost ) {1718 // if one of the candidates contains a deleted identifier, can pick the other, 1719 // since deleted expressions should not be ambiguous if there is another option 1500 found->second = PruneStruct{ candidate }; 1501 } else if ( candidate->cost == found->second.candidate->cost ) { 1502 // if one of the candidates contains a deleted identifier, can pick the other, 1503 // since deleted expressions should not be ambiguous if there is another option 1720 1504 // that is at least as good 1721 if ( findDeletedExpr( newCand->expr ) ) {1505 if ( findDeletedExpr( candidate->expr ) ) { 1722 1506 // do nothing 1723 1507 PRINT( std::cerr << "candidate is deleted" << std::endl; ) 1724 1508 } else if ( findDeletedExpr( found->second.candidate->expr ) ) { 1725 1509 PRINT( std::cerr << "current is deleted" << std::endl; ) 1726 found->second = PruneStruct{ newCand};1510 found->second = PruneStruct{ candidate }; 1727 1511 } else { 1728 1512 PRINT( std::cerr << "marking ambiguous" << std::endl; ) 1729 1513 found->second.ambiguous = true; 1730 1514 } 1731 } else { 1732 // xxx - can satisfyAssertions increase the cost? 1515 } else { 1733 1516 PRINT( 1734 std::cerr << "cost " << newCand->cost << " loses to "1517 std::cerr << "cost " << candidate->cost << " loses to " 1735 1518 << found->second.candidate->cost << std::endl; 1736 ) 1519 ) 1737 1520 } 1738 1521 } else { 1739 selected.emplace_hint( found, mangleName, newCand ); 1740 } 1741 } 1522 selected.emplace_hint( found, mangleName, candidate ); 1523 } 1524 } 1525 1526 // report unambiguous min-cost candidates 1527 CandidateList out; 1528 for ( auto & target : selected ) { 1529 if ( target.second.ambiguous ) continue; 1530 1531 CandidateRef cand = target.second.candidate; 1532 1533 ast::ptr< ast::Type > newResult = cand->expr->result; 1534 cand->env.applyFree( newResult ); 1535 cand->expr = ast::mutate_field( 1536 cand->expr.get(), &ast::Expr::result, move( newResult ) ); 1537 1538 out.emplace_back( cand ); 1539 } 1540 return out; 1742 1541 } 1743 1542 1744 // report unambiguous min-cost candidates 1745 // CandidateList out; 1746 for ( auto & target : selected ) { 1747 if ( target.second.ambiguous ) continue; 1748 1749 CandidateRef cand = target.second.candidate; 1750 1751 ast::ptr< ast::Type > newResult = cand->expr->result; 1752 cand->env.applyFree( newResult ); 1753 cand->expr = ast::mutate_field( 1754 cand->expr.get(), &ast::Expr::result, move( newResult ) ); 1755 1756 out.emplace_back( cand ); 1757 } 1758 // if everything is lost in satisfyAssertions, report the error 1759 return !selected.empty(); 1760 } 1543 } // anonymous namespace 1761 1544 1762 1545 void CandidateFinder::find( const ast::Expr * expr, ResolvMode mode ) { … … 1766 1549 1767 1550 if ( mode.failFast && candidates.empty() ) { 1768 switch(finder.core.reason.code) { 1769 case Finder::NotFound: 1770 { SemanticError( expr, "No alternatives for expression " ); break; } 1771 case Finder::NoMatch: 1772 { SemanticError( expr, "Invalid application of existing declaration(s) in expression " ); break; } 1773 case Finder::ArgsToFew: 1774 case Finder::ArgsToMany: 1775 case Finder::RetsToFew: 1776 case Finder::RetsToMany: 1777 case Finder::NoReason: 1778 default: 1779 { SemanticError( expr->location, "No reasonable alternatives for expression : reasons unkown" ); } 1780 } 1551 SemanticError( expr, "No reasonable alternatives for expression " ); 1781 1552 } 1782 1553 1783 /*1784 1554 if ( mode.satisfyAssns || mode.prune ) { 1785 1555 // trim candidates to just those where the assertions are satisfiable … … 1788 1558 std::vector< std::string > errors; 1789 1559 for ( CandidateRef & candidate : candidates ) { 1790 satisfyAssertions( candidate, localSyms, satisfied, errors );1560 satisfyAssertions( candidate, symtab, satisfied, errors ); 1791 1561 } 1792 1562 … … 1804 1574 candidates = move( satisfied ); 1805 1575 } 1806 */1807 1576 1808 1577 if ( mode.prune ) { … … 1813 1582 ) 1814 1583 1815 CandidateList pruned; 1816 std::vector<std::string> errors; 1817 bool found = pruneCandidates( candidates, pruned, errors ); 1818 1584 CandidateList pruned = pruneCandidates( candidates ); 1585 1819 1586 if ( mode.failFast && pruned.empty() ) { 1820 1587 std::ostringstream stream; 1821 if (found) { 1822 CandidateList winners = findMinCost( candidates ); 1823 stream << "Cannot choose between " << winners.size() << " alternatives for " 1824 "expression\n"; 1825 ast::print( stream, expr ); 1826 stream << " Alternatives are:\n"; 1827 print( stream, winners, 1 ); 1828 SemanticError( expr->location, stream.str() ); 1829 } 1830 else { 1831 stream << "No alternatives with satisfiable assertions for " << expr << "\n"; 1832 for ( const auto& err : errors ) { 1833 stream << err; 1834 } 1835 SemanticError( expr->location, stream.str() ); 1836 } 1588 CandidateList winners = findMinCost( candidates ); 1589 stream << "Cannot choose between " << winners.size() << " alternatives for " 1590 "expression\n"; 1591 ast::print( stream, expr ); 1592 stream << " Alternatives are:\n"; 1593 print( stream, winners, 1 ); 1594 SemanticError( expr->location, stream.str() ); 1837 1595 } 1838 1596 … … 1844 1602 ) 1845 1603 PRINT( 1846 std::cerr << "there are " << candidates.size() << " alternatives after elimination" 1604 std::cerr << "there are " << candidates.size() << " alternatives after elimination" 1847 1605 << std::endl; 1848 1606 ) 1849 1607 } 1850 1608 1851 // adjust types after pruning so that types substituted by pruneAlternatives are correctly 1609 // adjust types after pruning so that types substituted by pruneAlternatives are correctly 1852 1610 // adjusted 1853 1611 if ( mode.adjust ) { 1854 1612 for ( CandidateRef & r : candidates ) { 1855 r->expr = ast::mutate_field( 1856 r->expr.get(), &ast::Expr::result, 1857 adjustExprType( r->expr->result, r->env, localSyms) );1613 r->expr = ast::mutate_field( 1614 r->expr.get(), &ast::Expr::result, 1615 adjustExprType( r->expr->result, r->env, symtab ) ); 1858 1616 } 1859 1617 } … … 1867 1625 } 1868 1626 1869 std::vector< CandidateFinder > CandidateFinder::findSubExprs( 1870 const std::vector< ast::ptr< ast::Expr > > & xs 1627 std::vector< CandidateFinder > CandidateFinder::findSubExprs( 1628 const std::vector< ast::ptr< ast::Expr > > & xs 1871 1629 ) { 1872 1630 std::vector< CandidateFinder > out; 1873 1631 1874 1632 for ( const auto & x : xs ) { 1875 out.emplace_back( localSyms, env );1633 out.emplace_back( symtab, env ); 1876 1634 out.back().find( x, ResolvMode::withAdjustment() ); 1877 1635 1878 1636 PRINT( 1879 1637 std::cerr << "findSubExprs" << std::endl; -
src/ResolvExpr/CandidateFinder.hpp
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Wed Jun 5 14:30:00 2019 11 // Last Modified By : A ndrew Beach12 // Last Modified On : Tue Oct 1 9:51:00 201913 // Update Count : 211 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Wed Jun 5 14:30:00 2019 13 // Update Count : 1 14 14 // 15 15 … … 28 28 struct CandidateFinder { 29 29 CandidateList candidates; ///< List of candidate resolutions 30 const ast::SymbolTable & localSyms; ///< Symbol table to lookup candidates30 const ast::SymbolTable & symtab; ///< Symbol table to lookup candidates 31 31 const ast::TypeEnvironment & env; ///< Substitutions performed in this resolution 32 32 ast::ptr< ast::Type > targetType; ///< Target type for resolution 33 std::set< std::string > otypeKeys; /// different type may map to same key34 33 35 CandidateFinder( 36 const ast::SymbolTable & sym s, const ast::TypeEnvironment & env,34 CandidateFinder( 35 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, 37 36 const ast::Type * tt = nullptr ) 38 : candidates(), localSyms( syms), env( env ), targetType( tt ) {}37 : candidates(), symtab( symtab ), env( env ), targetType( tt ) {} 39 38 40 39 /// Fill candidates with feasible resolutions for `expr` 41 40 void find( const ast::Expr * expr, ResolvMode mode = {} ); 42 bool pruneCandidates( CandidateList & candidates, CandidateList & out, std::vector<std::string> & errors );43 41 44 42 /// Runs new candidate finder on each element in xs, returning the list of finders … … 51 49 iterator begin() { return candidates.begin(); } 52 50 const_iterator begin() const { return candidates.begin(); } 53 51 54 52 iterator end() { return candidates.end(); } 55 53 const_iterator end() const { return candidates.end(); } … … 57 55 58 56 /// Computes conversion cost between two types 59 Cost computeConversionCost( 60 const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue,61 const ast:: SymbolTable & symtab, const ast::TypeEnvironment & env );57 Cost computeConversionCost( 58 const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab, 59 const ast::TypeEnvironment & env ); 62 60 63 61 } // namespace ResolvExpr -
src/ResolvExpr/CastCost.cc
reef8dfb rbdfc032 10 10 // Created On : Sun May 17 06:57:43 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T ue Oct 4 15:00:00 201913 // Update Count : 912 // Last Modified On : Thu Aug 8 16:12:00 2019 13 // Update Count : 8 14 14 // 15 15 … … 142 142 143 143 CastCost_new( 144 const ast::Type * dst, bool srcIsLvalue,const ast::SymbolTable & symtab,144 const ast::Type * dst, const ast::SymbolTable & symtab, 145 145 const ast::TypeEnvironment & env, CostCalculation costFunc ) 146 : ConversionCost_new( dst, s rcIsLvalue, symtab, env, costFunc ) {}146 : ConversionCost_new( dst, symtab, env, costFunc ) {} 147 147 148 148 void postvisit( const ast::BasicType * basicType ) { … … 152 152 cost = Cost::unsafe; 153 153 } else { 154 cost = conversionCost( basicType, dst, s rcIsLvalue, symtab, env );154 cost = conversionCost( basicType, dst, symtab, env ); 155 155 } 156 156 } … … 165 165 } else { 166 166 ast::TypeEnvironment newEnv{ env }; 167 if ( auto wParams = pointerType->base.as< ast:: FunctionType >() ) {167 if ( auto wParams = pointerType->base.as< ast::ParameterizedType >() ) { 168 168 newEnv.add( wParams->forall ); 169 169 } … … 183 183 } 184 184 }; 185 186 #warning For overload resolution between the two versions.187 int localPtrsCastable(const ast::Type * t1, const ast::Type * t2,188 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) {189 return ptrsCastable( t1, t2, symtab, env );190 }191 Cost localCastCost(192 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,193 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env194 ) { return castCost( src, dst, srcIsLvalue, symtab, env ); }195 185 } // anonymous namespace 196 186 197 198 199 187 Cost castCost( 200 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,201 const ast:: SymbolTable & symtab, const ast::TypeEnvironment & env188 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab, 189 const ast::TypeEnvironment & env 202 190 ) { 203 191 if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 204 if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst) ) {192 if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name ) ) { 205 193 // check cast cost against bound type, if present 206 194 if ( eqvClass->bound ) { 207 return castCost( src, eqvClass->bound, s rcIsLvalue, symtab, env );195 return castCost( src, eqvClass->bound, symtab, env ); 208 196 } else { 209 197 return Cost::infinity; … … 213 201 auto type = strict_dynamic_cast< const ast::TypeDecl * >( named ); 214 202 if ( type->base ) { 215 return castCost( src, type->base, s rcIsLvalue, symtab, env ) + Cost::safe;203 return castCost( src, type->base, symtab, env ) + Cost::safe; 216 204 } 217 205 } … … 236 224 #warning cast on ptrsCastable artifact of having two functions, remove when port done 237 225 return convertToReferenceCost( 238 src, refType, srcIsLvalue, symtab, env, localPtrsCastable ); 226 src, refType, symtab, env, 227 ( int (*)( 228 const ast::Type *, const ast::Type *, const ast::SymbolTable &, 229 const ast::TypeEnvironment & ) 230 ) ptrsCastable ); 239 231 } else { 240 232 #warning cast on castCost artifact of having two functions, remove when port done 241 ast::Pass< CastCost_new > converter( 242 dst, srcIsLvalue, symtab, env, localCastCost ); 233 ast::Pass< CastCost_new > converter{ 234 dst, symtab, env, 235 ( Cost (*)( 236 const ast::Type *, const ast::Type *, const ast::SymbolTable &, 237 const ast::TypeEnvironment & ) 238 ) castCost }; 243 239 src->accept( converter ); 244 return converter. core.cost;240 return converter.pass.cost; 245 241 } 246 242 } -
src/ResolvExpr/CommonType.cc
reef8dfb rbdfc032 666 666 const ast::OpenVarSet & open; 667 667 public: 668 static size_t traceId;669 668 ast::ptr< ast::Type > result; 670 669 … … 713 712 const ast::Type * base = oPtr->base; 714 713 if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) { 715 auto entry = open.find( *var);714 auto entry = open.find( var->name ); 716 715 if ( entry != open.end() ) { 717 716 ast::AssertionSet need, have; … … 894 893 }; 895 894 896 // size_t CommonType_new::traceId = Stats::Heap::new_stacktrace_id("CommonType_new");897 895 namespace { 898 896 ast::ptr< ast::Type > handleReference( … … 941 939 ast::ptr< ast::Type > result; 942 940 const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >(); 943 const ast::ReferenceType * ref2 = type 2.as< ast::ReferenceType >();941 const ast::ReferenceType * ref2 = type1.as< ast::ReferenceType >(); 944 942 945 943 if ( depth1 > depth2 ) { … … 968 966 ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open }; 969 967 type1->accept( visitor ); 970 ast::ptr< ast::Type > result = visitor. core.result;968 ast::ptr< ast::Type > result = visitor.pass.result; 971 969 972 970 // handling for opaque type declarations (?) -
src/ResolvExpr/ConversionCost.cc
reef8dfb rbdfc032 10 10 // Created On : Sun May 17 07:06:19 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jul 29 16:11:00 202013 // Update Count : 2 812 // Last Modified On : Mon Aug 12 10:21:00 2019 13 // Update Count : 27 14 14 // 15 15 … … 392 392 void ConversionCost::postvisit( const FunctionType * ) {} 393 393 394 void ConversionCost::postvisit( const StructInstType * inst ) { 395 if ( const StructInstType * destAsInst = dynamic_cast< const StructInstType * >( dest ) ) { 396 if ( inst->name == destAsInst->name ) { 397 cost = Cost::zero; 398 } // if 399 } // if 400 } 401 402 void ConversionCost::postvisit( const UnionInstType * inst ) { 403 if ( const UnionInstType * destAsInst = dynamic_cast< const UnionInstType * >( dest ) ) { 404 if ( inst->name == destAsInst->name ) { 405 cost = Cost::zero; 406 } // if 407 } // if 408 } 409 394 410 void ConversionCost::postvisit( const EnumInstType * ) { 395 411 static Type::Qualifiers q; … … 481 497 } 482 498 483 namespace { 484 # warning For overload resolution between the two versions.485 int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,486 const ast::SymbolTable &, const ast::TypeEnvironment & env ) { 487 return ptrsAssignable( t1, t2, env ); 488 } 489 Cost localConversionCost( 490 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 491 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env492 ) { return conversionCost( src, dst, srcIsLvalue, symtab, env ); }493 }499 static int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2, 500 const ast::SymbolTable &, const ast::TypeEnvironment & env ) { 501 return ptrsAssignable( t1, t2, env ); 502 } 503 504 // TODO: This is used for overload resolution. It might be able to be dropped once the old system 505 // is removed. 506 static Cost localConversionCost( 507 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab, 508 const ast::TypeEnvironment & env 509 ) { return conversionCost( src, dst, symtab, env ); } 494 510 495 511 Cost conversionCost( 496 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,497 const ast:: SymbolTable & symtab, const ast::TypeEnvironment & env512 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab, 513 const ast::TypeEnvironment & env 498 514 ) { 499 515 if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 500 if ( const ast::EqvClass * eqv = env.lookup( *inst) ) {516 if ( const ast::EqvClass * eqv = env.lookup( inst->name ) ) { 501 517 if ( eqv->bound ) { 502 return conversionCost(src, eqv->bound, s rcIsLvalue, symtab, env );518 return conversionCost(src, eqv->bound, symtab, env ); 503 519 } else { 504 520 return Cost::infinity; … … 508 524 assertf( type, "Unexpected typedef." ); 509 525 if ( type->base ) { 510 return conversionCost( src, type->base, s rcIsLvalue, symtab, env ) + Cost::safe;526 return conversionCost( src, type->base, symtab, env ) + Cost::safe; 511 527 } 512 528 } … … 518 534 } else if ( const ast::ReferenceType * refType = 519 535 dynamic_cast< const ast::ReferenceType * >( dst ) ) { 520 return convertToReferenceCost( src, refType, s rcIsLvalue, symtab, env, localPtrsAssignable );536 return convertToReferenceCost( src, refType, symtab, env, localPtrsAssignable ); 521 537 } else { 522 return ast::Pass<ConversionCost_new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost ); 523 } 524 } 525 526 static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 538 ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost ); 539 src->accept( converter ); 540 return converter.pass.cost; 541 } 542 } 543 544 static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, 527 545 int diff, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, 528 PtrsCalculation func ) {546 NumCostCalculation func ) { 529 547 if ( 0 < diff ) { 530 548 Cost cost = convertToReferenceCost( 531 strict_dynamic_cast< const ast::ReferenceType * >( src )->base, dst,532 srcIsLvalue, (diff - 1), symtab, env, func );549 strict_dynamic_cast< const ast::ReferenceType * >( src )->base, 550 dst, (diff - 1), symtab, env, func ); 533 551 cost.incReference(); 534 552 return cost; … … 536 554 Cost cost = convertToReferenceCost( 537 555 src, strict_dynamic_cast< const ast::ReferenceType * >( dst )->base, 538 srcIsLvalue,(diff + 1), symtab, env, func );556 (diff + 1), symtab, env, func ); 539 557 cost.incReference(); 540 558 return cost; … … 561 579 } 562 580 } else { 563 return ast::Pass<ConversionCost_new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost ); 581 ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost ); 582 src->accept( converter ); 583 return converter.pass.cost; 564 584 } 565 585 } else { … … 568 588 assert( dstAsRef ); 569 589 if ( typesCompatibleIgnoreQualifiers( src, dstAsRef->base, symtab, env ) ) { 570 if ( src IsLvalue) {590 if ( src->is_lvalue() ) { 571 591 if ( src->qualifiers == dstAsRef->base->qualifiers ) { 572 592 return Cost::reference; … … 587 607 588 608 Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dst, 589 bool srcIsLvalue,const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,590 PtrsCalculation func ) {609 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, 610 NumCostCalculation func ) { 591 611 int sdepth = src->referenceDepth(), ddepth = dst->referenceDepth(); 592 return convertToReferenceCost( src, dst, s rcIsLvalue, sdepth - ddepth, symtab, env, func );612 return convertToReferenceCost( src, dst, sdepth - ddepth, symtab, env, func ); 593 613 } 594 614 … … 647 667 assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) ); 648 668 649 cost = costCalc( refType->base, dst, s rcIsLvalue, symtab, env );669 cost = costCalc( refType->base, dst, symtab, env ); 650 670 if ( refType->base->qualifiers == dst->qualifiers ) { 651 671 cost.incReference(); … … 661 681 } 662 682 683 void ConversionCost_new::postvisit( const ast::StructInstType * structInstType ) { 684 if ( const ast::StructInstType * dstAsInst = 685 dynamic_cast< const ast::StructInstType * >( dst ) ) { 686 if ( structInstType->name == dstAsInst->name ) { 687 cost = Cost::zero; 688 } 689 } 690 } 691 692 void ConversionCost_new::postvisit( const ast::UnionInstType * unionInstType ) { 693 if ( const ast::UnionInstType * dstAsInst = 694 dynamic_cast< const ast::UnionInstType * >( dst ) ) { 695 if ( unionInstType->name == dstAsInst->name ) { 696 cost = Cost::zero; 697 } 698 } 699 } 700 663 701 void ConversionCost_new::postvisit( const ast::EnumInstType * enumInstType ) { 664 702 (void)enumInstType; 665 static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicType::SignedInt ) };666 cost = costCalc( integer, dst, srcIsLvalue, symtab, env );703 static const ast::BasicType integer( ast::BasicType::SignedInt ); 704 cost = costCalc( &integer, dst, symtab, env ); 667 705 if ( cost < Cost::unsafe ) { 668 706 cost.incSafe(); … … 675 713 676 714 void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) { 677 if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) {678 cost = costCalc( eqv->bound, dst, s rcIsLvalue, symtab, env );715 if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) { 716 cost = costCalc( eqv->bound, dst, symtab, env ); 679 717 } else if ( const ast::TypeInstType * dstAsInst = 680 718 dynamic_cast< const ast::TypeInstType * >( dst ) ) { 681 if ( *typeInstType == *dstAsInst) {719 if ( typeInstType->name == dstAsInst->name ) { 682 720 cost = Cost::zero; 683 721 } … … 686 724 assertf( type, "Unexpected typedef."); 687 725 if ( type->base ) { 688 cost = costCalc( type->base, dst, s rcIsLvalue, symtab, env ) + Cost::safe;726 cost = costCalc( type->base, dst, symtab, env ) + Cost::safe; 689 727 } 690 728 } … … 699 737 auto dstEnd = dstAsTuple->types.end(); 700 738 while ( srcIt != srcEnd && dstIt != dstEnd ) { 701 Cost newCost = costCalc( * srcIt++, * dstIt++, s rcIsLvalue, symtab, env );739 Cost newCost = costCalc( * srcIt++, * dstIt++, symtab, env ); 702 740 if ( newCost == Cost::infinity ) { 703 741 return; … … 734 772 cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] ); 735 773 } 736 } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {737 cost = Cost::zero;738 // +1 for zero_t ->, +1 for disambiguation739 cost.incSafe( maxIntCost + 2 );740 774 } 741 775 } … … 755 789 cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] ); 756 790 } 757 } 758 } 759 // size_t ConversionCost_new::traceId = Stats::Heap::new_stacktrace_id("ConversionCost"); 791 } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) { 792 cost = Cost::zero; 793 cost.incSafe( maxIntCost + 2 ); 794 } 795 } 796 760 797 761 798 } // namespace ResolvExpr -
src/ResolvExpr/ConversionCost.h
reef8dfb rbdfc032 10 10 // Created On : Sun May 17 09:37:28 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jul 29 16:12:00 202013 // Update Count : 712 // Last Modified On : Thu Aug 8 16:13:00 2019 13 // Update Count : 6 14 14 // 15 15 … … 51 51 void postvisit( const ReferenceType * refType ); 52 52 void postvisit( const FunctionType * functionType ); 53 void postvisit( const StructInstType * aggregateUseType ); 54 void postvisit( const UnionInstType * aggregateUseType ); 53 55 void postvisit( const EnumInstType * aggregateUseType ); 54 56 void postvisit( const TraitInstType * aggregateUseType ); … … 72 74 73 75 // Some function pointer types, differ in return type. 74 using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, bool,76 using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, 75 77 const ast::SymbolTable &, const ast::TypeEnvironment &)>; 76 using PtrsCalculation = std::function<int(const ast::Type *, const ast::Type *,78 using NumCostCalculation = std::function<int(const ast::Type *, const ast::Type *, 77 79 const ast::SymbolTable &, const ast::TypeEnvironment &)>; 78 80 … … 81 83 protected: 82 84 const ast::Type * dst; 83 bool srcIsLvalue;84 85 const ast::SymbolTable & symtab; 85 86 const ast::TypeEnvironment & env; 86 87 CostCalculation costCalc; 87 88 public: 88 static size_t traceId;89 89 Cost cost; 90 Cost result() { return cost; }91 90 92 ConversionCost_new( const ast::Type * dst, bool srcIsLvalue,const ast::SymbolTable & symtab,91 ConversionCost_new( const ast::Type * dst, const ast::SymbolTable & symtab, 93 92 const ast::TypeEnvironment & env, CostCalculation costCalc ) : 94 dst( dst ), srcIsLvalue( srcIsLvalue ), symtab( symtab ), env( env ), 95 costCalc( costCalc ), cost( Cost::infinity ) 93 dst( dst ), symtab( symtab ), env( env ), costCalc( costCalc ), cost( Cost::infinity ) 96 94 {} 97 95 … … 104 102 void postvisit( const ast::ReferenceType * refType ); 105 103 void postvisit( const ast::FunctionType * functionType ); 104 void postvisit( const ast::StructInstType * structInstType ); 105 void postvisit( const ast::UnionInstType * unionInstType ); 106 106 void postvisit( const ast::EnumInstType * enumInstType ); 107 107 void postvisit( const ast::TraitInstType * traitInstType ); … … 114 114 115 115 Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dest, 116 bool srcIsLvalue, const ast::SymbolTable & indexer, const ast::TypeEnvironment & env, 117 PtrsCalculation func ); 116 const ast::SymbolTable & indexer, const ast::TypeEnvironment & env, NumCostCalculation func ); 118 117 119 118 } // namespace ResolvExpr -
src/ResolvExpr/CurrentObject.cc
reef8dfb rbdfc032 21 21 #include <string> // for string, operator<<, allocator 22 22 23 #include "AST/Copy.hpp" // for shallowCopy24 23 #include "AST/Expr.hpp" // for InitAlternative 25 24 #include "AST/GenericSubstitution.hpp" // for genericSubstitution 26 25 #include "AST/Init.hpp" // for Designation 27 26 #include "AST/Node.hpp" // for readonly 28 #include "AST/Print.hpp" // for readonly29 27 #include "AST/Type.hpp" 30 28 #include "Common/Indenter.h" // for Indenter, operator<< … … 594 592 class SimpleIterator final : public MemberIterator { 595 593 CodeLocation location; 596 const Type *type = nullptr;594 readonly< Type > type = nullptr; 597 595 public: 598 596 SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {} 599 597 600 void setPosition( 601 std::deque< ptr< Expr > >::const_iterator begin, 598 void setPosition( 599 std::deque< ptr< Expr > >::const_iterator begin, 602 600 std::deque< ptr< Expr > >::const_iterator end 603 601 ) override { … … 630 628 class ArrayIterator final : public MemberIterator { 631 629 CodeLocation location; 632 const ArrayType *array = nullptr;633 const Type *base = nullptr;630 readonly< ArrayType > array = nullptr; 631 readonly< Type > base = nullptr; 634 632 size_t index = 0; 635 633 size_t size = 0; … … 639 637 auto res = eval(expr); 640 638 if ( ! res.second ) { 641 SemanticError( location, 639 SemanticError( location, 642 640 toString("Array designator must be a constant expression: ", expr ) ); 643 641 } … … 646 644 647 645 public: 648 ArrayIterator( const CodeLocation & loc, const ArrayType * at ) 646 ArrayIterator( const CodeLocation & loc, const ArrayType * at ) 649 647 : location( loc ), array( at ), base( at->base ) { 650 648 PRINT( std::cerr << "Creating array iterator: " << at << std::endl; ) … … 657 655 658 656 void setPosition( const Expr * expr ) { 659 // need to permit integer-constant-expressions, including: integer constants, 660 // enumeration constants, character constants, sizeof expressions, alignof expressions, 657 // need to permit integer-constant-expressions, including: integer constants, 658 // enumeration constants, character constants, sizeof expressions, alignof expressions, 661 659 // cast expressions 662 660 if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) { … … 664 662 index = constExpr->intValue(); 665 663 } catch ( SemanticErrorException & ) { 666 SemanticError( expr, 664 SemanticError( expr, 667 665 "Constant expression of non-integral type in array designator: " ); 668 666 } 669 667 } else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) { 670 668 setPosition( castExpr->arg ); 671 } else if ( 672 dynamic_cast< const SizeofExpr * >( expr ) 673 || dynamic_cast< const AlignofExpr * >( expr ) 669 } else if ( 670 dynamic_cast< const SizeofExpr * >( expr ) 671 || dynamic_cast< const AlignofExpr * >( expr ) 674 672 ) { 675 673 index = 0; 676 674 } else { 677 assertf( false, 675 assertf( false, 678 676 "bad designator given to ArrayIterator: %s", toString( expr ).c_str() ); 679 677 } 680 678 } 681 679 682 void setPosition( 683 std::deque< ptr< Expr > >::const_iterator begin, 680 void setPosition( 681 std::deque< ptr< Expr > >::const_iterator begin, 684 682 std::deque< ptr< Expr > >::const_iterator end 685 683 ) override { … … 760 758 } 761 759 762 AggregateIterator( 763 const CodeLocation & loc, const std::string k, const std::string & n, const Type * i, 760 AggregateIterator( 761 const CodeLocation & loc, const std::string k, const std::string & n, const Type * i, 764 762 const MemberList & ms ) 765 : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ), 763 : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ), 766 764 sub( genericSubstitution( i ) ) { 767 765 PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; ) … … 770 768 771 769 public: 772 void setPosition( 773 std::deque< ptr< Expr > >::const_iterator begin, 770 void setPosition( 771 std::deque< ptr< Expr > >::const_iterator begin, 774 772 std::deque< ptr< Expr > >::const_iterator end 775 773 ) final { … … 788 786 return; 789 787 } 790 assertf( false, 788 assertf( false, 791 789 "could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() ); 792 790 } else { 793 assertf( false, 791 assertf( false, 794 792 "bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() ); 795 793 } … … 805 803 new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } ); 806 804 // need to substitute for generic types so that casts are to concrete types 807 alt.type = shallowCopy(alt.type.get());808 805 PRINT( std::cerr << " type is: " << alt.type; ) 809 806 sub.apply( alt.type ); // also apply to designation?? … … 845 842 for ( InitAlternative & alt : ret ) { 846 843 PRINT( std::cerr << "iterating and adding designators" << std::endl; ) 847 alt.designation.get_and_mutate()->designators.emplace_front( 844 alt.designation.get_and_mutate()->designators.emplace_front( 848 845 new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } ); 849 846 } … … 900 897 class TupleIterator final : public AggregateIterator { 901 898 public: 902 TupleIterator( const CodeLocation & loc, const TupleType * inst ) 903 : AggregateIterator( 904 loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members 899 TupleIterator( const CodeLocation & loc, const TupleType * inst ) 900 : AggregateIterator( 901 loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members 905 902 ) {} 906 903 … … 923 920 924 921 MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type ) { 925 if ( auto aggr = dynamic_cast< const BaseInstType * >( type ) ) {922 if ( auto aggr = dynamic_cast< const ReferenceToType * >( type ) ) { 926 923 if ( auto sit = dynamic_cast< const StructInstType * >( aggr ) ) { 927 924 return new StructIterator{ loc, sit }; … … 929 926 return new UnionIterator{ loc, uit }; 930 927 } else { 931 assertf( 932 dynamic_cast< const EnumInstType * >( type )933 || dynamic_cast< const TypeInstType * >( type ),934 "Encountered unhandled BaseInstType in createMemberIterator: %s",928 assertf( 929 dynamic_cast< const EnumInstType * >( aggr ) 930 || dynamic_cast< const TypeInstType * >( aggr ), 931 "Encountered unhandled ReferenceToType in createMemberIterator: %s", 935 932 toString( type ).c_str() ); 936 933 return new SimpleIterator{ loc, type }; … … 952 949 using DesignatorChain = std::deque< ptr< Expr > >; 953 950 PRINT( std::cerr << "___findNext" << std::endl; ) 954 951 955 952 // find all the d's 956 953 std::vector< DesignatorChain > desigAlts{ {} }, newDesigAlts; … … 965 962 DesignatorChain & d = *dit; 966 963 PRINT( std::cerr << "____actual: " << t << std::endl; ) 967 if ( auto refType = dynamic_cast< const BaseInstType * >( t ) ) {964 if ( auto refType = dynamic_cast< const ReferenceToType * >( t ) ) { 968 965 // concatenate identical field names 969 966 for ( const Decl * mem : refType->lookup( nexpr->name ) ) { … … 1016 1013 // set new designators 1017 1014 assertf( ! objStack.empty(), "empty object stack when setting designation" ); 1018 Designation * actualDesignation = 1015 Designation * actualDesignation = 1019 1016 new Designation{ designation->location, DesignatorChain{d} }; 1020 1017 objStack.back()->setPosition( d ); // destroys d -
src/ResolvExpr/FindOpenVars.cc
reef8dfb rbdfc032 112 112 // mark open/closed variables 113 113 if ( nextIsOpen ) { 114 for ( auto &decl : type->forall ) {115 open[ *decl ] = ast::TypeDecl::Data{ decl->base};116 }117 for ( auto & assert : type->assertions ) {118 need[ assert ].isUsed = false;114 for ( const ast::TypeDecl * decl : type->forall ) { 115 open[ decl->name ] = ast::TypeDecl::Data{ decl }; 116 for ( const ast::DeclWithType * assert : decl->assertions ) { 117 need[ assert ].isUsed = false; 118 } 119 119 } 120 120 } else { 121 for ( auto &decl : type->forall ) {122 closed[ *decl ] = ast::TypeDecl::Data{ decl->base };123 }124 for ( auto & assert : type->assertions ) {125 have[ assert ].isUsed = false;121 for ( const ast::TypeDecl * decl : type->forall ) { 122 closed[ decl->name ] = ast::TypeDecl::Data{ decl }; 123 for ( const ast::DeclWithType * assert : decl->assertions ) { 124 have[ assert ].isUsed = false; 125 } 126 126 } 127 127 } -
src/ResolvExpr/PolyCost.cc
reef8dfb rbdfc032 58 58 59 59 // TODO: When the old PolyCost is torn out get rid of the _new suffix. 60 class PolyCost_new { 60 struct PolyCost_new { 61 int result; 61 62 const ast::SymbolTable &symtab; 62 public:63 int result;64 63 const ast::TypeEnvironment &env_; 65 64 66 PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) 67 : symtab( symtab ), result( 0), env_( env ) {}65 PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) : 66 result( 0 ), symtab( symtab ), env_( env ) {} 68 67 69 68 void previsit( const ast::TypeInstType * type ) { 70 if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) {69 if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ if ( eqv->bound ) { 71 70 if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) { 72 71 if ( symtab.lookupType( otherType->name ) ) { … … 87 86 ast::Pass<PolyCost_new> costing( symtab, env ); 88 87 type->accept( costing ); 89 return costing. core.result;88 return costing.pass.result; 90 89 } 91 90 -
src/ResolvExpr/PtrsAssignable.cc
reef8dfb rbdfc032 134 134 } 135 135 void postvisit( const ast::TypeInstType * inst ) { 136 if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst) ) {136 if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name ) ) { 137 137 if ( eqv->bound ) { 138 138 // T * = S * for any S depends on the type bound to T … … 146 146 const ast::TypeEnvironment & env ) { 147 147 if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 148 if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst) ) {148 if ( const ast::EqvClass * eqv = env.lookup( dstAsInst->name ) ) { 149 149 return ptrsAssignable( src, eqv->bound, env ); 150 150 } … … 155 155 ast::Pass<PtrsAssignable_new> visitor( dst, env ); 156 156 src->accept( visitor ); 157 return visitor. core.result;157 return visitor.pass.result; 158 158 } 159 159 -
src/ResolvExpr/PtrsCastable.cc
reef8dfb rbdfc032 180 180 } 181 181 } 182 } else if ( const ast::EqvClass * eqvClass = env.lookup( *inst) ) {182 } else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) { 183 183 if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) { 184 184 return -1; … … 283 283 ) { 284 284 if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 285 if ( const ast::EqvClass * eqvClass = env.lookup( *inst) ) {285 if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) { 286 286 return ptrsAssignable( src, eqvClass->bound, env ); 287 287 } … … 293 293 ast::Pass< PtrsCastable_new > ptrs{ dst, env, symtab }; 294 294 src->accept( ptrs ); 295 return ptrs. core.result;295 return ptrs.pass.result; 296 296 } 297 297 } -
src/ResolvExpr/RenameVars.cc
reef8dfb rbdfc032 30 30 #include "SynTree/Visitor.h" // for acceptAll, maybeAccept 31 31 32 #include "AST/Copy.hpp"33 34 32 namespace ResolvExpr { 35 33 … … 38 36 int level = 0; 39 37 int resetCount = 0; 38 ScopedMap< std::string, std::string > nameMap; 40 39 41 int next_expr_id = 1;42 int next_usage_id = 1;43 ScopedMap< std::string, std::string > nameMap;44 ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap;45 40 public: 46 41 void reset() { … … 49 44 } 50 45 46 using mapConstIterator = ScopedMap< std::string, std::string >::const_iterator; 47 51 48 void rename( TypeInstType * type ) { 52 autoit = nameMap.find( type->name );49 mapConstIterator it = nameMap.find( type->name ); 53 50 if ( it != nameMap.end() ) { 54 51 type->name = it->second; 55 52 } 56 }57 58 void nextUsage() {59 ++next_usage_id;60 53 } 61 54 … … 72 65 // ditto for assertion names, the next level in 73 66 level++; 74 } 75 } 67 // acceptAll( td->assertions, *this ); 68 } // for 69 } // if 76 70 } 77 71 … … 83 77 84 78 const ast::TypeInstType * rename( const ast::TypeInstType * type ) { 85 // rename 86 auto it = idMap.find( type->name ); 87 if ( it != idMap.end() ) { 88 // unconditionally mutate because map will *always* have different name 89 ast::TypeInstType * mut = ast::shallowCopy( type ); 90 // reconcile base node since some copies might have been made 91 mut->base = it->second.base; 92 mut->formal_usage = it->second.formal_usage; 93 mut->expr_id = it->second.expr_id; 94 type = mut; 79 mapConstIterator it = nameMap.find( type->name ); 80 if ( it != nameMap.end() ) { 81 ast::TypeInstType * mutType = ast::mutate( type ); 82 mutType->name = it->second; 83 type = mutType; 95 84 } 96 97 85 return type; 98 86 } 99 87 100 const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) { 101 if ( type->forall.empty() ) return type; 102 idMap.beginScope(); 88 template<typename NodeT> 89 const NodeT * openLevel( const NodeT * type ) { 90 if ( !type->forall.empty() ) { 91 nameMap.beginScope(); 92 // Load new names from this forall clause and perform renaming. 93 NodeT * mutType = ast::mutate( type ); 94 for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) { 95 std::ostringstream output; 96 output << "_" << resetCount << "_" << level << "_" << td->name; 97 std::string newname( output.str() ); 98 nameMap[ td->name ] = newname; 99 ++level; 103 100 104 // Load new names from this forall clause and perform renaming. 105 auto mutType = ast::shallowCopy( type ); 106 // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" ); 107 for ( auto & td : mutType->forall ) { 108 auto mut = ast::shallowCopy( td.get() ); 109 // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" ); 110 111 if (mode == GEN_EXPR_ID) { 112 mut->expr_id = next_expr_id; 113 mut->formal_usage = -1; 114 ++next_expr_id; 101 ast::TypeDecl * decl = ast::mutate( td.get() ); 102 decl->name = newname; 103 td = decl; 115 104 } 116 else if (mode == GEN_USAGE) {117 assertf(mut->expr_id, "unfilled expression id in generating candidate type");118 mut->formal_usage = next_usage_id;119 }120 else {121 assert(false);122 }123 idMap[ td->name ] = ast::TypeInstType::TypeEnvKey(*mut);124 125 td = mut;126 105 } 127 128 return mutType; 106 return type; 129 107 } 130 108 131 void closeLevel( const ast::FunctionType * type ) { 132 if ( type->forall.empty() ) return; 133 idMap.endScope(); 109 template<typename NodeT> 110 const NodeT * closeLevel( const NodeT * type ) { 111 if ( !type->forall.empty() ) { 112 nameMap.endScope(); 113 } 114 return type; 134 115 } 135 116 }; … … 138 119 RenamingData renaming; 139 120 140 struct RenameVars _old{121 struct RenameVars { 141 122 void previsit( TypeInstType * instType ) { 142 123 renaming.openLevel( (Type*)instType ); … … 149 130 renaming.closeLevel( type ); 150 131 } 151 };152 153 struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ {154 RenameMode mode;155 132 156 133 const ast::FunctionType * previsit( const ast::FunctionType * type ) { 157 return renaming.openLevel( type , mode);134 return renaming.openLevel( type ); 158 135 } 159 160 /*161 136 const ast::StructInstType * previsit( const ast::StructInstType * type ) { 162 137 return renaming.openLevel( type ); … … 168 143 return renaming.openLevel( type ); 169 144 } 170 */171 172 145 const ast::TypeInstType * previsit( const ast::TypeInstType * type ) { 173 if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type 174 return renaming.rename( type ); 146 return renaming.rename( renaming.openLevel( type ) ); 175 147 } 176 void postvisit( const ast::FunctionType * type ) {177 re naming.closeLevel( type );148 const ast::ParameterizedType * postvisit( const ast::ParameterizedType * type ) { 149 return renaming.closeLevel( type ); 178 150 } 179 151 }; … … 182 154 183 155 void renameTyVars( Type * t ) { 184 PassVisitor<RenameVars _old> renamer;156 PassVisitor<RenameVars> renamer; 185 157 t->accept( renamer ); 186 158 } 187 159 188 const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode, bool reset ) { 189 // ast::Type *tc = ast::deepCopy(t); 190 ast::Pass<RenameVars_new> renamer; 191 renamer.core.mode = mode; 192 if (mode == GEN_USAGE && reset) { 193 renaming.nextUsage(); 194 } 160 const ast::Type * renameTyVars( const ast::Type * t ) { 161 ast::Pass<RenameVars> renamer; 195 162 return t->accept( renamer ); 196 163 } … … 198 165 void resetTyVarRenaming() { 199 166 renaming.reset(); 200 renaming.nextUsage();201 167 } 202 168 -
src/ResolvExpr/RenameVars.h
reef8dfb rbdfc032 30 30 /// Provides a consistent renaming of forall type names in a hierarchy by prefixing them with a unique "level" ID 31 31 void renameTyVars( Type * ); 32 33 enum RenameMode { 34 GEN_USAGE, // for type in VariableExpr 35 GEN_EXPR_ID // for type in decl 36 }; 37 const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true ); 38 32 const ast::Type * renameTyVars( const ast::Type * ); 39 33 40 34 /// resets internal state of renamer to avoid overflow 41 35 void resetTyVarRenaming(); 42 43 44 36 } // namespace ResolvExpr 45 37 -
src/ResolvExpr/ResolvMode.h
reef8dfb rbdfc032 22 22 const bool prune; ///< Prune alternatives to min-cost per return type? [true] 23 23 const bool failFast; ///< Fail on no resulting alternatives? [true] 24 const bool satisfyAssns; ///< Satisfy assertions? [false] 24 25 25 constexpr ResolvMode(bool a, bool p, bool ff) 26 : adjust(a), prune(p), failFast(ff) {} 26 private: 27 constexpr ResolvMode(bool a, bool p, bool ff, bool sa) 28 : adjust(a), prune(p), failFast(ff), satisfyAssns(sa) {} 27 29 30 public: 28 31 /// Default settings 29 constexpr ResolvMode() : adjust(false), prune(true), failFast(true) {}32 constexpr ResolvMode() : adjust(false), prune(true), failFast(true), satisfyAssns(false) {} 30 33 31 34 /// With adjust flag set; turns array and function types into equivalent pointers 32 static constexpr ResolvMode withAdjustment() { return { true, true, true }; }35 static constexpr ResolvMode withAdjustment() { return { true, true, true, false }; } 33 36 34 37 /// With adjust flag set but prune unset; pruning ensures there is at least one alternative 35 38 /// per result type 36 static constexpr ResolvMode withoutPrune() { return { true, false, true }; }39 static constexpr ResolvMode withoutPrune() { return { true, false, true, false }; } 37 40 38 41 /// With adjust and prune flags set but failFast unset; failFast ensures there is at least 39 42 /// one resulting alternative 40 static constexpr ResolvMode withoutFailFast() { return { true, true, false }; }43 static constexpr ResolvMode withoutFailFast() { return { true, true, false, false }; } 41 44 42 45 /// The same mode, but with satisfyAssns turned on; for top-level calls 43 ResolvMode atTopLevel() const { return { adjust, true, failFast}; }46 ResolvMode atTopLevel() const { return { adjust, prune, failFast, true }; } 44 47 }; 45 48 } // namespace ResolvExpr -
src/ResolvExpr/ResolveAssertions.cc
reef8dfb rbdfc032 277 277 const DeclarationWithType * candidate = cdata.id; 278 278 279 // ignore deleted candidates. 280 // NOTE: this behavior is different from main resolver. 281 // further investigations might be needed to determine 282 // if we should implement the same rule here 283 // (i.e. error if unique best match is deleted) 284 if (candidate->isDeleted) continue; 285 286 // build independent unification context. for candidate 279 // build independent unification context for candidate 287 280 AssertionSet have, newNeed; 288 281 TypeEnvironment newEnv{ resn.alt.env }; -
src/ResolvExpr/ResolveTypeof.cc
reef8dfb rbdfc032 15 15 16 16 #include "ResolveTypeof.h" 17 #include "RenameVars.h"18 17 19 18 #include <cassert> // for assert … … 30 29 #include "SynTree/Mutator.h" // for Mutator 31 30 #include "SynTree/Type.h" // for TypeofType, Type 32 #include "SymTab/Mangler.h"33 #include "InitTweak/InitTweak.h" // for isConstExpr34 31 35 32 namespace SymTab { … … 102 99 // replace basetypeof(<enum>) by int 103 100 if ( dynamic_cast<EnumInstType*>(newType) ) { 104 Type* newerType = 105 new BasicType{ newType->get_qualifiers(), BasicType::SignedInt, 101 Type* newerType = 102 new BasicType{ newType->get_qualifiers(), BasicType::SignedInt, 106 103 newType->attributes }; 107 104 delete newType; 108 105 newType = newerType; 109 106 } 110 newType->get_qualifiers().val 107 newType->get_qualifiers().val 111 108 = ( newType->get_qualifiers().val & ~Type::Qualifiers::Mask ) | oldQuals; 112 109 } else { 113 110 newType->get_qualifiers().val |= oldQuals; 114 111 } 115 112 116 113 return newType; 117 114 } … … 123 120 ResolveTypeof_new( const ast::SymbolTable & syms ) : localSymtab( syms ) {} 124 121 125 void pre visit( const ast::TypeofType * ) { visit_children = false; }122 void premutate( const ast::TypeofType * ) { visit_children = false; } 126 123 127 const ast::Type * post visit( const ast::TypeofType * typeofType ) {124 const ast::Type * postmutate( const ast::TypeofType * typeofType ) { 128 125 // pass on null expression 129 126 if ( ! typeofType->expr ) return typeofType; … … 136 133 // typeof wrapping expression 137 134 ast::TypeEnvironment dummy; 138 ast::ptr< ast::Expr > newExpr = 135 ast::ptr< ast::Expr > newExpr = 139 136 resolveInVoidContext( typeofType->expr, localSymtab, dummy ); 140 137 assert( newExpr->result && ! newExpr->result->isVoid() ); … … 146 143 // replace basetypeof(<enum>) by int 147 144 if ( newType.as< ast::EnumInstType >() ) { 148 newType = new ast::BasicType{ 145 newType = new ast::BasicType{ 149 146 ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) }; 150 147 } 151 reset_qualifiers( 152 newType, 148 reset_qualifiers( 149 newType, 153 150 ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers ); 154 151 } else { … … 156 153 } 157 154 158 return newType .release();155 return newType; 159 156 } 160 157 }; … … 166 163 } 167 164 168 struct FixArrayDimension {169 // should not require a mutable symbol table - prevent pass template instantiation170 const ast::SymbolTable & _symtab;171 FixArrayDimension(const ast::SymbolTable & symtab): _symtab(symtab) {}172 173 const ast::ArrayType * previsit (const ast::ArrayType * arrayType) {174 if (!arrayType->dimension) return arrayType;175 auto mutType = mutate(arrayType);176 ast::ptr<ast::Type> sizetype = ast::sizeType ? ast::sizeType : new ast::BasicType(ast::BasicType::LongUnsignedInt);177 mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, _symtab);178 179 if (InitTweak::isConstExpr(mutType->dimension)) {180 mutType->isVarLen = ast::LengthFlag::FixedLen;181 }182 else {183 mutType->isVarLen = ast::LengthFlag::VariableLen;184 }185 return mutType;186 }187 };188 189 const ast::Type * fixArrayType( const ast::Type * type, const ast::SymbolTable & symtab) {190 ast::Pass<FixArrayDimension> visitor {symtab};191 return type->accept(visitor);192 }193 194 const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ast::SymbolTable & symtab ) {195 if (!decl->isTypeFixed) {196 auto mutDecl = mutate(decl);197 auto resolvedType = resolveTypeof(decl->type, symtab);198 resolvedType = fixArrayType(resolvedType, symtab);199 mutDecl->type = resolvedType;200 201 // check variable length if object is an array.202 // xxx - should this be part of fixObjectType?203 204 /*205 if (auto arrayType = dynamic_cast<const ast::ArrayType *>(resolvedType)) {206 auto dimExpr = findSingleExpression(arrayType->dimension, ast::sizeType, symtab);207 if (auto varexpr = arrayType->dimension.as<ast::VariableExpr>()) {// hoisted previously208 if (InitTweak::isConstExpr(varexpr->var.strict_as<ast::ObjectDecl>()->init)) {209 auto mutType = mutate(arrayType);210 mutType->isVarLen = ast::LengthFlag::VariableLen;211 mutDecl->type = mutType;212 }213 }214 }215 */216 217 218 if (!mutDecl->name.empty())219 mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables220 221 mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);222 mutDecl->isTypeFixed = true;223 return mutDecl;224 }225 return decl;226 }227 228 165 } // namespace ResolvExpr 229 166 -
src/ResolvExpr/ResolveTypeof.h
reef8dfb rbdfc032 23 23 class Type; 24 24 class SymbolTable; 25 class ObjectDecl;26 25 } 27 26 … … 29 28 Type *resolveTypeof( Type*, const SymTab::Indexer &indexer ); 30 29 const ast::Type * resolveTypeof( const ast::Type *, const ast::SymbolTable & ); 31 const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ast::SymbolTable & symtab );32 30 } // namespace ResolvExpr 33 31 -
src/ResolvExpr/Resolver.cc
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Sun May 17 12:17:01 2015 11 // Last Modified By : A ndrew Beach12 // Last Modified On : Fri Mar 27 11:58:00 202013 // Update Count : 24 211 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Wed May 29 11:00:00 2019 13 // Update Count : 241 14 14 // 15 15 … … 26 26 #include "RenameVars.h" // for RenameVars, global_renamer 27 27 #include "Resolver.h" 28 #include "ResolveTypeof.h"29 28 #include "ResolvMode.h" // for ResolvMode 30 29 #include "typeops.h" // for extractResultType 31 30 #include "Unify.h" // for unify 32 #include "CompilationState.h"33 31 #include "AST/Chain.hpp" 34 32 #include "AST/Decl.hpp" … … 40 38 #include "Common/PassVisitor.h" // for PassVisitor 41 39 #include "Common/SemanticError.h" // for SemanticError 42 #include "Common/Stats/ResolveTime.h" // for ResolveTime::start(), ResolveTime::stop()43 40 #include "Common/utility.h" // for ValueGuard, group_iterate 44 41 #include "InitTweak/GenInit.h" … … 47 44 #include "SymTab/Autogen.h" // for SizeType 48 45 #include "SymTab/Indexer.h" // for Indexer 49 #include "SymTab/Mangler.h" // for Mangler50 46 #include "SynTree/Declaration.h" // for ObjectDecl, TypeDecl, Declar... 51 47 #include "SynTree/Expression.h" // for Expression, CastExpr, InitExpr … … 88 84 void previsit( ThrowStmt * throwStmt ); 89 85 void previsit( CatchStmt * catchStmt ); 90 void postvisit( CatchStmt * catchStmt );91 86 void previsit( WaitForStmt * stmt ); 92 87 … … 564 559 // TODO: Replace *exception type with &exception type. 565 560 if ( throwStmt->get_expr() ) { 566 const StructDecl * exception_decl = indexer.lookupStruct( "__cfa ehm_base_exception_t" );561 const StructDecl * exception_decl = indexer.lookupStruct( "__cfaabi_ehm__base_exception_t" ); 567 562 assert( exception_decl ); 568 563 Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, const_cast<StructDecl *>(exception_decl) ) ); … … 572 567 573 568 void Resolver_old::previsit( CatchStmt * catchStmt ) { 574 // Until we are very sure this invarent (ifs that move between passes have thenPart)575 // holds, check it. This allows a check for when to decode the mangling.576 if ( IfStmt * ifStmt = dynamic_cast<IfStmt *>( catchStmt->body ) ) {577 assert( ifStmt->thenPart );578 }579 // Encode the catchStmt so the condition can see the declaration.580 569 if ( catchStmt->cond ) { 581 IfStmt * ifStmt = new IfStmt( catchStmt->cond, nullptr, catchStmt->body ); 582 catchStmt->cond = nullptr; 583 catchStmt->body = ifStmt; 584 } 585 } 586 587 void Resolver_old::postvisit( CatchStmt * catchStmt ) { 588 // Decode the catchStmt so everything is stored properly. 589 IfStmt * ifStmt = dynamic_cast<IfStmt *>( catchStmt->body ); 590 if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) { 591 assert( ifStmt->condition ); 592 assert( ifStmt->elsePart ); 593 catchStmt->cond = ifStmt->condition; 594 catchStmt->body = ifStmt->elsePart; 595 ifStmt->condition = nullptr; 596 ifStmt->elsePart = nullptr; 597 delete ifStmt; 570 findSingleExpression( catchStmt->cond, new BasicType( noQualifiers, BasicType::Bool ), indexer ); 598 571 } 599 572 } … … 968 941 namespace { 969 942 /// Finds deleted expressions in an expression tree 970 struct DeleteFinder_new final : public ast::WithShortCircuiting , public ast::WithVisitorRef<DeleteFinder_new>{971 const ast::DeletedExpr * result= nullptr;943 struct DeleteFinder_new final : public ast::WithShortCircuiting { 944 const ast::DeletedExpr * delExpr = nullptr; 972 945 973 946 void previsit( const ast::DeletedExpr * expr ) { 974 if ( result ) { visit_children = false; } 975 else { result = expr; } 976 } 977 978 void previsit( const ast::Expr * expr ) { 979 if ( result ) { visit_children = false; } 980 if (expr->inferred.hasParams()) { 981 for (auto & imp : expr->inferred.inferParams() ) { 982 imp.second.expr->accept(*visitor); 983 } 984 } 947 if ( delExpr ) { visit_children = false; } 948 else { delExpr = expr; } 949 } 950 951 void previsit( const ast::Expr * ) { 952 if ( delExpr ) { visit_children = false; } 985 953 } 986 954 }; 987 955 } // anonymous namespace 956 988 957 /// Check if this expression is or includes a deleted expression 989 958 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) { 990 return ast::Pass<DeleteFinder_new>::read( expr ); 959 ast::Pass<DeleteFinder_new> finder; 960 expr->accept( finder ); 961 return finder.pass.delExpr; 991 962 } 992 963 … … 1078 1049 /// Strips extraneous casts out of an expression 1079 1050 struct StripCasts_new final { 1080 const ast::Expr * post visit( const ast::CastExpr * castExpr ) {1051 const ast::Expr * postmutate( const ast::CastExpr * castExpr ) { 1081 1052 if ( 1082 castExpr->isGenerated == ast::GeneratedCast1053 castExpr->isGenerated 1083 1054 && typesCompatible( castExpr->arg->result, castExpr->result ) 1084 1055 ) { … … 1112 1083 } 1113 1084 1114 1115 } // anonymous namespace 1116 /// Establish post-resolver invariants for expressions 1085 /// Establish post-resolver invariants for expressions 1117 1086 void finishExpr( 1118 1087 ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env, … … 1127 1096 StripCasts_new::strip( expr ); 1128 1097 } 1098 } // anonymous namespace 1099 1129 1100 1130 1101 ast::ptr< ast::Expr > resolveInVoidContext( … … 1134 1105 1135 1106 // set up and resolve expression cast to void 1136 ast:: ptr< ast::CastExpr >untyped = new ast::CastExpr{ expr };1107 ast::CastExpr * untyped = new ast::CastExpr{ expr }; 1137 1108 CandidateRef choice = findUnfinishedKindExpression( 1138 1109 untyped, symtab, "", anyCandidate, ResolvMode::withAdjustment() ); … … 1146 1117 } 1147 1118 1148 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 1119 namespace { 1120 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 1149 1121 /// context. 1150 1122 ast::ptr< ast::Expr > findVoidExpression( 1151 1123 const ast::Expr * untyped, const ast::SymbolTable & symtab 1152 1124 ) { 1125 resetTyVarRenaming(); 1153 1126 ast::TypeEnvironment env; 1154 1127 ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env ); … … 1156 1129 return newExpr; 1157 1130 } 1158 1159 namespace {1160 1161 1131 1162 1132 /// resolve `untyped` to the expression whose candidate satisfies `pred` with the … … 1170 1140 CandidateRef choice = 1171 1141 findUnfinishedKindExpression( untyped, symtab, kind, pred, mode ); 1172 ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );1142 finishExpr( choice->expr, choice->env, untyped->env ); 1173 1143 return std::move( choice->expr ); 1174 1144 } … … 1178 1148 const ast::Expr * untyped, const ast::SymbolTable & symtab 1179 1149 ) { 1180 Stats::ResolveTime::start( untyped ); 1181 auto res = findKindExpression( untyped, symtab ); 1182 Stats::ResolveTime::stop(); 1183 return res; 1150 return findKindExpression( untyped, symtab ); 1184 1151 } 1185 1152 } // anonymous namespace 1186 1153 1187 ast::ptr< ast::Expr > findSingleExpression(1188 const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab1189 ) {1190 assert( untyped && type );1191 ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };1192 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab );1193 removeExtraneousCast( newExpr, symtab );1194 return newExpr;1195 }1154 ast::ptr< ast::Expr > findSingleExpression( 1155 const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab 1156 ) { 1157 assert( untyped && type ); 1158 ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type }; 1159 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab ); 1160 removeExtraneousCast( newExpr, symtab ); 1161 return newExpr; 1162 } 1196 1163 1197 1164 namespace { 1198 bool structOrUnion( const Candidate & i ) {1199 const ast::Type * t = i.expr->result->stripReferences();1200 return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t );1201 }1202 1165 /// Predicate for "Candidate has integral type" 1203 1166 bool hasIntegralType( const Candidate & i ) { … … 1235 1198 template<typename Iter> 1236 1199 inline bool nextMutex( Iter & it, const Iter & end ) { 1237 while ( it != end && ! (*it)-> is_mutex() ) { ++it; }1200 while ( it != end && ! (*it)->get_type()->is_mutex() ) { ++it; } 1238 1201 return it != end; 1239 1202 } … … 1247 1210 ast::ptr< ast::Type > functionReturn = nullptr; 1248 1211 ast::CurrentObject currentObject; 1249 // for work previously in GenInit1250 static InitTweak::ManagedTypes_new managedTypes;1251 1252 1212 bool inEnumDecl = false; 1253 1213 1254 1214 public: 1255 static size_t traceId;1256 1215 Resolver_new() = default; 1257 1216 Resolver_new( const ast::SymbolTable & syms ) { symtab = syms; } 1258 1217 1259 const ast::FunctionDecl *previsit( const ast::FunctionDecl * );1218 void previsit( const ast::FunctionDecl * ); 1260 1219 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * ); 1261 const ast::ObjectDecl * previsit( const ast::ObjectDecl * ); 1262 void previsit( const ast::AggregateDecl * ); 1263 void previsit( const ast::StructDecl * ); 1220 void previsit( const ast::ObjectDecl * ); 1264 1221 void previsit( const ast::EnumDecl * ); 1265 1222 const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * ); … … 1280 1237 const ast::ThrowStmt * previsit( const ast::ThrowStmt * ); 1281 1238 const ast::CatchStmt * previsit( const ast::CatchStmt * ); 1282 const ast::CatchStmt * postvisit( const ast::CatchStmt * );1283 1239 const ast::WaitForStmt * previsit( const ast::WaitForStmt * ); 1284 const ast::WithStmt * previsit( const ast::WithStmt * );1285 1240 1286 1241 const ast::SingleInit * previsit( const ast::SingleInit * ); 1287 1242 const ast::ListInit * previsit( const ast::ListInit * ); 1288 1243 const ast::ConstructorInit * previsit( const ast::ConstructorInit * ); 1289 1290 void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd);1291 1292 void beginScope() { managedTypes.beginScope(); }1293 void endScope() { managedTypes.endScope(); }1294 bool on_error(ast::ptr<ast::Decl> & decl);1295 1244 }; 1296 // size_t Resolver_new::traceId = Stats::Heap::new_stacktrace_id("Resolver"); 1297 1298 InitTweak::ManagedTypes_new Resolver_new::managedTypes; 1299 1300 void resolve( ast::TranslationUnit& translationUnit ) { 1301 ast::Pass< Resolver_new >::run( translationUnit ); 1245 1246 void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit ) { 1247 ast::Pass< Resolver_new > resolver; 1248 accept_all( translationUnit, resolver ); 1302 1249 } 1303 1250 … … 1310 1257 } 1311 1258 1312 const ast::Expr *resolveStmtExpr(1259 ast::ptr< ast::Expr > resolveStmtExpr( 1313 1260 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab 1314 1261 ) { 1315 1262 assert( stmtExpr ); 1316 1263 ast::Pass< Resolver_new > resolver{ symtab }; 1317 auto ret = mutate(stmtExpr->accept(resolver)); 1318 strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult(); 1264 ast::ptr< ast::Expr > ret = stmtExpr; 1265 ret = ret->accept( resolver ); 1266 strict_dynamic_cast< ast::StmtExpr * >( ret.get_and_mutate() )->computeResult(); 1319 1267 return ret; 1320 1268 } 1321 1269 1322 namespace { 1323 const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ast::SymbolTable & symtab) { 1324 std::string name = attr->normalizedName(); 1325 if (name == "constructor" || name == "destructor") { 1326 if (attr->params.size() == 1) { 1327 auto arg = attr->params.front(); 1328 auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), symtab ); 1329 auto result = eval(arg); 1330 1331 auto mutAttr = mutate(attr); 1332 mutAttr->params.front() = resolved; 1333 if (! result.second) { 1334 SemanticWarning(loc, Warning::GccAttributes, 1335 toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) ); 1336 } 1337 else { 1338 auto priority = result.first; 1339 if (priority < 101) { 1340 SemanticWarning(loc, Warning::GccAttributes, 1341 toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) ); 1342 } else if (priority < 201 && ! buildingLibrary()) { 1343 SemanticWarning(loc, Warning::GccAttributes, 1344 toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) ); 1345 } 1346 } 1347 return mutAttr; 1348 } else if (attr->params.size() > 1) { 1349 SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) ); 1350 } else { 1351 SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) ); 1352 } 1353 } 1354 return attr; 1355 } 1356 } 1357 1358 const ast::FunctionDecl * Resolver_new::previsit( const ast::FunctionDecl * functionDecl ) { 1270 void Resolver_new::previsit( const ast::FunctionDecl * functionDecl ) { 1359 1271 GuardValue( functionReturn ); 1360 1361 assert (functionDecl->unique());1362 if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) {1363 SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations");1364 }1365 1366 if (!functionDecl->isTypeFixed) {1367 auto mutDecl = mutate(functionDecl);1368 auto mutType = mutDecl->type.get_and_mutate();1369 1370 for (auto & attr: mutDecl->attributes) {1371 attr = handleAttribute(mutDecl->location, attr, symtab);1372 }1373 1374 // handle assertions1375 1376 symtab.enterScope();1377 mutType->forall.clear();1378 mutType->assertions.clear();1379 for (auto & typeParam : mutDecl->type_params) {1380 symtab.addType(typeParam);1381 mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam));1382 }1383 for (auto & asst : mutDecl->assertions) {1384 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);1385 symtab.addId(asst);1386 mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));1387 }1388 1389 // temporarily adds params to symbol table.1390 // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl)1391 1392 std::vector<ast::ptr<ast::Type>> paramTypes;1393 std::vector<ast::ptr<ast::Type>> returnTypes;1394 1395 for (auto & param : mutDecl->params) {1396 param = fixObjectType(param.strict_as<ast::ObjectDecl>(), symtab);1397 symtab.addId(param);1398 paramTypes.emplace_back(param->get_type());1399 }1400 for (auto & ret : mutDecl->returns) {1401 ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), symtab);1402 returnTypes.emplace_back(ret->get_type());1403 }1404 // since function type in decl is just a view of param types, need to update that as well1405 mutType->params = std::move(paramTypes);1406 mutType->returns = std::move(returnTypes);1407 1408 auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));1409 1410 std::list<ast::ptr<ast::Stmt>> newStmts;1411 resolveWithExprs (mutDecl->withExprs, newStmts);1412 1413 if (mutDecl->stmts) {1414 auto mutStmt = mutDecl->stmts.get_and_mutate();1415 mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts));1416 mutDecl->stmts = mutStmt;1417 }1418 1419 symtab.leaveScope();1420 1421 mutDecl->type = renamedType;1422 mutDecl->mangleName = Mangle::mangle(mutDecl);1423 mutDecl->isTypeFixed = true;1424 functionDecl = mutDecl;1425 }1426 managedTypes.handleDWT(functionDecl);1427 1428 1272 functionReturn = extractResultType( functionDecl->type ); 1429 return functionDecl;1430 1273 } 1431 1274 … … 1433 1276 // default value expressions have an environment which shouldn't be there and trips up 1434 1277 // later passes. 1435 as sert( functionDecl->unique() );1436 ast::FunctionType * mutType = mutate( functionDecl->type.get() );1437 1438 for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) { 1439 if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) {1278 ast::ptr< ast::FunctionDecl > ret = functionDecl; 1279 for ( unsigned i = 0; i < functionDecl->type->params.size(); ++i ) { 1280 const ast::ptr<ast::DeclWithType> & d = functionDecl->type->params[i]; 1281 1282 if ( const ast::ObjectDecl * obj = d.as< ast::ObjectDecl >() ) { 1440 1283 if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) { 1441 1284 if ( init->value->env == nullptr ) continue; 1442 1285 // clone initializer minus the initializer environment 1443 auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() ); 1444 auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() ); 1445 auto mutValue = mutate( mutInit->value.get() ); 1446 1447 mutValue->env = nullptr; 1448 mutInit->value = mutValue; 1449 mutParam->init = mutInit; 1450 mutType->params[i] = mutParam; 1451 1452 assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env); 1286 ast::chain_mutate( ret ) 1287 ( &ast::FunctionDecl::type ) 1288 ( &ast::FunctionType::params )[i] 1289 ( &ast::ObjectDecl::init ) 1290 ( &ast::SingleInit::value )->env = nullptr; 1291 1292 assert( functionDecl != ret.get() || functionDecl->unique() ); 1293 assert( ! ret->type->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env ); 1453 1294 } 1454 1295 } 1455 1296 } 1456 mutate_field(functionDecl, &ast::FunctionDecl::type, mutType); 1457 return functionDecl; 1458 } 1459 1460 const ast::ObjectDecl * Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) { 1297 return ret.get(); 1298 } 1299 1300 void Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) { 1461 1301 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], 1462 1302 // class-variable `initContext` is changed multiple times because the LHS is analyzed … … 1466 1306 // selecting the RHS. 1467 1307 GuardValue( currentObject ); 1468 1308 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() }; 1469 1309 if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) { 1470 1310 // enumerator initializers should not use the enum type to initialize, since the 1471 1311 // enum type is still incomplete at this point. Use `int` instead. 1472 objectDecl = fixObjectType(objectDecl, symtab);1473 1312 currentObject = ast::CurrentObject{ 1474 1313 objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } }; 1475 1314 } 1476 else {1477 if (!objectDecl->isTypeFixed) {1478 auto newDecl = fixObjectType(objectDecl, symtab);1479 auto mutDecl = mutate(newDecl);1480 1481 // generate CtorInit wrapper when necessary.1482 // in certain cases, fixObjectType is called before reaching1483 // this object in visitor pass, thus disabling CtorInit codegen.1484 // this happens on aggregate members and function parameters.1485 if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) {1486 // constructed objects cannot be designated1487 if ( InitTweak::isDesignated( mutDecl->init ) ) SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object. If this is really what you want, then initialize with @=.\n" );1488 // constructed objects should not have initializers nested too deeply1489 if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " );1490 1491 mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl );1492 }1493 1494 objectDecl = mutDecl;1495 }1496 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };1497 }1498 1499 return objectDecl;1500 }1501 1502 void Resolver_new::previsit( const ast::AggregateDecl * _aggDecl ) {1503 auto aggDecl = mutate(_aggDecl);1504 assertf(aggDecl == _aggDecl, "type declarations must be unique");1505 1506 for (auto & member: aggDecl->members) {1507 // nested type decls are hoisted already. no need to do anything1508 if (auto obj = member.as<ast::ObjectDecl>()) {1509 member = fixObjectType(obj, symtab);1510 }1511 }1512 }1513 1514 void Resolver_new::previsit( const ast::StructDecl * structDecl ) {1515 previsit(static_cast<const ast::AggregateDecl *>(structDecl));1516 managedTypes.handleStruct(structDecl);1517 1315 } 1518 1316 … … 1520 1318 // in case we decide to allow nested enums 1521 1319 GuardValue( inEnumDecl ); 1522 inEnumDecl = true; 1523 // don't need to fix types for enum fields 1524 } 1525 1320 inEnumDecl = false; 1321 } 1526 1322 1527 1323 const ast::StaticAssertDecl * Resolver_new::previsit( … … 1536 1332 const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) { 1537 1333 if ( type->dimension ) { 1538 ast::ptr< ast::Type > sizeType = ast::sizeType; 1334 #warning should use new equivalent to Validate::SizeType rather than sizeType here 1335 ast::ptr< ast::Type > sizeType = new ast::BasicType{ ast::BasicType::LongUnsignedInt }; 1539 1336 ast::mutate_field( 1540 1337 type, &PtrType::dimension, … … 1657 1454 if ( throwStmt->expr ) { 1658 1455 const ast::StructDecl * exceptionDecl = 1659 symtab.lookupStruct( "__cfa ehm_base_exception_t" );1456 symtab.lookupStruct( "__cfaabi_ehm__base_exception_t" ); 1660 1457 assert( exceptionDecl ); 1661 1458 ast::ptr< ast::Type > exceptType = … … 1669 1466 1670 1467 const ast::CatchStmt * Resolver_new::previsit( const ast::CatchStmt * catchStmt ) { 1671 // Until we are very sure this invarent (ifs that move between passes have thenPart)1672 // holds, check it. This allows a check for when to decode the mangling.1673 if ( auto ifStmt = catchStmt->body.as<ast::IfStmt>() ) {1674 assert( ifStmt->thenPart );1675 }1676 // Encode the catchStmt so the condition can see the declaration.1677 1468 if ( catchStmt->cond ) { 1678 ast::CatchStmt * stmt = mutate( catchStmt ); 1679 stmt->body = new ast::IfStmt( stmt->location, stmt->cond, nullptr, stmt->body ); 1680 stmt->cond = nullptr; 1681 return stmt; 1682 } 1683 return catchStmt; 1684 } 1685 1686 const ast::CatchStmt * Resolver_new::postvisit( const ast::CatchStmt * catchStmt ) { 1687 // Decode the catchStmt so everything is stored properly. 1688 const ast::IfStmt * ifStmt = catchStmt->body.as<ast::IfStmt>(); 1689 if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) { 1690 assert( ifStmt->cond ); 1691 assert( ifStmt->elsePart ); 1692 ast::CatchStmt * stmt = ast::mutate( catchStmt ); 1693 stmt->cond = ifStmt->cond; 1694 stmt->body = ifStmt->elsePart; 1695 // ifStmt should be implicately deleted here. 1696 return stmt; 1469 ast::ptr< ast::Type > boolType = new ast::BasicType{ ast::BasicType::Bool }; 1470 catchStmt = ast::mutate_field( 1471 catchStmt, &ast::CatchStmt::cond, 1472 findSingleExpression( catchStmt->cond, boolType, symtab ) ); 1697 1473 } 1698 1474 return catchStmt; … … 1811 1587 // Check if the argument matches the parameter type in the current 1812 1588 // scope 1813 //ast::ptr< ast::Type > paramType = (*param)->get_type();1589 ast::ptr< ast::Type > paramType = (*param)->get_type(); 1814 1590 if ( 1815 1591 ! unify( 1816 arg->expr->result, *param, resultEnv, need, have, open,1592 arg->expr->result, paramType, resultEnv, need, have, open, 1817 1593 symtab ) 1818 1594 ) { … … 1821 1597 ss << "candidate function not viable: no known conversion " 1822 1598 "from '"; 1823 ast::print( ss, *param);1599 ast::print( ss, (*param)->get_type() ); 1824 1600 ss << "' to '"; 1825 1601 ast::print( ss, arg->expr->result ); … … 1951 1727 } 1952 1728 1953 const ast::WithStmt * Resolver_new::previsit( const ast::WithStmt * withStmt ) {1954 auto mutStmt = mutate(withStmt);1955 resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);1956 return mutStmt;1957 }1958 1959 void Resolver_new::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) {1960 for (auto & expr : exprs) {1961 // only struct- and union-typed expressions are viable candidates1962 expr = findKindExpression( expr, symtab, structOrUnion, "with expression" );1963 1964 // if with expression might be impure, create a temporary so that it is evaluated once1965 if ( Tuples::maybeImpure( expr ) ) {1966 static UniqueName tmpNamer( "_with_tmp_" );1967 const CodeLocation loc = expr->location;1968 auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) );1969 expr = new ast::VariableExpr( loc, tmp );1970 stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) );1971 if ( InitTweak::isConstructable( tmp->type ) ) {1972 // generate ctor/dtor and resolve them1973 tmp->init = InitTweak::genCtorInit( loc, tmp );1974 }1975 // since tmp is freshly created, this should modify tmp in-place1976 tmp->accept( *visitor );1977 }1978 }1979 }1980 1729 1981 1730 … … 2073 1822 } 2074 1823 2075 // suppress error on autogen functions and mark invalid autogen as deleted.2076 bool Resolver_new::on_error(ast::ptr<ast::Decl> & decl) {2077 if (auto functionDecl = decl.as<ast::FunctionDecl>()) {2078 // xxx - can intrinsic gen ever fail?2079 if (functionDecl->linkage == ast::Linkage::AutoGen) {2080 auto mutDecl = mutate(functionDecl);2081 mutDecl->isDeleted = true;2082 mutDecl->stmts = nullptr;2083 decl = mutDecl;2084 return false;2085 }2086 }2087 return true;2088 }2089 2090 1824 } // namespace ResolvExpr 2091 1825 -
src/ResolvExpr/Resolver.h
reef8dfb rbdfc032 35 35 class StmtExpr; 36 36 class SymbolTable; 37 struct TranslationUnit;38 37 class Type; 39 38 class TypeEnvironment; … … 56 55 57 56 /// Checks types and binds syntactic constructs to typed representations 58 void resolve( ast::TranslationUnit& translationUnit );57 void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit ); 59 58 /// Searches expr and returns the first DeletedExpr found, otherwise nullptr 60 59 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ); … … 63 62 ast::ptr< ast::Expr > resolveInVoidContext( 64 63 const ast::Expr * expr, const ast::SymbolTable & symtab, ast::TypeEnvironment & env ); 65 /// Resolve `untyped` to the single expression whose candidate is the best match for the 64 /// Resolve `untyped` to the single expression whose candidate is the best match for the 66 65 /// given type. 67 66 ast::ptr< ast::Expr > findSingleExpression( 68 67 const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab ); 69 ast::ptr< ast::Expr > findVoidExpression(70 const ast::Expr * untyped, const ast::SymbolTable & symtab);71 68 /// Resolves a constructor init expression 72 ast::ptr< ast::Init > resolveCtorInit( 69 ast::ptr< ast::Init > resolveCtorInit( 73 70 const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab ); 74 /// Resolves a statement expression 75 const ast::Expr * resolveStmtExpr(71 /// Resolves a statement expression 72 ast::ptr< ast::Expr > resolveStmtExpr( 76 73 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab ); 77 74 } // namespace ResolvExpr -
src/ResolvExpr/SatisfyAssertions.cpp
reef8dfb rbdfc032 9 9 // Author : Aaron B. Moss 10 10 // Created On : Mon Jun 10 17:45:00 2019 11 // Last Modified By : A ndrew Beach12 // Last Modified On : Tue Oct 1 13:56:00 201913 // Update Count : 211 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Mon Jun 10 17:45:00 2019 13 // Update Count : 1 14 14 // 15 15 … … 69 69 /// Reference to a single deferred item 70 70 struct DeferRef { 71 const ast:: VariableExpr * expr;71 const ast::DeclWithType * decl; 72 72 const ast::AssertionSetValue & info; 73 73 const AssnCandidate & match; … … 77 77 /// Acts like an indexed list of DeferRef 78 78 struct DeferItem { 79 const ast:: VariableExpr * expr;79 const ast::DeclWithType * decl; 80 80 const ast::AssertionSetValue & info; 81 81 AssnCandidateList matches; 82 82 83 83 DeferItem( 84 const ast:: VariableExpr* d, const ast::AssertionSetValue & i, AssnCandidateList && ms )85 : expr( d ), info( i ), matches( std::move( ms ) ) {}84 const ast::DeclWithType * d, const ast::AssertionSetValue & i, AssnCandidateList && ms ) 85 : decl( d ), info( i ), matches( std::move( ms ) ) {} 86 86 87 87 bool empty() const { return matches.empty(); } … … 89 89 AssnCandidateList::size_type size() const { return matches.size(); } 90 90 91 DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; }91 DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; } 92 92 }; 93 93 … … 138 138 void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) { 139 139 for ( auto & i : have ) { 140 if ( i.second.isUsed ) { symtab.addId( i.first ->var); }140 if ( i.second.isUsed ) { symtab.addId( i.first ); } 141 141 } 142 142 } … … 144 144 /// Binds a single assertion, updating satisfaction state 145 145 void bindAssertion( 146 const ast:: VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand,146 const ast::DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand, 147 147 AssnCandidate & match, InferCache & inferred 148 148 ) { … … 156 156 157 157 // place newly-inferred assertion in proper location in cache 158 inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{159 candidate->uniqueId, candidate, match.adjType, expr->result, varExpr };158 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{ 159 candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr }; 160 160 } 161 161 … … 167 167 // find candidates that unify with the desired type 168 168 AssnCandidateList matches; 169 170 std::vector<ast::SymbolTable::IdData> candidates; 171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name); 172 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) { 173 // prefilter special decls by argument type, if already known 174 ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base 175 .strict_as<ast::FunctionType>()->params[0] 176 .strict_as<ast::ReferenceType>()->base; 177 sat.cand->env.apply(thisArgType); 178 179 std::string otypeKey = ""; 180 if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer; 181 else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams); 182 183 candidates = sat.symtab.specialLookupId(kind, otypeKey); 184 } 185 else { 186 candidates = sat.symtab.lookupId(assn.first->var->name); 187 } 188 for ( const ast::SymbolTable::IdData & cdata : candidates ) { 169 for ( const ast::SymbolTable::IdData & cdata : sat.symtab.lookupId( assn.first->name ) ) { 189 170 const ast::DeclWithType * candidate = cdata.id; 190 191 // ignore deleted candidates.192 // NOTE: this behavior is different from main resolver.193 // further investigations might be needed to determine194 // if we should implement the same rule here195 // (i.e. error if unique best match is deleted)196 if (candidate->isDeleted && candidate->linkage == ast::Linkage::AutoGen) continue;197 171 198 172 // build independent unification context for candidate … … 200 174 ast::TypeEnvironment newEnv{ sat.cand->env }; 201 175 ast::OpenVarSet newOpen{ sat.cand->open }; 202 ast::ptr< ast::Type > toType = assn.first-> result;176 ast::ptr< ast::Type > toType = assn.first->get_type(); 203 177 ast::ptr< ast::Type > adjType = 204 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) , GEN_USAGE, false);178 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) ); 205 179 206 180 // only keep candidates which unify … … 214 188 215 189 matches.emplace_back( 216 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed),190 cdata, adjType, std::move( newEnv ), std::move( newNeed ), std::move( have ), 217 191 std::move( newOpen ), crntResnSlot ); 218 192 } … … 255 229 InferMatcher( InferCache & inferred ) : inferred( inferred ) {} 256 230 257 const ast::Expr * post visit( const ast::Expr * expr ) {231 const ast::Expr * postmutate( const ast::Expr * expr ) { 258 232 // Skip if no slots to find 259 if ( !expr->inferred.hasSlots() ) return expr; 260 // if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr; 261 std::vector<UniqueId> missingSlots; 233 if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr; 234 262 235 // find inferred parameters for resolution slots 263 ast::InferredParams * newInferred = new ast::InferredParams();236 ast::InferredParams newInferred; 264 237 for ( UniqueId slot : expr->inferred.resnSlots() ) { 265 238 // fail if no matching assertions found 266 239 auto it = inferred.find( slot ); 267 240 if ( it == inferred.end() ) { 268 // std::cerr << "missing assertion " << slot << std::endl; 269 missingSlots.push_back(slot); 270 continue; 241 assert(!"missing assertion"); 271 242 } 272 243 … … 274 245 for ( auto & entry : it->second ) { 275 246 // recurse on inferParams of resolved expressions 276 entry.second.expr = post visit( entry.second.expr );277 auto res = newInferred ->emplace( entry );247 entry.second.expr = postmutate( entry.second.expr ); 248 auto res = newInferred.emplace( entry ); 278 249 assert( res.second && "all assertions newly placed" ); 279 250 } … … 281 252 282 253 ast::Expr * ret = mutate( expr ); 283 ret->inferred.set_inferParams( newInferred ); 284 if (!missingSlots.empty()) ret->inferred.resnSlots() = missingSlots; 254 ret->inferred.set_inferParams( std::move( newInferred ) ); 285 255 return ret; 286 256 } … … 337 307 // compute conversion cost from satisfying decl to assertion 338 308 cost += computeConversionCost( 339 assn.match.adjType, assn. expr->result, false, symtab, env );309 assn.match.adjType, assn.decl->get_type(), symtab, env ); 340 310 341 311 // mark vars+specialization on function-type assertions … … 344 314 if ( ! func ) continue; 345 315 346 for ( const a uto ¶m : func->params ) {347 cost.decSpec( specCost( param ) );316 for ( const ast::DeclWithType * param : func->params ) { 317 cost.decSpec( specCost( param->get_type() ) ); 348 318 } 349 319 350 320 cost.incVar( func->forall.size() ); 351 321 352 cost.decSpec( func->assertions.size() ); 322 for ( const ast::TypeDecl * td : func->forall ) { 323 cost.decSpec( td->assertions.size() ); 324 } 353 325 } 354 326 } … … 385 357 386 358 /// Limit to depth of recursion of assertion satisfaction 387 static const int recursionLimit = 8;359 static const int recursionLimit = 7; 388 360 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 389 361 static const int deferLimit = 10; … … 417 389 if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat; 418 390 419 // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen. 420 for (unsigned resetCount = 0; ; ++resetCount) { 421 ast::AssertionList next; 422 resetTyVarRenaming(); 423 // make initial pass at matching assertions 424 for ( auto & assn : sat.need ) { 425 // fail early if any assertion is not satisfiable 426 if ( ! satisfyAssertion( assn, sat ) ) { 427 next.emplace_back(assn); 428 // goto nextSat; 429 } 430 } 431 // success 432 if (next.empty()) break; 433 // fail if nothing resolves 434 else if (next.size() == sat.need.size()) { 391 // make initial pass at matching assertions 392 for ( auto & assn : sat.need ) { 393 // fail early if any assertion is not satisfiable 394 if ( ! satisfyAssertion( assn, sat ) ) { 435 395 Indenter tabs{ 3 }; 436 396 std::ostringstream ss; … … 438 398 print( ss, *sat.cand, ++tabs ); 439 399 ss << (tabs-1) << "Could not satisfy assertion:\n"; 440 ast::print( ss, next[0].first, tabs );400 ast::print( ss, assn.first, tabs ); 441 401 442 402 errors.emplace_back( ss.str() ); 443 403 goto nextSat; 444 404 } 445 sat.need = std::move(next);446 405 } 447 406 … … 462 421 ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n"; 463 422 for ( const auto & d : sat.deferred ) { 464 ast::print( ss, d. expr, tabs );423 ast::print( ss, d.decl, tabs ); 465 424 } 466 425 … … 480 439 ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n"; 481 440 for ( const auto& d : sat.deferred ) { 482 ast::print( ss, d. expr, tabs );441 ast::print( ss, d.decl, tabs ); 483 442 } 484 443 … … 512 471 nextNewNeed.insert( match.need.begin(), match.need.end() ); 513 472 514 bindAssertion( r. expr, r.info, nextCand, match, nextInferred );473 bindAssertion( r.decl, r.info, nextCand, match, nextInferred ); 515 474 } 516 475 -
src/ResolvExpr/SatisfyAssertions.hpp
reef8dfb rbdfc032 27 27 namespace ResolvExpr { 28 28 29 /// Recursively satisfies all assertions provided in a candidate 30 /// returns true if it has been run (candidate has any assertions) 31 void satisfyAssertions( 32 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 29 /// Recursively satisfies all assertions provided in a candidate; returns true if succeeds 30 void satisfyAssertions( 31 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 33 32 std::vector<std::string> & errors ); 34 33 -
src/ResolvExpr/SpecCost.cc
reef8dfb rbdfc032 10 10 // Created On : Tue Oct 02 15:50:00 2018 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jul 3 11:07:00 2019 13 // Update Count : 3 14 // 15 16 #include <cassert> 12 // Last Modified On : Wed Jun 19 10:43:00 2019 13 // Update Count : 2 14 // 15 17 16 #include <limits> 18 17 #include <list> … … 130 129 typename std::add_pointer<ast::Type const *(typename T::value_type const &)>::type; 131 130 132 #warning Should use a standard maybe_accept133 void maybe_accept( ast::Type const * type ) {134 if ( type ) {135 auto node = type->accept( *visitor );136 assert( node == nullptr || node == type );137 }138 }139 140 131 // Update the minimum to the new lowest non-none value. 141 132 template<typename T> … … 143 134 for ( const auto & node : list ) { 144 135 count = -1; 145 ma ybe_accept( mapper( node ));136 mapper( node )->accept( *visitor ); 146 137 if ( count != -1 && count < minimum ) minimum = count; 147 138 } … … 178 169 void previsit( const ast::FunctionType * fty ) { 179 170 int minCount = std::numeric_limits<int>::max(); 180 updateMinimumPresent( minCount, fty->params, type_deref);181 updateMinimumPresent( minCount, fty->returns, type_deref);171 updateMinimumPresent( minCount, fty->params, decl_type ); 172 updateMinimumPresent( minCount, fty->returns, decl_type ); 182 173 // Add another level to minCount if set. 183 174 count = toNoneOrInc( minCount ); … … 217 208 } 218 209 ast::Pass<SpecCounter> counter; 219 type->accept( counter );220 return counter. core.get_count();210 type->accept( *counter.pass.visitor ); 211 return counter.pass.get_count(); 221 212 } 222 213 -
src/ResolvExpr/TypeEnvironment.cc
reef8dfb rbdfc032 20 20 #include <utility> // for pair, move 21 21 22 #include "CompilationState.h" // for deterministic_output23 22 #include "Common/utility.h" // for maybeClone 24 23 #include "SynTree/Type.h" // for Type, FunctionType, Type::Fora... … … 107 106 108 107 void EqvClass::print( std::ostream &os, Indenter indent ) const { 109 os << "("; 110 bool first = true; 111 for(const auto & var : vars) { 112 if(first) first = false; 113 else os << " "; 114 if( deterministic_output && isUnboundType(var) ) os << "[unbound]"; 115 else os << var; 116 } 108 os << "( "; 109 std::copy( vars.begin(), vars.end(), std::ostream_iterator< std::string >( os, " " ) ); 117 110 os << ")"; 118 111 if ( type ) { … … 242 235 // check safely bindable 243 236 if ( r.type && occursIn( r.type, s.vars.begin(), s.vars.end(), *this ) ) return false; 244 237 245 238 // merge classes in 246 239 r.vars.insert( s.vars.begin(), s.vars.end() ); -
src/ResolvExpr/TypeEnvironment.h
reef8dfb rbdfc032 149 149 iterator end() const { return env.end(); } 150 150 151 auto size() const { return env.size(); }152 153 151 private: 154 152 ClassList env; -
src/ResolvExpr/Unify.cc
reef8dfb rbdfc032 25 25 #include <vector> 26 26 27 #include "AST/Copy.hpp"28 27 #include "AST/Decl.hpp" 29 28 #include "AST/Node.hpp" 30 29 #include "AST/Pass.hpp" 31 #include "AST/Print.hpp"32 30 #include "AST/Type.hpp" 33 31 #include "AST/TypeEnvironment.hpp" … … 137 135 findOpenVars( newSecond, open, closed, need, have, FirstOpen ); 138 136 139 return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab ); 137 return unifyExact( 138 newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab ); 140 139 } 141 140 … … 149 148 newFirst->get_qualifiers() = Type::Qualifiers(); 150 149 newSecond->get_qualifiers() = Type::Qualifiers(); 151 150 /// std::cerr << "first is "; 151 /// first->print( std::cerr ); 152 /// std::cerr << std::endl << "second is "; 153 /// second->print( std::cerr ); 154 /// std::cerr << std::endl << "newFirst is "; 155 /// newFirst->print( std::cerr ); 156 /// std::cerr << std::endl << "newSecond is "; 157 /// newSecond->print( std::cerr ); 158 /// std::cerr << std::endl; 152 159 bool result = unifyExact( newFirst, newSecond, newEnv, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer ); 153 160 delete newFirst; … … 163 170 ast::AssertionSet need, have; 164 171 165 ast::Type * newFirst = shallowCopy( first ); 166 ast::Type * newSecond = shallowCopy( second ); 167 newFirst ->qualifiers = {}; 168 newSecond->qualifiers = {}; 169 ast::ptr< ast::Type > t1_(newFirst ); 170 ast::ptr< ast::Type > t2_(newSecond); 171 172 ast::ptr< ast::Type > subFirst = env.apply(newFirst).node; 173 ast::ptr< ast::Type > subSecond = env.apply(newSecond).node; 172 ast::ptr<ast::Type> newFirst{ first }, newSecond{ second }; 173 env.apply( newFirst ); 174 env.apply( newSecond ); 175 reset_qualifiers( newFirst ); 176 reset_qualifiers( newSecond ); 174 177 175 178 return unifyExact( 176 subFirst, 177 subSecond, 178 newEnv, need, have, open, noWiden(), symtab ); 179 newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab ); 179 180 } 180 181 … … 325 326 326 327 void markAssertionSet( AssertionSet &assertions, DeclarationWithType *assert ) { 328 /// std::cerr << "assertion set is" << std::endl; 329 /// printAssertionSet( assertions, std::cerr, 8 ); 330 /// std::cerr << "looking for "; 331 /// assert->print( std::cerr ); 332 /// std::cerr << std::endl; 327 333 AssertionSet::iterator i = assertions.find( assert ); 328 334 if ( i != assertions.end() ) { 335 /// std::cerr << "found it!" << std::endl; 329 336 i->second.isUsed = true; 330 337 } // if … … 395 402 396 403 template< typename Iterator1, typename Iterator2 > 397 bool unify TypeList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {404 bool unifyDeclList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) { 398 405 auto get_type = [](DeclarationWithType * dwt){ return dwt->get_type(); }; 399 406 for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) { … … 489 496 || flatOther->isTtype() 490 497 ) { 491 if ( unify TypeList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {492 if ( unify TypeList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {498 if ( unifyDeclList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) { 499 if ( unifyDeclList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) { 493 500 494 501 // the original types must be used in mark assertions, since pointer comparisons are used … … 702 709 const ast::SymbolTable & symtab; 703 710 public: 704 static size_t traceId;705 711 bool result; 706 712 … … 767 773 /// If this isn't done when satifying ttype assertions, then argument lists can have 768 774 /// different size and structure when they should be compatible. 769 struct TtypeExpander_new : public ast::WithShortCircuiting , public ast::PureVisitor{775 struct TtypeExpander_new : public ast::WithShortCircuiting { 770 776 ast::TypeEnvironment & tenv; 771 777 … … 773 779 774 780 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 775 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst) ) {781 if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name ) ) { 776 782 // expand ttype parameter into its actual type 777 783 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) { … … 784 790 785 791 /// returns flattened version of `src` 786 static std::vector< ast::ptr< ast:: Type > > flattenList(787 const std::vector< ast::ptr< ast:: Type > > & src, ast::TypeEnvironment & env792 static std::vector< ast::ptr< ast::DeclWithType > > flattenList( 793 const std::vector< ast::ptr< ast::DeclWithType > > & src, ast::TypeEnvironment & env 788 794 ) { 789 std::vector< ast::ptr< ast:: Type > > dst;795 std::vector< ast::ptr< ast::DeclWithType > > dst; 790 796 dst.reserve( src.size() ); 791 for ( const a uto &d : src ) {797 for ( const ast::DeclWithType * d : src ) { 792 798 ast::Pass<TtypeExpander_new> expander{ env }; 793 // TtypeExpander pass is impure (may mutate nodes in place) 794 // need to make nodes shared to prevent accidental mutation 795 ast::ptr<ast::Type> dc = d->accept(expander); 796 auto types = flatten( dc ); 799 d = d->accept( expander ); 800 auto types = flatten( d->get_type() ); 797 801 for ( ast::ptr< ast::Type > & t : types ) { 798 802 // outermost const, volatile, _Atomic qualifiers in parameters should not play … … 803 807 // requirements than a non-mutex function 804 808 remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic ); 805 dst.emplace_back( t);809 dst.emplace_back( new ast::ObjectDecl{ d->location, "", t } ); 806 810 } 807 811 } … … 811 815 /// Creates a tuple type based on a list of DeclWithType 812 816 template< typename Iter > 813 static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {817 static ast::ptr< ast::Type > tupleFromDecls( Iter crnt, Iter end ) { 814 818 std::vector< ast::ptr< ast::Type > > types; 815 819 while ( crnt != end ) { 816 820 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 817 821 // that this results in a flat tuple 818 flatten( *crnt, types );822 flatten( (*crnt)->get_type(), types ); 819 823 820 824 ++crnt; 821 825 } 822 826 823 return new ast::TupleType{ std::move(types)};827 return { new ast::TupleType{ std::move(types) } }; 824 828 } 825 829 826 830 template< typename Iter > 827 static bool unify TypeList(831 static bool unifyDeclList( 828 832 Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env, 829 833 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, … … 831 835 ) { 832 836 while ( crnt1 != end1 && crnt2 != end2 ) { 833 const ast::Type * t1 = *crnt1;834 const ast::Type * t2 = *crnt2;837 const ast::Type * t1 = (*crnt1)->get_type(); 838 const ast::Type * t2 = (*crnt2)->get_type(); 835 839 bool isTuple1 = Tuples::isTtype( t1 ); 836 840 bool isTuple2 = Tuples::isTtype( t2 ); … … 840 844 // combine remainder of list2, then unify 841 845 return unifyExact( 842 t1, tupleFrom Types( crnt2, end2 ), env, need, have, open,846 t1, tupleFromDecls( crnt2, end2 ), env, need, have, open, 843 847 noWiden(), symtab ); 844 848 } else if ( ! isTuple1 && isTuple2 ) { 845 849 // combine remainder of list1, then unify 846 850 return unifyExact( 847 tupleFrom Types( crnt1, end1 ), t2, env, need, have, open,851 tupleFromDecls( crnt1, end1 ), t2, env, need, have, open, 848 852 noWiden(), symtab ); 849 853 } … … 860 864 if ( crnt1 != end1 ) { 861 865 // try unifying empty tuple with ttype 862 const ast::Type * t1 = *crnt1;866 const ast::Type * t1 = (*crnt1)->get_type(); 863 867 if ( ! Tuples::isTtype( t1 ) ) return false; 864 868 return unifyExact( 865 t1, tupleFrom Types( crnt2, end2 ), env, need, have, open,869 t1, tupleFromDecls( crnt2, end2 ), env, need, have, open, 866 870 noWiden(), symtab ); 867 871 } else if ( crnt2 != end2 ) { 868 872 // try unifying empty tuple with ttype 869 const ast::Type * t2 = *crnt2;873 const ast::Type * t2 = (*crnt2)->get_type(); 870 874 if ( ! Tuples::isTtype( t2 ) ) return false; 871 875 return unifyExact( 872 tupleFrom Types( crnt1, end1 ), t2, env, need, have, open,876 tupleFromDecls( crnt1, end1 ), t2, env, need, have, open, 873 877 noWiden(), symtab ); 874 878 } … … 877 881 } 878 882 879 static bool unify TypeList(880 const std::vector< ast::ptr< ast:: Type > > & list1,881 const std::vector< ast::ptr< ast:: Type > > & list2,883 static bool unifyDeclList( 884 const std::vector< ast::ptr< ast::DeclWithType > > & list1, 885 const std::vector< ast::ptr< ast::DeclWithType > > & list2, 882 886 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 883 887 const ast::OpenVarSet & open, const ast::SymbolTable & symtab 884 888 ) { 885 return unify TypeList(889 return unifyDeclList( 886 890 list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open, 887 891 symtab ); 888 892 } 889 893 890 static void markAssertionSet( ast::AssertionSet & assns, const ast:: VariableExpr* assn ) {894 static void markAssertionSet( ast::AssertionSet & assns, const ast::DeclWithType * assn ) { 891 895 auto i = assns.find( assn ); 892 896 if ( i != assns.end() ) { … … 898 902 static void markAssertions( 899 903 ast::AssertionSet & assn1, ast::AssertionSet & assn2, 900 const ast:: FunctionType * type904 const ast::ParameterizedType * type 901 905 ) { 902 for ( auto & assert : type->assertions ) { 903 markAssertionSet( assn1, assert ); 904 markAssertionSet( assn2, assert ); 906 for ( const auto & tyvar : type->forall ) { 907 for ( const ast::DeclWithType * assert : tyvar->assertions ) { 908 markAssertionSet( assn1, assert ); 909 markAssertionSet( assn2, assert ); 910 } 905 911 } 906 912 } … … 926 932 ) return; 927 933 928 if ( ! unify TypeList( params, params2, tenv, need, have, open, symtab ) ) return;929 if ( ! unify TypeList(934 if ( ! unifyDeclList( params, params2, tenv, need, have, open, symtab ) ) return; 935 if ( ! unifyDeclList( 930 936 func->returns, func2->returns, tenv, need, have, open, symtab ) ) return; 931 937 … … 937 943 938 944 private: 939 // Returns: other, cast as XInstType 940 // Assigns this->result: whether types are compatible (up to generic parameters) 941 template< typename XInstType > 942 const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) { 945 template< typename RefType > 946 const RefType * handleRefType( const RefType * inst, const ast::Type * other ) { 943 947 // check that the other type is compatible and named the same 944 auto otherInst = dynamic_cast< const XInstType * >( other );945 this->result = otherInst && inst->name == otherInst->name;948 auto otherInst = dynamic_cast< const RefType * >( other ); 949 result = otherInst && inst->name == otherInst->name; 946 950 return otherInst; 947 951 } … … 964 968 } 965 969 966 template< typename XInstType >967 void handleGenericRefType( const XInstType * inst, const ast::Type * other ) {970 template< typename RefType > 971 void handleGenericRefType( const RefType * inst, const ast::Type * other ) { 968 972 // check that other type is compatible and named the same 969 const XInstType * otherInst= handleRefType( inst, other );970 if ( ! this->result) return;973 const RefType * inst2 = handleRefType( inst, other ); 974 if ( ! inst2 ) return; 971 975 972 976 // check that parameters of types unify, if any 973 977 const std::vector< ast::ptr< ast::Expr > > & params = inst->params; 974 const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params;978 const std::vector< ast::ptr< ast::Expr > > & params2 = inst2->params; 975 979 976 980 auto it = params.begin(); … … 1028 1032 1029 1033 void postvisit( const ast::TypeInstType * typeInst ) { 1030 assert( open.find( *typeInst) == open.end() );1034 assert( open.find( typeInst->name ) == open.end() ); 1031 1035 handleRefType( typeInst, type2 ); 1032 1036 } … … 1034 1038 private: 1035 1039 /// Creates a tuple type based on a list of Type 1036 static const ast::Type *tupleFromTypes(1040 static ast::ptr< ast::Type > tupleFromTypes( 1037 1041 const std::vector< ast::ptr< ast::Type > > & tys 1038 1042 ) { … … 1110 1114 1111 1115 ast::Pass<TtypeExpander_new> expander{ tenv }; 1112 1113 1116 const ast::Type * flat = tuple->accept( expander ); 1114 1117 const ast::Type * flat2 = tuple2->accept( expander ); … … 1137 1140 }; 1138 1141 1139 // size_t Unify_new::traceId = Stats::Heap::new_stacktrace_id("Unify_new");1140 1142 bool unify( 1141 1143 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, … … 1169 1171 auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 ); 1170 1172 ast::OpenVarSet::const_iterator 1171 entry1 = var1 ? open.find( *var1) : open.end(),1172 entry2 = var2 ? open.find( *var2) : open.end();1173 entry1 = var1 ? open.find( var1->name ) : open.end(), 1174 entry2 = var2 ? open.find( var2->name ) : open.end(); 1173 1175 bool isopen1 = entry1 != open.end(); 1174 1176 bool isopen2 = entry2 != open.end(); … … 1186 1188 ast::Pass<Unify_new> comparator{ type2, env, need, have, open, widen, symtab }; 1187 1189 type1->accept( comparator ); 1188 return comparator. core.result;1190 return comparator.pass.result; 1189 1191 } 1190 1192 } … … 1200 1202 // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and 1201 1203 // type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1 1202 ast::Type * t1 = shallowCopy(type1.get()); 1203 ast::Type * t2 = shallowCopy(type2.get()); 1204 t1->qualifiers = {}; 1205 t2->qualifiers = {}; 1206 ast::ptr< ast::Type > t1_(t1); 1207 ast::ptr< ast::Type > t2_(t2); 1204 ast::ptr<ast::Type> t1{ type1 }, t2{ type2 }; 1205 reset_qualifiers( t1 ); 1206 reset_qualifiers( t2 ); 1208 1207 1209 1208 if ( unifyExact( t1, t2, env, need, have, open, widen, symtab ) ) { 1209 t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones 1210 1210 1211 // if exact unification on unqualified types, try to merge qualifiers 1211 1212 if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) { 1212 t1->qualifiers = q1 | q2;1213 common = t1;1213 common = type1; 1214 reset_qualifiers( common, q1 | q2 ); 1214 1215 return true; 1215 1216 } else { … … 1218 1219 1219 1220 } else if (( common = commonType( t1, t2, widen, symtab, env, open ) )) { 1221 t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones 1222 1220 1223 // no exact unification, but common type 1221 auto c = shallowCopy(common.get()); 1222 c->qualifiers = q1 | q2; 1223 common = c; 1224 reset_qualifiers( common, q1 | q2 ); 1224 1225 return true; 1225 1226 } else { … … 1230 1231 ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) { 1231 1232 if ( func->returns.empty() ) return new ast::VoidType{}; 1232 if ( func->returns.size() == 1 ) return func->returns[0] ;1233 if ( func->returns.size() == 1 ) return func->returns[0]->get_type(); 1233 1234 1234 1235 std::vector<ast::ptr<ast::Type>> tys; 1235 for ( const a uto &decl : func->returns ) {1236 tys.emplace_back( decl );1236 for ( const ast::DeclWithType * decl : func->returns ) { 1237 tys.emplace_back( decl->get_type() ); 1237 1238 } 1238 1239 return new ast::TupleType{ std::move(tys) }; -
src/ResolvExpr/module.mk
reef8dfb rbdfc032 19 19 ResolvExpr/Alternative.cc \ 20 20 ResolvExpr/AlternativeFinder.cc \ 21 ResolvExpr/AlternativeFinder.h \22 ResolvExpr/Alternative.h \23 21 ResolvExpr/Candidate.cpp \ 24 22 ResolvExpr/CandidateFinder.cpp \ 25 ResolvExpr/CandidateFinder.hpp \26 ResolvExpr/Candidate.hpp \27 23 ResolvExpr/CastCost.cc \ 28 24 ResolvExpr/CommonType.cc \ 29 25 ResolvExpr/ConversionCost.cc \ 30 ResolvExpr/ConversionCost.h \31 ResolvExpr/Cost.h \32 26 ResolvExpr/CurrentObject.cc \ 33 ResolvExpr/CurrentObject.h \34 27 ResolvExpr/ExplodedActual.cc \ 35 ResolvExpr/ExplodedActual.h \36 28 ResolvExpr/ExplodedArg.cpp \ 37 ResolvExpr/ExplodedArg.hpp \38 29 ResolvExpr/FindOpenVars.cc \ 39 ResolvExpr/FindOpenVars.h \40 30 ResolvExpr/Occurs.cc \ 41 31 ResolvExpr/PolyCost.cc \ … … 43 33 ResolvExpr/PtrsCastable.cc \ 44 34 ResolvExpr/RenameVars.cc \ 45 ResolvExpr/RenameVars.h \46 35 ResolvExpr/ResolveAssertions.cc \ 47 ResolvExpr/ResolveAssertions.h \48 36 ResolvExpr/Resolver.cc \ 49 ResolvExpr/Resolver.h \50 37 ResolvExpr/ResolveTypeof.cc \ 51 ResolvExpr/ResolveTypeof.h \52 ResolvExpr/ResolvMode.h \53 38 ResolvExpr/SatisfyAssertions.cpp \ 54 ResolvExpr/SatisfyAssertions.hpp \55 39 ResolvExpr/SpecCost.cc \ 56 40 ResolvExpr/TypeEnvironment.cc \ 57 ResolvExpr/TypeEnvironment.h \ 58 ResolvExpr/typeops.h \ 59 ResolvExpr/Unify.cc \ 60 ResolvExpr/Unify.h \ 61 ResolvExpr/WidenMode.h 41 ResolvExpr/Unify.cc 62 42 63 64 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h 43 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc 65 44 SRCDEMANGLE += $(SRC_RESOLVEXPR) -
src/ResolvExpr/typeops.h
reef8dfb rbdfc032 10 10 // Created On : Sun May 17 07:28:22 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T ue Oct 1 09:45:00 201913 // Update Count : 612 // Last Modified On : Thu Aug 8 16:36:00 2019 13 // Update Count : 5 14 14 // 15 15 … … 83 83 const SymTab::Indexer & indexer, const TypeEnvironment & env ); 84 84 Cost castCost( 85 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,86 const ast:: SymbolTable & symtab, const ast::TypeEnvironment & env );85 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab, 86 const ast::TypeEnvironment & env ); 87 87 88 88 // in ConversionCost.cc … … 90 90 const SymTab::Indexer & indexer, const TypeEnvironment & env ); 91 91 Cost conversionCost( 92 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,93 const ast:: SymbolTable & symtab, const ast::TypeEnvironment & env );92 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab, 93 const ast::TypeEnvironment & env ); 94 94 95 95 // in AlternativeFinder.cc -
src/SymTab/Autogen.cc
reef8dfb rbdfc032 38 38 #include "SynTree/Type.h" // for FunctionType, Type, TypeInstType 39 39 #include "SynTree/Visitor.h" // for maybeAccept, Visitor, acceptAll 40 #include "CompilationState.h"41 40 42 41 class Attribute; … … 234 233 } 235 234 236 // shallow copy the pointer list for return237 std::vector<ast::ptr<ast::TypeDecl>> getGenericParams (const ast::Type * t) {238 if (auto structInst = dynamic_cast<const ast::StructInstType*>(t)) {239 return structInst->base->params;240 }241 if (auto unionInst = dynamic_cast<const ast::UnionInstType*>(t)) {242 return unionInst->base->params;243 }244 return {};245 }246 247 235 /// given type T, generate type of default ctor/dtor, i.e. function type void (*) (T *) 248 236 FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic ) { … … 256 244 ftype->parameters.push_back( dstParam ); 257 245 return ftype; 258 }259 260 ///261 ast::FunctionDecl * genDefaultFunc(const CodeLocation loc, const std::string fname, const ast::Type * paramType, bool maybePolymorphic) {262 std::vector<ast::ptr<ast::TypeDecl>> typeParams;263 if (maybePolymorphic) typeParams = getGenericParams(paramType);264 auto dstParam = new ast::ObjectDecl(loc, "_dst", new ast::ReferenceType(paramType), nullptr, {}, ast::Linkage::Cforall);265 return new ast::FunctionDecl(loc, fname, std::move(typeParams), {dstParam}, {}, new ast::CompoundStmt(loc));266 246 } 267 247 … … 347 327 void FuncGenerator::resolve( FunctionDecl * dcl ) { 348 328 try { 349 if (!useNewAST) // attempt to delay resolver call 350 ResolvExpr::resolveDecl( dcl, indexer ); 329 ResolvExpr::resolveDecl( dcl, indexer ); 351 330 if ( functionNesting == 0 ) { 352 331 // forward declare if top-level struct, so that … … 360 339 } catch ( SemanticErrorException & ) { 361 340 // okay if decl does not resolve - that means the function should not be generated 362 // delete dcl; 363 delete dcl->statements; 364 dcl->statements = nullptr; 365 dcl->isDeleted = true; 366 definitions.push_back( dcl ); 367 indexer.addId( dcl ); 341 delete dcl; 368 342 } 369 343 } -
src/SymTab/Autogen.h
reef8dfb rbdfc032 21 21 22 22 #include "AST/Decl.hpp" 23 #include "AST/Eval.hpp"24 23 #include "AST/Expr.hpp" 25 24 #include "AST/Init.hpp" … … 55 54 /// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic 56 55 FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic = true ); 57 58 ast::FunctionDecl * genDefaultFunc(const CodeLocation loc, const std::string fname, const ast::Type * paramType, bool maybePolymorphic = true);59 56 60 57 /// generate the type of a copy constructor for paramType. … … 167 164 fExpr->args.emplace_back( dstParam ); 168 165 169 ast::ptr<ast::Stmt>listInit = srcParam.buildListInit( fExpr );166 const ast::Stmt * listInit = srcParam.buildListInit( fExpr ); 170 167 171 168 // fetch next set of arguments … … 268 265 } 269 266 270 ast::ptr< ast::Expr > begin, end; 271 std::string cmp, update; 267 ast::ptr< ast::Expr > begin, end, cmp, update; 272 268 273 269 if ( forward ) { … … 275 271 begin = ast::ConstantExpr::from_int( loc, 0 ); 276 272 end = array->dimension; 277 cmp = "?<?";278 update = "++?";273 cmp = new ast::NameExpr{ loc, "?<?" }; 274 update = new ast::NameExpr{ loc, "++?" }; 279 275 } else { 280 276 // generate: for ( int i = N-1; i >= 0; --i ) 281 begin = ast::call( 282 loc, "?-?", array->dimension, ast::ConstantExpr::from_int( loc, 1 ) ); 277 begin = new ast::UntypedExpr{ 278 loc, new ast::NameExpr{ loc, "?-?" }, 279 { array->dimension, ast::ConstantExpr::from_int( loc, 1 ) } }; 283 280 end = ast::ConstantExpr::from_int( loc, 0 ); 284 cmp = "?>=?";285 update = "--?";281 cmp = new ast::NameExpr{ loc, "?>=?" }; 282 update = new ast::NameExpr{ loc, "--?" }; 286 283 } 287 284 … … 289 286 loc, indexName.newName(), new ast::BasicType{ ast::BasicType::SignedInt }, 290 287 new ast::SingleInit{ loc, begin } }; 291 ast::ptr< ast::Expr > indexVar = new ast::VariableExpr{ loc, index }; 292 293 ast::ptr< ast::Expr > cond = ast::call( loc, cmp, indexVar, end ); 294 295 ast::ptr< ast::Expr > inc = ast::call( loc, update, indexVar ); 296 297 ast::ptr< ast::Expr > dstIndex = ast::call( loc, "?[?]", dstParam, indexVar ); 288 289 ast::ptr< ast::Expr > cond = new ast::UntypedExpr{ 290 loc, cmp, { new ast::VariableExpr{ loc, index }, end } }; 291 292 ast::ptr< ast::Expr > inc = new ast::UntypedExpr{ 293 loc, update, { new ast::VariableExpr{ loc, index } } }; 294 295 ast::ptr< ast::Expr > dstIndex = new ast::UntypedExpr{ 296 loc, new ast::NameExpr{ loc, "?[?]" }, 297 { dstParam, new ast::VariableExpr{ loc, index } } }; 298 298 299 299 // srcParam must keep track of the array indices to build the source parameter and/or 300 300 // array list initializer 301 srcParam.addArrayIndex( indexVar, array->dimension );301 srcParam.addArrayIndex( new ast::VariableExpr{ loc, index }, array->dimension ); 302 302 303 303 // for stmt's body, eventually containing call … … 385 385 if ( isUnnamedBitfield( obj ) ) return {}; 386 386 387 ast::ptr< ast::Type > addCast ;387 ast::ptr< ast::Type > addCast = nullptr; 388 388 if ( (fname == "?{}" || fname == "^?{}") && ( ! obj || ( obj && ! obj->bitfieldWidth ) ) ) { 389 389 assert( dstParam->result ); -
src/SymTab/Demangle.cc
reef8dfb rbdfc032 10 10 // Created On : Thu Jul 19 12:52:41 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 11 15:09:18 202013 // Update Count : 1012 // Last Modified On : Fri Dec 13 14:54:15 2019 13 // Update Count : 4 14 14 // 15 15 … … 19 19 #include "CodeGen/GenType.h" 20 20 #include "Common/PassVisitor.h" 21 #include "Common/utility.h" // isPrefix22 21 #include "Mangler.h" 23 22 #include "SynTree/Type.h" … … 417 416 418 417 bool StringView::isPrefix(const std::string & pref) { 419 // if ( pref.size() > str.size()-idx ) return false; 420 // auto its = std::mismatch( pref.begin(), pref.end(), std::next(str.begin(), idx) ); 421 // if (its.first == pref.end()) { 422 // idx += pref.size(); 423 // return true; 424 // } 425 426 // This update is untested because there are no tests for this code. 427 if ( ::isPrefix( str, pref, idx ) ) { 418 if ( pref.size() > str.size()-idx ) return false; 419 auto its = std::mismatch( pref.begin(), pref.end(), std::next(str.begin(), idx) ); 420 if (its.first == pref.end()) { 428 421 idx += pref.size(); 429 422 return true; … … 436 429 PRINT( std::cerr << "====== " << str.size() << " " << str << std::endl; ) 437 430 if (str.size() < 2+Encoding::manglePrefix.size()) return false; // +2 for at least _1 suffix 438 if ( ! isPrefix(Encoding::manglePrefix) || ! isdigit(str.back() )) return false;431 if (! isPrefix(Encoding::manglePrefix) || ! isdigit(str.back())) return false; 439 432 440 433 // get name -
src/SymTab/FixFunction.cc
reef8dfb rbdfc032 106 106 bool isVoid = false; 107 107 108 void pre visit( const ast::FunctionDecl * ) { visit_children = false; }108 void premutate( const ast::FunctionDecl * ) { visit_children = false; } 109 109 110 const ast::DeclWithType * post visit( const ast::FunctionDecl * func ) {110 const ast::DeclWithType * postmutate( const ast::FunctionDecl * func ) { 111 111 return new ast::ObjectDecl{ 112 112 func->location, func->name, new ast::PointerType{ func->type }, nullptr, … … 114 114 } 115 115 116 void pre visit( const ast::ArrayType * ) { visit_children = false; }116 void premutate( const ast::ArrayType * ) { visit_children = false; } 117 117 118 const ast::Type * post visit( const ast::ArrayType * array ) {118 const ast::Type * postmutate( const ast::ArrayType * array ) { 119 119 return new ast::PointerType{ 120 120 array->base, array->dimension, array->isVarLen, array->isStatic, … … 122 122 } 123 123 124 void pre visit( const ast::VoidType * ) { isVoid = true; }124 void premutate( const ast::VoidType * ) { isVoid = true; } 125 125 126 void pre visit( const ast::BasicType * ) { visit_children = false; }127 void pre visit( const ast::PointerType * ) { visit_children = false; }128 void pre visit( const ast::StructInstType * ) { visit_children = false; }129 void pre visit( const ast::UnionInstType * ) { visit_children = false; }130 void pre visit( const ast::EnumInstType * ) { visit_children = false; }131 void pre visit( const ast::TraitInstType * ) { visit_children = false; }132 void pre visit( const ast::TypeInstType * ) { visit_children = false; }133 void pre visit( const ast::TupleType * ) { visit_children = false; }134 void pre visit( const ast::VarArgsType * ) { visit_children = false; }135 void pre visit( const ast::ZeroType * ) { visit_children = false; }136 void pre visit( const ast::OneType * ) { visit_children = false; }126 void premutate( const ast::BasicType * ) { visit_children = false; } 127 void premutate( const ast::PointerType * ) { visit_children = false; } 128 void premutate( const ast::StructInstType * ) { visit_children = false; } 129 void premutate( const ast::UnionInstType * ) { visit_children = false; } 130 void premutate( const ast::EnumInstType * ) { visit_children = false; } 131 void premutate( const ast::TraitInstType * ) { visit_children = false; } 132 void premutate( const ast::TypeInstType * ) { visit_children = false; } 133 void premutate( const ast::TupleType * ) { visit_children = false; } 134 void premutate( const ast::VarArgsType * ) { visit_children = false; } 135 void premutate( const ast::ZeroType * ) { visit_children = false; } 136 void premutate( const ast::OneType * ) { visit_children = false; } 137 137 }; 138 138 } // anonymous namespace … … 141 141 ast::Pass< FixFunction_new > fixer; 142 142 dwt = dwt->accept( fixer ); 143 isVoid |= fixer. core.isVoid;143 isVoid |= fixer.pass.isVoid; 144 144 return dwt; 145 145 } -
src/SymTab/Mangler.cc
reef8dfb rbdfc032 10 10 // Created On : Sun May 17 21:40:29 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Nov 18 12:01:38 202013 // Update Count : 6412 // Last Modified On : Fri Dec 13 23:43:49 2019 13 // Update Count : 28 14 14 // 15 15 #include "Mangler.h" … … 65 65 void postvisit( const QualifiedType * qualType ); 66 66 67 std::string get_mangleName() { return mangleName ; }67 std::string get_mangleName() { return mangleName.str(); } 68 68 private: 69 std:: string mangleName;///< Mangled name being constructed69 std::ostringstream mangleName; ///< Mangled name being constructed 70 70 typedef std::map< std::string, std::pair< int, int > > VarMapType; 71 71 VarMapType varNums; ///< Map of type variables to indices … … 127 127 isTopLevel = false; 128 128 } // if 129 mangleName +=Encoding::manglePrefix;130 const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( declaration->get_name() );131 if ( op Info) {132 mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName;129 mangleName << Encoding::manglePrefix; 130 CodeGen::OperatorInfo opInfo; 131 if ( operatorLookup( declaration->get_name(), opInfo ) ) { 132 mangleName << opInfo.outputName.size() << opInfo.outputName; 133 133 } else { 134 mangleName += std::to_string( declaration->name.size() ) +declaration->name;134 mangleName << declaration->name.size() << declaration->name; 135 135 } // if 136 136 maybeAccept( declaration->get_type(), *visitor ); … … 139 139 // so they need a different name mangling 140 140 if ( declaration->get_linkage() == LinkageSpec::AutoGen ) { 141 mangleName +=Encoding::autogen;141 mangleName << Encoding::autogen; 142 142 } else if ( declaration->get_linkage() == LinkageSpec::Intrinsic ) { 143 mangleName +=Encoding::intrinsic;143 mangleName << Encoding::intrinsic; 144 144 } else { 145 145 // if we add another kind of overridable function, this has to change … … 160 160 void Mangler_old::postvisit( const VoidType * voidType ) { 161 161 printQualifiers( voidType ); 162 mangleName +=Encoding::void_t;162 mangleName << Encoding::void_t; 163 163 } 164 164 … … 166 166 printQualifiers( basicType ); 167 167 assertf( basicType->kind < BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind ); 168 mangleName +=Encoding::basicTypes[ basicType->kind ];168 mangleName << Encoding::basicTypes[ basicType->kind ]; 169 169 } 170 170 … … 172 172 printQualifiers( pointerType ); 173 173 // mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers 174 if ( ! dynamic_cast<FunctionType *>( pointerType->base ) ) mangleName +=Encoding::pointer;174 if ( ! dynamic_cast<FunctionType *>( pointerType->base ) ) mangleName << Encoding::pointer; 175 175 maybeAccept( pointerType->base, *visitor ); 176 176 } … … 179 179 // TODO: encode dimension 180 180 printQualifiers( arrayType ); 181 mangleName += Encoding::array +"0";181 mangleName << Encoding::array << "0"; 182 182 maybeAccept( arrayType->base, *visitor ); 183 183 } … … 204 204 void Mangler_old::postvisit( const FunctionType * functionType ) { 205 205 printQualifiers( functionType ); 206 mangleName +=Encoding::function;206 mangleName << Encoding::function; 207 207 // turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters, 208 208 // since qualifiers on outermost parameter type do not differentiate function types, e.g., … … 211 211 inFunctionType = true; 212 212 std::list< Type* > returnTypes = getTypes( functionType->returnVals ); 213 if (returnTypes.empty()) mangleName +=Encoding::void_t;213 if (returnTypes.empty()) mangleName << Encoding::void_t; 214 214 else acceptAll( returnTypes, *visitor ); 215 mangleName +="_";215 mangleName << "_"; 216 216 std::list< Type* > paramTypes = getTypes( functionType->parameters ); 217 217 acceptAll( paramTypes, *visitor ); 218 mangleName +="_";218 mangleName << "_"; 219 219 } 220 220 … … 222 222 printQualifiers( refType ); 223 223 224 mangleName += prefix + std::to_string( refType->name.length() ) +refType->name;224 mangleName << prefix << refType->name.length() << refType->name; 225 225 226 226 if ( mangleGenericParams ) { 227 227 const std::list< Expression* > & params = refType->parameters; 228 228 if ( ! params.empty() ) { 229 mangleName +="_";229 mangleName << "_"; 230 230 for ( const Expression * param : params ) { 231 231 const TypeExpr * paramType = dynamic_cast< const TypeExpr * >( param ); … … 233 233 maybeAccept( paramType->type, *visitor ); 234 234 } 235 mangleName +="_";235 mangleName << "_"; 236 236 } 237 237 } … … 262 262 // are first found and prefixing with the appropriate encoding for the type class. 263 263 assertf( varNum->second.second < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second ); 264 mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first );264 mangleName << Encoding::typeVariables[varNum->second.second] << varNum->second.first; 265 265 } // if 266 266 } … … 268 268 void Mangler_old::postvisit( const TraitInstType * inst ) { 269 269 printQualifiers( inst ); 270 mangleName += std::to_string( inst->name.size() ) +inst->name;270 mangleName << inst->name.size() << inst->name; 271 271 } 272 272 273 273 void Mangler_old::postvisit( const TupleType * tupleType ) { 274 274 printQualifiers( tupleType ); 275 mangleName += Encoding::tuple + std::to_string( tupleType->types.size());275 mangleName << Encoding::tuple << tupleType->types.size(); 276 276 acceptAll( tupleType->types, *visitor ); 277 277 } … … 280 280 printQualifiers( varArgsType ); 281 281 static const std::string vargs = "__builtin_va_list"; 282 mangleName += Encoding::type + std::to_string( vargs.size() ) +vargs;282 mangleName << Encoding::type << vargs.size() << vargs; 283 283 } 284 284 285 285 void Mangler_old::postvisit( const ZeroType * ) { 286 mangleName +=Encoding::zero;286 mangleName << Encoding::zero; 287 287 } 288 288 289 289 void Mangler_old::postvisit( const OneType * ) { 290 mangleName +=Encoding::one;290 mangleName << Encoding::one; 291 291 } 292 292 … … 296 296 // N marks the start of a qualified type 297 297 inQualifiedType = true; 298 mangleName +=Encoding::qualifiedTypeStart;298 mangleName << Encoding::qualifiedTypeStart; 299 299 } 300 300 maybeAccept( qualType->parent, *visitor ); … … 303 303 // E marks the end of a qualified type 304 304 inQualifiedType = false; 305 mangleName +=Encoding::qualifiedTypeEnd;305 mangleName << Encoding::qualifiedTypeEnd; 306 306 } 307 307 } … … 315 315 assertf(false, "Mangler_old should not visit typedecl: %s", toCString(decl)); 316 316 assertf( decl->kind < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind ); 317 mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) +decl->name;317 mangleName << Encoding::typeVariables[ decl->kind ] << ( decl->name.length() ) << decl->name; 318 318 } 319 319 … … 330 330 std::list< std::string > assertionNames; 331 331 int dcount = 0, fcount = 0, vcount = 0, acount = 0; 332 mangleName +=Encoding::forall;332 mangleName << Encoding::forall; 333 333 for ( const TypeDecl * i : type->forall ) { 334 334 switch ( i->kind ) { … … 354 354 } // for 355 355 } // for 356 mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_"; 357 for(const auto & a : assertionNames) mangleName += a; 358 // std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 359 mangleName += "_"; 356 mangleName << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_"; 357 std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 358 mangleName << "_"; 360 359 } // if 361 360 if ( ! inFunctionType ) { 362 361 // these qualifiers do not distinguish the outermost type of a function parameter 363 362 if ( type->get_const() ) { 364 mangleName +=Encoding::qualifiers.at(Type::Const);363 mangleName << Encoding::qualifiers.at(Type::Const); 365 364 } // if 366 365 if ( type->get_volatile() ) { 367 mangleName +=Encoding::qualifiers.at(Type::Volatile);366 mangleName << Encoding::qualifiers.at(Type::Volatile); 368 367 } // if 369 368 // Removed due to restrict not affecting function compatibility in GCC 370 369 // if ( type->get_isRestrict() ) { 371 // mangleName +="E";370 // mangleName << "E"; 372 371 // } // if 373 372 if ( type->get_atomic() ) { 374 mangleName +=Encoding::qualifiers.at(Type::Atomic);373 mangleName << Encoding::qualifiers.at(Type::Atomic); 375 374 } // if 376 375 } 377 376 if ( type->get_mutex() ) { 378 mangleName +=Encoding::qualifiers.at(Type::Mutex);377 mangleName << Encoding::qualifiers.at(Type::Mutex); 379 378 } // if 380 379 if ( inFunctionType ) { … … 384 383 } 385 384 } 386 } // namespace385 } // namespace 387 386 } // namespace Mangler 388 387 } // namespace SymTab … … 418 417 void postvisit( const ast::QualifiedType * qualType ); 419 418 420 std::string get_mangleName() { return mangleName ; }419 std::string get_mangleName() { return mangleName.str(); } 421 420 private: 422 std:: string mangleName;///< Mangled name being constructed421 std::ostringstream mangleName; ///< Mangled name being constructed 423 422 typedef std::map< std::string, std::pair< int, int > > VarMapType; 424 423 VarMapType varNums; ///< Map of type variables to indices … … 438 437 private: 439 438 void mangleDecl( const ast::DeclWithType *declaration ); 440 void mangleRef( const ast:: BaseInstType *refType, std::string prefix );439 void mangleRef( const ast::ReferenceToType *refType, std::string prefix ); 441 440 442 441 void printQualifiers( const ast::Type *type ); … … 448 447 ast::Pass<Mangler_new> mangler( mode ); 449 448 maybeAccept( decl, mangler ); 450 return mangler. core.get_mangleName();449 return mangler.pass.get_mangleName(); 451 450 } 452 451 … … 471 470 isTopLevel = false; 472 471 } // if 473 mangleName +=Encoding::manglePrefix;474 const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( decl->name );475 if ( op Info) {476 mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName;472 mangleName << Encoding::manglePrefix; 473 CodeGen::OperatorInfo opInfo; 474 if ( operatorLookup( decl->name, opInfo ) ) { 475 mangleName << opInfo.outputName.size() << opInfo.outputName; 477 476 } else { 478 mangleName += std::to_string( decl->name.size() ) +decl->name;477 mangleName << decl->name.size() << decl->name; 479 478 } // if 480 479 maybeAccept( decl->get_type(), *visitor ); … … 483 482 // so they need a different name mangling 484 483 if ( decl->linkage == ast::Linkage::AutoGen ) { 485 mangleName +=Encoding::autogen;484 mangleName << Encoding::autogen; 486 485 } else if ( decl->linkage == ast::Linkage::Intrinsic ) { 487 mangleName +=Encoding::intrinsic;486 mangleName << Encoding::intrinsic; 488 487 } else { 489 488 // if we add another kind of overridable function, this has to change … … 504 503 void Mangler_new::postvisit( const ast::VoidType * voidType ) { 505 504 printQualifiers( voidType ); 506 mangleName +=Encoding::void_t;505 mangleName << Encoding::void_t; 507 506 } 508 507 … … 510 509 printQualifiers( basicType ); 511 510 assertf( basicType->kind < ast::BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind ); 512 mangleName +=Encoding::basicTypes[ basicType->kind ];511 mangleName << Encoding::basicTypes[ basicType->kind ]; 513 512 } 514 513 … … 516 515 printQualifiers( pointerType ); 517 516 // mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers 518 if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName +=Encoding::pointer;517 if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName << Encoding::pointer; 519 518 maybe_accept( pointerType->base.get(), *visitor ); 520 519 } … … 523 522 // TODO: encode dimension 524 523 printQualifiers( arrayType ); 525 mangleName += Encoding::array +"0";524 mangleName << Encoding::array << "0"; 526 525 maybeAccept( arrayType->base.get(), *visitor ); 527 526 } … … 546 545 void Mangler_new::postvisit( const ast::FunctionType * functionType ) { 547 546 printQualifiers( functionType ); 548 mangleName +=Encoding::function;547 mangleName << Encoding::function; 549 548 // turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters, 550 549 // since qualifiers on outermost parameter type do not differentiate function types, e.g., … … 552 551 GuardValue( inFunctionType ); 553 552 inFunctionType = true; 554 if (functionType->returns.empty()) mangleName += Encoding::void_t; 555 else accept_each( functionType->returns, *visitor ); 556 mangleName += "_"; 557 accept_each( functionType->params, *visitor ); 558 mangleName += "_"; 559 } 560 561 void Mangler_new::mangleRef( const ast::BaseInstType * refType, std::string prefix ) { 553 std::vector< ast::ptr< ast::Type > > returnTypes = getTypes( functionType->returns ); 554 if (returnTypes.empty()) mangleName << Encoding::void_t; 555 else accept_each( returnTypes, *visitor ); 556 mangleName << "_"; 557 std::vector< ast::ptr< ast::Type > > paramTypes = getTypes( functionType->params ); 558 accept_each( paramTypes, *visitor ); 559 mangleName << "_"; 560 } 561 562 void Mangler_new::mangleRef( const ast::ReferenceToType * refType, std::string prefix ) { 562 563 printQualifiers( refType ); 563 564 564 mangleName += prefix + std::to_string( refType->name.length() ) +refType->name;565 mangleName << prefix << refType->name.length() << refType->name; 565 566 566 567 if ( mangleGenericParams ) { 567 568 if ( ! refType->params.empty() ) { 568 mangleName +="_";569 mangleName << "_"; 569 570 for ( const ast::Expr * param : refType->params ) { 570 571 auto paramType = dynamic_cast< const ast::TypeExpr * >( param ); … … 572 573 maybeAccept( paramType->type.get(), *visitor ); 573 574 } 574 mangleName +="_";575 mangleName << "_"; 575 576 } 576 577 } … … 601 602 // are first found and prefixing with the appropriate encoding for the type class. 602 603 assertf( varNum->second.second < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second ); 603 mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first );604 mangleName << Encoding::typeVariables[varNum->second.second] << varNum->second.first; 604 605 } // if 605 606 } … … 607 608 void Mangler_new::postvisit( const ast::TraitInstType * inst ) { 608 609 printQualifiers( inst ); 609 mangleName += std::to_string( inst->name.size() ) +inst->name;610 mangleName << inst->name.size() << inst->name; 610 611 } 611 612 612 613 void Mangler_new::postvisit( const ast::TupleType * tupleType ) { 613 614 printQualifiers( tupleType ); 614 mangleName += Encoding::tuple + std::to_string( tupleType->types.size());615 mangleName << Encoding::tuple << tupleType->types.size(); 615 616 accept_each( tupleType->types, *visitor ); 616 617 } … … 619 620 printQualifiers( varArgsType ); 620 621 static const std::string vargs = "__builtin_va_list"; 621 mangleName += Encoding::type + std::to_string( vargs.size() ) +vargs;622 mangleName << Encoding::type << vargs.size() << vargs; 622 623 } 623 624 624 625 void Mangler_new::postvisit( const ast::ZeroType * ) { 625 mangleName +=Encoding::zero;626 mangleName << Encoding::zero; 626 627 } 627 628 628 629 void Mangler_new::postvisit( const ast::OneType * ) { 629 mangleName +=Encoding::one;630 mangleName << Encoding::one; 630 631 } 631 632 … … 635 636 // N marks the start of a qualified type 636 637 inQualifiedType = true; 637 mangleName +=Encoding::qualifiedTypeStart;638 mangleName << Encoding::qualifiedTypeStart; 638 639 } 639 640 maybeAccept( qualType->parent.get(), *visitor ); … … 642 643 // E marks the end of a qualified type 643 644 inQualifiedType = false; 644 mangleName +=Encoding::qualifiedTypeEnd;645 mangleName << Encoding::qualifiedTypeEnd; 645 646 } 646 647 } … … 654 655 assertf(false, "Mangler_new should not visit typedecl: %s", toCString(decl)); 655 656 assertf( decl->kind < ast::TypeDecl::Kind::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind ); 656 mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) +decl->name;657 mangleName << Encoding::typeVariables[ decl->kind ] << ( decl->name.length() ) << decl->name; 657 658 } 658 659 … … 666 667 // skip if not including qualifiers 667 668 if ( typeMode ) return; 668 if ( auto ptype = dynamic_cast< const ast:: FunctionType * >(type) ) {669 if ( auto ptype = dynamic_cast< const ast::ParameterizedType * >(type) ) { 669 670 if ( ! ptype->forall.empty() ) { 670 671 std::list< std::string > assertionNames; 671 672 int dcount = 0, fcount = 0, vcount = 0, acount = 0; 672 mangleName +=Encoding::forall;673 for ( auto &decl : ptype->forall ) {673 mangleName << Encoding::forall; 674 for ( const ast::TypeDecl * decl : ptype->forall ) { 674 675 switch ( decl->kind ) { 675 676 case ast::TypeDecl::Kind::Dtype: … … 686 687 } // switch 687 688 varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind ); 689 for ( const ast::DeclWithType * assert : decl->assertions ) { 690 ast::Pass<Mangler_new> sub_mangler( 691 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ); 692 assert->accept( sub_mangler ); 693 assertionNames.push_back( sub_mangler.pass.get_mangleName() ); 694 acount++; 695 } // for 688 696 } // for 689 for ( auto & assert : ptype->assertions ) { 690 ast::Pass<Mangler_new> sub_mangler( 691 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ); 692 assert->var->accept( sub_mangler ); 693 assertionNames.push_back( sub_mangler.core.get_mangleName() ); 694 acount++; 695 } // for 696 mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_"; 697 for(const auto & a : assertionNames) mangleName += a; 698 // std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 699 mangleName += "_"; 697 mangleName << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_"; 698 std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 699 mangleName << "_"; 700 700 } // if 701 701 } // if … … 703 703 // these qualifiers do not distinguish the outermost type of a function parameter 704 704 if ( type->is_const() ) { 705 mangleName +=Encoding::qualifiers.at(Type::Const);705 mangleName << Encoding::qualifiers.at(Type::Const); 706 706 } // if 707 707 if ( type->is_volatile() ) { 708 mangleName +=Encoding::qualifiers.at(Type::Volatile);708 mangleName << Encoding::qualifiers.at(Type::Volatile); 709 709 } // if 710 710 // Removed due to restrict not affecting function compatibility in GCC 711 711 // if ( type->get_isRestrict() ) { 712 // mangleName +="E";712 // mangleName << "E"; 713 713 // } // if 714 714 if ( type->is_atomic() ) { 715 mangleName +=Encoding::qualifiers.at(Type::Atomic);715 mangleName << Encoding::qualifiers.at(Type::Atomic); 716 716 } // if 717 717 } 718 718 if ( type->is_mutex() ) { 719 mangleName +=Encoding::qualifiers.at(Type::Mutex);719 mangleName << Encoding::qualifiers.at(Type::Mutex); 720 720 } // if 721 721 if ( inFunctionType ) { -
src/SymTab/Validate.cc
reef8dfb rbdfc032 64 64 #include "Common/UniqueName.h" // for UniqueName 65 65 #include "Common/utility.h" // for operator+, cloneAll, deleteAll 66 #include "CompilationState.h" // skip some passes in new-ast build67 66 #include "Concurrency/Keywords.h" // for applyKeywords 68 67 #include "FixFunction.h" // for FixFunction … … 271 270 }; 272 271 273 struct InitializerLength{272 struct ArrayLength : public WithIndexer { 274 273 /// for array types without an explicit length, compute the length and store it so that it 275 274 /// is known to the rest of the phases. For example, … … 282 281 283 282 void previsit( ObjectDecl * objDecl ); 284 };285 286 struct ArrayLength : public WithIndexer {287 static void computeLength( std::list< Declaration * > & translationUnit );288 289 283 void previsit( ArrayType * arrayType ); 290 284 }; … … 317 311 Stats::Heap::newPass("validate-A"); 318 312 Stats::Time::BlockGuard guard("validate-A"); 319 VerifyCtorDtorAssign::verify( translationUnit ); // must happen before autogen, because autogen examines existing ctor/dtors320 313 acceptAll( translationUnit, hoistDecls ); 321 314 ReplaceTypedef::replaceTypedef( translationUnit ); … … 343 336 Stats::Time::BlockGuard guard("validate-C"); 344 337 acceptAll( translationUnit, genericParams ); // check as early as possible - can't happen before LinkReferenceToTypes_old 338 VerifyCtorDtorAssign::verify( translationUnit ); // must happen before autogen, because autogen examines existing ctor/dtors 345 339 ReturnChecker::checkFunctionReturns( translationUnit ); 346 340 InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen … … 374 368 mutateAll( translationUnit, compoundliteral ); 375 369 }); 376 if (!useNewAST) { 377 Stats::Time::TimeBlock("Resolve With Expressions", [&]() { 378 ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables 379 }); 380 } 370 Stats::Time::TimeBlock("Resolve With Expressions", [&]() { 371 ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables 372 }); 381 373 } 382 374 { 383 375 Stats::Heap::newPass("validate-F"); 384 376 Stats::Time::BlockGuard guard("validate-F"); 385 if (!useNewAST) { 386 Stats::Time::TimeCall("Fix Object Type", 387 FixObjectType::fix, translationUnit); 388 } 389 Stats::Time::TimeCall("Initializer Length", 390 InitializerLength::computeLength, translationUnit); 391 if (!useNewAST) { 392 Stats::Time::TimeCall("Array Length", 393 ArrayLength::computeLength, translationUnit); 394 } 377 Stats::Time::TimeCall("Fix Object Type", 378 FixObjectType::fix, translationUnit); 379 Stats::Time::TimeCall("Array Length", 380 ArrayLength::computeLength, translationUnit); 395 381 Stats::Time::TimeCall("Find Special Declarations", 396 382 Validate::findSpecialDecls, translationUnit); 397 383 Stats::Time::TimeCall("Fix Label Address", 398 384 mutateAll<LabelAddressFixer>, translationUnit, labelAddrFixer); 399 if (!useNewAST) { 400 Stats::Time::TimeCall("Handle Attributes", 401 Validate::handleAttributes, translationUnit); 402 } 385 Stats::Time::TimeCall("Handle Attributes", 386 Validate::handleAttributes, translationUnit); 403 387 } 404 388 } … … 976 960 } 977 961 978 static bool isNonParameterAttribute( Attribute * attr ) {979 static const std::vector<std::string> bad_names = {980 "aligned", "__aligned__",981 };982 for ( auto name : bad_names ) {983 if ( name == attr->name ) {984 return true;985 }986 }987 return false;988 }989 990 962 Type * ReplaceTypedef::postmutate( TypeInstType * typeInst ) { 991 963 // instances of typedef types will come here. If it is an instance … … 996 968 ret->location = typeInst->location; 997 969 ret->get_qualifiers() |= typeInst->get_qualifiers(); 998 // GCC ignores certain attributes if they arrive by typedef, this mimics that. 999 if ( inFunctionType ) { 1000 ret->attributes.remove_if( isNonParameterAttribute ); 1001 } 1002 ret->attributes.splice( ret->attributes.end(), typeInst->attributes ); 970 // attributes are not carried over from typedef to function parameters/return values 971 if ( ! inFunctionType ) { 972 ret->attributes.splice( ret->attributes.end(), typeInst->attributes ); 973 } else { 974 deleteAll( ret->attributes ); 975 ret->attributes.clear(); 976 } 1003 977 // place instance parameters on the typedef'd type 1004 978 if ( ! typeInst->parameters.empty() ) { … … 1208 1182 if ( CodeGen::isCtorDtorAssign( funcDecl->get_name() ) ) { // TODO: also check /=, etc. 1209 1183 if ( params.size() == 0 ) { 1210 SemanticError( funcDecl ->location, "Constructors, destructors, and assignment functions require at least one parameter." );1184 SemanticError( funcDecl, "Constructors, destructors, and assignment functions require at least one parameter " ); 1211 1185 } 1212 1186 ReferenceType * refType = dynamic_cast< ReferenceType * >( params.front()->get_type() ); 1213 1187 if ( ! refType ) { 1214 SemanticError( funcDecl ->location, "First parameter of a constructor, destructor, or assignment function must be a reference." );1188 SemanticError( funcDecl, "First parameter of a constructor, destructor, or assignment function must be a reference " ); 1215 1189 } 1216 1190 if ( CodeGen::isCtorDtor( funcDecl->get_name() ) && returnVals.size() != 0 ) { 1217 if(!returnVals.front()->get_type()->isVoid()) { 1218 SemanticError( funcDecl->location, "Constructors and destructors cannot have explicit return values." ); 1219 } 1191 SemanticError( funcDecl, "Constructors and destructors cannot have explicit return values " ); 1220 1192 } 1221 1193 } … … 1341 1313 } 1342 1314 1343 void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) {1344 PassVisitor<InitializerLength> len;1345 acceptAll( translationUnit, len );1346 }1347 1348 1315 void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) { 1349 1316 PassVisitor<ArrayLength> len; … … 1351 1318 } 1352 1319 1353 void InitializerLength::previsit( ObjectDecl * objDecl ) {1320 void ArrayLength::previsit( ObjectDecl * objDecl ) { 1354 1321 if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) { 1355 1322 if ( at->dimension ) return; … … 1405 1372 /// Replaces enum types by int, and function/array types in function parameter and return 1406 1373 /// lists by appropriate pointers 1407 /*1408 1374 struct EnumAndPointerDecay_new { 1409 1375 const ast::EnumDecl * previsit( const ast::EnumDecl * enumDecl ) { … … 1456 1422 } 1457 1423 }; 1458 */1459 1424 1460 1425 /// expand assertions from a trait instance, performing appropriate type variable substitutions … … 1475 1440 } 1476 1441 1477 /*1478 1479 1442 /// Associates forward declarations of aggregates with their definitions 1480 1443 class LinkReferenceToTypes_new final … … 1543 1506 } 1544 1507 1545 void checkGenericParameters( const ast:: BaseInstType * inst ) {1508 void checkGenericParameters( const ast::ReferenceToType * inst ) { 1546 1509 for ( const ast::Expr * param : inst->params ) { 1547 1510 if ( ! dynamic_cast< const ast::TypeExpr * >( param ) ) { … … 1807 1770 static const node_t * forallFixer( 1808 1771 const CodeLocation & loc, const node_t * node, 1809 ast:: FunctionType::ForallList parent_t::* forallField1772 ast::ParameterizedType::ForallList parent_t::* forallField 1810 1773 ) { 1811 1774 for ( unsigned i = 0; i < (node->* forallField).size(); ++i ) { … … 1858 1821 } 1859 1822 }; 1860 */1861 1823 } // anonymous namespace 1862 1824 1863 /*1864 1825 const ast::Type * validateType( 1865 1826 const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) { 1866 //ast::Pass< EnumAndPointerDecay_new > epc;1827 ast::Pass< EnumAndPointerDecay_new > epc; 1867 1828 ast::Pass< LinkReferenceToTypes_new > lrt{ loc, symtab }; 1868 1829 ast::Pass< ForallPointerDecay_new > fpd{ loc }; 1869 1830 1870 return type->accept( lrt )->accept( fpd );1831 return type->accept( epc )->accept( lrt )->accept( fpd ); 1871 1832 } 1872 */1873 1833 1874 1834 } // namespace SymTab -
src/SymTab/module.mk
reef8dfb rbdfc032 17 17 SRC_SYMTAB = \ 18 18 SymTab/Autogen.cc \ 19 SymTab/Autogen.h \20 19 SymTab/FixFunction.cc \ 21 SymTab/FixFunction.h \22 20 SymTab/Indexer.cc \ 23 SymTab/Indexer.h \24 21 SymTab/Mangler.cc \ 25 22 SymTab/ManglerCommon.cc \ 26 SymTab/Mangler.h \ 27 SymTab/Validate.cc \ 28 SymTab/Validate.h 23 SymTab/Validate.cc 29 24 30 25 SRC += $(SRC_SYMTAB) -
src/SynTree/AggregateDecl.cc
reef8dfb rbdfc032 21 21 #include "Common/utility.h" // for printAll, cloneAll, deleteAll 22 22 #include "Declaration.h" // for AggregateDecl, TypeDecl, Declaration 23 #include "Expression.h"24 23 #include "Initializer.h" 25 24 #include "LinkageSpec.h" // for Spec, linkageName, Cforall … … 89 88 const char * StructDecl::typeString() const { return aggrString( kind ); } 90 89 91 StructInstType * StructDecl::makeInst( std::list< Expression * > const & new_parameters ) {92 std::list< Expression * > copy_parameters;93 cloneAll( new_parameters, copy_parameters );94 return makeInst( move( copy( copy_parameters ) ) );95 }96 97 StructInstType * StructDecl::makeInst( std::list< Expression * > && new_parameters ) {98 assert( parameters.size() == new_parameters.size() );99 StructInstType * type = new StructInstType( noQualifiers, this );100 type->parameters = std::move( new_parameters );101 return type;102 }103 104 90 const char * UnionDecl::typeString() const { return aggrString( Union ); } 105 91 -
src/SynTree/ApplicationExpr.cc
reef8dfb rbdfc032 34 34 35 35 ParamEntry::ParamEntry( const ParamEntry &other ) : 36 decl( other.decl ), declptr( other.declptr), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) ) {36 decl( other.decl ), declptr( maybeClone( other.declptr ) ), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) ) { 37 37 } 38 38 39 39 ParamEntry::~ParamEntry() { 40 //delete declptr;40 delete declptr; 41 41 delete actualType; 42 42 delete formalType; -
src/SynTree/Attribute.h
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 13 21:34:08 202013 // Update Count : 4012 // Last Modified On : Sat Jul 22 09:54:14 2017 13 // Update Count : 39 14 14 // 15 15 … … 38 38 virtual ~Attribute(); 39 39 40 const std::string &get_name() const { return name; }40 std::string get_name() const { return name; } 41 41 void set_name( const std::string & newValue ) { name = newValue; } 42 42 std::list< Expression * > & get_parameters() { return parameters; } -
src/SynTree/Declaration.h
reef8dfb rbdfc032 181 181 public: 182 182 Type * base; 183 std::list< TypeDecl * > parameters; 183 184 std::list< DeclarationWithType * > assertions; 184 185 … … 189 190 Type * get_base() const { return base; } 190 191 void set_base( Type * newValue ) { base = newValue; } 192 std::list< TypeDecl* > & get_parameters() { return parameters; } 191 193 std::list< DeclarationWithType * >& get_assertions() { return assertions; } 192 194 … … 300 302 301 303 bool is_coroutine() { return kind == Coroutine; } 302 bool is_generator() { return kind == Generator; } 303 bool is_monitor () { return kind == Monitor ; } 304 bool is_thread () { return kind == Thread ; } 305 306 // Make a type instance of this declaration. 307 StructInstType * makeInst( std::list< Expression * > const & parameters ); 308 StructInstType * makeInst( std::list< Expression * > && parameters ); 304 bool is_monitor() { return kind == Monitor; } 305 bool is_thread() { return kind == Thread; } 309 306 310 307 virtual StructDecl * clone() const override { return new StructDecl( *this ); } -
src/SynTree/Expression.cc
reef8dfb rbdfc032 30 30 #include "Type.h" // for Type, BasicType, Type::Qualifiers 31 31 #include "TypeSubstitution.h" // for TypeSubstitution 32 #include "CompilationState.h" // for deterministic_output33 32 34 33 #include "GenPoly/Lvalue.h" … … 71 70 printInferParams( inferParams, os, indent+1, 0 ); 72 71 73 if ( result ) {74 os << std::endl << indent << "with resolved type:" << std::endl;75 os << (indent+1);76 result->print( os, indent+1 );77 }78 79 72 if ( env ) { 80 73 os << std::endl << indent << "... with environment:" << std::endl; … … 300 293 } 301 294 302 KeywordCastExpr::KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target ) : Expression(), arg(arg), target( target ) {} 303 KeywordCastExpr::KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target, const KeywordCastExpr::Concrete & concrete_target ) : Expression(), arg(arg), target( target ), concrete_target(concrete_target) {} 304 305 KeywordCastExpr::KeywordCastExpr( const KeywordCastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), target( other.target ) {} 295 KeywordCastExpr::KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target ) : Expression(), arg(arg), target( target ) { 296 } 297 298 KeywordCastExpr::KeywordCastExpr( const KeywordCastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), target( other.target ) { 299 } 306 300 307 301 KeywordCastExpr::~KeywordCastExpr() { -
src/SynTree/Expression.h
reef8dfb rbdfc032 163 163 }; 164 164 165 /// VariableExpr represents an expression that simply refers to the value of a named variable.166 /// Does not take ownership of var.167 class VariableExpr : public Expression {168 public:169 DeclarationWithType * var;170 171 VariableExpr();172 VariableExpr( DeclarationWithType * var );173 VariableExpr( const VariableExpr & other );174 virtual ~VariableExpr();175 176 bool get_lvalue() const final;177 178 DeclarationWithType * get_var() const { return var; }179 void set_var( DeclarationWithType * newValue ) { var = newValue; }180 181 static VariableExpr * functionPointer( FunctionDecl * decl );182 183 virtual VariableExpr * clone() const override { return new VariableExpr( * this ); }184 virtual void accept( Visitor & v ) override { v.visit( this ); }185 virtual void accept( Visitor & v ) const override { v.visit( this ); }186 virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }187 virtual void print( std::ostream & os, Indenter indent = {} ) const override;188 };189 190 165 // The following classes are used to represent expression types that cannot be converted into 191 166 // function-call format. … … 231 206 public: 232 207 Expression * arg; 233 234 // Inidicates cast is introduced by the CFA type system. 235 // true for casts that the resolver introduces to force a return type 236 // false for casts from user code 237 // false for casts from desugaring advanced CFA features into simpler CFA 238 // example 239 // int * p; // declaration 240 // (float *) p; // use, with subject cast 241 // subject cast isGenerated means we are considering an interpretation with a type mismatch 242 // subject cast not isGenerated means someone in charge wants it that way 243 bool isGenerated = true; 208 bool isGenerated = true; // cast generated implicitly by code generation or explicit in program 244 209 245 210 CastExpr( Expression * arg, bool isGenerated = true ); … … 273 238 274 239 KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target ); 275 KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target, const Concrete & concrete_target );276 240 KeywordCastExpr( const KeywordCastExpr & other ); 277 241 virtual ~KeywordCastExpr(); … … 348 312 349 313 virtual MemberExpr * clone() const override { return new MemberExpr( * this ); } 314 virtual void accept( Visitor & v ) override { v.visit( this ); } 315 virtual void accept( Visitor & v ) const override { v.visit( this ); } 316 virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); } 317 virtual void print( std::ostream & os, Indenter indent = {} ) const override; 318 }; 319 320 /// VariableExpr represents an expression that simply refers to the value of a named variable. 321 /// Does not take ownership of var. 322 class VariableExpr : public Expression { 323 public: 324 DeclarationWithType * var; 325 326 VariableExpr(); 327 VariableExpr( DeclarationWithType * var ); 328 VariableExpr( const VariableExpr & other ); 329 virtual ~VariableExpr(); 330 331 bool get_lvalue() const final; 332 333 DeclarationWithType * get_var() const { return var; } 334 void set_var( DeclarationWithType * newValue ) { var = newValue; } 335 336 static VariableExpr * functionPointer( FunctionDecl * decl ); 337 338 virtual VariableExpr * clone() const override { return new VariableExpr( * this ); } 350 339 virtual void accept( Visitor & v ) override { v.visit( this ); } 351 340 virtual void accept( Visitor & v ) const override { v.visit( this ); } -
src/SynTree/LinkageSpec.cc
reef8dfb rbdfc032 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Sat May 16 13:22:09 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Mon Mar 2 16:13:00 202013 // Update Count : 2 911 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 16 15:02:29 2019 13 // Update Count : 28 14 14 // 15 15 … … 20 20 21 21 #include "LinkageSpec.h" 22 #include "Common/CodeLocation.h"23 22 #include "Common/SemanticError.h" 24 23 -
src/SynTree/LinkageSpec.h
reef8dfb rbdfc032 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Sat May 16 13:24:28 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Mon Mar 2 16:13:00 202013 // Update Count : 2 111 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 16 15:03:43 2019 13 // Update Count : 20 14 14 // 15 15 … … 18 18 #include <string> 19 19 20 struct CodeLocation; 20 #include "Common/CodeLocation.h" 21 21 22 22 namespace LinkageSpec { -
src/SynTree/Mutator.h
reef8dfb rbdfc032 51 51 virtual Statement * mutate( CatchStmt * catchStmt ) = 0; 52 52 virtual Statement * mutate( FinallyStmt * catchStmt ) = 0; 53 virtual Statement * mutate( SuspendStmt * suspendStmt ) = 0;54 53 virtual Statement * mutate( WaitForStmt * waitforStmt ) = 0; 55 54 virtual Declaration * mutate( WithStmt * withStmt ) = 0; -
src/SynTree/NamedTypeDecl.cc
reef8dfb rbdfc032 22 22 #include "LinkageSpec.h" // for Spec, Cforall, linkageName 23 23 #include "Type.h" // for Type, Type::StorageClasses 24 #include "CompilationState.h"25 24 26 25 NamedTypeDecl::NamedTypeDecl( const std::string &name, Type::StorageClasses scs, Type *base ) … … 29 28 NamedTypeDecl::NamedTypeDecl( const NamedTypeDecl &other ) 30 29 : Parent( other ), base( maybeClone( other.base ) ) { 30 cloneAll( other.parameters, parameters ); 31 31 cloneAll( other.assertions, assertions ); 32 32 } … … 34 34 NamedTypeDecl::~NamedTypeDecl() { 35 35 delete base; 36 deleteAll( parameters ); 36 37 deleteAll( assertions ); 37 38 } … … 40 41 using namespace std; 41 42 42 if ( ! name.empty() ) { 43 if( deterministic_output && isUnboundType(name) ) os << "[unbound]:"; 44 else os << name << ": "; 45 } 43 if ( name != "" ) os << name << ": "; 46 44 47 45 if ( linkage != LinkageSpec::Cforall ) { … … 53 51 os << " for "; 54 52 base->print( os, indent+1 ); 53 } // if 54 if ( ! parameters.empty() ) { 55 os << endl << indent << "... with parameters" << endl; 56 printAll( parameters, os, indent+1 ); 55 57 } // if 56 58 if ( ! assertions.empty() ) { … … 70 72 base->print( os, indent+1 ); 71 73 } // if 74 if ( ! parameters.empty() ) { 75 os << endl << indent << "... with parameters" << endl; 76 printAll( parameters, os, indent+1 ); 77 } // if 72 78 } 73 79 -
src/SynTree/ReferenceToType.cc
reef8dfb rbdfc032 24 24 #include "Type.h" // for TypeInstType, StructInstType, UnionInstType 25 25 #include "TypeSubstitution.h" // for TypeSubstitution 26 #include "CompilationState.h"27 26 28 27 class Attribute; … … 206 205 207 206 Type::print( os, indent ); 208 os << "instance of " << typeString() << " "; 209 const auto & name_ = get_name(); 210 if( deterministic_output && isUnboundType(name) ) os << "[unbound]"; 211 else os << name; 212 os << " (" << ( isFtype ? "" : "not" ) << " function type)"; 207 os << "instance of " << typeString() << " " << get_name() << " (" << ( isFtype ? "" : "not" ) << " function type)"; 213 208 if ( ! parameters.empty() ) { 214 209 os << endl << indent << "... with parameters" << endl; -
src/SynTree/Statement.cc
reef8dfb rbdfc032 420 420 } 421 421 422 SuspendStmt::SuspendStmt( const SuspendStmt & other )423 : Statement( other )424 , then( maybeClone(other.then) )425 {}426 427 SuspendStmt::~SuspendStmt() {428 delete then;429 }430 431 void SuspendStmt::print( std::ostream & os, Indenter indent ) const {432 os << "Suspend Statement";433 switch (type) {434 case None : os << " with implicit target"; break;435 case Generator: os << " for generator" ; break;436 case Coroutine: os << " for coroutine" ; break;437 }438 os << endl;439 indent += 1;440 441 if(then) {442 os << indent << " with post statement :" << endl;443 then->print( os, indent + 1);444 }445 }446 447 422 WaitForStmt::WaitForStmt() : Statement() { 448 423 timeout.time = nullptr; -
src/SynTree/Statement.h
reef8dfb rbdfc032 422 422 }; 423 423 424 class SuspendStmt : public Statement {425 public:426 CompoundStmt * then = nullptr;427 enum Type { None, Coroutine, Generator } type = None;428 429 SuspendStmt() = default;430 SuspendStmt( const SuspendStmt & );431 virtual ~SuspendStmt();432 433 virtual SuspendStmt * clone() const override { return new SuspendStmt( *this ); }434 virtual void accept( Visitor & v ) override { v.visit( this ); }435 virtual void accept( Visitor & v ) const override { v.visit( this ); }436 virtual Statement * acceptMutator( Mutator & m ) override { return m.mutate( this ); }437 virtual void print( std::ostream & os, Indenter indent = {} ) const override;438 };439 440 424 class WaitForStmt : public Statement { 441 425 public: … … 518 502 class ImplicitCtorDtorStmt : public Statement { 519 503 public: 520 // the constructor/destructor call statement; owned here for a while, eventually transferred elsewhere504 // Non-owned pointer to the constructor/destructor statement 521 505 Statement * callStmt; 522 506 -
src/SynTree/SynTree.h
reef8dfb rbdfc032 54 54 class CatchStmt; 55 55 class FinallyStmt; 56 class SuspendStmt;57 56 class WaitForStmt; 58 57 class WithStmt; -
src/SynTree/Type.cc
reef8dfb rbdfc032 156 156 const Type::Qualifiers noQualifiers; 157 157 158 bool isUnboundType(const Type * type) {159 if (auto typeInst = dynamic_cast<const TypeInstType *>(type)) {160 // xxx - look for a type name produced by renameTyVars.161 162 // TODO: once TypeInstType representation is updated, it should properly check163 // if the context id is filled. this is a temporary hack for now164 return isUnboundType(typeInst->name);165 }166 return false;167 }168 169 bool isUnboundType(const std::string & tname) {170 // xxx - look for a type name produced by renameTyVars.171 172 // TODO: once TypeInstType representation is updated, it should properly check173 // if the context id is filled. this is a temporary hack for now174 if (std::count(tname.begin(), tname.end(), '_') >= 3) {175 return true;176 }177 return false;178 }179 180 158 // Local Variables: // 181 159 // tab-width: 4 // -
src/SynTree/Type.h
reef8dfb rbdfc032 733 733 }; 734 734 735 736 bool isUnboundType(const Type * type);737 bool isUnboundType(const std::string & tname);738 739 735 // Local Variables: // 740 736 // tab-width: 4 // -
src/SynTree/TypeDecl.cc
reef8dfb rbdfc032 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Oct 8 18:18:55 202013 // Update Count : 2 212 // Last Modified On : Fri Dec 13 15:26:14 2019 13 // Update Count : 21 14 14 // 15 15 … … 21 21 #include "Type.h" // for Type, Type::StorageClasses 22 22 23 TypeDecl::TypeDecl( const std::string & name, Type::StorageClasses scs, Type * type, Kind kind, bool sized, Type * init ) : 24 Parent( name, scs, type ), kind( kind ), sized( kind == Ttype || sized ), init( init ) { 23 TypeDecl::TypeDecl( const std::string & name, Type::StorageClasses scs, Type * type, Kind kind, bool sized, Type * init ) : Parent( name, scs, type ), kind( kind ), sized( kind == Ttype || sized ), init( init ) { 25 24 } 26 25 -
src/SynTree/Visitor.h
reef8dfb rbdfc032 78 78 virtual void visit( FinallyStmt * node ) { visit( const_cast<const FinallyStmt *>(node) ); } 79 79 virtual void visit( const FinallyStmt * finallyStmt ) = 0; 80 virtual void visit( SuspendStmt * node ) { visit( const_cast<const SuspendStmt *>(node) ); }81 virtual void visit( const SuspendStmt * suspendStmt ) = 0;82 80 virtual void visit( WaitForStmt * node ) { visit( const_cast<const WaitForStmt *>(node) ); } 83 81 virtual void visit( const WaitForStmt * waitforStmt ) = 0; -
src/SynTree/module.mk
reef8dfb rbdfc032 20 20 SynTree/ApplicationExpr.cc \ 21 21 SynTree/ArrayType.cc \ 22 SynTree/AttrType.cc \ 22 23 SynTree/Attribute.cc \ 23 SynTree/Attribute.h \24 SynTree/AttrType.cc \25 SynTree/BaseSyntaxNode.h \26 24 SynTree/BasicType.cc \ 27 25 SynTree/CommaExpr.cc \ 28 26 SynTree/CompoundStmt.cc \ 29 27 SynTree/Constant.cc \ 30 SynTree/Constant.h \ 28 SynTree/DeclReplacer.cc \ 29 SynTree/DeclStmt.cc \ 31 30 SynTree/Declaration.cc \ 32 SynTree/Declaration.h \33 31 SynTree/DeclarationWithType.cc \ 34 SynTree/DeclReplacer.cc \35 SynTree/DeclReplacer.h \36 SynTree/DeclStmt.cc \37 32 SynTree/Expression.cc \ 38 SynTree/Expression.h \39 33 SynTree/FunctionDecl.cc \ 40 34 SynTree/FunctionType.cc \ 41 35 SynTree/Initializer.cc \ 42 SynTree/Initializer.h \43 SynTree/Label.h \44 36 SynTree/LinkageSpec.cc \ 45 SynTree/LinkageSpec.h \46 SynTree/Mutator.h \47 37 SynTree/NamedTypeDecl.cc \ 48 38 SynTree/ObjectDecl.cc \ … … 51 41 SynTree/ReferenceType.cc \ 52 42 SynTree/Statement.cc \ 53 SynTree/Statement.h \54 SynTree/SynTree.h \55 43 SynTree/TupleExpr.cc \ 56 44 SynTree/TupleType.cc \ … … 58 46 SynTree/TypeDecl.cc \ 59 47 SynTree/TypeExpr.cc \ 60 SynTree/Type .h\48 SynTree/TypeSubstitution.cc \ 61 49 SynTree/TypeofType.cc \ 62 SynTree/TypeSubstitution.cc \63 SynTree/TypeSubstitution.h \64 50 SynTree/VarArgsType.cc \ 65 SynTree/Visitor.h \66 51 SynTree/VoidType.cc \ 67 52 SynTree/ZeroOneType.cc -
src/Tuples/Explode.cc
reef8dfb rbdfc032 129 129 for ( const ast::Expr * expr : tupleExpr->exprs ) { 130 130 exprs.emplace_back( applyCast( expr, false ) ); 131 //exprs.emplace_back( ast::ptr< ast::Expr >( applyCast( expr, false ) ) ); 131 132 } 132 133 if ( first ) { … … 147 148 } 148 149 149 const ast::Expr * post visit( const ast::UniqueExpr * node ) {150 const ast::Expr * postmutate( const ast::UniqueExpr * node ) { 150 151 // move cast into unique expr so that the unique expr has type T& rather than 151 152 // type T. In particular, this transformation helps with generating the … … 161 162 castAdded = false; 162 163 const ast::Type * newType = getReferenceBase( newNode->result ); 163 return new ast::CastExpr{ newNode->location, n ewNode, newType };164 return new ast::CastExpr{ newNode->location, node, newType }; 164 165 } 165 166 return newNode; 166 167 } 167 168 168 const ast::Expr * post visit( const ast::TupleIndexExpr * tupleExpr ) {169 const ast::Expr * postmutate( const ast::TupleIndexExpr * tupleExpr ) { 169 170 // tuple index expr needs to be rebuilt to ensure that the type of the 170 171 // field is consistent with the type of the tuple expr, since the field … … 179 180 ast::Pass<CastExploderCore> exploder; 180 181 expr = expr->accept( exploder ); 181 if ( ! exploder. core.foundUniqueExpr ) {182 if ( ! exploder.pass.foundUniqueExpr ) { 182 183 expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } }; 183 184 } -
src/Tuples/Explode.h
reef8dfb rbdfc032 210 210 } 211 211 // Cast a reference away to a value-type to allow further explosion. 212 if ( local->result.as< ast::ReferenceType >() ) {212 if ( dynamic_cast< const ast::ReferenceType *>( local->result.get() ) ) { 213 213 local = new ast::CastExpr{ local, tupleType }; 214 214 } … … 220 220 // delete idx; 221 221 } 222 // delete local; 222 223 } 223 224 } else { -
src/Tuples/TupleAssignment.cc
reef8dfb rbdfc032 465 465 // resolve ctor/dtor for the new object 466 466 ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit( 467 InitTweak::genCtorInit( location, ret ), spotter.crntFinder. localSyms);467 InitTweak::genCtorInit( location, ret ), spotter.crntFinder.symtab ); 468 468 // remove environments from subexpressions of stmtExpr 469 469 ast::Pass< EnvRemover > rm{ env }; … … 560 560 // resolve the cast expression so that rhsCand return type is bound by the cast 561 561 // type as needed, and transfer the resulting environment 562 ResolvExpr::CandidateFinder finder{ spotter.crntFinder. localSyms, env };562 ResolvExpr::CandidateFinder finder{ spotter.crntFinder.symtab, env }; 563 563 finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() ); 564 564 assert( finder.candidates.size() == 1 ); … … 609 609 // explode the LHS so that each field of a tuple-valued expr is assigned 610 610 ResolvExpr::CandidateList lhs; 611 explode( *lhsCand, crntFinder. localSyms, back_inserter(lhs), true );611 explode( *lhsCand, crntFinder.symtab, back_inserter(lhs), true ); 612 612 for ( ResolvExpr::CandidateRef & cand : lhs ) { 613 613 // each LHS value must be a reference - some come in with a cast, if not … … 629 629 if ( isTuple( rhsCand->expr ) ) { 630 630 // multiple assignment 631 explode( *rhsCand, crntFinder. localSyms, back_inserter(rhs), true );631 explode( *rhsCand, crntFinder.symtab, back_inserter(rhs), true ); 632 632 matcher.reset( 633 633 new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } ); … … 648 648 // multiple assignment 649 649 ResolvExpr::CandidateList rhs; 650 explode( rhsCand, crntFinder. localSyms, back_inserter(rhs), true );650 explode( rhsCand, crntFinder.symtab, back_inserter(rhs), true ); 651 651 matcher.reset( 652 652 new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } ); … … 678 678 ) 679 679 680 ResolvExpr::CandidateFinder finder{ crntFinder. localSyms, matcher->env };680 ResolvExpr::CandidateFinder finder{ crntFinder.symtab, matcher->env }; 681 681 682 682 try { -
src/Tuples/TupleExpansion.cc
reef8dfb rbdfc032 323 323 std::vector<ast::ptr<ast::Type>> types; 324 324 ast::CV::Qualifiers quals{ 325 ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict | 325 ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict | ast::CV::Lvalue | 326 326 ast::CV::Atomic | ast::CV::Mutex }; 327 327 -
src/Tuples/Tuples.cc
reef8dfb rbdfc032 43 43 }; 44 44 struct ImpurityDetectorIgnoreUnique : public ImpurityDetector { 45 using ImpurityDetector::previsit;46 45 void previsit( ast::UniqueExpr const * ) { 47 46 visit_children = false; … … 53 52 ast::Pass<Detector> detector; 54 53 expr->accept( detector ); 55 return detector. core.maybeImpure;54 return detector.pass.maybeImpure; 56 55 } 57 56 } // namespace -
src/Tuples/module.mk
reef8dfb rbdfc032 15 15 ############################################################################### 16 16 17 SRC_TUPLES = \ 18 Tuples/Explode.cc \ 19 Tuples/Explode.h \ 20 Tuples/TupleAssignment.cc \ 21 Tuples/TupleExpansion.cc \ 22 Tuples/Tuples.cc \ 23 Tuples/Tuples.h 24 25 26 SRC += $(SRC_TUPLES) 27 SRCDEMANGLE += $(SRC_TUPLES) 17 SRC += Tuples/TupleAssignment.cc Tuples/TupleExpansion.cc Tuples/Explode.cc \ 18 Tuples/Tuples.cc 19 SRCDEMANGLE += Tuples/TupleAssignment.cc Tuples/TupleExpansion.cc Tuples/Explode.cc \ 20 Tuples/Tuples.cc -
src/Validate/module.mk
reef8dfb rbdfc032 15 15 ############################################################################### 16 16 17 SRC += Validate/HandleAttributes.cc Validate/ HandleAttributes.h Validate/FindSpecialDecls.cc Validate/FindSpecialDecls.h18 SRCDEMANGLE += Validate/HandleAttributes.cc Validate/ HandleAttributes.h Validate/FindSpecialDecls.cc Validate/FindSpecialDecls.h17 SRC += Validate/HandleAttributes.cc Validate/FindSpecialDecls.cc 18 SRCDEMANGLE += Validate/HandleAttributes.cc Validate/FindSpecialDecls.cc -
src/Virtual/ExpandCasts.cc
reef8dfb rbdfc032 10 10 // Created On : Mon Jul 24 13:59:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Jul 31 10:29:00 202013 // Update Count : 412 // Last Modified On : Tus Aug 2 14:59:00 2017 13 // Update Count : 1 14 14 // 15 15 … … 18 18 #include <cassert> // for assert, assertf 19 19 #include <iterator> // for back_inserter, inserter 20 #include <map> // for map, _Rb_tree_iterator, map<>::ite... 20 21 #include <string> // for string, allocator, operator==, ope... 22 #include <utility> // for pair 21 23 22 24 #include "Common/PassVisitor.h" // for PassVisitor 23 #include "Common/ScopedMap.h" // for ScopedMap24 25 #include "Common/SemanticError.h" // for SemanticError 25 #include "SymTab/Mangler.h" // for mangleType26 26 #include "SynTree/Declaration.h" // for ObjectDecl, StructDecl, FunctionDecl 27 27 #include "SynTree/Expression.h" // for VirtualCastExpr, CastExpr, Address... … … 32 32 namespace Virtual { 33 33 34 // Indented until the new ast code gets added.35 36 /// Maps virtual table types the instance for that type.37 class VirtualTableMap final {38 ScopedMap<std::string, ObjectDecl *> vtable_instances;39 public:40 void enterScope() {41 vtable_instances.beginScope();42 }43 void leaveScope() {44 vtable_instances.endScope();45 }46 47 ObjectDecl * insert( ObjectDecl * vtableDecl ) {48 std::string const & mangledName = SymTab::Mangler::mangleType( vtableDecl->type );49 ObjectDecl *& value = vtable_instances[ mangledName ];50 if ( value ) {51 if ( vtableDecl->storageClasses.is_extern ) {52 return nullptr;53 } else if ( ! value->storageClasses.is_extern ) {54 return value;55 }56 }57 value = vtableDecl;58 return nullptr;59 }60 61 ObjectDecl * lookup( const Type * vtableType ) {62 std::string const & mangledName = SymTab::Mangler::mangleType( vtableType );63 const auto it = vtable_instances.find( mangledName );64 return ( vtable_instances.end() == it ) ? nullptr : it->second;65 }66 };67 68 34 /* Currently virtual depends on the rather brittle name matching between 69 35 * a (strict/explicate) virtual type, its vtable type and the vtable … … 72 38 * and use that information to create better error messages. 73 39 */ 74 75 namespace {76 40 77 41 std::string get_vtable_name( std::string const & name ) { … … 91 55 } 92 56 57 bool is_vtable_name( std::string const & name ) { 58 return (name.substr( name.size() - 7 ) == "_vtable" ); 59 } 60 93 61 bool is_vtable_inst_name( std::string const & name ) { 94 62 return 17 < name.size() && … … 96 64 } 97 65 98 } // namespace 66 class VirtualCastCore { 67 std::map<std::string, ObjectDecl *> vtable_instances; 68 FunctionDecl *vcast_decl; 69 StructDecl *pvt_decl; 99 70 100 class VirtualCastCore {101 71 Type * pointer_to_pvt(int level_of_indirection) { 102 72 Type * type = new StructInstType( … … 110 80 public: 111 81 VirtualCastCore() : 112 indexer(), vcast_decl( nullptr ), pvt_decl( nullptr )82 vtable_instances(), vcast_decl( nullptr ), pvt_decl( nullptr ) 113 83 {} 114 84 … … 118 88 119 89 Expression * postmutate( VirtualCastExpr * castExpr ); 120 121 VirtualTableMap indexer;122 private:123 FunctionDecl *vcast_decl;124 StructDecl *pvt_decl;125 90 }; 126 91 … … 142 107 void VirtualCastCore::premutate( ObjectDecl * objectDecl ) { 143 108 if ( is_vtable_inst_name( objectDecl->get_name() ) ) { 144 if ( ObjectDecl * existing = indexer.insert( objectDecl ) ) { 145 std::string msg = "Repeated instance of virtual table, original found at: "; 146 msg += existing->location.filename; 147 msg += ":" + toString( existing->location.first_line ); 148 SemanticError( objectDecl->location, msg ); 149 } 109 vtable_instances[objectDecl->get_name()] = objectDecl; 150 110 } 151 111 } 152 112 153 namespace {154 155 /// Better error locations for generated casts.156 CodeLocation castLocation( const VirtualCastExpr * castExpr ) {157 if ( castExpr->location.isSet() ) {158 return castExpr->location;159 } else if ( castExpr->arg->location.isSet() ) {160 return castExpr->arg->location;161 } else if ( castExpr->result->location.isSet() ) {162 return castExpr->result->location;163 } else {164 return CodeLocation();165 }166 }167 168 [[noreturn]] void castError( const VirtualCastExpr * castExpr, std::string const & message ) {169 SemanticError( castLocation( castExpr ), message );170 }171 172 /// Get the virtual table type used in a virtual cast.173 Type * getVirtualTableType( const VirtualCastExpr * castExpr ) {174 const Type * objectType;175 if ( auto target = dynamic_cast<const PointerType *>( castExpr->result ) ) {176 objectType = target->base;177 } else if ( auto target = dynamic_cast<const ReferenceType *>( castExpr->result ) ) {178 objectType = target->base;179 } else {180 castError( castExpr, "Virtual cast type must be a pointer or reference type." );181 }182 assert( objectType );183 184 const StructInstType * structType = dynamic_cast<const StructInstType *>( objectType );185 if ( nullptr == structType ) {186 castError( castExpr, "Virtual cast type must refer to a structure type." );187 }188 const StructDecl * structDecl = structType->baseStruct;189 assert( structDecl );190 191 const ObjectDecl * fieldDecl = nullptr;192 if ( 0 < structDecl->members.size() ) {193 const Declaration * memberDecl = structDecl->members.front();194 assert( memberDecl );195 fieldDecl = dynamic_cast<const ObjectDecl *>( memberDecl );196 if ( fieldDecl && fieldDecl->name != "virtual_table" ) {197 fieldDecl = nullptr;198 }199 }200 if ( nullptr == fieldDecl ) {201 castError( castExpr, "Virtual cast type must have a leading virtual_table field." );202 }203 const PointerType * fieldType = dynamic_cast<const PointerType *>( fieldDecl->type );204 if ( nullptr == fieldType ) {205 castError( castExpr, "Virtual cast type virtual_table field is not a pointer." );206 }207 assert( fieldType->base );208 auto virtualStructType = dynamic_cast<const StructInstType *>( fieldType->base );209 assert( virtualStructType );210 211 // Here is the type, but if it is polymorphic it will have lost information.212 // (Always a clone so that it may always be deleted.)213 StructInstType * virtualType = virtualStructType->clone();214 if ( ! structType->parameters.empty() ) {215 deleteAll( virtualType->parameters );216 virtualType->parameters.clear();217 cloneAll( structType->parameters, virtualType->parameters );218 }219 return virtualType;220 }221 222 } // namespace223 224 113 Expression * VirtualCastCore::postmutate( VirtualCastExpr * castExpr ) { 225 assertf( castExpr-> result, "Virtual Cast target not found before expansion." );114 assertf( castExpr->get_result(), "Virtual Cast target not found before expansion." ); 226 115 227 116 assert( vcast_decl ); 228 117 assert( pvt_decl ); 229 118 230 const Type * vtable_type = getVirtualTableType( castExpr ); 231 ObjectDecl * table = indexer.lookup( vtable_type ); 232 if ( nullptr == table ) { 233 SemanticError( castLocation( castExpr ), 234 "Could not find virtual table instance." ); 119 // May only cast to a pointer or reference type. 120 // A earlier validation should give a syntax error, this is 121 // just to make sure errors don't creep during translation. 122 // Move to helper with more detailed error messages. 123 PointerType * target_type = 124 dynamic_cast<PointerType *>( castExpr->get_result() ); 125 assert( target_type ); 126 127 StructInstType * target_struct = 128 dynamic_cast<StructInstType *>( target_type->get_base() ); 129 assert( target_struct ); 130 131 StructDecl * target_decl = target_struct->get_baseStruct(); 132 133 std::map<std::string, ObjectDecl *>::iterator found = 134 vtable_instances.find( 135 get_vtable_inst_name( target_decl->get_name() ) ); 136 if ( vtable_instances.end() == found ) { 137 assertf( false, "virtual table instance not found." ); 235 138 } 139 ObjectDecl * table = found->second; 236 140 237 141 Expression * result = new CastExpr( 142 //new ApplicationExpr( 143 //new AddressExpr( new VariableExpr( vcast_decl ) ), 144 //new CastExpr( new VariableExpr( vcast_decl ), 145 // new PointerType( noQualifiers, 146 // vcast_decl->get_type()->clone() 147 // ) 148 // ), 238 149 new ApplicationExpr( VariableExpr::functionPointer( vcast_decl ), { 239 150 new CastExpr( … … 252 163 castExpr->set_result( nullptr ); 253 164 delete castExpr; 254 delete vtable_type;255 165 return result; 256 166 } -
src/Virtual/module.mk
reef8dfb rbdfc032 15 15 ############################################################################### 16 16 17 SRC += Virtual/ExpandCasts.cc Virtual/ExpandCasts.h \ 18 Virtual/Tables.cc Virtual/Tables.h 19 20 SRCDEMANGLE += Virtual/Tables.cc 17 SRC += Virtual/ExpandCasts.cc -
src/config.h.in
reef8dfb rbdfc032 27 27 /* Location of cfa install. */ 28 28 #undef CFA_PREFIX 29 30 /* Sets whether or not to use the new-ast, this is adefault value and can be31 overrided by --old-ast and --new-ast */32 #undef CFA_USE_NEW_AST33 29 34 30 /* Major.Minor */ -
src/main.cc
reef8dfb rbdfc032 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 : Mon Dec 7 15:29:00 202013 // Update Count : 6 3911 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 16 17:55:53 2019 13 // Update Count : 627 14 14 // 15 15 … … 31 31 using namespace std; 32 32 33 #include "AST/Convert.hpp" 33 34 34 #include "CompilationState.h" 35 35 #include "../config.h" // for CFA_LIBDIR … … 40 40 #include "CodeTools/ResolvProtoDump.h" // for dumpAsResolvProto 41 41 #include "CodeTools/TrackLoc.h" // for fillLocations 42 #include "Common/CodeLocationTools.hpp" // for forceFillCodeLocations43 42 #include "Common/CompilerError.h" // for CompilerError 44 43 #include "Common/Stats.h" … … 106 105 107 106 static void backtrace( int start ) { // skip first N stack frames 108 enum { Frames = 50 , }; // maximum number of stack frames107 enum { Frames = 50 }; 109 108 void * array[Frames]; 110 size_t size = ::backtrace( array, Frames );109 int size = ::backtrace( array, Frames ); 111 110 char ** messages = ::backtrace_symbols( array, size ); // does not demangle names 112 111 … … 115 114 116 115 // skip last 2 stack frames after main 117 for ( unsignedint i = start; i < size - 2 && messages != nullptr; i += 1 ) {116 for ( int i = start; i < size - 2 && messages != nullptr; i += 1 ) { 118 117 char * mangled_name = nullptr, * offset_begin = nullptr, * offset_end = nullptr; 119 118 … … 181 180 } // sigSegvBusHandler 182 181 183 static void sigFpeHandler( SIGPARMS ) {184 const char * msg;185 186 switch ( sfp->si_code ) {187 case FPE_INTDIV: case FPE_FLTDIV: msg = "divide by zero"; break;188 case FPE_FLTOVF: msg = "overflow"; break;189 case FPE_FLTUND: msg = "underflow"; break;190 case FPE_FLTRES: msg = "inexact result"; break;191 case FPE_FLTINV: msg = "invalid operation"; break;192 default: msg = "unknown";193 } // choose194 cerr << "Computation error " << msg << " at location " << sfp->si_addr << endl195 << "Possible cause is constant-expression evaluation invalid." << endl;196 backtrace( 2 ); // skip first 2 stack frames197 abort(); // cause core dump for debugging198 } // sigFpeHandler199 200 182 static void sigAbortHandler( SIGPARMS ) { 201 183 backtrace( 6 ); // skip first 6 stack frames … … 211 193 Signal( SIGSEGV, sigSegvBusHandler, SA_SIGINFO ); 212 194 Signal( SIGBUS, sigSegvBusHandler, SA_SIGINFO ); 213 Signal( SIGFPE, sigFpeHandler, SA_SIGINFO );214 195 Signal( SIGABRT, sigAbortHandler, SA_SIGINFO ); 215 196 … … 313 294 } // if 314 295 315 PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );316 296 PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) ); 317 297 PASS( "Fix Names", CodeGen::fixNames( translationUnit ) ); … … 341 321 } // if 342 322 343 if( useNewAST ) { 344 if (Stats::Counters::enabled) { 345 ast::pass_visitor_stats.avg = Stats::Counters::build<Stats::Counters::AverageCounter<double>>("Average Depth - New"); 346 ast::pass_visitor_stats.max = Stats::Counters::build<Stats::Counters::MaxCounter<double>>("Max depth - New"); 347 } 348 auto transUnit = convert( move( translationUnit ) ); 349 PASS( "Resolve", ResolvExpr::resolve( transUnit ) ); 350 if ( exprp ) { 351 translationUnit = convert( move( transUnit ) ); 352 dump( translationUnit ); 353 return EXIT_SUCCESS; 354 } // if 355 356 forceFillCodeLocations( transUnit ); 357 358 PASS( "Fix Init", InitTweak::fix(transUnit, buildingLibrary())); 359 translationUnit = convert( move( transUnit ) ); 360 } else { 361 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) ); 362 if ( exprp ) { 363 dump( translationUnit ); 364 return EXIT_SUCCESS; 365 } 366 367 PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) ); 368 } 323 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) ); 324 if ( exprp ) { 325 dump( translationUnit ); 326 return EXIT_SUCCESS; 327 } // if 369 328 370 329 // fix ObjectDecl - replaces ConstructorInit nodes 330 PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) ); 371 331 if ( ctorinitp ) { 372 332 dump ( translationUnit ); … … 376 336 PASS( "Expand Unique Expr", Tuples::expandUniqueExpr( translationUnit ) ); // xxx - is this the right place for this? want to expand ASAP so tha, sequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused 377 337 378 PASS( "Translate Tries" , ControlStruct::translateTries( translationUnit ) );338 PASS( "Translate EHM" , ControlStruct::translateEHM( translationUnit ) ); 379 339 380 340 PASS( "Gen Waitfor" , Concurrency::generateWaitFor( translationUnit ) ); … … 465 425 466 426 467 static const char optstring[] = ":c:ghlLmNnp dOAP:S:twW:D:";427 static const char optstring[] = ":c:ghlLmNnpP:S:twW:D:"; 468 428 469 429 enum { PreludeDir = 128 }; … … 478 438 { "no-prelude", no_argument, nullptr, 'n' }, 479 439 { "prototypes", no_argument, nullptr, 'p' }, 480 { "deterministic-out", no_argument, nullptr, 'd' },481 { "old-ast", no_argument, nullptr, 'O'},482 { "new-ast", no_argument, nullptr, 'A'},483 440 { "print", required_argument, nullptr, 'P' }, 484 441 { "prelude-dir", required_argument, nullptr, PreludeDir }, … … 492 449 493 450 static const char * description[] = { 494 "diagnostic color: never, always, or auto.", // -c495 "wait for gdb to attach", // -g496 "print help message", // -h497 "generate libcfa.c", // -l498 "generate line marks", // -L499 "do not replace main", // -m500 "do not generate line marks", // -N501 "do not read prelude", // -n451 "diagnostic color: never, always, or auto.", // -c 452 "wait for gdb to attach", // -g 453 "print help message", // -h 454 "generate libcfa.c", // -l 455 "generate line marks", // -L 456 "do not replace main", // -m 457 "do not generate line marks", // -N 458 "do not read prelude", // -n 502 459 "generate prototypes for prelude functions", // -p 503 "only print deterministic output", // -d 504 "Use the old-ast", // -O 505 "Use the new-ast", // -A 506 "print", // -P 460 "print", // -P 507 461 "<directory> prelude directory for debug/nodebug", // no flag 508 462 "<option-list> enable profiling information:\n counters,heap,time,all,none", // -S 509 "building cfa standard lib", // -t510 "", // -w511 "", // -W512 "", // -D463 "building cfa standard lib", // -t 464 "", // -w 465 "", // -W 466 "", // -D 513 467 }; // description 514 468 … … 608 562 genproto = true; 609 563 break; 610 case 'd': // don't print non-deterministic output611 deterministic_output = true;612 break;613 case 'O': // don't print non-deterministic output614 useNewAST = false;615 break;616 case 'A': // don't print non-deterministic output617 useNewAST = true;618 break;619 564 case 'P': // print options 620 565 for ( int i = 0;; i += 1 ) { -
tests/.expect/alloc.txt
reef8dfb rbdfc032 2 2 CFA malloc 0xdeadbeef 3 3 CFA alloc 0xdeadbeef 4 CFA array alloc, fill 0xde 4 5 CFA alloc, fill dededede 5 6 CFA alloc, fill 3 … … 14 15 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 15 16 CFA array alloc, fill 0xef 16 0x deadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef17 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 17 18 CFA array alloc, fill from array 18 0x deadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef, 0xdeadbeef 0xdeadbeef,19 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 0xefefefef 0xefefefef, 19 20 20 21 C realloc 22 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 23 CFA realloc 24 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 25 26 CFA resize array alloc 21 27 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 22 CFA re alloc28 CFA resize array alloc 23 29 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 24 25 CFA realloc array alloc 30 CFA resize array alloc 26 31 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 27 CFA re allocarray alloc28 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 29 CFA re allocarray alloc32 CFA resize array alloc 33 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 34 CFA resize array alloc 30 35 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 31 CFA realloc array alloc, fill 32 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 33 CFA realloc array alloc, fill 34 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 35 CFA realloc array alloc, fill 36 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 36 CFA resize array alloc, fill 37 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 37 38 38 39 C memalign 42 42.5 -
tests/.expect/array.txt
reef8dfb rbdfc032 1 array.cfa: In function '_X4mainFi___1':2 array.cfa:55:9: note: #pragma message: Compiled -
tests/.expect/cast.txt
reef8dfb rbdfc032 1 cast.cfa: In function '_X4mainFi_iPPKc__1':2 cast.cfa:18:9: note: #pragma message: Compiled -
tests/.expect/copyfile.txt
reef8dfb rbdfc032 8 8 // 9 9 // Author : Peter A. Buhr 10 // Created On : Fri Jun 19 13:44:05 202010 // Created On : Tue Jul 16 16:47:22 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 17:58:03 202013 // Update Count : 412 // Last Modified On : Wed Jul 17 18:04:44 2019 13 // Update Count : 26 14 14 // 15 15 16 16 #include <fstream.hfa> 17 #include < exception.hfa>17 #include <stdlib.hfa> // new/delete 18 18 19 19 int main( int argc, char * argv[] ) { 20 ifstream in = stdin; // copy default files 21 ofstream out = stdout; 22 20 ifstream * in = &stdin; // default files 21 ofstream * out = &stdout; 23 22 try { 24 23 choose ( argc ) { 25 24 case 2, 3: 26 open( in, argv[1] );// open input file first as output creates file27 if ( argc == 3 ) open( out, argv[2] ); // do not create output unless input opens28 case 1: ; // use default files25 in = new( (const char *)argv[1] ); // open input file first as output creates file 26 if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent 27 case 1: ; // use default files 29 28 default: 30 exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]";29 exit | "Usage [ input-file (default stdin) [ output-file (default stdout) ] ]"; 31 30 } // choose 32 } catch( Open_Failure * ex ; ex->istream == &in ) { 33 exit | "Unable to open input file" | argv[1]; 34 } catch( Open_Failure * ex ; ex->ostream == &out ) { 35 close( in ); // optional 36 exit | "Unable to open output file" | argv[2]; 31 32 char ch; 33 *out | nlOff; // turn off auto newline 34 *in | nlOn; // turn on reading newline 35 36 for () { // read all characters 37 *in | ch; 38 if ( eof( *in ) ) break; // eof ? 39 *out | ch; 40 } // for 41 } finally { 42 if ( in != &stdin ) delete( in ); // close file, do not delete stdin! 43 if ( out != &stdout ) delete( out ); // close file, do not delete stdout! 37 44 } // try 38 39 out | nlOff; // turn off auto newline40 in | nlOn; // turn on reading newline41 42 char ch;43 for () { // read all characters44 in | ch;45 if ( eof( in ) ) break; // eof ?46 out | ch;47 } //for48 45 } // main 49 46 -
tests/.expect/declarationSpecifier.x64.txt
reef8dfb rbdfc032 1129 1129 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); } 1130 1130 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 1131 signed int _X13cfa_args_argci_1;1132 char **_X13cfa_args_argvPPc_1;1133 char **_X13cfa_args_envpPPc_1;1134 1131 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 1135 1132 __attribute__ ((unused)) signed int _X12_retval_maini_1; 1136 1133 { 1137 ((void)(_X13cfa_args_argci_1=_X4argci_1));1138 }1139 1140 {1141 ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));1142 }1143 1144 {1145 ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));1146 }1147 1148 {1149 1134 signed int _tmp_cp_ret4; 1150 1135 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/declarationSpecifier.x86.txt
reef8dfb rbdfc032 1129 1129 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); } 1130 1130 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 1131 signed int _X13cfa_args_argci_1;1132 char **_X13cfa_args_argvPPc_1;1133 char **_X13cfa_args_envpPPc_1;1134 1131 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 1135 1132 __attribute__ ((unused)) signed int _X12_retval_maini_1; 1136 1133 { 1137 ((void)(_X13cfa_args_argci_1=_X4argci_1));1138 }1139 1140 {1141 ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));1142 }1143 1144 {1145 ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));1146 }1147 1148 {1149 1134 signed int _tmp_cp_ret4; 1150 1135 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/enum.txt
reef8dfb rbdfc032 1 done -
tests/.expect/expression.txt
reef8dfb rbdfc032 1 expression.cfa: In function '_X4mainFi___1':2 expression.cfa:89:9: note: #pragma message: Compiled -
tests/.expect/forall.txt
reef8dfb rbdfc032 1 forall.cfa: In function '_X4mainFi___1':2 forall.cfa:218:9: note: #pragma message: Compiled -
tests/.expect/gccExtensions.x64.txt
reef8dfb rbdfc032 321 321 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); } 322 322 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 323 signed int _X13cfa_args_argci_1;324 char **_X13cfa_args_argvPPc_1;325 char **_X13cfa_args_envpPPc_1;326 323 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 327 324 __attribute__ ((unused)) signed int _X12_retval_maini_1; 328 325 { 329 ((void)(_X13cfa_args_argci_1=_X4argci_1));330 }331 332 {333 ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));334 }335 336 {337 ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));338 }339 340 {341 326 signed int _tmp_cp_ret4; 342 327 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/gccExtensions.x86.txt
reef8dfb rbdfc032 299 299 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return _X4mainFi_iPPKc__1((signed int )argc, (const char **)argv); } 300 300 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 301 signed int _X13cfa_args_argci_1;302 char **_X13cfa_args_argvPPc_1;303 char **_X13cfa_args_envpPPc_1;304 301 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 305 302 __attribute__ ((unused)) signed int _X12_retval_maini_1; 306 303 { 307 ((void)(_X13cfa_args_argci_1=_X4argci_1));308 }309 310 {311 ((void)(_X13cfa_args_argvPPc_1=_X4argvPPc_1));312 }313 314 {315 ((void)(_X13cfa_args_envpPPc_1=_X4envpPPc_1));316 }317 318 {319 304 signed int _tmp_cp_ret4; 320 305 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/heap.txt
reef8dfb rbdfc032 1 done -
tests/.expect/identFuncDeclarator.txt
reef8dfb rbdfc032 1 identFuncDeclarator.cfa: In function '_X4mainFi___1':2 identFuncDeclarator.cfa:116:9: note: #pragma message: Compiled -
tests/.expect/identParamDeclarator.txt
reef8dfb rbdfc032 1 done -
tests/.expect/labelledExit.txt
reef8dfb rbdfc032 1 labelledExit.cfa: In function '_X4mainFi_iPPKc__1':2 labelledExit.cfa:183:9: note: #pragma message: Compiled -
tests/.expect/limits.txt
reef8dfb rbdfc032 1 limits.cfa: In function '_X4mainFi_iPPKc__1':2 limits.cfa:154:9: note: #pragma message: Compiled -
tests/.expect/maybe.txt
reef8dfb rbdfc032 1 done -
tests/.expect/minmax.txt
reef8dfb rbdfc032 1 1 char z a min a 2 signed int 4 -3 min -32 signed int 4 3 min 3 3 3 unsigned int 4 3 min 3 4 signed long int 4 -3 min -34 signed long int 4 3 min 3 5 5 unsigned long int 4 3 min 3 6 signed long long int 4 -3 min -36 signed long long int 4 3 min 3 7 7 unsigned long long int 4 3 min 3 8 8 float 4. 3.1 min 3.1 … … 11 11 12 12 char z a max z 13 signed int 4 -3 max 413 signed int 4 3 max 4 14 14 unsigned int 4 3 max 4 15 signed long int 4 -3 max 415 signed long int 4 3 max 4 16 16 unsigned long int 4 3 max 4 17 signed long long int 4 -3 max 417 signed long long int 4 3 max 4 18 18 unsigned long long int 4 3 max 4 19 19 float 4. 3.1 max 4. -
tests/.expect/nested-types-ERR1.txt
reef8dfb rbdfc032 1 nested-types.cfa: 83:1 error: Use of undefined type T1 nested-types.cfa:70:1 error: Use of undefined type T -
tests/.expect/nested-types-ERR2.txt
reef8dfb rbdfc032 1 nested-types.cfa: 86:1 error: Use of undefined global type Z2 nested-types.cfa: 87:1 error: Qualified type requires an aggregate on the left, but has: signed int3 nested-types.cfa: 88:1 error: Undefined type in qualified type: Qualified Type:1 nested-types.cfa:73:1 error: Use of undefined global type Z 2 nested-types.cfa:74:1 error: Qualified type requires an aggregate on the left, but has: signed int 3 nested-types.cfa:75:1 error: Undefined type in qualified type: Qualified Type: 4 4 instance of struct S with body 1 5 5 instance of type Z (not function type) -
tests/.expect/nested-types.txt
reef8dfb rbdfc032 1 nested-types.cfa: In function '_X4mainFi___1':2 nested-types.cfa:102:9: note: #pragma message: Compiled -
tests/.expect/numericConstants.txt
reef8dfb rbdfc032 1 numericConstants.cfa: In function '_X4mainFi___1':2 numericConstants.cfa:68:9: note: #pragma message: Compiled -
tests/.expect/operators.txt
reef8dfb rbdfc032 1 done -
tests/.expect/rational.txt
reef8dfb rbdfc032 1 1 constructor 2 3/1 4/1 0/1 0/1 1/12 3/1 4/1 0/1 3 3 1/2 5/7 4 4 2/3 -3/2 -
tests/.expect/result.txt
reef8dfb rbdfc032 1 done -
tests/.expect/stdincludes.txt
reef8dfb rbdfc032 1 stdincludes.cfa: In function '_X4mainFi___1':2 stdincludes.cfa:52:9: note: #pragma message: Compiled -
tests/.expect/switch.txt
reef8dfb rbdfc032 1 switch.cfa: In function '_X4mainFi___1':2 switch.cfa:105:9: note: #pragma message: Compiled -
tests/.expect/time.txt
reef8dfb rbdfc032 1 1 10800 2 3.375 12 1.00001 2 0.125 0.0333333333333333 3.375 12000. 1000010.3 2 0 2 3.375 4 3 7 7 7 -
tests/.expect/typedefRedef-ERR1.txt
reef8dfb rbdfc032 1 1 typedefRedef.cfa:4:1 error: Cannot redefine typedef: Foo 2 typedefRedef.cfa: 59:1 error: Cannot redefine typedef: ARR2 typedefRedef.cfa:60:1 error: Cannot redefine typedef: ARR -
tests/.expect/typedefRedef.txt
reef8dfb rbdfc032 1 typedefRedef.cfa: In function '_X4mainFi___1':2 typedefRedef.cfa:71:9: note: #pragma message: Compiled -
tests/.expect/typeof.txt
reef8dfb rbdfc032 1 done -
tests/.expect/variableDeclarator.txt
reef8dfb rbdfc032 1 variableDeclarator.cfa: In function '_X4mainFi_iPPKc__1':2 variableDeclarator.cfa:182:9: note: #pragma message: Compiled -
tests/.expect/voidPtr.txt
reef8dfb rbdfc032 1 done -
tests/.in/copyfile.txt
reef8dfb rbdfc032 8 8 // 9 9 // Author : Peter A. Buhr 10 // Created On : Fri Jun 19 13:44:05 202010 // Created On : Tue Jul 16 16:47:22 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 19 17:58:03 202013 // Update Count : 412 // Last Modified On : Wed Jul 17 18:04:44 2019 13 // Update Count : 26 14 14 // 15 15 16 16 #include <fstream.hfa> 17 #include < exception.hfa>17 #include <stdlib.hfa> // new/delete 18 18 19 19 int main( int argc, char * argv[] ) { 20 ifstream in = stdin; // copy default files 21 ofstream out = stdout; 22 20 ifstream * in = &stdin; // default files 21 ofstream * out = &stdout; 23 22 try { 24 23 choose ( argc ) { 25 24 case 2, 3: 26 open( in, argv[1] );// open input file first as output creates file27 if ( argc == 3 ) open( out, argv[2] ); // do not create output unless input opens28 case 1: ; // use default files25 in = new( (const char *)argv[1] ); // open input file first as output creates file 26 if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent 27 case 1: ; // use default files 29 28 default: 30 exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]";29 exit | "Usage [ input-file (default stdin) [ output-file (default stdout) ] ]"; 31 30 } // choose 32 } catch( Open_Failure * ex ; ex->istream == &in ) { 33 exit | "Unable to open input file" | argv[1]; 34 } catch( Open_Failure * ex ; ex->ostream == &out ) { 35 close( in ); // optional 36 exit | "Unable to open output file" | argv[2]; 31 32 char ch; 33 *out | nlOff; // turn off auto newline 34 *in | nlOn; // turn on reading newline 35 36 for () { // read all characters 37 *in | ch; 38 if ( eof( *in ) ) break; // eof ? 39 *out | ch; 40 } // for 41 } finally { 42 if ( in != &stdin ) delete( in ); // close file, do not delete stdin! 43 if ( out != &stdout ) delete( out ); // close file, do not delete stdout! 37 44 } // try 38 39 out | nlOff; // turn off auto newline40 in | nlOn; // turn on reading newline41 42 char ch;43 for () { // read all characters44 in | ch;45 if ( eof( in ) ) break; // eof ?46 out | ch;47 } //for48 45 } // main 49 46 -
tests/.in/manipulatorsInput.txt
reef8dfb rbdfc032 27 27 3.5 3.5 3.456E+23.456E+2 -0x1.2p-3 3.5 0X1.23p3 3.5 28 28 3.5 3.5 3.456E+23.456E+2 -0x1.2p-3 3.5 0X1.23p3 3.5 29 25 -25 4279830 1402432282 150585019699324451531 39474975866324913551134232 1293515469620470611239183439433 34 42385914912841041439537283499455135 36 37 1388901659863974706323493549705763158738 17014118346046923173168730371588410572739 34028236692093846346337460743176821145540 -34028236692093846346337460743176821145541 34028236692093846346337460743176821145599942 1234567890123456789 -1234567890123456789 -
tests/Makefile.am
reef8dfb rbdfc032 11 11 ## Created On : Sun May 31 09:08:15 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Fri Oct 9 23:13:07 202014 ## Update Count : 8613 ## Last Modified On : Tue Nov 20 11:18:51 2018 14 ## Update Count : 68 15 15 ############################################################################### 16 16 … … 18 18 ACLOCAL_AMFLAGS = -I automake 19 19 20 include $(top_srcdir)/tools/build/cfa.make 21 22 DEFAULT_INCLUDES = -I${abs_srcdir} 20 include $(top_srcdir)/src/cfa.make 23 21 24 22 debug=yes … … 38 36 # since automake doesn't have support for CFA we have to 39 37 AM_CFLAGS = $(if $(test), 2> $(test), ) \ 40 -fdebug-prefix-map=$(abspath ${abs_srcdir})= \41 -fdebug-prefix-map=/tmp= \42 -fno-diagnostics-show-caret \43 38 -g \ 44 39 -Wall \ … … 47 42 -DIN_DIR="${abs_srcdir}/.in/" 48 43 49 AM_CFAFLAGS = -XCFA --deterministic-out50 51 44 # get the desired cfa to test 52 45 TARGET_CFA = $(if $(filter $(installed),yes), @CFACC_INSTALL@, @CFACC@) 53 46 54 47 # adjust CC to current flags 55 CC = LC_ALL=C $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) ${ARCH_FLAGS} ${AST_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS} ${AST_FLAGS})48 CC = $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) ${ARCH_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS}) 56 49 CFACC = $(CC) 57 50 … … 60 53 61 54 # adjusted CC but without the actual distcc call 62 CFACCLOCAL = $(if $(DISTCC_CFA_PATH),$(DISTCC_CFA_PATH) ${ARCH_FLAGS} ${AST_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS} ${AST_FLAGS}) 63 CFACCLINK = $(CFACCLOCAL) -quiet $(if $(test), 2> $(test), ) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g')) 55 CFACCLOCAL = $(if $(DISTCC_CFA_PATH),$(DISTCC_CFA_PATH) ${ARCH_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS}) 64 56 65 57 PRETTY_PATH=mkdir -p $(dir $(abspath ${@})) && cd ${srcdir} && … … 68 60 .INTERMEDIATE: .validate .validate.cfa 69 61 EXTRA_PROGRAMS = avl_test .dummy_hack # build but do not install 70 EXTRA_DIST = test.py \71 pybin/__init__.py \72 pybin/print-core.gdb \73 pybin/settings.py \74 pybin/test_run.py \75 pybin/tools.py \76 long_tests.hfa \77 .in/io.data \78 avltree/avl.h \79 avltree/avl-private.h \80 concurrent/clib.c \81 exceptions/with-threads.hfa \82 exceptions/except-io.hfa83 84 dist-hook:85 echo "Gathering test files"86 for file in `${TEST_PY} --list-dist`; do \87 if test -f ${srcdir}/$${file}; then \88 $(MKDIR_P) $$(dirname ${distdir}/$${file}); \89 cp -df ${srcdir}/$${file} ${distdir}/$${file}; \90 fi; \91 done92 62 93 63 avl_test_SOURCES = avltree/avl_test.cfa avltree/avl0.cfa avltree/avl1.cfa avltree/avl2.cfa avltree/avl3.cfa avltree/avl4.cfa avltree/avl-private.cfa 94 64 # automake doesn't know we still need C/CPP rules so pretend like we have a C program 95 nodist__dummy_hack_SOURCES = .dummy_hack.c .dummy_hackxx.cpp65 _dummy_hack_SOURCES = .dummy_hack.c .dummy_hackxx.cpp 96 66 97 67 #---------------------------------------------------------------------------------------------------------------- … … 102 72 @+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program 103 73 104 mostlyclean-local : 105 find ${builddir} -not -path './__pycache__/*' -path '*.o' -delete 106 find ${builddir} -not -path './__pycache__/*' -path '*/.err/*.log' -delete 107 find ${builddir} -not -path './__pycache__/*' -path '*/.out/*.log' -delete 74 clean-local : 108 75 rm -f ${EXTRA_PROGRAMS} 109 rm -rf __pycache__110 111 distclean-local :112 find ${builddir} -path '*.Po' -delete113 76 114 77 list : … … 143 106 % : %.cfa $(CFACCBIN) 144 107 $(CFACOMPILETEST) -c -o $(abspath ${@}).o 145 $(CFACCLINK) ${@}.o -o $(abspath ${@}) 146 rm $(abspath ${@}).o 108 $(CFACCLOCAL) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g')) $(abspath ${@}).o -o $(abspath ${@}) 147 109 148 110 # implicit rule for c++ test … … 163 125 $(CFACOMPILETEST) -CFA -XCFA -p -c -fsyntax-only -o $(abspath ${@}) 164 126 127 # Use for tests where the make command is expected to succeed but the expected.txt should be compared to stderr 128 EXPECT_STDERR = builtins/sync warnings/self-assignment 129 $(EXPECT_STDERR): % : %.cfa $(CFACCBIN) 130 $(CFACOMPILETEST) -c -fsyntax-only 2> $(abspath ${@}) 131 165 132 #------------------------------------------------------------------------------ 166 133 # CUSTOM TARGET 167 134 #------------------------------------------------------------------------------ 168 # tests that just validate syntax and compiler output should be compared to stderr169 CFACOMPILE_SYNTAX = $(CFACOMPILETEST) -Wno-unused-variable -Wno-unused-label -c -fsyntax-only -o $(abspath ${@})170 171 SYNTAX_ONLY_CODE = expression typedefRedef variableDeclarator switch numericConstants identFuncDeclarator forall \172 init1 limits nested-types stdincludes cast labelledExit array builtins/sync warnings/self-assignment173 $(SYNTAX_ONLY_CODE): % : %.cfa $(CFACCBIN)174 $(CFACOMPILE_SYNTAX)175 $(if $(test), cp $(test) $(abspath ${@}), )176 177 135 # expected failures 178 # use custom target since they require a custom define *and* have a name that doesn't match the file136 # use custom target since they require a custom define and custom dependencies 179 137 alloc-ERROR : alloc.cfa $(CFACCBIN) 180 $(CFACOMPILE_SYNTAX) -DERR1 181 -cp $(test) $(abspath ${@}) 182 183 init1-ERROR : init1.cfa $(CFACCBIN) 184 $(CFACOMPILE_SYNTAX) -DERR1 185 -cp $(test) $(abspath ${@}) 138 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 186 139 187 140 typedefRedef-ERR1 : typedefRedef.cfa $(CFACCBIN) 188 $(CFACOMPILE_SYNTAX) -DERR1 189 -cp $(test) $(abspath ${@}) 141 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 190 142 191 143 nested-types-ERR1 : nested-types.cfa $(CFACCBIN) 192 $(CFACOMPILE_SYNTAX) -DERR1 193 -cp $(test) $(abspath ${@}) 144 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 194 145 195 146 nested-types-ERR2 : nested-types.cfa $(CFACCBIN) 196 $(CFACOMPILE_SYNTAX) -DERR2 197 -cp $(test) $(abspath ${@}) 147 $(CFACOMPILETEST) -DERR2 -c -fsyntax-only -o $(abspath ${@}) 198 148 199 149 raii/memberCtors-ERR1 : raii/memberCtors.cfa $(CFACCBIN) 200 $(CFACOMPILE_SYNTAX) -DERR1 201 -cp $(test) $(abspath ${@}) 150 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 202 151 203 152 raii/ctor-autogen-ERR1 : raii/ctor-autogen.cfa $(CFACCBIN) 204 $(CFACOMPILE_SYNTAX) -DERR1 205 -cp $(test) $(abspath ${@}) 153 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 206 154 207 155 raii/dtor-early-exit-ERR1 : raii/dtor-early-exit.cfa $(CFACCBIN) 208 $(CFACOMPILE_SYNTAX) -DERR1 209 -cp $(test) $(abspath ${@}) 156 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 210 157 211 158 raii/dtor-early-exit-ERR2 : raii/dtor-early-exit.cfa $(CFACCBIN) 212 $(CFACOMPILE_SYNTAX) -DERR2 213 -cp $(test) $(abspath ${@}) 214 215 # Exception Tests 216 # Test with libcfathread; it changes how storage works. 217 218 exceptions/%-threads : exceptions/%.cfa $(CFACCBIN) 219 $(CFACOMPILETEST) -include exceptions/with-threads.hfa -c -o $(abspath ${@}).o 220 $(CFACCLOCAL) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g')) $(abspath ${@}).o -o $(abspath ${@}) 221 222 # Linking tests 223 # Meta tests to make sure we see linking errors (can't compile with -O2 since it may multiply number of calls) 224 linking/linkerror : linking/linkerror.cfa $(CFACCBIN) 225 $(CFACOMPILETEST) -O0 -c -o $(abspath ${@}).o 226 $(CFACCLINK) -O0 ${@}.o -o $(abspath ${@}) 227 rm $(abspath ${@}).o 159 $(CFACOMPILETEST) -DERR2 -c -fsyntax-only -o $(abspath ${@}) 228 160 229 161 #------------------------------------------------------------------------------ -
tests/alloc.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Feb 3 07:56:22 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Nov 12 10:02:18 202013 // Update Count : 4 3212 // Last Modified On : Fri Nov 22 15:34:19 2019 13 // Update Count : 404 14 14 // 15 15 … … 28 28 size_t dim = 10; 29 29 char fill = '\xde'; 30 int * ip, * ip1;30 int * p, * p1; 31 31 32 32 // allocation, non-array types 33 33 34 ip = (int *)malloc( sizeof(*ip) ); // C malloc, type unsafe 35 *ip = 0xdeadbeef; 36 printf( "C malloc %#x\n", *ip ); 37 free( ip ); 38 39 ip = malloc(); // CFA malloc, type safe 40 *ip = 0xdeadbeef; 41 printf( "CFA malloc %#x\n", *ip ); 42 free( ip ); 43 44 ip = alloc(); // CFA alloc, type safe 45 *ip = 0xdeadbeef; 46 printf( "CFA alloc %#x\n", *ip ); 47 free( ip ); 48 49 ip = alloc( fill`fill ); // CFA alloc, fill 50 printf( "CFA alloc, fill %08x\n", *ip ); 51 free( ip ); 52 53 ip = alloc( 3`fill ); // CFA alloc, fill 54 printf( "CFA alloc, fill %d\n", *ip ); 55 free( ip ); 34 p = (int *)malloc( sizeof(*p) ); // C malloc, type unsafe 35 *p = 0xdeadbeef; 36 printf( "C malloc %#x\n", *p ); 37 free( p ); 38 39 p = malloc(); // CFA malloc, type safe 40 *p = 0xdeadbeef; 41 printf( "CFA malloc %#x\n", *p ); 42 free( p ); 43 44 p = alloc(); // CFA alloc, type safe 45 *p = 0xdeadbeef; 46 printf( "CFA alloc %#x\n", *p ); 47 free( p ); 48 49 p = alloc_set( fill ); // CFA alloc, fill 50 printf( "CFA array alloc, fill %#hhx\n", fill ); 51 printf( "CFA alloc, fill %08x\n", *p ); 52 free( p ); 53 54 p = alloc_set( 3 ); // CFA alloc, fill 55 printf( "CFA alloc, fill %d\n", *p ); 56 free( p ); 56 57 57 58 … … 59 60 printf( "\n" ); 60 61 61 ip = (int *)calloc( dim, sizeof( *ip ) );// C array calloc, type unsafe62 p = (int *)calloc( dim, sizeof( *p ) ); // C array calloc, type unsafe 62 63 printf( "C array calloc, fill 0\n" ); 63 for ( i; dim ) { printf( "%#x ", ip[i] ); }64 printf( "\n" ); 65 free( ip );66 67 ip = calloc( dim );// CFA array calloc, type safe64 for ( i; dim ) { printf( "%#x ", p[i] ); } 65 printf( "\n" ); 66 free( p ); 67 68 p = calloc( dim ); // CFA array calloc, type safe 68 69 printf( "CFA array calloc, fill 0\n" ); 69 for ( i; dim ) { printf( "%#x ", ip[i] ); }70 printf( "\n" ); 71 free( ip );72 73 ip = alloc( dim );// CFA array alloc, type safe74 for ( i; dim ) { ip[i] = 0xdeadbeef; }70 for ( i; dim ) { printf( "%#x ", p[i] ); } 71 printf( "\n" ); 72 free( p ); 73 74 p = alloc( dim ); // CFA array alloc, type safe 75 for ( i; dim ) { p[i] = 0xdeadbeef; } 75 76 printf( "CFA array alloc, no fill\n" ); 76 for ( i; dim ) { printf( "%#x ", ip[i] ); }77 printf( "\n" ); 78 free( ip );79 80 ip = alloc( 2 * dim, fill`fill );// CFA array alloc, fill77 for ( i; dim ) { printf( "%#x ", p[i] ); } 78 printf( "\n" ); 79 free( p ); 80 81 p = alloc_set( 2 * dim, fill ); // CFA array alloc, fill 81 82 printf( "CFA array alloc, fill %#hhx\n", fill ); 82 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); }83 printf( "\n" ); 84 free( ip );85 86 ip = alloc( 2 * dim, ((int)0xdeadbeef)`fill); // CFA array alloc, fill83 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); } 84 printf( "\n" ); 85 free( p ); 86 87 p = alloc_set( 2 * dim, 0xdeadbeef ); // CFA array alloc, fill 87 88 printf( "CFA array alloc, fill %#hhx\n", 0xdeadbeef ); 88 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); }89 printf( "\n" ); 90 // do not free 91 92 ip1 = alloc( 2 * dim, [ip, 2 * dim]`fill );// CFA array alloc, fill89 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); } 90 printf( "\n" ); 91 // do not free 92 93 p1 = alloc_set( 2 * dim, p ); // CFA array alloc, fill 93 94 printf( "CFA array alloc, fill from array\n" ); 94 for ( i; 2 * dim ) { printf( "%#x %#x, ", ip[i], ip1[i] ); }95 free( ip1 );96 printf( "\n" ); 97 98 99 // re alloc, non-array types100 printf( "\n" ); 101 102 ip = (int *)realloc( ip, dim * sizeof(*ip) );// C realloc95 for ( i; 2 * dim ) { printf( "%#x %#x, ", p[i], p1[i] ); } 96 free( p1 ); 97 printf( "\n" ); 98 99 100 // resize, non-array types 101 printf( "\n" ); 102 103 p = (int *)realloc( p, dim * sizeof(*p) ); // C realloc 103 104 printf( "C realloc\n" ); 104 for ( i; dim ) { printf( "%#x ", ip[i] ); }105 printf( "\n" ); 106 // do not free 107 108 ip = realloc( ip, 2 * dim * sizeof(*ip) );// CFA realloc109 for ( i; dim ~ 2 * dim ) { ip[i] = 0x1010101; }105 for ( i; dim ) { printf( "%#x ", p[i] ); } 106 printf( "\n" ); 107 // do not free 108 109 p = realloc( p, 2 * dim * sizeof(*p) ); // CFA realloc 110 for ( i; dim ~ 2 * dim ) { p[i] = 0x1010101; } 110 111 printf( "CFA realloc\n" ); 111 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); } 112 printf( "\n" ); 113 // do not free 114 115 116 // realloc, array types 117 printf( "\n" ); 118 119 ip = alloc( dim, ip`realloc ); // CFA realloc array alloc 120 for ( i; dim ) { ip[i] = 0xdeadbeef; } 121 printf( "CFA realloc array alloc\n" ); 122 for ( i; dim ) { printf( "%#x ", ip[i] ); } 123 printf( "\n" ); 124 // do not free 125 126 ip = alloc( 2 * dim, ip`realloc ); // CFA realloc array alloc 127 for ( i; dim ~ 2 * dim ) { ip[i] = 0x1010101; } // fill upper part 128 printf( "CFA realloc array alloc\n" ); 129 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); } 130 printf( "\n" ); 131 // do not free 132 133 ip = alloc( dim, ip`realloc ); // CFA realloc array alloc 134 printf( "CFA realloc array alloc\n" ); 135 for ( i; dim ) { printf( "%#x ", ip[i] ); } 136 printf( "\n" ); 137 // do not free 138 139 ip = alloc( 3 * dim, ip`realloc, fill`fill ); // CFA realloc array alloc, fill 140 printf( "CFA realloc array alloc, fill\n" ); 141 for ( i; 3 * dim ) { printf( "%#x ", ip[i] ); } 142 printf( "\n" ); 143 // do not free 144 145 ip = alloc( dim, ip`realloc, fill`fill ); // CFA realloc array alloc, fill 146 printf( "CFA realloc array alloc, fill\n" ); 147 for ( i; dim ) { printf( "%#x ", ip[i] ); } 148 printf( "\n" ); 149 // do not free 150 151 ip = alloc( 3 * dim, ip`realloc, fill`fill ); // CFA realloc array alloc, fill 152 printf( "CFA realloc array alloc, fill\n" ); 153 for ( i; 3 * dim ) { printf( "%#x ", ip[i] ); } 154 printf( "\n" ); 155 // do not free 156 #if 0 // FIX ME 157 ip = alloc( 5 * dim, ip`realloc, 5`fill ); // CFA realloc array alloc, 5 158 printf( "CFA realloc array alloc, 5\n" ); 159 for ( i; 5 * dim ) { printf( "%#x ", ip[i] ); } 160 printf( "\n" ); 161 // do not free 162 163 ip = alloc( dim, ip`realloc, 5`fill ); // CFA realloc array alloc, 5 164 printf( "CFA realloc array alloc, 5\n" ); 165 for ( i; dim ) { printf( "%#x ", ip[i] ); } 166 printf( "\n" ); 167 // do not free 168 169 ip = alloc( 5 * dim, ip`realloc, 5`fill ); // CFA realloc array alloc, 5 170 printf( "CFA realloc array alloc, 5\n" ); 171 for ( i; 5 * dim ) { printf( "%#x ", ip[i] ); } 172 printf( "\n" ); 173 #endif // 0 174 free( ip ); 175 176 // resize, non-array types 177 178 struct S { 179 int a[5]; 180 }; 181 182 ip = alloc(); 183 *ip = 5; 184 double * dp = alloc( ip`resize ); 185 *dp = 5.5; 186 S * sp = alloc( dp`resize ); 187 *sp = (S){ {0, 1, 2, 3, 4} }; 188 ip = alloc( sp`resize ); 189 *ip = 3; 190 free( ip ); 112 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); } 113 printf( "\n" ); 114 // do not free 191 115 192 116 193 117 // resize, array types 194 195 ip = alloc( 5 ); 196 for ( i; 5 ) { ip[i] = 5; } 197 dp = alloc( 5, ip`resize ); 198 for ( i; 5 ) { dp[i] = 5.5; } 199 sp = alloc( 5, dp`resize ); 200 for ( i; 5 ) { sp[i] = (S){ {0, 1, 2, 3, 4} }; } 201 ip = alloc( 3, sp`resize ); 202 for ( i; 3 ) { ip[i] = 3; } 203 ip = alloc( 7, ip`realloc ); 204 for ( i; 7 ) { ip[i] = 7; } 205 ip = alloc( 7, ip`resize ); 206 for ( i; 7 ) { ip[i] = 7; } 207 free( ip ); 208 209 210 int const_count, dest_count; 118 printf( "\n" ); 119 120 p = alloc( p, dim ); // CFA resize array alloc 121 for ( i; dim ) { p[i] = 0xdeadbeef; } 122 printf( "CFA resize array alloc\n" ); 123 for ( i; dim ) { printf( "%#x ", p[i] ); } 124 printf( "\n" ); 125 // do not free 126 127 p = alloc( p, 2 * dim ); // CFA resize array alloc 128 for ( i; dim ~ 2 * dim ) { p[i] = 0x1010101; } // fill upper part 129 printf( "CFA resize array alloc\n" ); 130 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); } 131 printf( "\n" ); 132 // do not free 133 134 p = alloc( p, dim ); // CFA resize array alloc 135 printf( "CFA resize array alloc\n" ); 136 for ( i; dim ) { printf( "%#x ", p[i] ); } 137 printf( "\n" ); 138 // do not free 139 140 p = alloc_set( p, 3 * dim, fill ); // CFA resize array alloc, fill 141 printf( "CFA resize array alloc\n" ); 142 for ( i; 3 * dim ) { printf( "%#x ", p[i] ); } 143 printf( "\n" ); 144 // do not free 145 146 p = alloc_set( p, dim, fill ); // CFA resize array alloc, fill 147 printf( "CFA resize array alloc\n" ); 148 for ( i; dim ) { printf( "%#x ", p[i] ); } 149 printf( "\n" ); 150 // do not free 151 152 p = alloc_set( p, 3 * dim, fill ); // CFA resize array alloc, fill 153 printf( "CFA resize array alloc, fill\n" ); 154 for ( i; 3 * dim ) { printf( "%#x ", p[i] );; } 155 printf( "\n" ); 156 free( p ); 157 158 211 159 struct Struct { int x; double y; }; 212 void ?{}( Struct & a ) { // construct213 a.[ x, y ] = [ -1, -1.0 ];214 }215 void ?{}( Struct & a, int x, double y ) { // initialize216 a.[ x, y ] = [ x, y ];217 const_count++;218 }219 void ^?{}( Struct & a ) { dest_count++; } // destruct220 160 Struct st, st1, sta[dim], sta1[dim], * stp, * stp1; 221 161 … … 229 169 free( stp ); 230 170 231 stp = &(*memalign( Alignment )){ 42, 42.5 }; // CFA memalign171 stp = &(*memalign( Alignment )){ 42, 42.5 }; // CFA memalign 232 172 assert( (uintptr_t)stp % Alignment == 0 ); 233 173 printf( "CFA memalign %d %g\n", stp->x, stp->y ); … … 246 186 free( stp ); 247 187 248 stp = &(*alloc ( Alignment`align)){ 42, 42.5 }; // CFA alloc_align188 stp = &(*alloc_align( Alignment)){ 42, 42.5 }; // CFA alloc_align 249 189 assert( (uintptr_t)stp % Alignment == 0 ); 250 190 printf( "CFA alloc_align %d %g\n", stp->x, stp->y ); 251 191 free( stp ); 252 192 253 stp = &(*alloc ( Alignment`align)){ 42, 42.5 }; // CFA alloc_align193 stp = &(*alloc_align( Alignment )){ 42, 42.5 }; // CFA alloc_align 254 194 assert( (uintptr_t)stp % Alignment == 0 ); 255 195 printf( "CFA alloc_align %d %g\n", stp->x, stp->y ); 256 196 free( stp ); 257 197 258 stp = alloc ( Alignment`align, fill`fill ); // CFA memalign, fill198 stp = alloc_align_set( Alignment, fill ); // CFA memalign, fill 259 199 assert( (uintptr_t)stp % Alignment == 0 ); 260 200 printf( "CFA alloc_align fill %#x %a\n", stp->x, stp->y ); 261 201 free( stp ); 262 202 263 stp = alloc ( Alignment`align, (Struct){ 42, 42.5 }`fill); // CFA memalign, fill203 stp = alloc_align_set( Alignment, (Struct){ 42, 42.5 } ); // CFA memalign, fill 264 204 assert( (uintptr_t)stp % Alignment == 0 ); 265 205 printf( "CFA alloc_align fill %d %g\n", stp->x, stp->y ); 266 206 // do not free 267 207 268 stp = &(*alloc ( stp`realloc, 4096`align)){ 42, 42.5 }; // CFA realign208 stp = &(*alloc_align( stp, 4096 )){ 42, 42.5 }; // CFA realign 269 209 assert( (uintptr_t)stp % 4096 == 0 ); 270 210 printf( "CFA alloc_align %d %g\n", stp->x, stp->y ); … … 275 215 printf( "\n" ); 276 216 277 stp = alloc ( dim, Alignment`align); // CFA array memalign217 stp = alloc_align( Alignment, dim ); // CFA array memalign 278 218 assert( (uintptr_t)stp % Alignment == 0 ); 279 219 for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; } … … 283 223 free( stp ); 284 224 285 stp = alloc ( dim, Alignment`align, fill`fill ); // CFA array memalign, fill225 stp = alloc_align_set( Alignment, dim, fill ); // CFA array memalign, fill 286 226 assert( (uintptr_t)stp % Alignment == 0 ); 287 227 printf( "CFA array alloc_align, fill\n" ); … … 290 230 free( stp ); 291 231 292 stp = alloc ( dim, Alignment`align, ((Struct){ 42, 42.5 })`fill); // CFA array memalign, fill232 stp = alloc_align_set( Alignment, dim, (Struct){ 42, 42.5 } ); // CFA array memalign, fill 293 233 assert( (uintptr_t)stp % Alignment == 0 ); 294 234 printf( "CFA array alloc_align, fill\n" ); … … 297 237 // do not free 298 238 299 stp1 = alloc ( dim, Alignment`align, [stp, dim]`fill );// CFA array memalign, fill239 stp1 = alloc_align_set( Alignment, dim, stp ); // CFA array memalign, fill 300 240 assert( (uintptr_t)stp % Alignment == 0 ); 301 241 printf( "CFA array alloc_align, fill array\n" ); … … 304 244 free( stp1 ); 305 245 306 stp = alloc ( dim, stp`realloc, 4096`align); // CFA aligned realloc array246 stp = alloc_align( stp, 4096, dim ); // CFA aligned realloc array 307 247 assert( (uintptr_t)stp % 4096 == 0 ); 308 248 for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; } … … 335 275 printf( "\n" ); 336 276 277 337 278 // new, non-array types 338 279 printf( "\n" ); 339 280 340 const_count = dest_count = 0;341 281 stp = new( 42, 42.5 ); 342 assert( const_count == 1 && dest_count == 0 ); // assertion for testing343 282 stp1 = new( 42, 42.5 ); 344 assert( const_count == 2 && dest_count == 0 ); // assertion for testing345 346 283 printf( "CFA new initialize\n%d %g %d %g\n", stp->x, stp->y, stp1->x, stp1->y ); 347 284 delete( stp, stp1 ); 348 assert( const_count == 2 && dest_count == 2 ); // assertion for testing349 285 350 286 // new, array types 351 287 stp = anew( dim, 42, 42.5 ); 352 assert( const_count == 2 + dim && dest_count == 2 ); // assertion for testing353 288 printf( "CFA array new initialize\n" ); 354 289 for ( i; dim ) { printf( "%d %g, ", stp[i].x, stp[i].y ); } 355 290 printf( "\n" ); 356 357 291 stp1 = anew( dim, 42, 42.5 ); 358 assert( const_count == 2 + 2 * dim && dest_count == 2 ); // assertion for testing359 292 for ( i; dim ) { printf( "%d %g, ", stp1[i].x, stp1[i].y ); } 360 293 printf( "\n" ); 361 adelete( stp, stp1 ); 362 assert( const_count == 2 + 2 * dim && dest_count == 2 + 2 * dim); // assertion for testing 294 adelete( dim, stp, dim, stp1 ); 363 295 364 296 // extras … … 369 301 free( fp - 1 ); 370 302 371 ip = foo( bar( baz( malloc(), 0 ), 0 ), 0 ); 372 *ip = 0xdeadbeef; 373 printf( "CFA deep malloc %#x\n", *ip ); 374 375 dp = alloc(5.0`fill); // just for testing multiple free 376 assert(*dp == 5.0); 377 free( ip, dp, 0p ); 303 p = foo( bar( baz( malloc(), 0 ), 0 ), 0 ); 304 *p = 0xdeadbeef; 305 printf( "CFA deep malloc %#x\n", *p ); 306 free( p ); 378 307 379 308 #ifdef ERR1 380 309 stp = malloc(); 381 310 printf( "\nSHOULD FAIL\n" ); 382 ip = realloc( stp, dim * sizeof( *stp ) ); 383 ip = memset( stp, 10 ); 384 ip = memcpy( &st1, &st ); 385 #endif // ERR1 311 p = realloc( stp, dim * sizeof( *stp ) ); 312 p = alloc( stp, dim * sizeof( *stp ) ); 313 p = memset( stp, 10 ); 314 p = memcpy( &st1, &st ); 315 #endif 386 316 } // main 387 317 -
tests/array.cfa
reef8dfb rbdfc032 1 // -*- Mode: C -*- 2 // 1 // -*- Mode: C -*- 2 // 3 3 // Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo 4 4 // 5 5 // The contents of this file are covered under the licence agreement in the 6 6 // file "LICENCE" distributed with Cforall. 7 // 7 // 8 8 // array.cfa -- test array declarations 9 // 9 // 10 10 // Author : Peter A. Buhr 11 11 // Created On : Tue Feb 19 21:18:06 2019 12 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Sun Sep 27 09:05:40 202014 // Update Count : 415 // 13 // Last Modified On : Tue Feb 19 21:18:46 2019 14 // Update Count : 1 15 // 16 16 17 int a1[ 0];17 int a1[]; 18 18 //int a2[*]; 19 19 //double a4[3.0]; 20 20 21 int m1[ 0][3];21 int m1[][3]; 22 22 //int m2[*][*]; 23 23 int m4[3][3]; … … 49 49 } 50 50 51 int main() { 52 #if !defined(NO_COMPILED_PRAGMA) 53 #pragma message( "Compiled" ) // force non-empty .expect file 54 #endif 55 } 51 int main() {} 56 52 57 53 // Local Variables: // -
tests/avltree/avl1.cfa
reef8dfb rbdfc032 24 24 tree(K, V) * create(K key, V value) { 25 25 // infinite loop trying to resolve ... t = malloc(); 26 tree(K, V) * t = ( tree(K, V) * )malloc(sizeof(tree(K,V)));26 tree(K, V) * t = malloc(sizeof(tree(K,V))); 27 27 (*t){ key, value }; 28 28 return t; -
tests/builtins/.expect/sync.txt
reef8dfb rbdfc032 1 builtins/sync.cfa: In function '_X4mainFi___1':2 builtins/sync.cfa:358:9: note: #pragma message: Compiled -
tests/builtins/sync.cfa
reef8dfb rbdfc032 66 66 #if defined(__SIZEOF_INT128__) 67 67 { __int128 ret; ret = __sync_fetch_and_nand(vplll, vlll); } 68 { __int128 ret; ret = __sync_fetch_and_nand_16(vplll, vlll); } 68 69 #endif 69 70 … … 354 355 355 356 int main() { 356 #pragma message( "Compiled" ) // force non-empty .expect file357 return 0; 357 358 } -
tests/cast.cfa
reef8dfb rbdfc032 13 13 14 14 //Dummy main 15 int main( int argc, char const * argv[] ) { 16 #pragma message( "Compiled" ) // force non-empty .expect file 15 int main(int argc, char const *argv[]) 16 { 17 return 0; 17 18 } -
tests/castError.cfa
reef8dfb rbdfc032 14 14 // 15 15 16 forall(otype T) struct S { T p; };17 16 int f; 18 S(int) sint;19 17 20 18 void f() { … … 27 25 short int v; 28 26 3, v; // implicit void cast 29 30 (S(char)) sint;31 27 } 32 28 -
tests/complex.cfa
reef8dfb rbdfc032 14 14 // 15 15 16 #include <stdio.h> 16 17 #include <complex.h> 17 18 #ifdef __CFA__ -
tests/concurrent/.expect/monitor.txt
reef8dfb rbdfc032 1 30000001 4000000 -
tests/concurrent/coroutineYield.cfa
reef8dfb rbdfc032 33 33 sout | "Coroutine 2"; 34 34 #endif 35 suspend ;35 suspend(); 36 36 } 37 37 } -
tests/concurrent/examples/.expect/datingService.txt
reef8dfb rbdfc032 1 done 1 Girl:17 is dating Boy at 2 with ccode 17 2 Boy:2 is dating Girl 17 with ccode 17 3 Boy:14 is dating Girl 5 with ccode 5 4 Girl:5 is dating Boy at 14 with ccode 5 5 Boy:9 is dating Girl 10 with ccode 10 6 Girl:10 is dating Boy at 9 with ccode 10 7 Boy:1 is dating Girl 18 with ccode 18 8 Girl:18 is dating Boy at 1 with ccode 18 9 Boy:16 is dating Girl 3 with ccode 3 10 Girl:3 is dating Boy at 16 with ccode 3 11 Boy:5 is dating Girl 14 with ccode 14 12 Girl:14 is dating Boy at 5 with ccode 14 13 Boy:15 is dating Girl 4 with ccode 4 14 Girl:4 is dating Boy at 15 with ccode 4 15 Girl:0 is dating Boy at 19 with ccode 0 16 Boy:19 is dating Girl 0 with ccode 0 17 Girl:9 is dating Boy at 10 with ccode 9 18 Boy:10 is dating Girl 9 with ccode 9 19 Girl:11 is dating Boy at 8 with ccode 11 20 Boy:8 is dating Girl 11 with ccode 11 21 Boy:12 is dating Girl 7 with ccode 7 22 Girl:7 is dating Boy at 12 with ccode 7 23 Boy:11 is dating Girl 8 with ccode 8 24 Girl:8 is dating Boy at 11 with ccode 8 25 Girl:16 is dating Boy at 3 with ccode 16 26 Boy:3 is dating Girl 16 with ccode 16 27 Girl:15 is dating Boy at 4 with ccode 15 28 Boy:4 is dating Girl 15 with ccode 15 29 Girl:19 is dating Boy at 0 with ccode 19 30 Boy:0 is dating Girl 19 with ccode 19 31 Girl:2 is dating Boy at 17 with ccode 2 32 Boy:17 is dating Girl 2 with ccode 2 33 Boy:13 is dating Girl 6 with ccode 6 34 Girl:6 is dating Boy at 13 with ccode 6 35 Boy:7 is dating Girl 12 with ccode 12 36 Girl:12 is dating Boy at 7 with ccode 12 37 Girl:13 is dating Boy at 6 with ccode 13 38 Boy:6 is dating Girl 13 with ccode 13 39 Girl:1 is dating Boy at 18 with ccode 1 40 Boy:18 is dating Girl 1 with ccode 1 -
tests/concurrent/examples/boundedBufferEXT.cfa
reef8dfb rbdfc032 1 1 // 2 2 // Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo 3 // 3 // 4 4 // The contents of this file are covered under the licence agreement in the 5 5 // file "LICENCE" distributed with Cforall. … … 87 87 } 88 88 89 enum { Prods = 4, Cons = 5 };90 Producer * prods[Prods];91 Consumer * cons[Cons];92 93 89 int main() { 94 90 Buffer(int) buffer; 91 enum { Prods = 4, Cons = 5 }; 92 Producer * prods[Prods]; 93 Consumer * cons[Cons]; 95 94 int sums[Cons]; 96 95 int i; -
tests/concurrent/examples/datingService.cfa
reef8dfb rbdfc032 1 1 // 2 2 // Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo 3 // 3 // 4 4 // The contents of this file are covered under the licence agreement in the 5 5 // file "LICENCE" distributed with Cforall. … … 10 10 // Created On : Mon Oct 30 12:56:20 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 15:42:25 202013 // Update Count : 4012 // Last Modified On : Fri Jun 21 11:32:34 2019 13 // Update Count : 38 14 14 // 15 15 … … 35 35 signal_block( Boys[ccode] ); // restart boy to set phone number 36 36 } // if 37 //sout | "Girl:" | PhoneNo | "is dating Boy at" | BoyPhoneNo | "with ccode" | ccode;37 sout | "Girl:" | PhoneNo | "is dating Boy at" | BoyPhoneNo | "with ccode" | ccode; 38 38 return BoyPhoneNo; 39 39 } // DatingService girl … … 47 47 signal_block( Girls[ccode] ); // restart girl to set phone number 48 48 } // if 49 //sout | " Boy:" | PhoneNo | "is dating Girl" | GirlPhoneNo | "with ccode" | ccode;49 sout | " Boy:" | PhoneNo | "is dating Girl" | GirlPhoneNo | "with ccode" | ccode; 50 50 return GirlPhoneNo; 51 51 } // DatingService boy … … 108 108 if ( girlck[ boyck[i] ] != boyck[ girlck[i] ] ) abort(); 109 109 } // for 110 111 printf( "done\n" ); // non-empty .expect file112 110 } // main 113 111 -
tests/concurrent/examples/quickSort.cfa
reef8dfb rbdfc032 11 11 // Created On : Wed Dec 6 12:15:52 2017 12 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Wed Feb 12 18:24:47 202014 // Update Count : 17 713 // Last Modified On : Thu Oct 10 13:58:18 2019 14 // Update Count : 176 15 15 // 16 16 … … 27 27 28 28 void ?{}( Quicksort & qs, int values[], int size, int depth ) { 29 qs. [values, low, high, depth] = [values, 0, size, depth];29 qs.values = values; qs.low = 0; qs.high = size; qs.depth = depth; 30 30 } // Quicksort 31 31 … … 167 167 values[counter] = size - counter; // descending values 168 168 } // for 169 for ( i ; 200 ) {// random shuffle a few values169 for ( int i = 0; i < 200; i +=1 ) { // random shuffle a few values 170 170 swap( values[rand() % size], values[rand() % size] ); 171 171 } // for -
tests/concurrent/monitor.cfa
reef8dfb rbdfc032 29 29 30 30 void main( MyThread & this ) { 31 for(int i = 0; i < 750_000; i++) {31 for(int i = 0; i < 1_000_000; i++) { 32 32 increment( global ); 33 33 } -
tests/concurrent/multi-monitor.cfa
reef8dfb rbdfc032 11 11 12 12 void increment( monitor_t & mutex p1, monitor_t & mutex p2, int & value ) { 13 assert(active_thread() == get_monitor(p1)->owner);14 assert(active_thread() == get_monitor(p2)->owner);15 13 value += 1; 16 assert(active_thread() == get_monitor(p1)->owner);17 assert(active_thread() == get_monitor(p2)->owner);18 14 } 19 15 -
tests/concurrent/signal/block.cfa
reef8dfb rbdfc032 33 33 34 34 monitor global_data_t { 35 $thread* last_thread;36 $thread* last_signaller;35 thread_desc * last_thread; 36 thread_desc * last_signaller; 37 37 }; 38 38 … … 82 82 if( !is_empty( cond ) ) { 83 83 84 $thread * next = ( $thread * )front( cond );84 thread_desc * next = front( cond ); 85 85 86 86 if( ! signal_block( cond ) ) { -
tests/concurrent/signal/disjoint.cfa
reef8dfb rbdfc032 21 21 #endif 22 22 23 // This tests checks what happens when someone barges in the midle of the release24 // of a bulk of monitors.25 26 23 enum state_t { WAIT, SIGNAL, BARGE }; 27 24 28 25 monitor global_t {}; 26 global_t mut; 29 27 30 28 monitor global_data_t; … … 35 33 int counter; 36 34 state_t state; 37 }; 38 39 // Use a global struct because the order needs to match with Signaller thread 40 struct { 41 global_t mut; 42 global_data_t data; 43 } globals; 35 } data; 44 36 45 37 condition cond; … … 48 40 49 41 void ?{}( global_data_t & this ) { 50 this.counter = 0;42 this.counter == 0; 51 43 this.state = BARGE; 52 44 } … … 61 53 62 54 thread Barger {}; 63 void ?{}( Barger & this ) {64 ((thread&)this){ "Barger Thread" };65 }66 55 67 56 void main( Barger & this ) { 68 57 while( !all_done ) { 69 barge( globals.data );58 barge( data ); 70 59 yield(); 71 60 } … … 89 78 90 79 thread Waiter {}; 91 void ?{}( Waiter & this ) {92 ((thread&)this){ "Waiter Thread" };93 }94 80 95 81 void main( Waiter & this ) { 96 while( wait( globals.mut, globals.data ) ) { KICK_WATCHDOG; yield(); }82 while( wait( mut, data ) ) { KICK_WATCHDOG; yield(); } 97 83 } 98 84 … … 106 92 107 93 void logic( global_t & mutex a ) { 108 signal( cond, a, globals.data );94 signal( cond, a, data ); 109 95 110 96 yield( random( 10 ) ); 111 97 112 98 //This is technically a mutual exclusion violation but the mutex monitor protects us 113 bool running = TEST( globals.data.counter < N) && globals.data.counter > 0;114 if( globals.data.state != SIGNAL && running ) {115 sout | "ERROR Eager signal" | globals.data.state;99 bool running = TEST(data.counter < N) && data.counter > 0; 100 if( data.state != SIGNAL && running ) { 101 sout | "ERROR Eager signal" | data.state; 116 102 } 117 103 } 118 104 119 105 thread Signaller {}; 120 void ?{}( Signaller & this ) {121 ((thread&)this){ "Signaller Thread" };122 }123 106 124 107 void main( Signaller & this ) { 125 108 while( !all_done ) { 126 logic( globals.mut );109 logic( mut ); 127 110 yield(); 128 111 } -
tests/concurrent/waitfor/when.cfa
reef8dfb rbdfc032 57 57 58 58 void arbiter( global_t & mutex this ) { 59 // There is a race at start where callers can get in before the arbiter.60 // It doesn't really matter here so just restart the loop correctly and move on61 this.last_call = 6;62 63 59 for( int i = 0; i < N; i++ ) { 64 60 when( this.last_call == 6 ) waitfor( call1 : this ) { if( this.last_call != 1) { serr | "Expected last_call to be 1 got" | this.last_call; } } -
tests/config.py.in
reef8dfb rbdfc032 9 9 HOSTARCH = "@host_cpu@" 10 10 DISTRIBUTE = @HAS_DISTCC@ 11 NEWAST = @DEFAULT_NEW_AST@ -
tests/copyfile.cfa
reef8dfb rbdfc032 8 8 // 9 9 // Author : Peter A. Buhr 10 // Created On : Fri Jun 19 13:44:05 202010 // Created On : Tue Jul 16 16:47:22 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 15 15:00:48 202013 // Update Count : 612 // Last Modified On : Wed Jul 17 18:04:44 2019 13 // Update Count : 26 14 14 // 15 15 16 16 #include <fstream.hfa> 17 #include < exception.hfa>17 #include <stdlib.hfa> // new/delete 18 18 19 19 int main( int argc, char * argv[] ) { 20 ifstream in = stdin; // copy default files 21 ofstream out = stdout; 20 ifstream * in = &stdin; // default files 21 ofstream * out = &stdout; 22 try { 23 choose ( argc ) { 24 case 2, 3: 25 in = new( (const char *)argv[1] ); // open input file first as output creates file 26 if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent 27 case 1: ; // use default files 28 default: 29 exit | "Usage [ input-file (default stdin) [ output-file (default stdout) ] ]"; 30 } // choose 22 31 23 try { 24 choose ( argc ) { // terminate if command-line errors 25 case 2, 3: 26 open( in, argv[1] ); // open input file first as output creates file 27 if ( argc == 3 ) open( out, argv[2] ); // do not create output unless input opens 28 case 1: ; // use default files 29 default: // wrong number of options 30 exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]"; 31 } // choose 32 } catch( Open_Failure * ex ; ex->istream == &in ) { 33 exit | "Unable to open input file" | argv[1]; 34 } catch( Open_Failure * ex ; ex->ostream == &out ) { 35 close( in ); // optional 36 exit | "Unable to open output file" | argv[2]; 32 char ch; 33 *out | nlOff; // turn off auto newline 34 *in | nlOn; // turn on reading newline 35 36 for () { // read all characters 37 *in | ch; 38 if ( eof( *in ) ) break; // eof ? 39 *out | ch; 40 } // for 41 } finally { 42 if ( in != &stdin ) delete( in ); // close file, do not delete stdin! 43 if ( out != &stdout ) delete( out ); // close file, do not delete stdout! 37 44 } // try 38 39 out | nlOff; // turn off auto newline40 in | nlOn; // turn on reading newline41 42 char ch;43 for () { // read all characters44 in | ch;45 if ( eof( in ) ) break; // eof ?46 out | ch;47 } // for48 45 } // main 49 46 -
tests/coroutine/.expect/fmtLines.txt
reef8dfb rbdfc032 48 48 { // f or n ewli 49 49 ne c hara cter s su 50 spen d ; i f ( fmt.51 ch ! = '\ n' ) bre ak;52 // igno re n ewli ne53 } // f or so ut |54 fmt .ch; / / pr55 int char acte r } //56 for s out | " ";57 / / pr int bloc58 k se para tor } / / fo59 r s out | nl ;60 // pri nt g roup sep61 arat or } // for} //62 main void prt ( Fo rmat63 & f mt, char ch ) {64 f mt.c h = ch; r65 esum e( f mt ) ;} / / pr66 tint mai n() { Fo rmat67 fmt ; ch ar c h; f or (68 ;; ) { sin | c h;69 // r ead one70 char acte r if ( eof71 ( si n ) ) br eak;72 / / eo f ? prt ( fm73 t, c h ); } / / fo r} /74 / ma in// Loc al V aria75 bles : // // t ab-w idth76 : 4 //// com pile -com77 mand : "c fa f mtLi nes.78 cfa" /// / En d: //50 spen d(); if ( fm 51 t.ch != '\n' ) b reak 52 ; / / ig nore new line 53 } // for sout 54 | f mt.c h; // 55 prin t ch arac ter } 56 // f or sou t | " " 57 ; // prin t bl 58 ock sepa rato r } // 59 for sou t | nl; 60 // p rint gro up s 61 epar ator } / / fo r} / 62 / ma invo id p rt( Form 63 at & fmt , ch ar c h ) 64 { fmt .ch = ch ; 65 res ume( fmt );} // 66 prti nt m ain( ) { Form 67 at f mt; char ch; for 68 ( ; ; ) { s in | ch; 69 // rea d on 70 e ch arac ter if ( e 71 of( sin ) ) brea k; 72 // eof ? p rt( 73 fmt, ch ); } // for} 74 // main // L ocal Var 75 iabl es: //// tab -wid 76 th: 4 // // c ompi le-c 77 omma nd: "cfa fmt Line 78 s.cf a" / /// End: // -
tests/coroutine/.in/fmtLines.txt
reef8dfb rbdfc032 35 35 for ( fmt.b = 0; fmt.b < 4; fmt.b += 1 ) { // blocks of 4 characters 36 36 for ( ;; ) { // for newline characters 37 suspend ;37 suspend(); 38 38 if ( fmt.ch != '\n' ) break; // ignore newline 39 39 } // for -
tests/coroutine/cntparens.cfa
reef8dfb rbdfc032 1 // 1 // 2 2 // Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo 3 3 // 4 4 // The contents of this file are covered under the licence agreement in the 5 5 // file "LICENCE" distributed with Cforall. 6 // 6 // 7 7 // cntparens.cfa -- match left/right parenthesis 8 // 8 // 9 9 // Author : Peter A. Buhr 10 10 // Created On : Sat Apr 20 11:04:45 2019 … … 12 12 // Last Modified On : Sat Apr 20 11:06:21 2019 13 13 // Update Count : 1 14 // 14 // 15 15 16 16 #include <fstream.hfa> … … 26 26 void main( CntParens & cpns ) with( cpns ) { 27 27 for ( ; ch == '('; cnt += 1 ) { // left parenthesis 28 suspend ;28 suspend(); 29 29 } 30 30 for ( ; ch == ')' && cnt > 1; cnt -= 1 ) { // right parenthesis 31 suspend ;31 suspend(); 32 32 } 33 33 status = ch == ')' ? Match : Error; 34 34 } // main 35 35 36 36 void ?{}( CntParens & cpns ) with( cpns ) { status = Cont; cnt = 0; } 37 37 -
tests/coroutine/devicedriver.cfa
reef8dfb rbdfc032 1 // 1 // 2 2 // Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo 3 3 // 4 4 // The contents of this file are covered under the licence agreement in the 5 5 // file "LICENCE" distributed with Cforall. 6 // 7 // devicedriver.cfa -- 8 // 6 // 7 // devicedriver.cfa -- 8 // 9 9 // Author : Peter A. Buhr 10 10 // Created On : Sat Mar 16 15:30:34 2019 … … 12 12 // Last Modified On : Sat Apr 20 09:07:19 2019 13 13 // Update Count : 90 14 // 14 // 15 15 16 16 #include <fstream.hfa> … … 29 29 30 30 void checkCRC( Driver & d, unsigned int sum ) with( d ) { 31 suspend ;31 suspend(); 32 32 unsigned short int crc = byte << 8; // sign extension over written 33 suspend ;33 suspend(); 34 34 // prevent sign extension for signed char 35 35 status = (crc | (unsigned char)byte) == sum ? MSG : ECRC; … … 41 41 status = CONT; 42 42 unsigned int lnth = 0, sum = 0; 43 while ( byte != STX ) suspend ;43 while ( byte != STX ) suspend(); 44 44 emsg: for () { 45 suspend ;45 suspend(); 46 46 choose ( byte ) { // process byte 47 47 case STX: 48 status = ESTX; suspend ; continue msg;48 status = ESTX; suspend(); continue msg; 49 49 case ETX: 50 50 break emsg; 51 51 case ESC: 52 suspend ;52 suspend(); 53 53 } // choose 54 54 if ( lnth >= MaxMsg ) { // buffer full ? 55 status = ELNTH; suspend ; continue msg;55 status = ELNTH; suspend(); continue msg; 56 56 } // if 57 57 msg[lnth++] = byte; … … 60 60 msg[lnth] = '\0'; // terminate string 61 61 checkCRC( d, sum ); // refactor CRC check 62 suspend ;62 suspend(); 63 63 } // for 64 64 } // main -
tests/coroutine/fibonacci.cfa
reef8dfb rbdfc032 22 22 int fn1, fn2; // retained between resumes 23 23 fn = 0; fn1 = fn; // 1st case 24 suspend ; // restart last resume24 suspend(); // restart last resume 25 25 fn = 1; fn2 = fn1; fn1 = fn; // 2nd case 26 suspend ; // restart last resume26 suspend(); // restart last resume 27 27 for () { 28 28 fn = fn1 + fn2; fn2 = fn1; fn1 = fn; // general case 29 suspend ; // restart last resume29 suspend(); // restart last resume 30 30 } // for 31 31 } -
tests/coroutine/fibonacci_1.cfa
reef8dfb rbdfc032 12 12 // Last Modified On : Thu Mar 21 08:10:45 2019 13 13 // Update Count : 25 14 // 14 // 15 15 16 16 #include <fstream.hfa> … … 23 23 [fn1, fn] = [0, 1]; // precompute first two states 24 24 for () { 25 suspend ; // restart last resume25 suspend(); // restart last resume 26 26 [fn1, fn] = [fn, fn1 + fn]; // general case 27 27 } // for -
tests/coroutine/fmtLines.cfa
reef8dfb rbdfc032 27 27 for ( b = 0; b < 4; b += 1 ) { // blocks of 4 characters 28 28 for () { // for newline characters 29 suspend ;29 suspend(); 30 30 if ( ch != '\n' ) break; // ignore newline 31 31 } // for -
tests/coroutine/raii.cfa
reef8dfb rbdfc032 39 39 Raii raii = { "Coroutine" }; 40 40 sout | "Before Suspend"; 41 suspend ;41 suspend(); 42 42 sout | "After Suspend"; 43 43 } -
tests/coroutine/runningTotal.cfa
reef8dfb rbdfc032 25 25 void update( RunTotal & rntl, int input ) with( rntl ) { // helper 26 26 total += input; // remember between activations 27 suspend ; // inactivate on stack27 suspend(); // inactivate on stack 28 28 } 29 29 -
tests/coroutine/suspend_then.cfa
reef8dfb rbdfc032 15 15 16 16 #include <fstream.hfa> 17 #include <coroutine.hfa> 17 18 18 generator Fibonacci { 19 int fn; // used for communication 20 int fn1, fn2; // retained between resumes 21 }; 19 void then() { 20 sout | "Then!"; 21 } 22 23 coroutine Fibonacci { int fn; }; // used for communication 22 24 23 25 void main( Fibonacci & fib ) with( fib ) { // called on first resume 26 int fn1, fn2; // retained between resumes 24 27 fn = 0; fn1 = fn; // 1st case 25 suspend { sout | "Then!"; }// restart last resume28 suspend_then(then); // restart last resume 26 29 fn = 1; fn2 = fn1; fn1 = fn; // 2nd case 27 suspend { sout | "Then!"; }// restart last resume30 suspend_then(then); // restart last resume 28 31 for () { 29 32 fn = fn1 + fn2; fn2 = fn1; fn1 = fn; // general case 30 suspend { sout | "Then!"; }// restart last resume33 suspend_then(then); // restart last resume 31 34 } // for 32 35 } -
tests/enum.cfa
reef8dfb rbdfc032 26 26 //Dummy main 27 27 int main(int argc, char const *argv[]) { 28 printf( "done\n" ); // non-empty .expect file29 28 } -
tests/expression.cfa
reef8dfb rbdfc032 8 8 9 9 int main() { 10 int a[3] = { 0, 0, 0 };11 S s = { 3 }, * ps = &s;12 [int] t = { 3 };13 * [int] pt = &t;14 int i = 1, j = 2;10 int a[3] = { 0, 0, 0 }; 11 S s = { 3 }, * ps = &s; 12 [int] t = { 3 }; 13 * [int] pt = &t; 14 int i = 1, j = 2; 15 15 16 // operators16 // operators 17 17 18 !i;19 ~i;20 +i;21 -i;22 *ps;23 ++ps;24 --ps;25 ps++;26 ps--;18 !i; 19 ~i; 20 +i; 21 -i; 22 *ps; 23 ++ps; 24 --ps; 25 ps++; 26 ps--; 27 27 28 i + j;29 i - j;30 i * j;28 i + j; 29 i - j; 30 i * j; 31 31 32 i / j;33 i % j;34 i ^ j;35 i & j;36 i | j;37 i < j;38 i > j;39 i = j;32 i / j; 33 i % j; 34 i ^ j; 35 i & j; 36 i | j; 37 i < j; 38 i > j; 39 i = j; 40 40 41 i == j;42 i != j;43 i << j;44 i >> j;45 i <= j;46 i >= j;47 i && j;48 i || j;49 ps->i;41 i == j; 42 i != j; 43 i << j; 44 i >> j; 45 i <= j; 46 i >= j; 47 i && j; 48 i || j; 49 ps->i; 50 50 51 i *= j;52 i /= j;53 i %= j;54 i += j;55 i -= j;56 i &= j;57 i |= j;58 i ^= j;59 i <<= j;60 i >>= j;51 i *= j; 52 i /= j; 53 i %= j; 54 i += j; 55 i -= j; 56 i &= j; 57 i |= j; 58 i ^= j; 59 i <<= j; 60 i >>= j; 61 61 62 i ? i : j;62 i ? i : j; 63 63 64 // postfix function call64 // postfix function call 65 65 66 (3 + 4)`mary; 67 ({3 + 4;})`mary; 68 [3, 4]`mary; 69 3`mary; 70 a[0]`mary; 71 a[0]`mary`mary; 72 s{0}`mary; 73 a[3]`jane++; 74 jack(3)`mary; 75 s.i`mary; 76 t.0`mary; 77 s.[i]`mary; 78 ps->i`mary; 79 pt->0`mary; 80 ps->[i]`mary; 81 i++`mary; 82 i--`mary; 83 (S){2}`mary; 84 (S)@{2}`mary; 85 86 #if !defined(NO_COMPILED_PRAGMA) 87 #pragma message( "Compiled" ) // force non-empty .expect file 88 #endif 66 (3 + 4)`mary; 67 ({3 + 4;})`mary; 68 [3, 4]`mary; 69 3`mary; 70 a[0]`mary; 71 a[0]`mary`mary; 72 s{0}`mary; 73 a[3]`jane++; 74 jack(3)`mary; 75 s.i`mary; 76 t.0`mary; 77 s.[i]`mary; 78 ps->i`mary; 79 pt->0`mary; 80 ps->[i]`mary; 81 i++`mary; 82 i--`mary; 83 (S){2}`mary; 84 (S)@{2}`mary; 89 85 } // main -
tests/forall.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 9 08:48:15 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 08:43:20 202013 // Update Count : 3 512 // Last Modified On : Tue Mar 19 08:29:38 2019 13 // Update Count : 32 14 14 // 15 15 … … 158 158 } 159 159 forall( otype T ) inline static { 160 int RT9( T ) { T t; return 3;}160 int RT9( T ) { T t; } 161 161 } 162 162 … … 213 213 // w3 g3; 214 214 215 int main( void ) { 216 #pragma message( "Compiled" ) // force non-empty .expect file 217 } 215 int main( void ) {} 218 216 219 217 // Local Variables: // -
tests/heap.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Nov 6 17:54:56 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Dec 15 12:11:51 202013 // Update Count : 7912 // Last Modified On : Sun Nov 24 12:34:51 2019 13 // Update Count : 28 14 14 // 15 15 … … 27 27 // } 28 28 29 size_t default_heap_expansion() { 30 return 10 * 1024 * 1024; 31 } // default_heap_expansion 32 33 size_t default_mmap_start() { 34 return 512 * 1024 + 1; 29 #define __U_DEFAULT_MMAP_START__ (512 * 1024 + 1) 30 size_t default_mmap_start() __attribute__(( weak )) { 31 return __U_DEFAULT_MMAP_START__; 35 32 } // default_mmap_start 36 33 … … 78 75 size_t s = (i + 1) * 20; 79 76 char * area = (char *)malloc( s ); 77 if ( area == 0p ) abort( "malloc/free out of memory" ); 80 78 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last 81 79 area[malloc_usable_size( area ) - 1] = '\345'; // fill ultimate byte … … 86 84 size_t s = i + 1; // +1 to make initialization simpler 87 85 locns[i] = (char *)malloc( s ); 86 if ( locns[i] == 0p ) abort( "malloc/free out of memory" ); 88 87 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last 89 88 locns[i][malloc_usable_size( locns[i] ) - 1] = '\345'; // fill ultimate byte … … 101 100 size_t s = i + default_mmap_start(); // cross over point 102 101 char * area = (char *)malloc( s ); 102 if ( area == 0p ) abort( "malloc/free out of memory" ); 103 103 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last 104 104 area[malloc_usable_size( area ) - 1] = '\345'; // fill ultimate byte … … 109 109 size_t s = i + default_mmap_start(); // cross over point 110 110 locns[i] = (char *)malloc( s ); 111 if ( locns[i] == 0p ) abort( "malloc/free out of memory" ); 111 112 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last 112 113 locns[i][malloc_usable_size( locns[i] ) - 1] = '\345'; // fill ultimate byte … … 124 125 size_t s = (i + 1) * 20; 125 126 char * area = (char *)calloc( 5, s ); 127 if ( area == 0p ) abort( "calloc/free out of memory" ); 126 128 if ( area[0] != '\0' || area[s - 1] != '\0' || 127 area[malloc_ size( area ) - 1] != '\0' ||129 area[malloc_usable_size( area ) - 1] != '\0' || 128 130 ! malloc_zero_fill( area ) ) abort( "calloc/free corrupt storage1" ); 129 131 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last … … 135 137 size_t s = i + 1; 136 138 locns[i] = (char *)calloc( 5, s ); 139 if ( locns[i] == 0p ) abort( "calloc/free out of memory" ); 137 140 if ( locns[i][0] != '\0' || locns[i][s - 1] != '\0' || 138 locns[i][malloc_ size( locns[i] ) - 1] != '\0' ||141 locns[i][malloc_usable_size( locns[i] ) - 1] != '\0' || 139 142 ! malloc_zero_fill( locns[i] ) ) abort( "calloc/free corrupt storage2" ); 140 143 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last … … 153 156 size_t s = i + default_mmap_start(); // cross over point 154 157 char * area = (char *)calloc( 1, s ); 158 if ( area == 0p ) abort( "calloc/free out of memory" ); 155 159 if ( area[0] != '\0' || area[s - 1] != '\0' ) abort( "calloc/free corrupt storage4.1" ); 156 if ( area[malloc_ size( area ) - 1] != '\0' ) abort( "calloc/free corrupt storage4.2" );160 if ( area[malloc_usable_size( area ) - 1] != '\0' ) abort( "calloc/free corrupt storage4.2" ); 157 161 if ( ! malloc_zero_fill( area ) ) abort( "calloc/free corrupt storage4.3" ); 158 162 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last … … 164 168 size_t s = i + default_mmap_start(); // cross over point 165 169 locns[i] = (char *)calloc( 1, s ); 170 if ( locns[i] == 0p ) abort( "calloc/free out of memory" ); 166 171 if ( locns[i][0] != '\0' || locns[i][s - 1] != '\0' || 167 locns[i][malloc_ size( locns[i] ) - 1] != '\0' ||172 locns[i][malloc_usable_size( locns[i] ) - 1] != '\0' || 168 173 ! malloc_zero_fill( locns[i] ) ) abort( "calloc/free corrupt storage5" ); 169 174 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last … … 183 188 for ( s; 1 ~ NoOfAllocs ) { // allocation of size 0 can return null 184 189 char * area = (char *)memalign( a, s ); 190 if ( area == 0p ) abort( "memalign/free out of memory" ); 185 191 //sout | i | area; 186 192 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 200 206 size_t s = i + default_mmap_start(); // cross over point 201 207 char * area = (char *)memalign( a, s ); 208 if ( area == 0p ) abort( "memalign/free out of memory" ); 202 209 //sout | i | area; 203 210 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 210 217 } // for 211 218 212 // check malloc/resize/free (sbrk)213 214 for ( i; 2 ~ NoOfAllocs ~ 12 ) {215 // initial N byte allocation216 char * area = (char *)malloc( i );217 area[0] = '\345'; area[i - 1] = '\345'; // fill first/penultimate byte218 219 // Do not start this loop index at 0 because resize of 0 bytes frees the storage.220 int prev = i;221 for ( s; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request222 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/resize/free corrupt storage" );223 area = (char *)resize( area, s ); // attempt to reuse storage224 area[0] = area[s - 1] = '\345'; // fill last byte225 prev = s;226 } // for227 free( area );228 } // for229 230 // check malloc/resize/free (mmap)231 232 for ( i; 2 ~ NoOfAllocs ~ 12 ) {233 // initial N byte allocation234 size_t s = i + default_mmap_start(); // cross over point235 char * area = (char *)malloc( s );236 area[0] = '\345'; area[s - 1] = '\345'; // fill first/penultimate byte237 238 // Do not start this loop index at 0 because resize of 0 bytes frees the storage.239 int prev = s;240 for ( r; s ~ 256 * 1024 ~ 26 ) { // start at initial memory request241 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/resize/free corrupt storage" );242 area = (char *)resize( area, s ); // attempt to reuse storage243 area[0] = area[r - 1] = '\345'; // fill last byte244 prev = r;245 } // for246 free( area );247 } // for248 249 // check malloc/realloc/free (sbrk)250 251 for ( i; 2 ~ NoOfAllocs ~ 12 ) {252 // initial N byte allocation253 char * area = (char *)malloc( i );254 area[0] = '\345'; area[i - 1] = '\345'; // fill first/penultimate byte255 256 // Do not start this loop index at 0 because realloc of 0 bytes frees the storage.257 int prev = i;258 for ( s; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request259 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/realloc/free corrupt storage" );260 area = (char *)realloc( area, s ); // attempt to reuse storage261 area[s - 1] = '\345'; // fill last byte262 prev = s;263 } // for264 free( area );265 } // for266 267 // check malloc/realloc/free (mmap)268 269 for ( i; 2 ~ NoOfAllocs ~ 12 ) {270 // initial N byte allocation271 size_t s = i + default_mmap_start(); // cross over point272 char * area = (char *)malloc( s );273 area[0] = '\345'; area[s - 1] = '\345'; // fill first/penultimate byte274 275 // Do not start this loop index at 0 because realloc of 0 bytes frees the storage.276 int prev = s;277 for ( r; s ~ 256 * 1024 ~ 26 ) { // start at initial memory request278 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/realloc/free corrupt storage" );279 area = (char *)realloc( area, s ); // attempt to reuse storage280 area[r - 1] = '\345'; // fill last byte281 prev = r;282 } // for283 free( area );284 } // for285 286 219 // check calloc/realloc/free (sbrk) 287 220 … … 289 222 // initial N byte allocation 290 223 char * area = (char *)calloc( 5, i ); 224 if ( area == 0p ) abort( "calloc/realloc/free out of memory" ); 291 225 if ( area[0] != '\0' || area[i - 1] != '\0' || 292 area[malloc_ size( area ) - 1] != '\0' ||226 area[malloc_usable_size( area ) - 1] != '\0' || 293 227 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage1" ); 294 228 … … 296 230 for ( s; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request 297 231 area = (char *)realloc( area, s ); // attempt to reuse storage 232 if ( area == 0p ) abort( "calloc/realloc/free out of memory" ); 298 233 if ( area[0] != '\0' || area[s - 1] != '\0' || 299 area[malloc_ size( area ) - 1] != '\0' ||234 area[malloc_usable_size( area ) - 1] != '\0' || 300 235 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage2" ); 301 236 } // for … … 309 244 size_t s = i + default_mmap_start(); // cross over point 310 245 char * area = (char *)calloc( 1, s ); 246 if ( area == 0p ) abort( "calloc/realloc/free out of memory" ); 311 247 if ( area[0] != '\0' || area[s - 1] != '\0' || 312 area[malloc_ size( area ) - 1] != '\0' ||313 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage 3" );248 area[malloc_usable_size( area ) - 1] != '\0' || 249 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage1" ); 314 250 315 251 // Do not start this loop index at 0 because realloc of 0 bytes frees the storage. 316 252 for ( r; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request 317 253 area = (char *)realloc( area, r ); // attempt to reuse storage 254 if ( area == 0p ) abort( "calloc/realloc/free out of memory" ); 318 255 if ( area[0] != '\0' || area[r - 1] != '\0' || 319 area[malloc_ size( area ) - 1] != '\0' ||320 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage 4" );256 area[malloc_usable_size( area ) - 1] != '\0' || 257 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage2" ); 321 258 } // for 322 259 free( area ); … … 329 266 // initial N byte allocation 330 267 char * area = (char *)memalign( a, amount ); // aligned N-byte allocation 268 if ( area == 0p ) abort( "memalign/realloc/free out of memory" ); // no storage ? 331 269 //sout | alignments[a] | area; 332 270 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 339 277 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "memalign/realloc/free corrupt storage" ); 340 278 area = (char *)realloc( area, s ); // attempt to reuse storage 279 if ( area == 0p ) abort( "memalign/realloc/free out of memory" ); // no storage ? 341 280 //sout | i | area; 342 281 if ( (size_t)area % a != 0 ) { // check for initial alignment … … 354 293 for ( s; 1 ~ limit ) { // allocation of size 0 can return null 355 294 char * area = (char *)cmemalign( a, 1, s ); 295 if ( area == 0p ) abort( "cmemalign/free out of memory" ); 356 296 //sout | i | area; 357 297 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 359 299 } // if 360 300 if ( area[0] != '\0' || area[s - 1] != '\0' || 361 area[malloc_ size( area ) - 1] != '\0' ||301 area[malloc_usable_size( area ) - 1] != '\0' || 362 302 ! malloc_zero_fill( area ) ) abort( "cmemalign/free corrupt storage" ); 363 303 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last byte … … 372 312 // initial N byte allocation 373 313 char * area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation 314 if ( area == 0p ) abort( "cmemalign/realloc/free out of memory" ); // no storage ? 374 315 //sout | alignments[a] | area; 375 316 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 377 318 } // if 378 319 if ( area[0] != '\0' || area[amount - 1] != '\0' || 379 area[malloc_ size( area ) - 1] != '\0' ||320 area[malloc_usable_size( area ) - 1] != '\0' || 380 321 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage1" ); 381 322 area[0] = '\345'; area[amount - 2] = '\345'; // fill first/penultimate byte … … 385 326 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "cmemalign/realloc/free corrupt storage2" ); 386 327 area = (char *)realloc( area, s ); // attempt to reuse storage 328 if ( area == 0p ) abort( "cmemalign/realloc/free out of memory" ); // no storage ? 387 329 //sout | i | area; 388 330 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment 389 331 abort( "cmemalign/realloc/free bad alignment %p", area ); 390 332 } // if 391 if ( area[ 0] != '\345' || area[s - 1] != '\0' ||392 area[malloc_ size( area ) - 1] != '\0' ||333 if ( area[s - 1] != '\0' || area[s - 1] != '\0' || 334 area[malloc_usable_size( area ) - 1] != '\0' || 393 335 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage3" ); 394 336 area[s - 1] = '\345'; // fill last byte … … 397 339 } // for 398 340 399 // check memalign/re sizewith align/free341 // check memalign/realloc with align/free 400 342 401 343 amount = 2; … … 403 345 // initial N byte allocation 404 346 char * area = (char *)memalign( a, amount ); // aligned N-byte allocation 405 //sout | alignments[a] | area | endl; 406 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment 407 abort( "memalign/resize with align/free bad alignment : memalign(%d,%d) = %p", (int)a, (int)amount, area ); 408 } // if 409 area[0] = '\345'; area[amount - 2] = '\345'; // fill first/penultimate byte 410 411 // Do not start this loop index at 0 because resize of 0 bytes frees the storage. 412 for ( s; amount ~ 256 * 1024 ) { // start at initial memory request 413 area = (char *)resize( area, a * 2, s ); // attempt to reuse storage 414 //sout | i | area | endl; 415 if ( (size_t)area % a * 2 != 0 ) { // check for initial alignment 416 abort( "memalign/resize with align/free bad alignment %p", area ); 417 } // if 418 area[s - 1] = '\345'; // fill last byte 419 } // for 420 free( area ); 421 } // for 422 423 // check memalign/realloc with align/free 424 425 amount = 2; 426 for ( a; libAlign() ~= limit ~ a ) { // generate powers of 2 427 // initial N byte allocation 428 char * area = (char *)memalign( a, amount ); // aligned N-byte allocation 347 if ( area == 0p ) abort( "memalign/realloc with align/free out of memory" ); // no storage ? 429 348 //sout | alignments[a] | area | endl; 430 349 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 437 356 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "memalign/realloc/free corrupt storage" ); 438 357 area = (char *)realloc( area, a * 2, s ); // attempt to reuse storage 358 if ( area == 0p ) abort( "memalign/realloc with align/free out of memory" ); // no storage ? 439 359 //sout | i | area | endl; 440 360 if ( (size_t)area % a * 2 != 0 ) { // check for initial alignment … … 451 371 for ( size_t a = libAlign() + libAlign(); a <= limit; a += a ) { // generate powers of 2 452 372 // initial N byte allocation 453 char * area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation 373 char *area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation 374 if ( area == 0p ) abort( "cmemalign/realloc with align/free out of memory" ); // no storage ? 454 375 //sout | alignments[a] | area | endl; 455 376 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 457 378 } // if 458 379 if ( area[0] != '\0' || area[amount - 1] != '\0' || 459 area[malloc_ size( area ) - 1] != '\0' ||380 area[malloc_usable_size( area ) - 1] != '\0' || 460 381 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc with align/free corrupt storage1" ); 461 382 area[0] = '\345'; area[amount - 2] = '\345'; // fill first/penultimate byte … … 465 386 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "cmemalign/realloc with align/free corrupt storage2" ); 466 387 area = (char *)realloc( area, a * 2, s ); // attempt to reuse storage 388 if ( area == 0p ) abort( "cmemalign/realloc with align/free out of memory" ); // no storage ? 467 389 //sout | i | area | endl; 468 390 if ( (size_t)area % a * 2 != 0 || malloc_alignment( area ) != a * 2 ) { // check for initial alignment 469 abort( "cmemalign/realloc with align/free bad alignment %p % zd %zd", area, malloc_alignment( area ), a * 2 );391 abort( "cmemalign/realloc with align/free bad alignment %p %jd %jd", area, malloc_alignment( area ), a * 2 ); 470 392 } // if 471 393 if ( area[s - 1] != '\0' || area[s - 1] != '\0' || 472 area[malloc_ size( area ) - 1] != '\0' ||394 area[malloc_usable_size( area ) - 1] != '\0' || 473 395 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage3" ); 474 396 area[s - 1] = '\345'; // fill last byte … … 488 410 // checkFreeOn(); 489 411 // malloc_stats(); 490 printf( "done\n" ); // non-empty .expect file491 412 } 492 413 -
tests/identFuncDeclarator.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Aug 17 08:36:34 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 08:20:46 202013 // Update Count : 512 // Last Modified On : Tue Nov 6 17:56:33 2018 13 // Update Count : 3 14 14 // 15 15 … … 111 111 int (* (* const f80)(int))(); 112 112 int (* const(* const f81)(int))(); 113 114 #pragma message( "Compiled" ) // force non-empty .expect file115 113 } 116 114 -
tests/identParamDeclarator.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Aug 17 08:37:56 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Sep 25 14:31:08 202013 // Update Count : 412 // Last Modified On : Tue Nov 6 17:56:44 2018 13 // Update Count : 3 14 14 // 15 15 … … 158 158 159 159 int main( int argc, char const *argv[] ) { // dummy main 160 printf( "done\n" ); // non-empty .expect file160 return 0; 161 161 } 162 162 -
tests/io2.cfa
reef8dfb rbdfc032 121 121 122 122 [int, int, const char *, double] t3 = { 3, 4, "a", 7.2 }; 123 sout | [ 3, 4, (const char*)"a", 7.2 ]; // workaround trac#207: the const cast should not be needed123 sout | [ 3, 4, "a", 7.2 ]; 124 124 sout | t3; 125 125 sepSetTuple( sout, " " ); -
tests/labelledExit.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Aug 10 07:29:39 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 09:01:34 202013 // Update Count : 1212 // Last Modified On : Fri Oct 25 17:41:51 2019 13 // Update Count : 7 14 14 // 15 15 … … 162 162 163 163 // computed goto 164 {165 void *array[] = { &&foo, &&bar, &&hack };166 foo: bar: hack:167 &&foo;168 &&bar;169 goto *array[i];170 }164 // { 165 // void *array[] = { &&foo, &&bar, &&hack }; 166 // foo: bar: hack: 167 // &&foo; 168 // &&bar; 169 // goto *array[i]; 170 // } 171 171 172 172 Q: if ( i > 5 ) { … … 179 179 180 180 int main( int argc, char const *argv[] ) { 181 #pragma message( "Compiled" ) // force non-empty .expect file181 /* code */ 182 182 } 183 183 -
tests/limits.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue May 10 20:44:20 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 08:45:43 202013 // Update Count : 1012 // Last Modified On : Tue Nov 6 17:57:55 2018 13 // Update Count : 8 14 14 // 15 16 // Note: For testing the ability to load the constants defined in libcfa/src/limits.cfa,17 // see discussion in test const-init.18 15 19 16 #include <limits.hfa> … … 150 147 151 148 int main(int argc, char const *argv[]) { 152 #pragma message( "Compiled" ) // force non-empty .expect file 149 //DUMMY 150 return 0; 153 151 } 154 152 -
tests/linking/withthreads.cfa
reef8dfb rbdfc032 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // withthreads.cfa --7 // nothreads.cfa -- 8 8 // 9 9 // Author : Thierry Delisle -
tests/literals.cfa
reef8dfb rbdfc032 10 10 // Created On : Sat Sep 9 16:34:38 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 29 10:57:56 202013 // Update Count : 22 612 // Last Modified On : Tue Feb 12 08:07:39 2019 13 // Update Count : 224 14 14 // 15 15 … … 151 151 -0X0123456789.0123456789P-09; -0X0123456789.0123456789P-09f; -0X0123456789.0123456789P-09l; -0X0123456789.0123456789P-09F; -0X0123456789.0123456789P-09L; 152 152 153 #if defined( __i386 ) || defined( __x86_64 )154 153 #if defined(__GNUC__) && __GNUC_PREREQ(7,0) // gcc version >= 7 155 154 // floating with length, gcc f16/f128x unsupported and no prelude code for any _FloatXXx, so they work by conversion to long double … … 195 194 /* -0x123456789.0123456789P-09F16; */ -0x123456789.0123456789P-09F32; -0x123456789.0123456789P-09F32x; -0x123456789.0123456789P-09F64; -0x123456789.0123456789P-09F64x; -0x123456789.0123456789P-09W; -0x123456789.0123456789P-09F128; -0x123456789.0123456789P-09q; /* -0x123456789.0123456789P-09q; */ 196 195 #endif // __GNUC_PREREQ(7,0) 197 #endif // __i386 ) || __x86_64198 196 199 197 #ifdef __CFA__ … … 216 214 -01234567_l8; -01234567_l16; -01234567_l32; -01234567_l64; -01234567_l8u; -01234567_ul16; -01234567_l32u; -01234567_ul64; 217 215 218 #if defined( __SIZEOF_INT128__ )216 #ifdef __LP64__ // 64-bit processor 219 217 01234567_l128; 01234567_ul128; 220 218 +01234567_l128; +01234567_ul128; 221 219 -01234567_l128; -01234567_ul128; 222 #endif // __ SIZEOF_INT128__220 #endif // __LP64__ 223 221 224 222 // decimal … … 227 225 -1234567890L8; -1234567890L16; -1234567890l32; -1234567890l64; -1234567890UL8; -1234567890L16U; -1234567890Ul32; -1234567890l64u; 228 226 229 #if defined( __SIZEOF_INT128__ )227 #ifdef __LP64__ // 64-bit processor 230 228 1234567890l128; 1234567890l128u; 231 229 +1234567890l128; +1234567890l128u; 232 230 -1234567890l128; -1234567890l128u; 233 1234567890123456789_L128u; 1234567890123456789_L128u; 234 18446708753438544741_l64u; 18446708753438544741_Ul64; 235 #endif // __SIZEOF_INT128__ 231 #endif // __LP64__ 236 232 237 233 // hexadecimal -
tests/manipulatorsInput.cfa
reef8dfb rbdfc032 7 7 // Created On : Sat Jun 8 17:58:54 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Wed Jul 15 15:56:03 202010 // Update Count : 479 // Last Modified On : Thu Jun 13 17:41:43 2019 10 // Update Count : 37 11 11 // 12 12 … … 152 152 sin | ignore( wdi( 8, ldc ) ); sout | ldc; 153 153 } 154 #if defined( __SIZEOF_INT128__ )155 {156 int128 val;157 for ( 15 ) {158 sin | val;159 sout | val;160 }161 }162 #endif // __SIZEOF_INT128__163 154 } // main 164 155 -
tests/manipulatorsOutput1.cfa
reef8dfb rbdfc032 7 7 // Created On : Sat Jun 8 18:04:11 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Fri May 1 11:51:44 202010 // Update Count : 99 // Last Modified On : Mon Jun 10 12:37:28 2019 10 // Update Count : 8 11 11 // 12 12 … … 17 17 signed char sc = -12; 18 18 printf( "%hhd %2hhd %5.2hhd %-5.2hhd %hho %#hho %hhx %#hhx %#8hhx %#8.10hhx %#8.3hhX %+-8.3hhd %08hhd\n", sc, sc, sc, sc, sc, sc, sc, sc, sc, sc, sc, sc, sc ); 19 sout | sc | wd(2,sc) | wd(5,2,sc) | left(wd(5,2,sc)) | nobase(oct(sc)) | oct(sc) | nonl; 20 sout | nobase(hex(sc)) | hex(sc) | wd(8,hex(sc)) | wd(8,10,hex(sc)) | upcase(wd(8,3,hex(sc))) | nonl; 21 sout | left(sign(upcase(wd(8,3,sc)))) | pad0(wd(8,sc)); 19 sout | sc | wd(2,sc) | wd(5,2,sc) | left(wd(5,2,sc)) | nobase(oct(sc)) | oct(sc) | nobase(hex(sc)) | hex(sc) | wd(8,hex(sc)) | wd(8,10,hex(sc)) | upcase(wd(8,3,hex(sc))) | left(sign(upcase(wd(8,3,sc)))) | pad0(wd(8,sc)); 22 20 23 21 sout | "unsigned char"; 24 22 unsigned char usc = 12; 25 23 printf( "%hhu %2hhu %5.2hhu %-5.2hhu %hho %#hho %hhx %#hhx %#8hhx %#8.10hhx %#8.3hhX %-8.3hhu %08hhu\n", usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc ); 26 sout | usc | wd(2,usc) | wd(5,2,usc) | left(wd(5,2,usc)) | nobase(oct(usc)) | oct(usc) | nonl; 27 sout | nobase(hex(usc)) | hex(usc) | wd(8,hex(usc)) | wd(8,10,hex(usc)) | upcase(wd(8,3,hex(usc))) | nonl; 28 sout | left(upcase(wd(8,3,usc))) | pad0(wd(8,usc)); 24 sout | usc | wd(2,usc) | wd(5,2,usc) | left(wd(5,2,usc)) | nobase(oct(usc)) | oct(usc) | nobase(hex(usc)) | hex(usc) | wd(8,hex(usc)) | wd(8,10,hex(usc)) | upcase(wd(8,3,hex(usc))) | left(upcase(wd(8,3,usc))) | pad0(wd(8,usc)); 29 25 30 26 sout | "signed short int"; 31 27 signed short int si = -12; 32 28 printf( "%hd %2hd %5.2hd %-5.2hd %ho %#ho %hx %#hx %#8hx %#8.10hx %#8.3hX %+-8.3hd %08hd\n", si, si, si, si, si, si, si, si, si, si, si, si, si ); 33 sout | si | wd(2,si) | wd(5,2,si) | left(wd(5,2,si)) | nobase(oct(si)) | oct(si) | nonl; 34 sout | nobase(hex(si)) | hex(si) | wd(8,hex(si)) | wd(8,10,hex(si)) | upcase(wd(8,3,hex(si))) | nonl; 35 sout | left(sign(upcase(wd(8,3,si)))) | pad0(wd(8,si)); 29 sout | si | wd(2,si) | wd(5,2,si) | left(wd(5,2,si)) | nobase(oct(si)) | oct(si) | nobase(hex(si)) | hex(si) | wd(8,hex(si)) | wd(8,10,hex(si)) | upcase(wd(8,3,hex(si))) | left(sign(upcase(wd(8,3,si)))) | pad0(wd(8,si)); 36 30 37 31 sout | "unsigned short int"; 38 32 unsigned short int usi = 12; 39 33 printf( "%hu %2hu %5.2hu %-5.2hu %ho %#ho %hx %#hx %#8hx %#8.10hx %#8.3hX %-8.3hu %08hu\n", usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi ); 40 sout | usi | wd(2,usi) | wd(5,2,usi) | left(wd(5,2,usi)) | nobase(oct(usi)) | oct(usi) | nonl; 41 sout | nobase(hex(usi)) | hex(usi) | wd(8,hex(usi)) | wd(8,10,hex(usi)) | upcase(wd(8,3,hex(usi))) | nonl; 42 sout | left(upcase(wd(8,3,usi))) | pad0(wd(8,usi)); 34 sout | usi | wd(2,usi) | wd(5,2,usi) | left(wd(5,2,usi)) | nobase(oct(usi)) | oct(usi) | nobase(hex(usi)) | hex(usi) | wd(8,hex(usi)) | wd(8,10,hex(usi)) | upcase(wd(8,3,hex(usi))) | left(upcase(wd(8,3,usi))) | pad0(wd(8,usi)); 43 35 44 36 sout | "signed int"; 45 37 signed int i = -12; 46 38 printf( "%d %2d %5.2d %-5.2d %o %#o %x %#x %#8x %#8.10x %#8.3X %+-8.3d %08d\n", i, i, i, i, i, i, i, i, i, i, i, i, i ); 47 sout | i | wd(2,i) | wd(5,2,i) | left(wd(5,2,i)) | nobase(oct(i)) | oct(i) | nonl; 48 sout | nobase(hex(i)) | hex(i) | wd(8,hex(i)) | wd(8,10,hex(i)) | upcase(wd(8,3,hex(i))) | nonl; 49 sout | left(sign(upcase(wd(8,3,i)))) | pad0(wd(8,i)); 39 sout | i | wd(2,i) | wd(5,2,i) | left(wd(5,2,i)) | nobase(oct(i)) | oct(i) | nobase(hex(i)) | hex(i) | wd(8,hex(i)) | wd(8,10,hex(i)) | upcase(wd(8,3,hex(i))) | left(sign(upcase(wd(8,3,i)))) | pad0(wd(8,i)); 50 40 51 41 sout | "unsigned int"; 52 42 unsigned int ui = 12; 53 43 printf( "%u %2u %5.2u %-5.2u %o %#o %x %#x %#8x %#8.10x %#8.3X %-8.3u %08u\n", ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui ); 54 sout | ui | wd(2,ui) | wd(5,2,ui) | left(wd(5,2,ui)) | nobase(oct(ui)) | oct(ui) | nonl; 55 sout | nobase(hex(ui)) | hex(ui) | wd(8,hex(ui)) | wd(8,10,hex(ui)) | upcase(wd(8,3,hex(ui))) | nonl; 56 sout | left(upcase(wd(8,3,ui))) | pad0(wd(8,ui)); 44 sout | ui | wd(2,ui) | wd(5,2,ui) | left(wd(5,2,ui)) | nobase(oct(ui)) | oct(ui) | nobase(hex(ui)) | hex(ui) | wd(8,hex(ui)) | wd(8,10,hex(ui)) | upcase(wd(8,3,hex(ui))) | left(upcase(wd(8,3,ui))) | pad0(wd(8,ui)); 57 45 58 46 sout | "signed long long int"; 59 47 signed long long int lli = -12; 60 48 printf( "%lld %2lld %5.2lld %-5.2lld %llo %#llo %llx %#llx %#8llx %#8.10llx %#8.3llX %+-8.3lld %08lld\n", lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli ); 61 sout | lli | wd(2,lli) | wd(5,2,lli) | left(wd(5,2,lli)) | nobase(oct(lli)) | oct(lli) | nonl; 62 sout | nobase(hex(lli)) | hex(lli) | wd(8,hex(lli)) | wd(8,10,hex(lli)) | upcase(wd(8,3,hex(lli))) | nonl; 63 sout | left(sign(upcase(wd(8,3,lli)))) | pad0(wd(8,lli)); 49 sout | lli | wd(2,lli) | wd(5,2,lli) | left(wd(5,2,lli)) | nobase(oct(lli)) | oct(lli) | nobase(hex(lli)) | hex(lli) | wd(8,hex(lli)) | wd(8,10,hex(lli)) | upcase(wd(8,3,hex(lli))) | left(sign(upcase(wd(8,3,lli)))) | pad0(wd(8,lli)); 64 50 65 51 sout | "unsigned long long int"; 66 52 unsigned long long int ulli = 12; 67 53 printf( "%llu %2llu %5.2llu %-5.2llu %llo %#llo %llx %#llx %#8llx %#8.10llx %#8.3llX %-8.3llu %08llu\n", ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli ); 68 sout | ulli | wd(2,ulli) | wd(5,2,ulli) | left(wd(5,2,ulli)) | nobase(oct(ulli)) | oct(ulli) | nonl; 69 sout | nobase(hex(ulli)) | hex(ulli) | wd(8,hex(ulli)) | wd(8,10,hex(ulli)) | upcase(wd(8,3,hex(ulli))) | nonl; 70 sout | left(upcase(wd(8,3,ulli))) | pad0(wd(8,ulli)); 54 sout | ulli | wd(2,ulli) | wd(5,2,ulli) | left(wd(5,2,ulli)) | nobase(oct(ulli)) | oct(ulli) | nobase(hex(ulli)) | hex(ulli) | wd(8,hex(ulli)) | wd(8,10,hex(ulli)) | upcase(wd(8,3,hex(ulli))) | left(upcase(wd(8,3,ulli))) | pad0(wd(8,ulli)); 71 55 72 56 sout | nl | "binary integral"; 73 sout | bin(0) | bin(13) | upcase(bin(13)) | nobase(bin(13)) | left(wd(8,bin(13))) | wd(8,bin(13)) | nonl; 74 sout | pad0(left(wd(8,bin(13)))) | pad0(wd(8,bin(13))) | pad0(wd(8,10,bin(13))) | pad0(wd(8,6,bin(13))); 57 sout | bin(0) | bin(13) | upcase(bin(13)) | nobase(bin(13)) | left(wd(8,bin(13))) | wd(8,bin(13)) | pad0(left(wd(8,bin(13)))) | pad0(wd(8,bin(13))) | pad0(wd(8,10,bin(13))) | pad0(wd(8,6,bin(13))); 75 58 76 59 … … 79 62 printf( "%g %8g %#8g %g %8g %8.0g %#8.0g %8.2g %#8.2g %-8.2g %-8.2g %-#8.2g %-+8.2g %-+#8.2g %08.2g %8.2E %8.2a %#8.2A %#8.2e\n", 80 63 0.0,3.0F,3.0F, f, f, f, f, f, f, 3.0F, f, f, f, f, f, f, f, f, f ); 81 sout | 0.0 | wd(8, 3.0F) | nodp(wd(8, 3.0F)) | f | wd(8, f) | ws(8,0, f) | nodp(ws(8,0, f)) | ws(8,2, f) | nodp(ws(8,2, f)) | nonl; 82 sout | left(ws(8,2, 3.0F)) | left(ws(8,2, f)) | left(nodp(ws(8,2, f))) | left(sign(ws(8,2, f))) | left(sign(nodp(ws(8,2, f)))) | nonl; 83 sout | pad0(ws(8,2, f)) | upcase(wd(8,2, sci(f))) | wd(8,2, hex(f)) | upcase(wd(8,2, hex(f))) | nodp(wd(8,2, sci(f))); 64 sout | 0.0 | wd(8, 3.0F) | nodp(wd(8, 3.0F)) | f | wd(8, f) | ws(8,0, f) | nodp(ws(8,0, f)) | ws(8,2, f) | nodp(ws(8,2, f)) | left(ws(8,2, 3.0F)) | left(ws(8,2, f)) | left(nodp(ws(8,2, f))) | left(sign(ws(8,2, f))) | left(sign(nodp(ws(8,2, f)))) | pad0(ws(8,2, f)) | upcase(wd(8,2, sci(f))) | wd(8,2, hex(f)) | upcase(wd(8,2, hex(f))) | nodp(wd(8,2, sci(f))); 84 65 85 66 sout | "double"; … … 87 68 printf( "%g %#8f %g %8f %#8.0f %8.0f %8.2f %-8.2f %-+#8.2f %08.2F %8.2E %8.2a %8.2A %8.2e\n", 88 69 0.0, 3.0, d, d, d, d, d, d, d, d, d, d, d, d ); 89 sout | 0.0 | wd(8, 3.0) | d | wd(8, d) | nodp(wd(8,0, d)) | wd(8,0, d) | wd(8,2, d) | nonl; 90 sout | left(wd(8,2, d)) | left(sign(wd(8,2, d))) | pad0(upcase(wd(8,2, d))) | upcase(wd(8,2, sci(d))) | wd(8,2, hex(d)) | upcase(wd(8,2, hex(d))) | wd(8,2, sci(d)); 70 sout | 0.0 | wd(8, 3.0) | d | wd(8, d) | nodp(wd(8,0, d)) | wd(8,0, d) | wd(8,2, d) | left(wd(8,2, d)) | left(sign(wd(8,2, d))) | pad0(upcase(wd(8,2, d))) | upcase(wd(8,2, sci(d))) | wd(8,2, hex(d)) | upcase(wd(8,2, hex(d))) | wd(8,2, sci(d)); 91 71 92 72 sout | "long double"; … … 94 74 printf( "%Lg %#8Lf %Lg %8Lf %#8.0Lf %8.0Lf %8.2Lf %-8.2Lf %-+#8.2Lf %08.2LF %8.2LE %8.2La %8.2LA %8.2Le\n", 95 75 0.0L, 3.0L, ld, ld, ld, ld, ld, ld, ld, ld, ld, ld, ld, ld ); 96 sout | 0.0L | wd(8, 3.0L) | ld | wd(8, ld) | nodp(wd(8,0, ld)) | wd(8,0, ld) | wd(8,2, ld) | nonl; 97 sout | left(wd(8,2, ld)) | left(sign(wd(8,2, ld))) | pad0(upcase(wd(8,2, ld))) | upcase(wd(8,2, sci(ld))) | wd(8,2, hex(ld)) | upcase(wd(8,2, hex(ld))) | wd(8,2, sci(ld)); 76 sout | 0.0L | wd(8, 3.0L) | ld | wd(8, ld) | nodp(wd(8,0, ld)) | wd(8,0, ld) | wd(8,2, ld) | left(wd(8,2, ld)) | left(sign(wd(8,2, ld))) | pad0(upcase(wd(8,2, ld))) | upcase(wd(8,2, sci(ld))) | wd(8,2, hex(ld)) | upcase(wd(8,2, hex(ld))) | wd(8,2, sci(ld)); 98 77 99 78 … … 101 80 char c = 'a'; 102 81 printf( "%c %2c %5c %-5c %hho %#hho %hhx %#hhx %#8hhx %#8hhX %-8c %8c\n", c, c, c, c, c, c, c, c, c, c, c, c ); 103 sout | c | ' ' | wd(2,c) | wd(5,c) | left(wd(5,c)) | nobase(oct(c)) | oct(c) | nonl; 104 sout | nobase(hex(c)) | hex(c) | wd(8,hex(c)) | upcase(wd(8,hex(c))) | left(wd(8,c)) | wd(8,c); 82 sout | c | ' ' | wd(2,c) | wd(5,c) | left(wd(5,c)) | nobase(oct(c)) | oct(c) | nobase(hex(c)) | hex(c) | wd(8,hex(c)) | upcase(wd(8,hex(c))) | left(wd(8,c)) | wd(8,c); 105 83 106 84 sout | nl | "string"; -
tests/manipulatorsOutput2.cfa
reef8dfb rbdfc032 7 7 // Created On : Sat Jun 8 18:04:11 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Sun Nov 15 08:11:53 202010 // Update Count : 99 // Last Modified On : Mon Jun 10 12:37:57 2019 10 // Update Count : 8 11 11 // 12 12 … … 52 52 // Local Variables: // 53 53 // tab-width: 4 // 54 // compile-command: "cfa -Wall -Wextra manipulatorsOutput2.cfa" //54 // compile-command: "cfa -Wall -Wextra amanipulatorsOutput2.cfa" // 55 55 // End: // -
tests/math4.cfa
reef8dfb rbdfc032 10 10 // Created On : Thu May 24 20:56:54 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Aug 25 17:56:45 202013 // Update Count : 712 // Last Modified On : Tue Dec 4 18:15:01 2018 13 // Update Count : 4 14 14 // 15 15 … … 18 18 19 19 int main( void ) { 20 signed char sc, scr1, scr2, scr3;21 unsigned char uc, ucr1, ucr2, ucr3;22 short int si, sir1, sir2, sir3;23 unsigned short int usi, usir1, usir2, usir3;24 int i, ir1, ir2, ir3;25 unsigned int ui, uir1, uir2, uir3;26 long int li, lir1, lir2, lir3;27 unsigned long int uli, ulir1, ulir2, ulir3;28 long long int lli, llir1, llir2, llir3;29 unsigned long long int ulli, ullir1, ullir2, ullir3;30 31 20 float f; 32 21 double d; … … 34 23 35 24 //---------------------- Nearest Integer ---------------------- 36 37 //============================================================38 #if 139 sout | nl | "floor" | nl | nl;40 41 printf( "signed char\n" );42 for ( sc = 1; sc != 0; sc <<= 1 ) {43 scr1 = floor( sc, sc ); scr2 = floor( sc + 2hh, sc ); scr3 = floor( -sc - 2hh, sc );44 printf( "floor(%hhd, %hhd) = %hhd, floor(%hhd, %hhd) = %hhd, floor(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 );45 } // for46 printf( "\n" );47 48 printf( "unsigned char\n" );49 for ( uc = 1; uc != 0; uc <<= 1 ) {50 ucr1 = floor( uc, uc ); ucr2 = floor( uc + 2hh, uc ); ucr3 = floor( -uc - 2hh, uc );51 printf( "floor(%hhu, %hhu) = %hhu, floor(%hhu, %hhu) = %hhu, floor(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 );52 } // for53 printf( "\n" );54 55 printf( "short int\n" );56 for ( si = 1; si != 0; si <<= 1 ) {57 sir1 = floor( si, si ); sir2 = floor( si + 2hh, si ); sir3 = floor( -si - 2hh, si );58 printf( "floor(%hd, %hd) = %hd, floor(%hd, %hd) = %hd, floor(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 );59 } // for60 printf( "\n" );61 62 printf( "unsigned short int\n" );63 for ( usi = 1; usi != 0; usi <<= 1 ) {64 usir1 = floor( usi, usi ); usir2 = floor( usi + 2hh, usi ); usir3 = floor( -usi - 2hh, usi );65 printf( "floor(%hu, %hu) = %hu, floor(%hu, %hu) = %hu, floor(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 );66 } // for67 printf( "\n" );68 69 printf( "int\n" );70 for ( i = 1; i != 0; i <<= 1 ) {71 ir1 = floor( i, i ); ir2 = floor( i + 2hh, i ); ir3 = floor( -i - 2hh, i );72 printf( "floor(%d, %d) = %d, floor(%d, %d) = %d, floor(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 );73 } // for74 printf( "\n" );75 76 printf( "unsigned int\n" );77 for ( ui = 1; ui != 0; ui <<= 1 ) {78 uir1 = floor( ui, ui ); uir2 = floor( ui + 2hh, ui ); uir3 = floor( -ui - 2hh, ui );79 printf( "floor(%u, %u) = %u, floor(%u, %u) = %u, floor(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 );80 } // for81 printf( "\n" );82 83 printf( "long int\n" );84 for ( li = 1; li != 0; li <<= 1 ) {85 lir1 = floor( li, li ); lir2 = floor( li + 2hh, li ); lir3 = floor( -li - 2hh, li );86 printf( "floor(%ld, %ld) = %ld, floor(%ld, %ld) = %ld, floor(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 );87 } // for88 printf( "\n" );89 90 printf( "unsigned long int\n" );91 for ( uli = 1; uli != 0; uli <<= 1 ) {92 ulir1 = floor( uli, uli ); ulir2 = floor( uli + 2hh, uli ); ulir3 = floor( -uli - 2hh, uli );93 printf( "floor(%lu, %lu) = %lu, floor(%lu, %lu) = %lu, floor(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 );94 } // for95 printf( "\n" );96 97 printf( "long long int\n" );98 for ( lli = 1; lli != 0; lli <<= 1 ) {99 llir1 = floor( lli, lli ); llir2 = floor( lli + 2hh, lli ); llir3 = floor( -lli - 2hh, lli );100 printf( "floor(%lld, %lld) = %lld, floor(%lld, %lld) = %lld, floor(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 );101 } // for102 printf( "\n" );103 104 printf( "unsigned long long int\n" );105 for ( ulli = 1; ulli != 0; ulli <<= 1 ) {106 ullir1 = floor( ulli, ulli ); ullir2 = floor( ulli + 2hh, ulli ); ullir3 = floor( -ulli - 2hh, ulli );107 printf( "floor(%llu, %llu) = %llu, floor(%llu, %llu) = %llu, floor(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 );108 } // for109 printf( "\n" );110 #endif // 0111 //============================================================112 #if 1113 sout | nl | "ceiling_div" | nl | nl;114 115 printf( "signed char\n" );116 for ( sc = 1; sc != 0; sc <<= 1 ) {117 scr1 = ceiling_div( sc, sc ); scr2 = ceiling_div( sc + 2hh, sc ); scr3 = ceiling_div( -sc - 2hh, sc );118 printf( "ceiling_div(%hhd, %hhd) = %hhd, ceiling_div(%hhd, %hhd) = %hhd, ceiling_div(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 );119 } // for120 printf( "\n" );121 122 printf( "unsigned char\n" );123 for ( uc = 1; uc != 0; uc <<= 1 ) {124 ucr1 = ceiling_div( uc, uc ); ucr2 = ceiling_div( uc + 2hh, uc ); ucr3 = ceiling_div( -uc - 2hh, uc );125 printf( "ceiling_div(%hhu, %hhu) = %hhu, ceiling_div(%hhu, %hhu) = %hhu, ceiling_div(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 );126 } // for127 printf( "\n" );128 129 printf( "short int\n" );130 for ( si = 1; si != 0; si <<= 1 ) {131 sir1 = ceiling_div( si, si ); sir2 = ceiling_div( si + 2hh, si ); sir3 = ceiling_div( -si - 2hh, si );132 printf( "ceiling_div(%hd, %hd) = %hd, ceiling_div(%hd, %hd) = %hd, ceiling_div(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 );133 } // for134 printf( "\n" );135 136 printf( "unsigned short int\n" );137 for ( usi = 1; usi != 0; usi <<= 1 ) {138 usir1 = ceiling_div( usi, usi ); usir2 = ceiling_div( usi + 2hh, usi ); usir3 = ceiling_div( -usi - 2hh, usi );139 printf( "ceiling_div(%hu, %hu) = %hu, ceiling_div(%hu, %hu) = %hu, ceiling_div(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 );140 } // for141 printf( "\n" );142 143 printf( "int\n" );144 for ( i = 1; i != 0; i <<= 1 ) {145 ir1 = ceiling_div( i, i ); ir2 = ceiling_div( i + 2hh, i ); ir3 = ceiling_div( -i - 2hh, i );146 printf( "ceiling_div(%d, %d) = %d, ceiling_div(%d, %d) = %d, ceiling_div(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 );147 } // for148 printf( "\n" );149 150 printf( "unsigned int\n" );151 for ( ui = 1; ui != 0; ui <<= 1 ) {152 uir1 = ceiling_div( ui, ui ); uir2 = ceiling_div( ui + 2hh, ui ); uir3 = ceiling_div( -ui - 2hh, ui );153 printf( "ceiling_div(%u, %u) = %u, ceiling_div(%u, %u) = %u, ceiling_div(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 );154 } // for155 printf( "\n" );156 157 printf( "long int\n" );158 for ( li = 1; li != 0; li <<= 1 ) {159 lir1 = ceiling_div( li, li ); lir2 = ceiling_div( li + 2hh, li ); lir3 = ceiling_div( -li - 2hh, li );160 printf( "ceiling_div(%ld, %ld) = %ld, ceiling_div(%ld, %ld) = %ld, ceiling_div(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 );161 } // for162 printf( "\n" );163 164 printf( "unsigned long int\n" );165 for ( uli = 1; uli != 0; uli <<= 1 ) {166 ulir1 = ceiling_div( uli, uli ); ulir2 = ceiling_div( uli + 2hh, uli ); ulir3 = ceiling_div( -uli - 2hh, uli );167 printf( "ceiling_div(%lu, %lu) = %lu, ceiling_div(%lu, %lu) = %lu, ceiling_div(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 );168 } // for169 printf( "\n" );170 171 printf( "long long int\n" );172 for ( lli = 1; lli != 0; lli <<= 1 ) {173 llir1 = ceiling_div( lli, lli ); llir2 = ceiling_div( lli + 2hh, lli ); llir3 = ceiling_div( -lli - 2hh, lli );174 printf( "ceiling_div(%lld, %lld) = %lld, ceiling_div(%lld, %lld) = %lld, ceiling_div(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 );175 } // for176 printf( "\n" );177 178 printf( "unsigned long long int\n" );179 for ( ulli = 1; ulli != 0; ulli <<= 1 ) {180 ullir1 = ceiling_div( ulli, ulli ); ullir2 = ceiling_div( ulli + 2hh, ulli ); ullir3 = ceiling_div( -ulli - 2hh, ulli );181 printf( "ceiling_div(%llu, %llu) = %llu, ceiling_div(%llu, %llu) = %llu, ceiling_div(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 );182 } // for183 printf( "\n" );184 #endif // 0185 //============================================================186 #if 1187 sout | nl | "ceiling" | nl | nl;188 189 printf( "signed char\n" );190 for ( sc = 1; sc != 0; sc <<= 1 ) {191 scr1 = ceiling( sc, sc ); scr2 = ceiling( sc + 2hh, sc ); scr3 = ceiling( -sc - 2hh, sc );192 printf( "ceiling(%hhd, %hhd) = %hhd, ceiling(%hhd, %hhd) = %hhd, ceiling(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 );193 } // for194 printf( "\n" );195 196 printf( "unsigned char\n" );197 for ( uc = 1; uc != 0; uc <<= 1 ) {198 ucr1 = ceiling( uc, uc ); ucr2 = ceiling( uc + 2hh, uc ); ucr3 = ceiling( -uc - 2hh, uc );199 printf( "ceiling(%hhu, %hhu) = %hhu, ceiling(%hhu, %hhu) = %hhu, ceiling(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 );200 } // for201 printf( "\n" );202 203 printf( "short int\n" );204 for ( si = 1; si != 0; si <<= 1 ) {205 sir1 = ceiling( si, si ); sir2 = ceiling( si + 2hh, si ); sir3 = ceiling( -si - 2hh, si );206 printf( "ceiling(%hd, %hd) = %hd, ceiling(%hd, %hd) = %hd, ceiling(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 );207 } // for208 printf( "\n" );209 210 printf( "unsigned short int\n" );211 for ( usi = 1; usi != 0; usi <<= 1 ) {212 usir1 = ceiling( usi, usi ); usir2 = ceiling( usi + 2hh, usi ); usir3 = ceiling( -usi - 2hh, usi );213 printf( "ceiling(%hu, %hu) = %hu, ceiling(%hu, %hu) = %hu, ceiling(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 );214 } // for215 printf( "\n" );216 217 printf( "int\n" );218 for ( i = 1; i != 0; i <<= 1 ) {219 ir1 = ceiling( i, i ); ir2 = ceiling( i + 2hh, i ); ir3 = ceiling( -i - 2hh, i );220 printf( "ceiling(%d, %d) = %d, ceiling(%d, %d) = %d, ceiling(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 );221 } // for222 printf( "\n" );223 224 printf( "unsigned int\n" );225 for ( ui = 1; ui != 0; ui <<= 1 ) {226 uir1 = ceiling( ui, ui ); uir2 = ceiling( ui + 2hh, ui ); uir3 = ceiling( -ui - 2hh, ui );227 printf( "ceiling(%u, %u) = %u, ceiling(%u, %u) = %u, ceiling(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 );228 } // for229 printf( "\n" );230 231 printf( "long int\n" );232 for ( li = 1; li != 0; li <<= 1 ) {233 lir1 = ceiling( li, li ); lir2 = ceiling( li + 2hh, li ); lir3 = ceiling( -li - 2hh, li );234 printf( "ceiling(%ld, %ld) = %ld, ceiling(%ld, %ld) = %ld, ceiling(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 );235 } // for236 printf( "\n" );237 238 printf( "unsigned long int\n" );239 for ( uli = 1; uli != 0; uli <<= 1 ) {240 ulir1 = ceiling( uli, uli ); ulir2 = ceiling( uli + 2hh, uli ); ulir3 = ceiling( -uli - 2hh, uli );241 printf( "ceiling(%lu, %lu) = %lu, ceiling(%lu, %lu) = %lu, ceiling(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 );242 } // for243 printf( "\n" );244 245 printf( "long long int\n" );246 for ( lli = 1; lli != 0; lli <<= 1 ) {247 llir1 = ceiling( lli, lli ); llir2 = ceiling( lli + 2hh, lli ); llir3 = ceiling( -lli - 2hh, lli );248 printf( "ceiling(%lld, %lld) = %lld, ceiling(%lld, %lld) = %lld, ceiling(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 );249 } // for250 printf( "\n" );251 252 printf( "unsigned long long int\n" );253 for ( ulli = 1; ulli != 0; ulli <<= 1 ) {254 ullir1 = ceiling( ulli, ulli ); ullir2 = ceiling( ulli + 2hh, ulli ); ullir3 = ceiling( -ulli - 2hh, ulli );255 printf( "ceiling(%llu, %llu) = %llu, ceiling(%llu, %llu) = %llu, ceiling(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 );256 } // for257 printf( "\n" );258 #endif // 0259 25 260 26 sout | "floor:" | floor( 1.2F ) | floor( 1.2D ) | floor( 1.2L ); … … 303 69 // Local Variables: // 304 70 // tab-width: 4 // 305 // compile-command: "cfa math 4.cfa" //71 // compile-command: "cfa math3.cfa" // 306 72 // End: // -
tests/maybe.cfa
reef8dfb rbdfc032 10 10 // Created On : Thr May 25 16:02:00 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Sep 25 15:13:28 202013 // Update Count : 212 // Last Modified On : Thu Jul 20 15:24:07 2017 13 // Update Count : 1 14 14 // 15 15 … … 65 65 //checkNamedConstructors(); 66 66 checkSetters(); 67 printf( "done\n" ); // non-empty .expect file68 67 } -
tests/minmax.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 15 08:28:01 202013 // Update Count : 5 412 // Last Modified On : Tue Dec 4 21:45:31 2018 13 // Update Count : 52 14 14 // 15 15 … … 23 23 24 24 sout | "char\t\t\t" | 'z' | ' ' | 'a' | "\tmin " | min( 'z', 'a' ); 25 sout | "signed int\t\t" | 4 | -3 | "\tmin" | min( 4, -3 );25 sout | "signed int\t\t" | 4 | 3 | "\tmin" | min( 4, 3 ); 26 26 sout | "unsigned int\t\t" | 4u | 3u | "\tmin" | min( 4u, 3u ); 27 sout | "signed long int\t\t" | 4l | -3l | "\tmin" | min( 4l, -3l );27 sout | "signed long int\t\t" | 4l | 3l | "\tmin" | min( 4l, 3l ); 28 28 sout | "unsigned long int\t" | 4ul | 3ul | "\tmin" | min( 4ul, 3ul ); 29 sout | "signed long long int\t" | 4ll | -3ll | "\tmin" | min( 4ll, -3ll );29 sout | "signed long long int\t" | 4ll | 3ll | "\tmin" | min( 4ll, 3ll ); 30 30 sout | "unsigned long long int\t" | 4ull | 3ull | "\tmin" | min( 4ull, 3ull ); 31 31 sout | "float\t\t\t" | 4.0f | 3.1f | "\tmin" | min( 4.0f, 3.1f ); … … 36 36 37 37 sout | "char\t\t\t" | 'z' | ' ' | 'a' | "\tmax " | max( 'z', 'a' ); 38 sout | "signed int\t\t" | 4 | -3 | "\tmax" | max( 4, -3 );38 sout | "signed int\t\t" | 4 | 3 | "\tmax" | max( 4, 3 ); 39 39 sout | "unsigned int\t\t" | 4u | 3u | "\tmax" | max( 4u, 3u ); 40 sout | "signed long int\t\t" | 4l | -3l | "\tmax" | max( 4l, -3l );40 sout | "signed long int\t\t" | 4l | 3l | "\tmax" | max( 4l, 3l ); 41 41 sout | "unsigned long int\t" | 4ul | 3ul | "\tmax" | max( 4ul, 3ul ); 42 sout | "signed long long int\t" | 4ll | -3ll | "\tmax" | max( 4ll, -3ll );42 sout | "signed long long int\t" | 4ll | 3ll | "\tmax" | max( 4ll, 3ll ); 43 43 sout | "unsigned long long int\t" | 4ull | 3ull | "\tmax" | max( 4ull, 3ull ); 44 44 sout | "float\t\t\t" | 4.0f | 3.1f | "\tmax" | max( 4.0f, 3.1f ); -
tests/nested-types.cfa
reef8dfb rbdfc032 10 10 // Created On : Mon Jul 9 10:20:03 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 08:48:59 202013 // Update Count : 612 // Last Modified On : Tue Nov 6 17:59:40 2018 13 // Update Count : 2 14 14 // 15 15 16 16 typedef int N; 17 17 struct A { 18 forall(otype T)19 struct N {20 T x;21 };18 forall(otype T) 19 struct N { 20 T x; 21 }; 22 22 }; 23 23 24 24 struct S { 25 struct T {26 int i;27 typedef int Bar;28 };29 T x;25 struct T { 26 int i; 27 typedef int Bar; 28 }; 29 T x; 30 30 31 // struct U;32 typedef T Bar;33 typedef int Baz;31 // struct U; 32 typedef T Bar; 33 typedef int Baz; 34 34 }; 35 35 … … 51 51 // }; 52 52 53 // struct S { 54 // enum C { R, G, B }; 55 // int i; 56 // struct T { 57 // int i; 58 // }; 59 // T t; 60 // }; 53 int main() { 54 // access nested struct 55 S.T x; 61 56 62 // S s; 63 // S.C c; 64 // S.T t; 57 { 58 struct S { 59 int i; 60 struct Z { 61 double d; 62 }; 63 }; 65 64 66 int main() { 67 // access nested struct 68 S.T x; 65 S.Z z; // gets local S 66 .S.T y; // lookup at global scope only 69 67 70 { 71 struct S { 72 int i; 73 struct Z { 74 double d; 75 }; 76 }; 77 78 S.Z z; // gets local S 79 .S.T y; // lookup at global scope only 80 81 const volatile .S.T q; 68 const volatile .S.T q; 82 69 #if ERR1 83 T err1;// error: no T in scope70 T err1; // error: no T in scope 84 71 #endif 85 72 #if ERR2 86 .Z err2;// error: no Z in global scope87 .S.Baz.Bar err3;// error: .S.Baz => int, int is not aggregate and should not appear left of the dot88 .S.Z err4;// error: no Z in global S73 .Z err2; // error: no Z in global scope 74 .S.Baz.Bar err3; // error: .S.Baz => int, int is not aggregate and should not appear left of the dot 75 .S.Z err4; // error: no Z in global S 89 76 #endif 90 }77 } 91 78 92 // U.S un;79 // U.S un; 93 80 94 S.Bar y;95 S.Baz x;96 S.T.Bar z;81 S.Bar y; 82 S.Baz x; 83 S.T.Bar z; 97 84 98 // A.N(int) x; // xxx - should not be an error, but currently is. 99 100 #pragma message( "Compiled" ) // force non-empty .expect file 85 // A.N(int) x; // xxx - should not be an error, but currently is. 101 86 } 102 87 -
tests/numericConstants.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 24 22:10:36 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 07:55:22 202013 // Update Count : 712 // Last Modified On : Tue Feb 5 08:58:16 2019 13 // Update Count : 5 14 14 // 15 15 … … 63 63 0x_ff.ffp0; // hex real 64 64 0x_1.ffff_ffff_p_128_l; 65 66 #pragma message( "Compiled" ) // force non-empty .expect file67 65 } // main 68 66 -
tests/operators.cfa
reef8dfb rbdfc032 31 31 int main(int argc, char const *argv[]) { 32 32 /* code */ 33 printf( "done\n" ); // non-empty .expect file33 return 0; 34 34 } 35 35 -
tests/pybin/settings.py
reef8dfb rbdfc032 23 23 class Architecture: 24 24 KnownArchitectures = { 25 'x64' : 'x64', 26 'x86-64' : 'x64', 27 'x86_64' : 'x64', 28 'x86' : 'x86', 29 'aarch64' : 'arm64', 30 'arm64' : 'arm64', 31 'ARM64' : 'arm64', 32 'i386' : 'x86', 33 'i486' : 'x86', 34 'i686' : 'x86', 35 'Intel 80386' : 'x86', 36 'arm' : 'arm32', 37 'ARM' : 'arm32', 38 'arm32' : 'arm32', 39 'ARM32' : 'arm32', 25 'x64' : 'x64', 26 'x86-64' : 'x64', 27 'x86_64' : 'x64', 28 'x86' : 'x86', 29 'aarch64' : 'arm', 30 'i386' : 'x86', 31 'i486' : 'x86', 32 'i686' : 'x86', 33 'Intel 80386' : 'x86', 34 'arm' : 'arm', 35 'ARM' : 'arm', 40 36 } 41 37 42 38 CrossCompileFlags = { 43 'x64' : 'ARCH_FLAGS=-m64', 44 'x86' : 'ARCH_FLAGS=-m32', 45 'arm64': 'ARCH_FLAGS=', 46 'arm32': 'ARCH_FLAGS=', 39 'x64' : 'ARCH_FLAGS=-m64', 40 'x86' : 'ARCH_FLAGS=-m32', 47 41 } 48 42 … … 83 77 print("updated to %s" % self.target) 84 78 85 def filter(self, tests):86 return [test for test in tests if not test.arch or self.target == test.arch]79 def match(self, arch): 80 return True if not arch else self.target == arch 87 81 88 @ staticmethod89 def make_canonical( arch):82 @classmethod 83 def make_canonical(_, arch): 90 84 return Architecture.KnownArchitectures[arch] 91 85 … … 96 90 self.flags = """DEBUG_FLAGS=%s""" % ("-debug -O0" if value else "-nodebug -O2") 97 91 self.path = "debug" if value else "nodebug" 98 99 class AST:100 def __init__(self, ast):101 if ast == "new":102 self.target = ast103 self.string = "New AST"104 self.flags = """AST_FLAGS=-XCFA,--new-ast"""105 elif ast == "old":106 self.target = ast107 self.string = "Old AST"108 self.flags = """AST_FLAGS=-XCFA,--old-ast"""109 elif ast == None:110 self.target = "new" if config.NEWAST else "old"111 self.string = "Default AST (%s)" % self.target112 self.flags = """AST_FLAGS="""113 else:114 print("""ERROR: Invalid ast configuration, must be "old", "new" or left unspecified, was %s""" % (value), file=sys.stderr)115 sys.exit(1)116 117 def filter(self, tests):118 119 return [test for test in tests if not test.astv or self.target == test.astv]120 92 121 93 class Install: … … 132 104 self.total = Timeouts.check(tg) 133 105 134 @ staticmethod135 def check( value):106 @classmethod 107 def check(_, value): 136 108 if value < 1: 137 109 print("Timeouts must be at least 1 second", file=sys.stderr) … … 141 113 142 114 def init( options ): 143 global all_ast144 global all_arch145 global all_debug146 global all_install147 global ast148 115 global arch 116 global archive 149 117 global debug 150 global archive 151 global install 152 153 global continue_ 118 global distcc 154 119 global dry_run 155 120 global generating 121 global install 156 122 global make 157 123 global output_width 158 124 global timeout 159 global timeout2gdb160 125 161 all_ast = [AST(o) for o in list(dict.fromkeys(options.ast ))] if options.ast else [AST(None)] 162 all_arch = [Architecture(o) for o in list(dict.fromkeys(options.arch ))] if options.arch else [Architecture(None)] 163 all_debug = [Debug(o) for o in list(dict.fromkeys(options.debug ))] 164 all_install = [Install(o) for o in list(dict.fromkeys(options.install))] 126 arch = Architecture(options.arch) 165 127 archive = os.path.abspath(os.path.join(original_path, options.archive_errors)) if options.archive_errors else None 166 continue_ = options.continue_128 debug = Debug(options.debug) 167 129 dry_run = options.dry_run # must be called before tools.config_hash() 130 distcc = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash() 168 131 generating = options.regenerate_expected 132 install = Install(options.install) 169 133 make = ['make'] 170 134 output_width = 24 171 135 timeout = Timeouts(options.timeout, options.global_timeout) 172 timeout2gdb = options.timeout_with_gdb173 136 174 137 # if we distribute, distcc errors will fail tests, use log file for distcc … … 183 146 184 147 def validate(): 185 """Validate the current configuration and update globals"""186 187 global distcc188 distcc = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()189 148 errf = os.path.join(BUILDDIR, ".validate.err") 190 149 make_ret, out = tools.make( ".validate", error_file = errf, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL ) -
tests/pybin/test_run.py
reef8dfb rbdfc032 11 11 self.path = '' 12 12 self.arch = '' 13 self.astv = ''14 13 15 14 def toString(self): 16 return "{:25s} ({:5s} arch, {:s} ast: {:s})".format( self.name, self.arch if self.arch else "Any", self.astv if self.astvelse "Any", self.target() )15 return "{:25s} ({:5s} {:s})".format( self.name, self.arch if self.arch else "Any", self.target() ) 17 16 18 17 def prepare(self): … … 21 20 22 21 def expect(self): 23 arch = '' if not self.arch else ".%s" % self.arch 24 astv = '' if not self.astv else ".nast" if self.astv == "new" else ".oast" 25 return os.path.normpath( os.path.join(settings.SRCDIR , self.path, ".expect", "%s%s%s.txt" % (self.name,astv,arch)) ) 22 return os.path.normpath( os.path.join(settings.SRCDIR , self.path, ".expect", "%s%s.txt" % (self.name,'' if not self.arch else ".%s" % self.arch)) ) 26 23 27 24 def error_log(self): … … 43 40 return os.path.normpath( os.path.join(settings.BUILDDIR, self.path, self.name) ) 44 41 45 @ staticmethod46 def valid_name( name):42 @classmethod 43 def valid_name(_, name): 47 44 return not name.endswith( ('.c', '.cc', '.cpp', '.cfa') ) 48 45 49 @ staticmethod50 def new_target(target, arch, astv):46 @classmethod 47 def from_target(_, target): 51 48 test = Test() 52 49 test.name = os.path.basename(target) 53 50 test.path = os.path.relpath (os.path.dirname(target), settings.SRCDIR) 54 test.arch = arch.target if arch else '' 55 test.astv = astv.target if astv else '' 51 test.arch = settings.arch.target if settings.arch.cross_compile else '' 56 52 return test 57 53 … … 73 69 else : text = "FAILED with code %d" % retcode 74 70 75 text += " C%s - R%s" % ( fmtDur(duration[0]),fmtDur(duration[1]))71 text += " C%s - R%s" % (cls.fmtDur(duration[0]), cls.fmtDur(duration[1])) 76 72 return text 73 74 @classmethod 75 def fmtDur( cls, duration ): 76 if duration : 77 hours, rem = divmod(duration, 3600) 78 minutes, rem = divmod(rem, 60) 79 seconds, millis = divmod(rem, 1) 80 return "%2d:%02d.%03d" % (minutes, seconds, millis * 1000) 81 return " n/a" -
tests/pybin/tools.py
reef8dfb rbdfc032 73 73 ) 74 74 75 return proc.returncode, out.decode(" latin-1") if out else None75 return proc.returncode, out.decode("utf-8") if out else None 76 76 except subprocess.TimeoutExpired: 77 if settings.timeout2gdb: 78 print("Process {} timeout".format(proc.pid)) 79 proc.communicate() 80 return 124, str(None) 81 else: 82 proc.send_signal(signal.SIGABRT) 83 proc.communicate() 84 return 124, str(None) 77 proc.send_signal(signal.SIGABRT) 78 proc.communicate() 79 return 124, str(None) 85 80 86 81 except Exception as ex: … … 88 83 raise 89 84 90 def is_empty(fname):91 if not os.path.isfile(fname):92 return True93 94 if os.stat(fname).st_size == 0:95 return True96 97 return False98 99 85 def is_ascii(fname): 100 86 if settings.dry_run: 101 87 print("is_ascii: %s" % fname) 102 return (True, "")88 return True 103 89 104 90 if not os.path.isfile(fname): 105 return (False, "No file")106 107 code, out = sh("file ",fname, output_file=subprocess.PIPE)91 return False 92 93 code, out = sh("file %s" % fname, output_file=subprocess.PIPE) 108 94 if code != 0: 109 return (False, "'file EXPECT' failed with code {}".format(code))95 return False 110 96 111 97 match = re.search(".*: (.*)", out) 112 98 113 99 if not match: 114 return (False, "Unreadable file type: '{}'".format(out)) 115 116 if "ASCII text" in match.group(1): 117 return (True, "") 118 119 return (False, "File type should be 'ASCII text', was '{}'".format(match.group(1))) 100 return False 101 102 return match.group(1).startswith("ASCII text") 120 103 121 104 def is_exe(fname): … … 132 115 return None 133 116 134 file = open(file, mode , encoding="latin-1") # use latin-1 so all chars mean something.117 file = open(file, mode) 135 118 exitstack.push(file) 136 119 return file … … 181 164 '-s' if silent else None, 182 165 test_param, 183 settings.ast.flags,184 166 settings.arch.flags, 185 167 settings.debug.flags, … … 192 174 return sh(*cmd, output_file=output_file, error=error) 193 175 194 def make_recon(target):195 cmd = [196 *settings.make,197 '-W',198 os.path.abspath(os.path.join(settings.BUILDDIR, '../driver/cfa')),199 '--recon',200 target201 ]202 cmd = [s for s in cmd if s]203 return sh(*cmd, output_file=subprocess.PIPE)204 205 176 def which(program): 206 fpath, fname = os.path.split(program) 207 if fpath: 208 if is_exe(program): 209 return program 210 else: 211 for path in os.environ["PATH"].split(os.pathsep): 212 exe_file = os.path.join(path, program) 213 if is_exe(exe_file): 214 return exe_file 215 return None 177 fpath, fname = os.path.split(program) 178 if fpath: 179 if is_exe(program): 180 return program 181 else: 182 for path in os.environ["PATH"].split(os.pathsep): 183 exe_file = os.path.join(path, program) 184 if is_exe(exe_file): 185 return exe_file 186 187 return None 216 188 217 189 @contextlib.contextmanager … … 262 234 # helper function to check if a files contains only a specific string 263 235 def file_contains_only(file, text) : 264 with open(file , encoding="latin-1") as f: # use latin-1 so all chars mean something.236 with open(file) as f: 265 237 ff = f.read().strip() 266 238 result = ff == text.strip() … … 270 242 # transform path to canonical form 271 243 def canonical_path(path): 272 abspath = os.path.abspath( os.path.realpath(__main__.__file__))244 abspath = os.path.abspath(__main__.__file__) 273 245 dname = os.path.dirname(abspath) 274 246 return os.path.join(dname, os.path.normpath(path) ) … … 351 323 raise argparse.ArgumentTypeError(msg) 352 324 353 # Convert a function that converts a string to one that converts comma separated string.354 def comma_separated(elements):355 return lambda string: [elements(part) for part in string.split(',')]356 357 325 def fancy_print(text): 358 326 column = which('column') … … 397 365 398 366 class Timed: 399 def __enter__(self):400 self.start = time.time()401 return self402 403 def __exit__(self, *args):404 self.end = time.time()405 self.duration = self.end - self.start367 def __enter__(self): 368 self.start = time.time() 369 return self 370 371 def __exit__(self, *args): 372 self.end = time.time() 373 self.duration = self.end - self.start 406 374 407 375 def timed(src, timeout): 408 376 expire = time.time() + timeout 409 377 i = iter(src) 410 with contextlib.suppress(StopIteration): 411 while True: 412 yield i.next(max(expire - time.time(), 0)) 413 414 def fmtDur( duration ): 415 if duration : 416 hours, rem = divmod(duration, 3600) 417 minutes, rem = divmod(rem, 60) 418 seconds, millis = divmod(rem, 1) 419 return "%2d:%02d.%03d" % (minutes, seconds, millis * 1000) 420 return " n/a" 378 while True: 379 yield i.next(max(expire - time.time(), 0)) -
tests/quotedKeyword.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 7 19:07:07202013 // Update Count : 2 512 // Last Modified On : Sat Feb 1 00:02:22 2020 13 // Update Count : 24 14 14 // 15 15 … … 17 17 18 18 struct { 19 int ``otype ;20 int ``struct ;19 int ``otype``; 20 int ``struct``; 21 21 } st = { 10, 10 }; 22 22 23 typedef int ``forall ;24 ``forall xxx = 10;23 typedef int ``forall``; 24 ``forall`` xxx = 10; 25 25 26 int ``_Alignas , ``_Alignof, ``__alignof, ``__alignof__, ``asm, ``__asm, ``__asm__, ``_At, ``_Atomic, ``__attribute,27 ``__attribute__ , ``auto, ``_Bool, ``break, ``case, ``catch, ``catchResume, ``char, ``choose, ``_Complex, ``__complex,28 ``__complex__ , ``const, ``__const, ``__const__, ``continue, ``default, ``disable, ``do, ``double, ``dtype, ``else,29 ``enable , ``enum, ``__extension__, ``extern, ``fallthru, ``finally, ``float, ``__float128, ``for, ``forall, ``fortran,30 ``ftype , ``_Generic, ``goto, ``if, ``_Imaginary, ``__imag, ``__imag__, ``inline, ``__inline, ``__inline__, ``int,31 ``__int128 , ``__label__, ``long, ``lvalue, ``_Noreturn, ``__builtin_offsetof, ``otype, ``register, ``restrict,32 ``__restrict , ``__restrict__, ``return, ``short, ``signed, ``__signed, ``__signed__, ``sizeof, ``static,33 ``_Static_assert , ``struct, ``switch, ``_Thread_local, ``throw, ``throwResume, ``trait, ``try, ``typedef,34 ``typeof , ``__typeof, ``__typeof__, ``union, ``unsigned, ``__builtin_va_list, ``void, ``volatile, ``__volatile,35 ``__volatile__ , ``while;26 int ``_Alignas``, ``_Alignof``, ``__alignof``, ``__alignof__``, ``asm``, ``__asm``, ``__asm__``, ``_At``, ``_Atomic``, ``__attribute``, 27 ``__attribute__``, ``auto``, ``_Bool``, ``break``, ``case``, ``catch``, ``catchResume``, ``char``, ``choose``, ``_Complex``, ``__complex``, 28 ``__complex__``, ``const``, ``__const``, ``__const__``, ``continue``, ``default``, ``disable``, ``do``, ``double``, ``dtype``, ``else``, 29 ``enable``, ``enum``, ``__extension__``, ``extern``, ``fallthru``, ``finally``, ``float``, ``__float128``, ``for``, ``forall``, ``fortran``, 30 ``ftype``, ``_Generic``, ``goto``, ``if``, ``_Imaginary``, ``__imag``, ``__imag__``, ``inline``, ``__inline``, ``__inline__``, ``int``, 31 ``__int128``, ``__label__``, ``long``, ``lvalue``, ``_Noreturn``, ``__builtin_offsetof``, ``otype``, ``register``, ``restrict``, 32 ``__restrict``, ``__restrict__``, ``return``, ``short``, ``signed``, ``__signed``, ``__signed__``, ``sizeof``, ``static``, 33 ``_Static_assert``, ``struct``, ``switch``, ``_Thread_local``, ``throw``, ``throwResume``, ``trait``, ``try``, ``typedef``, 34 ``typeof``, ``__typeof``, ``__typeof__``, ``union``, ``unsigned``, ``__builtin_va_list``, ``void``, ``volatile``, ``__volatile``, 35 ``__volatile__``, ``while``; 36 36 37 37 int main() { 38 int ``if = 0;39 ``catch = 1;40 st.``otype = 2;41 st.``struct = 3;42 ``throw = 4;43 sout | ``catch + st.``otype + st.``struct + ``throw;38 int ``if`` = 0; 39 ``catch`` = 1; 40 st.``otype`` = 2; 41 st.``struct`` = 3; 42 ``throw`` = 4; 43 sout | ``catch`` + st.``otype`` + st.``struct`` + ``throw``; 44 44 } 45 45 -
tests/raii/.expect/ctor-autogen.txt
reef8dfb rbdfc032 1 done -
tests/raii/.expect/init_once.txt
reef8dfb rbdfc032 1 done -
tests/raii/ctor-autogen.cfa
reef8dfb rbdfc032 151 151 identity(gcs); 152 152 identity(gcu); 153 printf( "done\n" ); // non-empty .expect file154 153 } -
tests/raii/init_once.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Jun 14 15:43:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Sep 25 15:36:39 202013 // Update Count : 512 // Last Modified On : Fri Mar 22 13:41:26 2019 13 // Update Count : 4 14 14 // 15 15 … … 188 188 static_variable(); 189 189 } 190 printf( "done\n" ); // non-empty .expect file191 190 } 192 191 -
tests/rational.cfa
reef8dfb rbdfc032 10 10 // Created On : Mon Mar 28 08:43:12 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 8 18:46:23 202013 // Update Count : 8 612 // Last Modified On : Wed Mar 27 07:37:17 2019 13 // Update Count : 80 14 14 // 15 15 … … 19 19 #include <fstream.hfa> 20 20 21 typedef Rational(int) RatInt; 22 double convert( int i ) { return (double)i; } // used by narrow/widen 21 double convert( int i ) { return (double)i; } 23 22 int convert( double d ) { return (int)d; } 24 23 25 24 int main() { 26 25 sout | "constructor"; 27 Rat Int a = { 3 }, b = { 4 }, c, d = 0, e = 1;28 sout | a | b | c | d | e;26 Rational(int) a = { 3 }, b = { 4 }, c; 27 sout | a | b | c; 29 28 30 a = (Rat Int){ 4, 8 };31 b = (Rat Int){ 5, 7 };29 a = (Rational(int)){ 4, 8 }; 30 b = (Rational(int)){ 5, 7 }; 32 31 sout | a | b; 33 a = (Rat Int){ -2, -3 };34 b = (Rat Int){ 3, -2 };32 a = (Rational(int)){ -2, -3 }; 33 b = (Rational(int)){ 3, -2 }; 35 34 sout | a | b; 36 a = (Rat Int){ -2, 3 };37 b = (Rat Int){ 3, 2 };35 a = (Rational(int)){ -2, 3 }; 36 b = (Rational(int)){ 3, 2 }; 38 37 sout | a | b; 39 38 40 39 sout | "logical"; 41 a = (Rat Int){ -2 };42 b = (Rat Int){ -3, 2 };40 a = (Rational(int)){ -2 }; 41 b = (Rational(int)){ -3, 2 }; 43 42 sout | a | b; 44 43 // sout | a == 1; // FIX ME … … 59 58 60 59 sout | "conversion"; 61 a = (Rat Int){ 3, 4 };60 a = (Rational(int)){ 3, 4 }; 62 61 sout | widen( a ); 63 a = (Rat Int){ 1, 7 };62 a = (Rational(int)){ 1, 7 }; 64 63 sout | widen( a ); 65 a = (Rat Int){ 355, 113 };64 a = (Rational(int)){ 355, 113 }; 66 65 sout | widen( a ); 67 66 sout | narrow( 0.75, 4 ); … … 75 74 76 75 sout | "more tests"; 77 Rat Intx = { 1, 2 }, y = { 2 };76 Rational(int) x = { 1, 2 }, y = { 2 }; 78 77 sout | x - y; 79 78 sout | x > y; … … 81 80 sout | y | denominator( y, -2 ) | y; 82 81 83 Rat Intz = { 0, 5 };82 Rational(int) z = { 0, 5 }; 84 83 sout | z; 85 84 86 85 sout | x | numerator( x, 0 ) | x; 87 86 88 x = (Rat Int){ 1, MAX } + (RatInt){ 1, MAX };87 x = (Rational(int)){ 1, MAX } + (Rational(int)){ 1, MAX }; 89 88 sout | x; 90 x = (Rat Int){ 3, MAX } + (RatInt){ 2, MAX };89 x = (Rational(int)){ 3, MAX } + (Rational(int)){ 2, MAX }; 91 90 sout | x; 92 91 -
tests/references.cfa
reef8dfb rbdfc032 124 124 int *p = &a; 125 125 asm ( 126 #if defined( __i386 ) || defined( __x86_64 ) 127 "incl %[p]\n\t" 128 : [p] "+m" (*p) 129 #elif defined( __aarch64__ ) 130 "ldr w1, %[p]\n\t" 131 "add w1, w1, 1\n\t" 132 "str w1, %[p]\n\t" 133 : [p] "+m" (*p) ::"w1" 134 #endif 126 "incl %[p]\n\t" 127 : [p] "+m" (*p) 135 128 ); 136 129 printf("%d\n", a); -
tests/result.cfa
reef8dfb rbdfc032 10 10 // Created On : Thr May 25 16:50:00 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Sep 25 15:22:59 202013 // Update Count : 212 // Last Modified On : Thu Jul 20 15:24:12 2017 13 // Update Count : 1 14 14 // 15 15 … … 66 66 checkGetters(); 67 67 checkSetters(); 68 printf( "done\n" ); // non-empty .expect file69 68 } -
tests/searchsort.cfa
reef8dfb rbdfc032 38 38 } // for 39 39 sout | nl; 40 for ( i; 0 ~ size ) { // C version , returns void*40 for ( i; 0 ~ size ) { // C version 41 41 int key = size - i; 42 int * v = ( int * )bsearch( &key, iarr, size, sizeof( iarr[0] ), comp );42 int * v = bsearch( &key, iarr, size, sizeof( iarr[0] ), comp ); 43 43 sout | key | ':' | *v | ", "; 44 44 } // for -
tests/stdincludes.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Aug 29 08:26:14 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 08:51:38 202013 // Update Count : 812 // Last Modified On : Tue Nov 6 18:00:53 2018 13 // Update Count : 6 14 14 // 15 15 … … 47 47 #include <wctype.h> 48 48 49 int main() { 50 #pragma message( "Compiled" ) // force non-empty .expect file 51 } 49 int main() {} 52 50 53 51 // Local Variables: // -
tests/switch.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Jul 12 06:50:22 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 08:35:02 202013 // Update Count : 4312 // Last Modified On : Tue Nov 6 18:01:34 2018 13 // Update Count : 37 14 14 // 15 15 … … 100 100 j = 5; 101 101 } // choose 102 103 #pragma message( "Compiled" ) // force non-empty .expect file104 102 } // main 105 103 -
tests/test.py
reef8dfb rbdfc032 6 6 7 7 import argparse 8 import itertools9 8 import re 10 9 import sys … … 24 23 25 24 def match_test(path): 26 match = re.search("^%s\/([\w\/\-_]*).expect\/([\w\-_]+)(\. nast|\.oast)?(\.[\w\-_]+)?\.txt$" % settings.SRCDIR, path)25 match = re.search("^%s\/([\w\/\-_]*).expect\/([\w\-_]+)(\.[\w\-_]+)?\.txt$" % settings.SRCDIR, path) 27 26 if match : 28 27 test = Test() 29 28 test.name = match.group(2) 30 29 test.path = match.group(1) 31 test.arch = match.group(4)[1:] if match.group(4) else None 32 33 astv = match.group(3)[1:] if match.group(3) else None 34 if astv == 'oast': 35 test.astv = 'old' 36 elif astv == 'nast': 37 test.astv = 'new' 38 elif astv: 39 print('ERROR: "%s", expect file has astv but it is not "nast" or "oast"' % testname, file=sys.stderr) 40 sys.exit(1) 41 42 expected.append(test) 30 test.arch = match.group(3)[1:] if match.group(3) else None 31 if settings.arch.match(test.arch): 32 expected.append(test) 43 33 44 34 path_walk( match_test ) … … 63 53 ] 64 54 65 # sort the test alphabetically for convenience66 test_list.sort(key=lambda t: ('~' if t.arch else '') + t.target() + (t.arch if t.arch else ''))67 68 55 return test_list 69 56 … … 76 63 if options.regenerate_expected : 77 64 for testname in options.tests : 78 testname = os.path.normpath( os.path.join(settings.SRCDIR, testname) ) 79 80 # first check if this is a valid name to regenerate 65 testname = canonical_path( testname ) 81 66 if Test.valid_name(testname): 82 # this is a valid name, let's check if it already exists83 67 found = [test for test in all_tests if canonical_path( test.target() ) == testname] 84 setup = itertools.product(settings.all_arch if options.arch else [None], settings.all_ast if options.ast else [None]) 85 if not found: 86 # it's a new name, create it according to the name and specified architecture/ast version 87 tests.extend( [Test.new_target(testname, arch, ast) for arch, ast in setup] ) 88 elif len(found) == 1 and not found[0].arch: 89 # we found a single test, the user better be wanting to create a cross platform test 90 if options.arch: 91 print('ERROR: "%s", test has no specified architecture but --arch was specified, ignoring it' % testname, file=sys.stderr) 92 elif options.ast: 93 print('ERROR: "%s", test has no specified ast version but --ast was specified, ignoring it' % testname, file=sys.stderr) 94 else: 95 tests.append( found[0] ) 96 else: 97 # this test is already cross platform, just add a test for each platform the user asked 98 tests.extend( [Test.new_target(testname, arch, ast) for arch, ast in setup] ) 99 100 # print a warning if it users didn't ask for a specific architecture 101 found_arch = [f.arch for f in found if f.arch] 102 if found_arch and not options.arch: 103 print('WARNING: "%s", test has architecture specific expected files but --arch was not specified, regenerating only for current host' % testname, file=sys.stderr) 104 105 106 # print a warning if it users didn't ask for a specific ast version 107 found_astv = [f.astv for f in found if f.astv] 108 if found_astv and not options.ast: 109 print('WARNING: "%s", test has ast version specific expected files but --ast was not specified, regenerating only for current ast' % testname, file=sys.stderr) 110 68 tests.append( found[0] if len(found) == 1 else Test.from_target(testname) ) 111 69 else : 112 70 print('ERROR: "%s", tests are not allowed to end with a C/C++/CFA extension, ignoring it' % testname, file=sys.stderr) … … 118 76 119 77 if test : 120 tests. extend( test)78 tests.append( test[0] ) 121 79 else : 122 80 print('ERROR: No expected file for test %s, ignoring it' % testname, file=sys.stderr) … … 128 86 # create a parser with the arguments for the tests script 129 87 parser = argparse.ArgumentParser(description='Script which runs cforall tests') 130 parser.add_argument('--ast', help='Test for specific ast', type=comma_separated(str), default=None) 131 parser.add_argument('--arch', help='Test for specific architecture', type=comma_separated(str), default=None) 132 parser.add_argument('--debug', help='Run all tests in debug or release', type=comma_separated(yes_no), default='yes') 133 parser.add_argument('--install', help='Run all tests based on installed binaries or tree binaries', type=comma_separated(yes_no), default='no') 134 parser.add_argument('--continue', help='When multiple specifications are passed (debug/install/arch), sets whether or not to continue if the last specification failed', type=yes_no, default='yes', dest='continue_') 135 parser.add_argument('--timeout', help='Maximum duration in seconds after a single test is considered to have timed out', type=int, default=120) 88 parser.add_argument('--debug', help='Run all tests in debug or release', type=yes_no, default='yes') 89 parser.add_argument('--install', help='Run all tests based on installed binaries or tree binaries', type=yes_no, default='no') 90 parser.add_argument('--arch', help='Test for specific architecture', type=str, default='') 91 parser.add_argument('--timeout', help='Maximum duration in seconds after a single test is considered to have timed out', type=int, default=60) 136 92 parser.add_argument('--global-timeout', help='Maximum cumulative duration in seconds after the ALL tests are considered to have timed out', type=int, default=7200) 137 parser.add_argument('--timeout-with-gdb', help='Instead of killing the command when it times out, orphan it and print process id to allow gdb to attach', type=yes_no, default="no")138 93 parser.add_argument('--dry-run', help='Don\'t run the tests, only output the commands', action='store_true') 139 94 parser.add_argument('--list', help='List all test available', action='store_true') … … 143 98 parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously', type=int) 144 99 parser.add_argument('--list-comp', help='List all valide arguments', action='store_true') 145 parser.add_argument('--list-dist', help='List all tests for distribution', action='store_true')146 100 parser.add_argument('-I','--include', help='Directory of test to include, can be used multiple time, All if omitted', action='append') 147 101 parser.add_argument('-E','--exclude', help='Directory of test to exclude, can be used multiple time, None if omitted', action='append') … … 156 110 157 111 # script must have at least some tests to run or be listing 158 listing = options.list or options.list_comp or options.list_dist112 listing = options.list or options.list_comp 159 113 all_tests = options.all 160 114 some_tests = len(options.tests) > 0 … … 191 145 test.prepare() 192 146 193 # ----------194 # MAKE195 # ----------196 147 # build, skipping to next test on error 197 148 with Timed() as comp_dur: 198 149 make_ret, _ = make( test.target(), output_file=subprocess.DEVNULL, error=out_file, error_file = err_file ) 199 150 200 # ---------- 201 # RUN 202 # ---------- 151 run_dur = None 203 152 # run everything in a temp directory to make sure core file are handled properly 204 run_dur = None205 153 with tempdir(): 206 # if the make command succe eds continue otherwise skip to diff154 # if the make command succeds continue otherwise skip to diff 207 155 if success(make_ret): 208 156 with Timed() as run_dur: … … 218 166 if success(retcode): 219 167 if settings.generating : 220 # if we are o nly generating the output we still need to check that the test actually exists168 # if we are ounly generating the output we still need to check that the test actually exists 221 169 if no_rule(out_file, test.target()) : 222 170 retcode = 1 … … 230 178 231 179 else: 232 if os.stat(out_file).st_size < 1048576: 233 with open (out_file, "r", encoding='latin-1') as myfile: # use latin-1 so all chars mean something. 234 error = myfile.read() 235 else: 236 error = "Output log can't be read, file is bigger than 1MB, see {} for actual error\n".format(out_file) 180 with open (out_file, "r") as myfile: 181 error = myfile.read() 237 182 238 183 ret, info = core_info(exe_file) … … 269 214 except KeyboardInterrupt: 270 215 return False, "" 271 #except Exception as ex:272 # print("Unexpected error in worker thread running {}: {}".format(t.target(), ex), file=sys.stderr)273 #sys.stderr.flush()274 #return False, ""216 except Exception as ex: 217 print("Unexpected error in worker thread: %s" % ex, file=sys.stderr) 218 sys.stderr.flush() 219 return False, "" 275 220 276 221 … … 280 225 make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL) 281 226 282 # create the executor for our jobs 283 pool = multiprocessing.Pool(jobs) 227 # since python prints stacks by default on a interrupt, redo the interrupt handling to be silent 228 def worker_init(): 229 def sig_int(signal_num, frame): 230 pass 231 232 signal.signal(signal.SIGINT, sig_int) 233 234 # create the executor for our jobs and handle the signal properly 235 pool = multiprocessing.Pool(jobs, worker_init) 284 236 285 237 failed = False 238 239 def stop(x, y): 240 print("Tests interrupted by user", file=sys.stderr) 241 sys.exit(1) 242 signal.signal(signal.SIGINT, stop) 286 243 287 244 # for each test to run … … 321 278 make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL) 322 279 323 return failed280 return 1 if failed else 0 324 281 325 282 … … 335 292 settings.init( options ) 336 293 337 # --------------------------------------------------338 # list all the test for auto completion programs339 # not pretty, single line, with the command line options340 if options.list_comp :341 # fetch the liest of all valid tests342 tests = list_tests( None, None )343 344 # print the possible options345 print("-h --help --debug --dry-run --list --ast=new --ast=old --arch --all --regenerate-expected --archive-errors --install --timeout --global-timeout --timeout-with-gdb -j --jobs -I --include -E --exclude --continue ", end='')346 print(" ".join(map(lambda t: "%s" % (t.target()), tests)))347 348 # done349 sys.exit(0)350 351 # --------------------------------------------------352 # list all the test for auto completion programs353 if options.list_dist :354 # fetch the liest of all valid tests355 tests = list_tests( None, None )356 357 for t in tests:358 print(os.path.relpath(t.expect(), settings.SRCDIR), end=' ')359 print(os.path.relpath(t.input() , settings.SRCDIR), end=' ')360 code, out = make_recon(t.target())361 362 if code != 0:363 print('ERROR: recond failed for test {}'.format(t.target()), file=sys.stderr)364 sys.exit(1)365 366 print(' '.join(re.findall('([^\s]+\.cfa)', out)), end=' ')367 368 print('')369 370 # done371 sys.exit(0)372 373 374 # --------------------------------------------------375 # list all the tests for users, in a pretty format376 if options.list :377 # fetch the liest of all valid tests378 tests = list_tests( options.include, options.exclude )379 380 # print the available tests381 fancy_print("\n".join(map(lambda t: t.toString(), tests)))382 383 # done384 sys.exit(0)385 386 294 # fetch the liest of all valid tests 387 295 all_tests = list_tests( options.include, options.exclude ) 388 296 297 389 298 # if user wants all tests than no other treatement of the test list is required 390 if options.all or options. include :299 if options.all or options.list or options.list_comp or options.include : 391 300 tests = all_tests 392 301 … … 400 309 sys.exit(1) 401 310 402 # prep invariants 403 settings.prep_output(tests) 404 failed = 0 405 406 # check if the expected files aren't empty 407 if not options.regenerate_expected: 408 for t in tests: 409 if is_empty(t.expect()): 410 print('WARNING: test "{}" has empty .expect file'.format(t.target()), file=sys.stderr) 411 412 # for each build configurations, run the test 413 with Timed() as total_dur: 414 for ast, arch, debug, install in itertools.product(settings.all_ast, settings.all_arch, settings.all_debug, settings.all_install): 415 settings.ast = ast 416 settings.arch = arch 417 settings.debug = debug 418 settings.install = install 419 420 # filter out the tests for a different architecture 421 # tests are the same across debug/install 422 local_tests = settings.ast.filter( tests ) 423 local_tests = settings.arch.filter( local_tests ) 424 options.jobs, forceJobs = job_count( options, local_tests ) 425 settings.update_make_cmd(forceJobs, options.jobs) 426 427 # check the build configuration works 428 settings.validate() 429 430 # print configuration 431 print('%s %i tests on %i cores (%s:%s - %s)' % ( 432 'Regenerating' if settings.generating else 'Running', 433 len(local_tests), 434 options.jobs, 435 settings.ast.string, 436 settings.arch.string, 437 settings.debug.string 438 )) 439 if not local_tests : 440 print('WARNING: No tests for this configuration') 441 continue 442 443 # otherwise run all tests and make sure to return the correct error code 444 failed = run_tests(local_tests, options.jobs) 445 if failed: 446 result = 1 447 if not settings.continue_: 448 break 449 450 print('Tests took %s' % fmtDur( total_dur.duration )) 451 sys.exit( failed ) 311 312 # sort the test alphabetically for convenience 313 tests.sort(key=lambda t: (t.arch if t.arch else '') + t.target()) 314 315 # users may want to simply list the tests 316 if options.list_comp : 317 print("-h --help --debug --dry-run --list --arch --all --regenerate-expected --archive-errors --install --timeout --global-timeout -j --jobs ", end='') 318 print(" ".join(map(lambda t: "%s" % (t.target()), tests))) 319 320 elif options.list : 321 print("Listing for %s:%s"% (settings.arch.string, settings.debug.string)) 322 fancy_print("\n".join(map(lambda t: t.toString(), tests))) 323 324 else : 325 # check the build configuration works 326 settings.prep_output(tests) 327 settings.validate() 328 329 options.jobs, forceJobs = job_count( options, tests ) 330 settings.update_make_cmd(forceJobs, options.jobs) 331 332 print('%s %i tests on %i cores (%s:%s)' % ( 333 'Regenerating' if settings.generating else 'Running', 334 len(tests), 335 options.jobs, 336 settings.arch.string, 337 settings.debug.string 338 )) 339 340 # otherwise run all tests and make sure to return the correct error code 341 sys.exit( run_tests(tests, options.jobs) ) -
tests/time.cfa
reef8dfb rbdfc032 10 10 // Created On : Tue Mar 27 17:24:56 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jun 18 18:14:49202013 // Update Count : 3 712 // Last Modified On : Sun Jan 5 18:27:37 2020 13 // Update Count : 34 14 14 // 15 15 … … 20 20 Duration d1 = 3`h, d2 = 2`s, d3 = 3.375`s, d4 = 12`s, d5 = 1`s + 10_000`ns; 21 21 sout | d1 | d2 | d3 | d4 | d5; 22 sout | d1`dd | d2`dm | d3`ds | d4`dms | d5`dus;23 22 d1 = 0; 24 23 sout | d1 | d2 | d3; -
tests/tuple/tupleAssign.cfa
reef8dfb rbdfc032 44 44 double d = 0.0; 45 45 int i = 0; 46 signedchar c = '\0';46 char c = '\0'; 47 47 struct X { 48 48 int z; … … 55 55 [t, x, d, i, c, x] = (double)94.12; 56 56 printf( "d=%lg i=%d c=%c t=[%d, %lg, %d]\n", d, i, (int)c, t ); 57 sout | "d=" | d | "i=" | i | "c=" | (char)c | ' ' | "t=[" | t | "]";57 sout | "d=" | d | "i=" | i | "c=" | c | ' ' | "t=[" | t | "]"; 58 58 [x, c, i, d, x, t] = (double)-94.12; 59 59 printf( "d=%lg i=%d c=%c t=[%d, %lg, %d]\n", d, i, c, t ); 60 sout | "d=" | d | "i=" | i | "c=" | (char)c | ' ' | "t=[" | t | "]";60 sout | "d=" | d | "i=" | i | "c=" | c | ' ' | "t=[" | t | "]"; 61 61 } 62 62 } -
tests/typedefRedef.cfa
reef8dfb rbdfc032 27 27 typedef int ARR[]; 28 28 typedef int ARR[]; 29 #ifdef ERR1 30 // if a typedef has an array dimension, it can only be redefined to the same dimension 29 // #ifdef ERR1 30 // if a typedef has an array dimension, 31 // it can only be redefined to the same dimension 31 32 typedef int ARR[2]; 32 #endif33 // #endif 33 34 34 35 typedef int X; … … 53 54 54 55 int main() { 55 typedef int ARR[sz];56 typedef int ARR[sz]; 56 57 57 // can't redefine typedef which is VLA58 // can't redefine typedef which is VLA 58 59 #if ERR1 59 typedef int ARR[sz];60 typedef int ARR[sz]; 60 61 #endif 61 62 62 Foo *x;63 Foo *x; 63 64 64 typedef struct Bar Foo;65 Foo *y;65 typedef struct Bar Foo; 66 Foo *y; 66 67 67 typedef int *** pt; 68 69 #pragma message( "Compiled" ) // force non-empty .expect file 68 typedef int *** pt; 70 69 } -
tests/typeof.cfa
reef8dfb rbdfc032 1 1 int main() { 2 int *v1; 3 typeof(v1) v2; 4 typeof(*v1) v3[4]; 5 char *v4[4]; 6 typeof(typeof(char *)[4]) v5; 7 typeof (int *) v6; 8 typeof( int ( int, int p ) ) *v7; 9 typeof( [int] ( int, int p ) ) *v8; 10 (typeof(v1)) v2; // cast with typeof 11 printf( "done\n" ); // non-empty .expect file 2 int *v1; 3 typeof(v1) v2; 4 typeof(*v1) v3[4]; 5 char *v4[4]; 6 typeof(typeof(char *)[4]) v5; 7 typeof (int *) v6; 8 typeof( int ( int, int p ) ) *v7; 9 typeof( [int] ( int, int p ) ) *v8; 10 (typeof(v1)) v2; // cast with typeof 12 11 } -
tests/userLiterals.cfa
reef8dfb rbdfc032 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // user Literals.cfa --7 // user_literals.cfa -- 8 8 // 9 9 // Author : Peter A. Buhr 10 10 // Created On : Wed Sep 6 21:40:50 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 19 07:48:45 202013 // Update Count : 7412 // Last Modified On : Tue Dec 4 22:03:10 2018 13 // Update Count : 56 14 14 // 15 15 … … 24 24 int ?`__thingy_( int x ) { sout | "_thingy_" | x; return x; } 25 25 26 int ?`s( const char * s ) { sout | "s " | s; return 0; }27 int ?`m( const char16_t * m ) { sout | "m " | m; return 0;}28 int ?`h( const char32_t * h ) { sout | "h " | h; return 0; }26 int ?`s( const char * s ) { sout | "secs" | s; return 0; } 27 int ?`m( const char16_t * m ) { sout | "mins" | m; return 0;} 28 int ?`h( const char32_t * h ) { sout | "hours" | h; return 0; } 29 29 int ?`_A_( const wchar_t * str ) { sout | "_A_" | str; return 0; } 30 30 int ?`__thingy_( const char * str ) { sout | "_thingy_" | str; return 0; } … … 37 37 return (Weight){ l.stones + r.stones }; 38 38 } 39 ofstream & ?|?( ofstream & os, Weight w ) { return os | wd(1,1, w.stones); } 40 void ?|?( ofstream & os, Weight w ) { (ofstream)(os | w); ends( os ); } 39 ofstream & ?|?( ofstream & os, Weight w ) { return os | w.stones; } 41 40 42 41 Weight ?`st( double w ) { return (Weight){ w }; } // backquote for user literals … … 61 60 sout | w; 62 61 63 0`s;62 // 0`secs; 64 63 1`s; 65 64 23`s; … … 83 82 84 83 "abc"`s; 85 // FIX ME: requires char16_t, char32_t, and wchar_t be unique types 86 // u"abc"`m; 87 // U_"abc"`h; 88 // L"abc"`_A_; 84 // u"abc"`m; 85 // U_"abc"`h; 86 // L"abc"`_A_; 89 87 u8_"abc"`__thingy_; 90 88 } // main … … 92 90 // Local Variables: // 93 91 // tab-width: 4 // 94 // compile-command: "cfa user Literals.cfa" //92 // compile-command: "cfa user_literals.cfa" // 95 93 // End: // -
tests/variableDeclarator.cfa
reef8dfb rbdfc032 10 10 // Created On : Wed Aug 17 08:41:42 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 07:46:17 202013 // Update Count : 1312 // Last Modified On : Tue Nov 6 18:02:16 2018 13 // Update Count : 2 14 14 // 15 15 … … 18 18 int (f2); 19 19 20 int * f3;21 int ** f4;22 int * const * f5;20 int *f3; 21 int **f4; 22 int * const *f5; 23 23 int * const * const f6; 24 24 25 int * (f7);26 int ** (f8);27 int * const * (f9);25 int *(f7); 26 int **(f8); 27 int * const *(f9); 28 28 int * const * const (f10); 29 29 30 int (* f11);31 int (** f12);32 int (* const * f13);30 int (*f11); 31 int (**f12); 32 int (* const *f13); 33 33 int (* const * const f14); 34 34 35 int f15[ 0];35 int f15[]; 36 36 int f16[10]; 37 int (f17[ 0]);37 int (f17[]); 38 38 int (f18[10]); 39 39 40 int * f19[0];41 int * f20[10];42 int ** f21[0];43 int ** f22[10];44 int * const * f23[0];45 int * const * f24[10];46 int * const * const f25[ 0];40 int *f19[]; 41 int *f20[10]; 42 int **f21[]; 43 int **f22[10]; 44 int * const *f23[]; 45 int * const *f24[10]; 46 int * const * const f25[]; 47 47 int * const * const f26[10]; 48 48 49 int *(f27[ 0]);49 int *(f27[]); 50 50 int *(f28[10]); 51 int **(f29[ 0]);51 int **(f29[]); 52 52 int **(f30[10]); 53 int * const *(f31[ 0]);53 int * const *(f31[]); 54 54 int * const *(f32[10]); 55 int * const * const (f33[ 0]);55 int * const * const (f33[]); 56 56 int * const * const (f34[10]); 57 57 58 int (* f35)[];59 int (* f36)[10];60 int (** f37)[];61 int (** f38)[10];62 int (* const * f39)[];63 int (* const * f40)[10];58 int (*f35)[]; 59 int (*f36)[10]; 60 int (**f37)[]; 61 int (**f38)[10]; 62 int (* const *f39)[]; 63 int (* const *f40)[10]; 64 64 int (* const * const f41)[]; 65 65 int (* const * const f42)[10]; 66 66 67 int f43[ 0][3];67 int f43[][3]; 68 68 int f44[3][3]; 69 int (f45[ 0])[3];69 int (f45[])[3]; 70 70 int (f46[3])[3]; 71 int ((f47[ 0]))[3];71 int ((f47[]))[3]; 72 72 int ((f48[3]))[3]; 73 73 74 int * f49[0][3];75 int * f50[3][3];76 int ** f51[0][3];77 int ** f52[3][3];78 int * const * f53[0][3];79 int * const * f54[3][3];80 int * const * const f55[ 0][3];74 int *f49[][3]; 75 int *f50[3][3]; 76 int **f51[][3]; 77 int **f52[3][3]; 78 int * const *f53[][3]; 79 int * const *f54[3][3]; 80 int * const * const f55[][3]; 81 81 int * const * const f56[3][3]; 82 82 83 int (* f57[0][3]);84 int (* f58[3][3]);85 int (** f59[0][3]);86 int (** f60[3][3]);87 int (* const * f61[0][3]);88 int (* const * f62[3][3]);89 int (* const * const f63[ 0][3]);83 int (*f57[][3]); 84 int (*f58[3][3]); 85 int (**f59[][3]); 86 int (**f60[3][3]); 87 int (* const *f61[][3]); 88 int (* const *f62[3][3]); 89 int (* const * const f63[][3]); 90 90 int (* const * const f64[3][3]); 91 91 … … 93 93 int (f66)(int); 94 94 95 int * f67(int);96 int ** f68(int);97 int * const * f69(int);95 int *f67(int); 96 int **f68(int); 97 int * const *f69(int); 98 98 int * const * const f70(int); 99 99 … … 104 104 int * const * const (f74)(int); 105 105 106 int (* f75)(int);107 int (** f76)(int);108 int (* const * f77)(int);106 int (*f75)(int); 107 int (**f76)(int); 108 int (* const *f77)(int); 109 109 int (* const * const f78)(int); 110 110 111 int (*(* f79)(int))();111 int (*(*f79)(int))(); 112 112 int (*(* const f80)(int))(); 113 113 int (* const(* const f81)(int))(); … … 119 119 //int fe2()[]; // returning an array 120 120 //int fe3()(); // returning a function 121 //int (* fe4)()(); // returning a function122 //int ((* fe5())())[]; // returning an array121 //int (*fe4)()(); // returning a function 122 //int ((*fe5())())[]; // returning an array 123 123 124 #ifdef __CFA__125 124 // Cforall extensions 126 125 … … 130 129 const * const * int cf6; 131 130 132 [ 0] int cf15;131 [] int cf15; 133 132 [10] int cf16; 134 133 135 [ 0] * int cf19;134 [] * int cf19; 136 135 [10] * int cf20; 137 int ** cf21[0];136 int **cf21[]; 138 137 [10] * * int cf22; 139 [ 0] * const * int cf23;138 [] * const * int cf23; 140 139 [10] * const * int cf24; 141 [ 0] const * const * int cf25;140 [] const * const * int cf25; 142 141 [10] const * const * int cf26; 143 142 … … 151 150 const * const * [10] int cf42; 152 151 153 [ 0][3] int cf43;152 [][3] int cf43; 154 153 [3][3] int cf44; 155 154 156 [ 0][3] * int cf49;155 [][3] * int cf49; 157 156 [3][3] * int cf50; 158 [ 0][3] * * int cf51;157 [][3] * * int cf51; 159 158 [3][3] * * int cf52; 160 [ 0][3] const * int cf53;159 [][3] const * int cf53; 161 160 [3][3] * const * int cf54; 162 [ 0][3] const * const * int cf55;161 [][3] const * const * int cf55; 163 162 [3][3] const * const * int cf56; 164 163 … … 174 173 175 174 *[]*[]* [ *[]*[] int ]( *[]*[] int, *[]*[] int ) v3; 176 #endif // __CFA__177 175 178 176 //Dummy main 179 int main( int argc, char const * argv[] ) { 180 #pragma message( "Compiled" ) // force non-empty .expect file 177 int main(int argc, char const *argv[]) 178 { 179 return 0; 181 180 } 182 181 -
tests/vector.cfa
reef8dfb rbdfc032 14 14 // 15 15 16 #include <fstream.hfa> 16 17 #include <vector.hfa> 17 #include <fstream.hfa>18 18 19 19 #undef assert … … 28 28 int main() { 29 29 vector( int ) iv; 30 31 assert( ((uintptr_t)&iv.storage.storage ) == (((uintptr_t)&iv)) );32 assert( ((uintptr_t)&iv.storage.capacity) == (((uintptr_t)&iv) + sizeof(void *)) );33 assert( ((uintptr_t)&iv.size ) == (((uintptr_t)&iv) + sizeof(void *) + sizeof(size_t)) );34 30 35 31 assert( empty( &iv ) ); -
tests/voidPtr.cfa
reef8dfb rbdfc032 13 13 if ( ! a ) { 14 14 abort(); 15 } 16 printf( "done\n" ); // non-empty .expect file 15 } 17 16 } 18 17 -
tests/warnings/self-assignment.cfa
reef8dfb rbdfc032 10 10 // Created On : Thu Mar 1 13:53:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Sep 27 09:24:34 202013 // Update Count : 612 // Last Modified On : Wed Feb 20 07:56:17 2019 13 // Update Count : 3 14 14 // 15 15 … … 31 31 s.i = s.i; 32 32 t.s.i = t.s.i; 33 34 #pragma message( "Compiled" ) // force non-empty .expect file35 33 } 36 34 37 35 // Local Variables: // 38 36 // tab-width: 4 // 39 // compile-command: "cfa self-assignment.cfa" //37 // compile-command: "cfa dtor-early-exit" // 40 38 // End: // -
tools/Makefile.am
reef8dfb rbdfc032 18 18 ACLOCAL_AMFLAGS = -I automake 19 19 20 AM_CFLAGS = -Wall -Wextra -O2 -g 21 20 22 noinst_PROGRAMS = busy catchsig repeat watchdog 21 AM_CFLAGS = -Wall -Wextra -O2 -g 23 24 busy_SOURCES = busy.c 22 25 busy_LDFLAGS = -pthread 23 24 nodist_busy_SOURCES = busy.c 25 nodist_catchsig_SOURCES = catchsig.c 26 nodist_repeat_SOURCES = repeat.c 27 nodist_watchdog_SOURCES = watchdog.c 26 catchsig_SOURCES = catchsig.c 27 repeat_SOURCES = repeat.c 28 watchdog_SOURCES = watchdog.c -
tools/build/push2dist.sh
reef8dfb rbdfc032 2 2 3 3 hash="$1" 4 bwlim="$2"5 4 valid=$(distcc -j 2> /dev/null) 6 5 # if test "${valid}" != 0 … … 20 19 # echo "Copying to machines : ${hosts} (hash=${hash})" 21 20 22 files="../../../driver/cfa ../../../driver/cfa-cpp ../../../driver/cc1 ../../../driver/as defines.hfa$(find . -name '*.c*' | tr '\n' ' ')"21 files="../../../driver/cfa ../../../driver/cfa-cpp ../../../driver/cc1 ../../../driver/as $(find . -name '*.c*' | tr '\n' ' ')" 23 22 # echo "Files ${files}" 24 23 25 24 function push() { 26 25 ssh ${host} "mkdir -p ~/.cfadistcc/${hash}/" 27 rsync - -bwlimit=${bwlim} -a ${dV} ${files} ${host}:~/.cfadistcc/${hash}/.26 rsync -a ${dV} ${files} ${host}:~/.cfadistcc/${hash}/. 28 27 } 29 28 -
tools/cfa.nanorc
reef8dfb rbdfc032 14 14 15 15 # Declarations 16 color brightgreen "\<(struct|union|typedef|trait|coroutine| generator)\>"17 color brightgreen "\<( monitor|thread|with)\>"16 color brightgreen "\<(struct|union|typedef|trait|coroutine|monitor|thread)\>" 17 color brightgreen "\<(with)\>" 18 18 19 19 # Control Flow Structures 20 20 color brightyellow "\<(if|else|while|do|for|switch|choose|case|default)\>" 21 color brightyellow "\<(disable|enable|waitfor|when|timeout |suspend)\>"21 color brightyellow "\<(disable|enable|waitfor|when|timeout)\>" 22 22 color brightyellow "\<(try|catch(Resume)?|finally)\>" 23 23 … … 26 26 27 27 # Escaped Keywords, now Identifiers. 28 color white "` `\w+"28 color white "`\w+`" 29 29 30 30 # Operator Names … … 37 37 ## Update/Redistribute 38 38 # GCC builtins 39 color cyan "__attribute__[[:space:]]*\(\( ([^)]|[^)]\))*\)\)"39 color cyan "__attribute__[[:space:]]*\(\([^()]*(\([^()]*\)[^()]*)*\)\)" 40 40 ##color cyan "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__" 41 41 -
tools/prettyprinter/Makefile.am
reef8dfb rbdfc032 30 30 tools_prettyprinter_PROGRAMS = pretty 31 31 tools_prettyprinterdir = ../ 32 nodist_pretty_SOURCES = ${SRC}32 pretty_SOURCES = ${SRC} 33 33 pretty_LDADD = ${LEXLIB} -ldl # yywrap 34 34 pretty_CXXFLAGS = -Wno-deprecated -Wall -DYY_NO_INPUT -O2 -g -std=c++14 -
tools/stat.py
reef8dfb rbdfc032 1 #!/usr/bin/python 31 #!/usr/bin/python 2 2 3 3 import sys … … 17 17 avg = numpy.mean (content) 18 18 std = numpy.std (content) 19 print ("median {0:.1f} avg {1:.1f} stddev {2:.1f}".format( med, avg, std ))19 print "median {0:.1f} avg {1:.1f} stddev {2:.1f}".format( med, avg, std ) 20 20 21 21 -
tools/vscode/uwaterloo.cforall-0.1.0/package.json
reef8dfb rbdfc032 2 2 "name": "cforall", 3 3 "version": "0.1.0", 4 "displayName": "C ā (C-for-all)Language Support",4 "displayName": "Cforall Language Support", 5 5 "description": "Cforall - colorizer, grammar and snippets.", 6 6 "publisher": "uwaterloo", … … 9 9 "vscode": "^1.5.0" 10 10 }, 11 "icon": "images/icon. png",11 "icon": "images/icon.svg", 12 12 "categories": [ 13 " ProgrammingLanguages",13 "Languages", 14 14 "Linters", 15 15 "Other" 16 16 ], 17 "activationEvents": [18 "onLanguage:cforall"19 ],20 "main": "./client/main.js",21 17 "contributes": { 22 18 "languages": [ … … 25 21 "aliases": [ 26 22 "Cā", 23 "Cforall", 27 24 "CForAll", 28 "Cforall",29 25 "cforall" 30 26 ], 31 27 "extensions": [ 32 ".cfa", 33 ".hfa", 34 ".ifa" 28 ".cf" 35 29 ], 36 30 "configuration": "./cforall.configuration.json" … … 40 34 { 41 35 "language": "cforall", 42 "scopeName": "source.cf a",43 "path": "./syntaxes/cfa.tmLanguage .json"36 "scopeName": "source.cf", 37 "path": "./syntaxes/cfa.tmLanguage" 44 38 } 45 ], 46 "configuration": { 47 "type": "object", 48 "title": "Example configuration", 49 "properties": { 50 "cforall.maxNumberOfProblems": { 51 "scope": "resource", 52 "type": "number", 53 "default": 100, 54 "description": "Controls the maximum number of problems produced by the server." 55 }, 56 "cforall.trace.server": { 57 "scope": "window", 58 "type": "string", 59 "enum": [ 60 "off", 61 "messages", 62 "verbose" 63 ], 64 "default": "off", 65 "description": "Traces the communication between VS Code and the language server." 66 } 67 } 68 } 69 }, 70 "dependencies": { 71 "vscode-languageclient": "^4.1.4" 72 }, 73 "devDependencies": { 74 "vscode-languageclient": "^4.1.4" 39 ] 75 40 } 76 41 }
Note:
See TracChangeset
for help on using the changeset viewer.