Changes in / [3c64c668:58fe85a]
- Files:
-
- 535 added
- 182 deleted
- 359 edited
-
.gitignore (modified) (4 diffs)
-
Jenkins/Distribute (added)
-
Jenkins/FullBuild (modified) (6 diffs)
-
Jenkins/tools.groovy (added)
-
Jenkinsfile (modified) (27 diffs)
-
Makefile.am (modified) (4 diffs)
-
Makefile.in (deleted)
-
aclocal.m4 (deleted)
-
autogen.sh (added)
-
automake/cfa.m4 (deleted)
-
automake/compile (deleted)
-
automake/config.guess (deleted)
-
automake/config.sub (deleted)
-
automake/depcomp (deleted)
-
automake/install-sh (deleted)
-
automake/libtool.m4 (deleted)
-
automake/ltmain.sh (deleted)
-
automake/ltoptions.m4 (deleted)
-
automake/ltsugar.m4 (deleted)
-
automake/ltversion.m4 (deleted)
-
automake/lt~obsolete.m4 (deleted)
-
automake/missing (deleted)
-
automake/test-driver (deleted)
-
automake/ylwrap (deleted)
-
benchmark/Cargo.toml.in (added)
-
benchmark/Makefile.am (modified) (18 diffs)
-
benchmark/Makefile.in (deleted)
-
benchmark/bench.rs (added)
-
benchmark/benchcltr.hfa (added)
-
benchmark/creation/JavaThread.java (modified) (3 diffs)
-
benchmark/creation/cfa_gen.cfa (modified) (2 diffs)
-
benchmark/creation/rust_tokio_thrd.rs (added)
-
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/rust_await.rs (added)
-
benchmark/ctxswitch/rust_tokio_thrd.rs (added)
-
benchmark/exclude (modified) (1 diff)
-
benchmark/io/batch-readv.c (added)
-
benchmark/io/http/Makefile.am (added)
-
benchmark/io/http/channel.cfa (added)
-
benchmark/io/http/channel.hfa (added)
-
benchmark/io/http/filecache.cfa (added)
-
benchmark/io/http/filecache.hfa (added)
-
benchmark/io/http/main.cfa (added)
-
benchmark/io/http/options.cfa (added)
-
benchmark/io/http/options.hfa (added)
-
benchmark/io/http/protocol.cfa (added)
-
benchmark/io/http/protocol.hfa (added)
-
benchmark/io/http/worker.cfa (added)
-
benchmark/io/http/worker.hfa (added)
-
benchmark/io/io_uring.h (added)
-
benchmark/io/readv-posix.c (added)
-
benchmark/io/readv.cfa (added)
-
benchmark/mutex/JavaThread.java (modified) (3 diffs)
-
benchmark/mutexC/JavaThread.java (modified) (4 diffs)
-
benchmark/plot.py (added)
-
benchmark/readyQ/bench.go (added)
-
benchmark/readyQ/cycle.cc (added)
-
benchmark/readyQ/cycle.cfa (added)
-
benchmark/readyQ/cycle.cpp (added)
-
benchmark/readyQ/cycle.go (added)
-
benchmark/readyQ/cycle.rs (added)
-
benchmark/readyQ/locality.cfa (added)
-
benchmark/readyQ/locality.cpp (added)
-
benchmark/readyQ/locality.go (added)
-
benchmark/readyQ/locality.rs (added)
-
benchmark/readyQ/rq_bench.hfa (added)
-
benchmark/readyQ/rq_bench.hpp (added)
-
benchmark/readyQ/yield.cfa (added)
-
benchmark/readyQ/yield.cpp (added)
-
benchmark/rmit.py (added)
-
benchmark/schedint/JavaThread.java (modified) (2 diffs)
-
benchmark/vector/glm_vec2.cc (added)
-
benchmark/vector/vec2_notemplates.cfa (added)
-
benchmark/vector/vec2_templates.cfa (added)
-
configure (deleted)
-
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 (added)
-
doc/papers/concurrency/examples/Fib.js (added)
-
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 (added)
-
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 (deleted)
-
doc/papers/concurrency/examples/RWMonitorEXT.cfa (added)
-
doc/papers/concurrency/examples/RWMonitorINT.cfa (added)
-
doc/papers/concurrency/examples/Refactor.py (modified) (1 diff)
-
doc/papers/concurrency/examples/channels.go (added)
-
doc/papers/concurrency/examples/channels.rs (added)
-
doc/papers/concurrency/examples/future.rs (added)
-
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 (added)
-
doc/papers/concurrency/response2 (added)
-
doc/papers/concurrency/response3 (added)
-
doc/proposals/ZeroCostPreemption.md (added)
-
doc/proposals/function_type_change.md (added)
-
doc/proposals/vtable.md (modified) (3 diffs)
-
doc/refrat/refrat.tex (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/.gitignore (added)
-
doc/theses/andrew_beach_MMath/Makefile (added)
-
doc/theses/andrew_beach_MMath/cfalab.sty (added)
-
doc/theses/andrew_beach_MMath/existing.tex (added)
-
doc/theses/andrew_beach_MMath/features.tex (added)
-
doc/theses/andrew_beach_MMath/future.tex (added)
-
doc/theses/andrew_beach_MMath/glossaries.tex (added)
-
doc/theses/andrew_beach_MMath/thesis-frontpgs.tex (added)
-
doc/theses/andrew_beach_MMath/thesis.bib (added)
-
doc/theses/andrew_beach_MMath/thesis.tex (added)
-
doc/theses/andrew_beach_MMath/unwinding.tex (added)
-
doc/theses/andrew_beach_MMath/uw-ethesis.cls (added)
-
doc/theses/fangren_yu_COOP_S20/Makefile (added)
-
doc/theses/fangren_yu_COOP_S20/Report.tex (added)
-
doc/theses/fangren_yu_COOP_S20/cfa_developer_reference.pdf (added)
-
doc/theses/fangren_yu_COOP_S20/figures/DeepNodeSharing.fig (added)
-
doc/theses/fangren_yu_COOP_S20/figures/DeepNodeSharing.fig.bak (added)
-
doc/theses/thierry_delisle_PhD/.gitignore (modified) (1 diff)
-
doc/theses/thierry_delisle_PhD/code/Makefile (deleted)
-
doc/theses/thierry_delisle_PhD/code/assert.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/bts_test.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/prefetch.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/processor.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/processor_list.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/processor_list_fast.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/processor_list_good.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/randbit.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/Makefile (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/proto-gui/main.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/Makefile (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/cforall.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/fibre.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/pthread.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/thread.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readQ_example/thrdlib/thread.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/Makefile (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/assert.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/bitbench/select.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/bts.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/bts_test.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/links.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/prefetch.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/process.sh (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list_fast.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list_good.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/randbit.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list_layout.cpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/runperf.sh (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/scale.sh (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzi-packed.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzi.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzm.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/utils.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/readyQ_proto/work_stealing.hpp (added)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/relaxed_list_layout.cpp (deleted)
-
doc/theses/thierry_delisle_PhD/code/scale.sh (deleted)
-
doc/theses/thierry_delisle_PhD/code/utils.hpp (deleted)
-
doc/theses/thierry_delisle_PhD/comp_II/Makefile (modified) (5 diffs)
-
doc/theses/thierry_delisle_PhD/comp_II/comp_II.tex (modified) (5 diffs)
-
doc/theses/thierry_delisle_PhD/comp_II/comp_II_PAB.tex (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/base.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/empty.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/emptybit.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/emptytls.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/emptytree.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/resize.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/img/system.fig (added)
-
doc/theses/thierry_delisle_PhD/comp_II/local.bib (modified) (2 diffs)
-
doc/theses/thierry_delisle_PhD/comp_II/presentation.tex (added)
-
doc/theses/thierry_delisle_PhD/comp_II/presentationstyle.sty (added)
-
doc/theses/thierry_delisle_PhD/thesis/Makefile (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/base.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/empty.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/emptybit.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/emptytls.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/emptytree.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/fairness.py (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/resize.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/fig/system.fig (added)
-
doc/theses/thierry_delisle_PhD/thesis/glossary.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/local.bib (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/core.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/existing.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/front.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/intro.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/io.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/practice.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/text/runtime.tex (added)
-
doc/theses/thierry_delisle_PhD/thesis/thesis.tex (added)
-
doc/user/Makefile (modified) (1 diff)
-
doc/user/user.tex (modified) (24 diffs)
-
doc/working/unified_semaphores/semaphore.cfa (added)
-
doc/working/unified_semaphores/semaphore.hfa (added)
-
driver/Makefile.am (modified) (1 diff)
-
driver/Makefile.in (deleted)
-
driver/cc1.cc (modified) (22 diffs)
-
driver/cfa.cc (modified) (22 diffs)
-
example/io/cat.c (added)
-
example/io/filereader.c (added)
-
example/io/simple/client.c (added)
-
example/io/simple/server.c (added)
-
example/io/simple/server.cfa (added)
-
example/io/simple/server_epoll.c (added)
-
examples/ArrayN.c (deleted)
-
examples/Initialization.c (deleted)
-
examples/Initialization2.c (deleted)
-
examples/Makefile.example (deleted)
-
examples/Members.c (deleted)
-
examples/Misc.c (deleted)
-
examples/MiscError.c (deleted)
-
examples/Rank2.c (deleted)
-
examples/Tuple.c (deleted)
-
examples/abstype.c (deleted)
-
examples/constructors.c (deleted)
-
examples/forward.c (deleted)
-
examples/gc_no_raii/.gitignore (deleted)
-
examples/gc_no_raii/bug-repro/assert.c (deleted)
-
examples/gc_no_raii/bug-repro/blockers.tar.gz (deleted)
-
examples/gc_no_raii/bug-repro/blockers/explicit_cast.c (deleted)
-
examples/gc_no_raii/bug-repro/blockers/file_scope.c (deleted)
-
examples/gc_no_raii/bug-repro/blockers/recursive_realloc.c (deleted)
-
examples/gc_no_raii/bug-repro/crash.c (deleted)
-
examples/gc_no_raii/bug-repro/deref.c (deleted)
-
examples/gc_no_raii/bug-repro/field.c (deleted)
-
examples/gc_no_raii/bug-repro/find.c (deleted)
-
examples/gc_no_raii/bug-repro/inline.c (deleted)
-
examples/gc_no_raii/bug-repro/malloc.c (deleted)
-
examples/gc_no_raii/bug-repro/not_equal.c (deleted)
-
examples/gc_no_raii/bug-repro/oddtype.c (deleted)
-
examples/gc_no_raii/bug-repro/push_back.c (deleted)
-
examples/gc_no_raii/bug-repro/push_back.h (deleted)
-
examples/gc_no_raii/bug-repro/realloc.c (deleted)
-
examples/gc_no_raii/bug-repro/return.c (deleted)
-
examples/gc_no_raii/bug-repro/return_template.c (deleted)
-
examples/gc_no_raii/bug-repro/slow_malloc.c (deleted)
-
examples/gc_no_raii/bug-repro/static_const_local.c (deleted)
-
examples/gc_no_raii/bug-repro/test-assert.cpp (deleted)
-
examples/gc_no_raii/bug-repro/void_pointer.c (deleted)
-
examples/gc_no_raii/bug-repro/while.c (deleted)
-
examples/gc_no_raii/bug-repro/zero.c (deleted)
-
examples/gc_no_raii/pool-alloc/allocate-malign.c (deleted)
-
examples/gc_no_raii/pool-alloc/allocate-malloc.c (deleted)
-
examples/gc_no_raii/pool-alloc/allocate-mmap.c (deleted)
-
examples/gc_no_raii/pool-alloc/allocate-win-valloc.c (deleted)
-
examples/gc_no_raii/premake4.lua (deleted)
-
examples/gc_no_raii/src/allocate-pool.c (deleted)
-
examples/gc_no_raii/src/allocate-pool.h (deleted)
-
examples/gc_no_raii/src/gc.h (deleted)
-
examples/gc_no_raii/src/gcpointers.c (deleted)
-
examples/gc_no_raii/src/gcpointers.h (deleted)
-
examples/gc_no_raii/src/internal/card_table.h (deleted)
-
examples/gc_no_raii/src/internal/collector.c (deleted)
-
examples/gc_no_raii/src/internal/collector.h (deleted)
-
examples/gc_no_raii/src/internal/gc_tools.h (deleted)
-
examples/gc_no_raii/src/internal/globals.h (deleted)
-
examples/gc_no_raii/src/internal/memory_pool.c (deleted)
-
examples/gc_no_raii/src/internal/memory_pool.h (deleted)
-
examples/gc_no_raii/src/internal/object_header.c (deleted)
-
examples/gc_no_raii/src/internal/object_header.h (deleted)
-
examples/gc_no_raii/src/internal/state.c (deleted)
-
examples/gc_no_raii/src/internal/state.h (deleted)
-
examples/gc_no_raii/src/test_include.c (deleted)
-
examples/gc_no_raii/src/tools.h (deleted)
-
examples/gc_no_raii/src/tools/checks.h (deleted)
-
examples/gc_no_raii/src/tools/print.c (deleted)
-
examples/gc_no_raii/src/tools/print.h (deleted)
-
examples/gc_no_raii/src/tools/worklist.h (deleted)
-
examples/gc_no_raii/test/badlll.c (deleted)
-
examples/gc_no_raii/test/gctest.c (deleted)
-
examples/gc_no_raii/test/operators.c (deleted)
-
examples/huge.c (deleted)
-
examples/includes.c (deleted)
-
examples/index.h (deleted)
-
examples/it_out.c (deleted)
-
examples/multicore.c (deleted)
-
examples/new.c (deleted)
-
examples/poly-bench.c (deleted)
-
examples/prolog.c (deleted)
-
examples/quad.c (deleted)
-
examples/s.c (deleted)
-
examples/simplePoly.c (deleted)
-
examples/simpler.c (deleted)
-
examples/specialize.c (deleted)
-
examples/square.c (deleted)
-
examples/twice.c (deleted)
-
examples/wrapper/.gitignore (deleted)
-
examples/wrapper/premake4.lua (deleted)
-
examples/wrapper/src/main.c (deleted)
-
examples/wrapper/src/pointer.h (deleted)
-
examples/zero_one.c (deleted)
-
libcfa/Makefile.dist.am (added)
-
libcfa/Makefile.in (deleted)
-
libcfa/aclocal.m4 (deleted)
-
libcfa/automake/compile (deleted)
-
libcfa/automake/config.guess (deleted)
-
libcfa/automake/config.sub (deleted)
-
libcfa/automake/depcomp (deleted)
-
libcfa/automake/install-sh (deleted)
-
libcfa/automake/libtool.m4 (deleted)
-
libcfa/automake/ltmain.sh (deleted)
-
libcfa/automake/ltoptions.m4 (deleted)
-
libcfa/automake/ltsugar.m4 (deleted)
-
libcfa/automake/ltversion.m4 (deleted)
-
libcfa/automake/lt~obsolete.m4 (deleted)
-
libcfa/automake/missing (deleted)
-
libcfa/configure (deleted)
-
libcfa/configure.ac (modified) (7 diffs)
-
libcfa/prelude/Makefile.am (modified) (2 diffs)
-
libcfa/prelude/Makefile.in (deleted)
-
libcfa/prelude/bootloader.cf (modified) (1 diff)
-
libcfa/prelude/builtins.c (modified) (3 diffs)
-
libcfa/prelude/defines.hfa.in (added)
-
libcfa/src/Makefile.am (modified) (6 diffs)
-
libcfa/src/Makefile.in (deleted)
-
libcfa/src/bitmanip.hfa (added)
-
libcfa/src/bits/collection.hfa (added)
-
libcfa/src/bits/containers.hfa (modified) (8 diffs)
-
libcfa/src/bits/debug.cfa (modified) (2 diffs)
-
libcfa/src/bits/debug.hfa (modified) (6 diffs)
-
libcfa/src/bits/defs.hfa (modified) (3 diffs)
-
libcfa/src/bits/locks.hfa (modified) (6 diffs)
-
libcfa/src/bits/queue.hfa (added)
-
libcfa/src/bits/random.hfa (added)
-
libcfa/src/bits/sequence.hfa (added)
-
libcfa/src/bits/signal.hfa (modified) (3 diffs)
-
libcfa/src/bits/stack.hfa (added)
-
libcfa/src/common.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/CtxSwitch-arm.S (deleted)
-
libcfa/src/concurrency/CtxSwitch-arm32.S (added)
-
libcfa/src/concurrency/CtxSwitch-arm64.S (added)
-
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 (added)
-
libcfa/src/concurrency/clib/cfathread.h (added)
-
libcfa/src/concurrency/coroutine.cfa (modified) (9 diffs)
-
libcfa/src/concurrency/coroutine.hfa (modified) (8 diffs)
-
libcfa/src/concurrency/exception.cfa (added)
-
libcfa/src/concurrency/exception.hfa (added)
-
libcfa/src/concurrency/future.hfa (added)
-
libcfa/src/concurrency/invoke.c (modified) (6 diffs)
-
libcfa/src/concurrency/invoke.h (modified) (11 diffs)
-
libcfa/src/concurrency/io.cfa (added)
-
libcfa/src/concurrency/io/call.cfa.in (added)
-
libcfa/src/concurrency/io/setup.cfa (added)
-
libcfa/src/concurrency/io/types.hfa (added)
-
libcfa/src/concurrency/iofwd.hfa (added)
-
libcfa/src/concurrency/kernel.cfa (modified) (23 diffs)
-
libcfa/src/concurrency/kernel.hfa (modified) (10 diffs)
-
libcfa/src/concurrency/kernel/fwd.hfa (added)
-
libcfa/src/concurrency/kernel/startup.cfa (added)
-
libcfa/src/concurrency/kernel_private.hfa (modified) (5 diffs)
-
libcfa/src/concurrency/locks.cfa (added)
-
libcfa/src/concurrency/locks.hfa (added)
-
libcfa/src/concurrency/monitor.cfa (modified) (28 diffs)
-
libcfa/src/concurrency/monitor.hfa (modified) (2 diffs)
-
libcfa/src/concurrency/mutex.cfa (modified) (6 diffs)
-
libcfa/src/concurrency/preemption.cfa (modified) (25 diffs)
-
libcfa/src/concurrency/preemption.hfa (modified) (1 diff)
-
libcfa/src/concurrency/ready_queue.cfa (added)
-
libcfa/src/concurrency/ready_subqueue.hfa (added)
-
libcfa/src/concurrency/snzi.hfa (added)
-
libcfa/src/concurrency/stats.cfa (added)
-
libcfa/src/concurrency/stats.hfa (added)
-
libcfa/src/concurrency/thread.cfa (modified) (6 diffs)
-
libcfa/src/concurrency/thread.hfa (modified) (4 diffs)
-
libcfa/src/containers/list.hfa (added)
-
libcfa/src/containers/stackLockFree.hfa (added)
-
libcfa/src/containers/vector.hfa (modified) (1 diff)
-
libcfa/src/exception.c (modified) (18 diffs)
-
libcfa/src/exception.h (modified) (2 diffs)
-
libcfa/src/exception.hfa (added)
-
libcfa/src/executor.cfa (modified) (1 diff)
-
libcfa/src/fstream.cfa (modified) (8 diffs)
-
libcfa/src/fstream.hfa (modified) (4 diffs)
-
libcfa/src/heap.cfa (modified) (59 diffs)
-
libcfa/src/heap.hfa (added)
-
libcfa/src/interpose.cfa (modified) (5 diffs)
-
libcfa/src/iostream.cfa (modified) (17 diffs)
-
libcfa/src/iostream.hfa (modified) (15 diffs)
-
libcfa/src/math.hfa (modified) (2 diffs)
-
libcfa/src/memory.cfa (added)
-
libcfa/src/memory.hfa (added)
-
libcfa/src/parseargs.cfa (added)
-
libcfa/src/parseargs.hfa (added)
-
libcfa/src/startup.cfa (modified) (3 diffs)
-
libcfa/src/stdhdr/assert.h (modified) (1 diff)
-
libcfa/src/stdhdr/malloc.h (modified) (1 diff)
-
libcfa/src/stdhdr/sys/mman.h (added)
-
libcfa/src/stdhdr/sys/time.h (added)
-
libcfa/src/stdhdr/sys/ucontext.h (deleted)
-
libcfa/src/stdhdr/unwind.h (added)
-
libcfa/src/stdlib.cfa (modified) (6 diffs)
-
libcfa/src/stdlib.hfa (modified) (9 diffs)
-
libcfa/src/time.hfa (modified) (3 diffs)
-
libcfa/src/vec/vec.hfa (added)
-
libcfa/src/vec/vec2.hfa (added)
-
libcfa/src/vec/vec3.hfa (added)
-
libcfa/src/vec/vec4.hfa (added)
-
longrun_tests/Makefile.am (modified) (2 diffs)
-
longrun_tests/Makefile.in (deleted)
-
longrun_tests/locks.cfa (added)
-
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 (added)
-
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 (added)
-
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 (added)
-
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) (1 diff)
-
src/CodeGen/FixMain.cc (modified) (2 diffs)
-
src/CodeGen/FixNames.cc (modified) (2 diffs)
-
src/CodeGen/module.mk (modified) (1 diff)
-
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 (added)
-
src/Common/CodeLocationTools.hpp (added)
-
src/Common/Eval.cc (modified) (1 diff)
-
src/Common/Examine.cc (added)
-
src/Common/Examine.h (added)
-
src/Common/PassVisitor.h (modified) (4 diffs)
-
src/Common/PassVisitor.impl.h (modified) (13 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/Stats/Heap.cc (modified) (6 diffs)
-
src/Common/Stats/Heap.h (modified) (1 diff)
-
src/Common/Stats/ResolveTime.cc (added)
-
src/Common/Stats/ResolveTime.h (added)
-
src/Common/Stats/Stats.cc (modified) (2 diffs)
-
src/Common/module.mk (modified) (1 diff)
-
src/Common/utility.h (modified) (1 diff)
-
src/CompilationState.cc (modified) (2 diffs)
-
src/CompilationState.h (modified) (1 diff)
-
src/Concurrency/Keywords.cc (modified) (27 diffs)
-
src/Concurrency/Waitfor.cc (modified) (3 diffs)
-
src/Concurrency/module.mk (modified) (1 diff)
-
src/ControlStruct/ExceptTranslate.cc (modified) (22 diffs)
-
src/ControlStruct/ExceptTranslate.h (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) (2 diffs)
-
src/InitTweak/FixInit.h (modified) (1 diff)
-
src/InitTweak/FixInitNew.cpp (added)
-
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/Makefile.am (modified) (2 diffs)
-
src/Makefile.in (deleted)
-
src/Parser/DeclarationNode.cc (modified) (6 diffs)
-
src/Parser/ExpressionNode.cc (modified) (8 diffs)
-
src/Parser/ParseNode.h (modified) (8 diffs)
-
src/Parser/StatementNode.cc (modified) (3 diffs)
-
src/Parser/TypeData.cc (modified) (2 diffs)
-
src/Parser/lex.ll (modified) (4 diffs)
-
src/Parser/module.mk (modified) (2 diffs)
-
src/Parser/parser.yy (modified) (29 diffs)
-
src/ResolvExpr/AdjustExprType.cc (modified) (1 diff)
-
src/ResolvExpr/AlternativeFinder.cc (modified) (4 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) (2 diffs)
-
src/ResolvExpr/ResolveTypeof.cc (modified) (8 diffs)
-
src/ResolvExpr/ResolveTypeof.h (modified) (2 diffs)
-
src/ResolvExpr/Resolver.cc (modified) (28 diffs)
-
src/ResolvExpr/Resolver.h (modified) (3 diffs)
-
src/ResolvExpr/SatisfyAssertions.cpp (modified) (28 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/FixFunction.cc (modified) (4 diffs)
-
src/SymTab/Mangler.cc (modified) (42 diffs)
-
src/SymTab/Validate.cc (modified) (14 diffs)
-
src/SymTab/module.mk (modified) (1 diff)
-
src/SynTree/AggregateDecl.cc (modified) (2 diffs)
-
src/SynTree/ApplicationExpr.cc (modified) (1 diff)
-
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 (added)
-
src/Virtual/Tables.h (added)
-
src/Virtual/module.mk (modified) (1 diff)
-
src/cfa.make (deleted)
-
src/config.h.in (modified) (1 diff)
-
src/main.cc (modified) (10 diffs)
-
tests/.expect/KRfunctions.nast.arm64.txt (added)
-
tests/.expect/KRfunctions.nast.x64.txt (added)
-
tests/.expect/KRfunctions.nast.x86.txt (added)
-
tests/.expect/KRfunctions.oast.arm64.txt (added)
-
tests/.expect/KRfunctions.oast.x64.txt (added)
-
tests/.expect/KRfunctions.oast.x86.txt (added)
-
tests/.expect/KRfunctions.x64.txt (deleted)
-
tests/.expect/KRfunctions.x86.txt (deleted)
-
tests/.expect/abs.arm64.txt (added)
-
tests/.expect/abs.txt (deleted)
-
tests/.expect/abs.x64.txt (added)
-
tests/.expect/abs.x86.txt (added)
-
tests/.expect/alloc-ERROR.nast.txt (added)
-
tests/.expect/alloc-ERROR.oast.txt (added)
-
tests/.expect/alloc-ERROR.txt (deleted)
-
tests/.expect/alloc.txt (modified) (1 diff)
-
tests/.expect/alloc.txt.old (added)
-
tests/.expect/alloc2.txt (added)
-
tests/.expect/array.txt (modified) (1 diff)
-
tests/.expect/ato.arm64.txt (added)
-
tests/.expect/ato.txt (deleted)
-
tests/.expect/ato.x64.txt (added)
-
tests/.expect/ato.x86.txt (added)
-
tests/.expect/attributes.nast.arm64.txt (added)
-
tests/.expect/attributes.nast.x64.txt (added)
-
tests/.expect/attributes.nast.x86.txt (added)
-
tests/.expect/attributes.oast.arm64.txt (added)
-
tests/.expect/attributes.oast.x64.txt (added)
-
tests/.expect/attributes.oast.x86.txt (added)
-
tests/.expect/attributes.x64.txt (deleted)
-
tests/.expect/attributes.x86.txt (deleted)
-
tests/.expect/bitmanip1.arm64.txt (added)
-
tests/.expect/bitmanip1.x64.txt (added)
-
tests/.expect/bitmanip1.x86.txt (added)
-
tests/.expect/bitmanip2.arm64.txt (added)
-
tests/.expect/bitmanip2.x64.txt (added)
-
tests/.expect/bitmanip2.x86.txt (added)
-
tests/.expect/bitmanip3.arm64.txt (added)
-
tests/.expect/bitmanip3.x64.txt (added)
-
tests/.expect/bitmanip3.x86.txt (added)
-
tests/.expect/cast.txt (modified) (1 diff)
-
tests/.expect/castError.nast.txt (added)
-
tests/.expect/castError.oast.txt (added)
-
tests/.expect/castError.txt (deleted)
-
tests/.expect/const-init.txt (added)
-
tests/.expect/copyfile.txt (modified) (1 diff)
-
tests/.expect/declarationSpecifier.arm64.txt (added)
-
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 (added)
-
tests/.expect/forall.txt (modified) (1 diff)
-
tests/.expect/functions.nast.arm64.txt (added)
-
tests/.expect/functions.nast.x64.txt (added)
-
tests/.expect/functions.nast.x86.txt (added)
-
tests/.expect/functions.oast.arm64.txt (added)
-
tests/.expect/functions.oast.x64.txt (added)
-
tests/.expect/functions.oast.x86.txt (added)
-
tests/.expect/functions.x64.txt (deleted)
-
tests/.expect/functions.x86.txt (deleted)
-
tests/.expect/gccExtensions.arm64.txt (added)
-
tests/.expect/gccExtensions.x64.txt (modified) (1 diff)
-
tests/.expect/gccExtensions.x86.txt (modified) (1 diff)
-
tests/.expect/global-monomorph.txt (added)
-
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 (added)
-
tests/.expect/init1-ERROR.oast.txt (added)
-
tests/.expect/init1.txt (added)
-
tests/.expect/labelledExit.txt (modified) (1 diff)
-
tests/.expect/limits.txt (modified) (1 diff)
-
tests/.expect/malloc.txt (added)
-
tests/.expect/manipulatorsInput.arm64.txt (added)
-
tests/.expect/manipulatorsInput.txt (deleted)
-
tests/.expect/manipulatorsInput.x64.txt (added)
-
tests/.expect/manipulatorsInput.x86.txt (added)
-
tests/.expect/manipulatorsOutput1.arm64.txt (added)
-
tests/.expect/manipulatorsOutput1.txt (deleted)
-
tests/.expect/manipulatorsOutput1.x64.txt (added)
-
tests/.expect/manipulatorsOutput1.x86.txt (added)
-
tests/.expect/manipulatorsOutput2.arm64.txt (added)
-
tests/.expect/manipulatorsOutput3.arm64.txt (added)
-
tests/.expect/manipulatorsOutput3.x64.txt (added)
-
tests/.expect/math1.arm64.txt (added)
-
tests/.expect/math1.txt (deleted)
-
tests/.expect/math1.x64.txt (added)
-
tests/.expect/math1.x86.txt (added)
-
tests/.expect/math2.arm64.txt (added)
-
tests/.expect/math2.txt (deleted)
-
tests/.expect/math2.x64.txt (added)
-
tests/.expect/math2.x86.txt (added)
-
tests/.expect/math3.arm64.txt (added)
-
tests/.expect/math3.txt (deleted)
-
tests/.expect/math3.x64.txt (added)
-
tests/.expect/math3.x86.txt (added)
-
tests/.expect/math4.arm64.txt (added)
-
tests/.expect/math4.txt (deleted)
-
tests/.expect/math4.x64.txt (added)
-
tests/.expect/math4.x86.txt (added)
-
tests/.expect/maybe.txt (modified) (1 diff)
-
tests/.expect/minmax.txt (modified) (2 diffs)
-
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 (added)
-
tests/.expect/poly-o-cycle.txt (added)
-
tests/.expect/random.arm64.txt (added)
-
tests/.expect/random.txt (deleted)
-
tests/.expect/random.x64.txt (added)
-
tests/.expect/random.x86.txt (added)
-
tests/.expect/result.txt (modified) (1 diff)
-
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/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 (deleted)
-
tests/alloc.cfa (modified) (12 diffs)
-
tests/alloc2.cfa (added)
-
tests/array.cfa (modified) (2 diffs)
-
tests/avltree/avl1.cfa (modified) (1 diff)
-
tests/bitmanip1.cfa (added)
-
tests/bitmanip2.cfa (added)
-
tests/bitmanip3.cfa (added)
-
tests/bugs/10.cfa (added)
-
tests/bugs/104.cfa (added)
-
tests/bugs/140.cfa (added)
-
tests/bugs/194.cfa (added)
-
tests/bugs/196.cfa (added)
-
tests/bugs/20.cfa (added)
-
tests/bugs/203-2.cfa (added)
-
tests/bugs/203-7.cfa (added)
-
tests/bugs/203-9.cfa (added)
-
tests/bugs/44.cfa (added)
-
tests/bugs/46.cfa (added)
-
tests/bugs/5.cfa (added)
-
tests/bugs/66.cfa (added)
-
tests/bugs/7.cfa (added)
-
tests/bugs/91.cfa (added)
-
tests/bugs/92.cfa (added)
-
tests/bugs/95.cfa (added)
-
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 (added)
-
tests/collections/.expect/queue.txt (added)
-
tests/collections/.expect/sequence.txt (added)
-
tests/collections/.expect/stack.txt (added)
-
tests/collections/multi_list.cfa (added)
-
tests/collections/queue.cfa (added)
-
tests/collections/sequence.cfa (added)
-
tests/collections/stack.cfa (added)
-
tests/complex.cfa (modified) (1 diff)
-
tests/concurrent/.expect/clib.txt (added)
-
tests/concurrent/.expect/cluster.txt (added)
-
tests/concurrent/.expect/join.txt (added)
-
tests/concurrent/.expect/joinerror.sed (added)
-
tests/concurrent/.expect/monitor.txt (modified) (1 diff)
-
tests/concurrent/.expect/suspend_then.txt (added)
-
tests/concurrent/clib.c (added)
-
tests/concurrent/cluster.cfa (added)
-
tests/concurrent/coroutineThen.cfa (deleted)
-
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) (4 diffs)
-
tests/concurrent/futures/.expect/abandon.txt (added)
-
tests/concurrent/futures/.expect/basic.txt (added)
-
tests/concurrent/futures/.expect/multi.txt (added)
-
tests/concurrent/futures/.expect/typed.txt (added)
-
tests/concurrent/futures/abandon.cfa (added)
-
tests/concurrent/futures/basic.cfa (added)
-
tests/concurrent/futures/multi.cfa (added)
-
tests/concurrent/futures/typed.cfa (added)
-
tests/concurrent/join.cfa (added)
-
tests/concurrent/joinerror.cfa (added)
-
tests/concurrent/monitor.cfa (modified) (1 diff)
-
tests/concurrent/park/.expect/force_preempt.txt (modified) (1 diff)
-
tests/concurrent/park/.expect/start_parked.txt (added)
-
tests/concurrent/park/contention.cfa (modified) (1 diff)
-
tests/concurrent/park/force_preempt.cfa (modified) (4 diffs)
-
tests/concurrent/park/start_parked.cfa (added)
-
tests/concurrent/signal/block.cfa (modified) (1 diff)
-
tests/concurrent/signal/disjoint.cfa (modified) (6 diffs)
-
tests/concurrent/suspend_then.cfa (added)
-
tests/concurrent/waitfor/when.cfa (modified) (1 diff)
-
tests/config.py.in (modified) (1 diff)
-
tests/const-init.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/enum.cfa (modified) (1 diff)
-
tests/errors/.expect/completeType.nast.arm64.txt (added)
-
tests/errors/.expect/completeType.nast.x64.txt (added)
-
tests/errors/.expect/completeType.nast.x86.txt (added)
-
tests/errors/.expect/completeType.oast.arm64.txt (added)
-
tests/errors/.expect/completeType.oast.x64.txt (added)
-
tests/errors/.expect/completeType.oast.x86.txt (added)
-
tests/errors/.expect/completeType.txt (deleted)
-
tests/errors/suspend.cfa (added)
-
tests/exceptions/.expect/conditional-threads.txt (added)
-
tests/exceptions/.expect/conditional.txt (added)
-
tests/exceptions/.expect/data-except.txt (added)
-
tests/exceptions/.expect/defaults-threads.txt (added)
-
tests/exceptions/.expect/defaults.txt (added)
-
tests/exceptions/.expect/except-0.txt (deleted)
-
tests/exceptions/.expect/except-1.txt (deleted)
-
tests/exceptions/.expect/except-2.txt (deleted)
-
tests/exceptions/.expect/except-3.txt (deleted)
-
tests/exceptions/.expect/finally-threads.txt (added)
-
tests/exceptions/.expect/finally.txt (added)
-
tests/exceptions/.expect/interact.txt (added)
-
tests/exceptions/.expect/polymorphic.txt (added)
-
tests/exceptions/.expect/resume-threads.txt (added)
-
tests/exceptions/.expect/resume.txt (added)
-
tests/exceptions/.expect/terminate-threads.txt (added)
-
tests/exceptions/.expect/terminate.txt (added)
-
tests/exceptions/.expect/trash.txt (added)
-
tests/exceptions/.expect/type-check.txt (added)
-
tests/exceptions/.expect/virtual-cast.txt (added)
-
tests/exceptions/.expect/virtual-poly.txt (added)
-
tests/exceptions/cancel/.expect/coroutine.txt (added)
-
tests/exceptions/cancel/.expect/thread.txt (added)
-
tests/exceptions/cancel/coroutine.cfa (added)
-
tests/exceptions/cancel/thread.cfa (added)
-
tests/exceptions/conditional.cfa (added)
-
tests/exceptions/data-except.cfa (added)
-
tests/exceptions/defaults.cfa (added)
-
tests/exceptions/except-0.cfa (deleted)
-
tests/exceptions/except-1.cfa (deleted)
-
tests/exceptions/except-2.cfa (deleted)
-
tests/exceptions/except-3.cfa (deleted)
-
tests/exceptions/except-io.hfa (added)
-
tests/exceptions/except-mac.hfa (deleted)
-
tests/exceptions/finally.cfa (added)
-
tests/exceptions/interact.cfa (added)
-
tests/exceptions/polymorphic.cfa (added)
-
tests/exceptions/resume.cfa (added)
-
tests/exceptions/terminate.cfa (added)
-
tests/exceptions/trash.cfa (added)
-
tests/exceptions/type-check.cfa (added)
-
tests/exceptions/virtual-cast.cfa (added)
-
tests/exceptions/virtual-poly.cfa (added)
-
tests/exceptions/with-threads.hfa (added)
-
tests/expression.cfa (modified) (1 diff)
-
tests/forall.cfa (modified) (3 diffs)
-
tests/generator/.expect/fibonacci.txt (added)
-
tests/generator/.expect/fmtLines.txt (added)
-
tests/generator/.expect/suspend_then.txt (added)
-
tests/generator/.in/fmtLines.txt (added)
-
tests/generator/fibonacci.cfa (added)
-
tests/generator/fmtLines.cfa (added)
-
tests/generator/suspend_then.cfa (added)
-
tests/global-monomorph.cfa (added)
-
tests/heap.cfa (modified) (30 diffs)
-
tests/identFuncDeclarator.cfa (modified) (2 diffs)
-
tests/identParamDeclarator.cfa (modified) (2 diffs)
-
tests/init1.cfa (added)
-
tests/io2.cfa (modified) (1 diff)
-
tests/labelledExit.cfa (modified) (2 diffs)
-
tests/limits.cfa (modified) (2 diffs)
-
tests/linking/.expect/exception-nothreads.txt (added)
-
tests/linking/.expect/exception-withthreads.txt (added)
-
tests/linking/exception-nothreads.cfa (added)
-
tests/linking/exception-withthreads.cfa (added)
-
tests/linking/withthreads.cfa (modified) (1 diff)
-
tests/list/.expect/dlist-insert-remove.txt (added)
-
tests/list/dlist-insert-remove.cfa (added)
-
tests/literals.cfa (modified) (5 diffs)
-
tests/malloc.cfa (added)
-
tests/manipulatorsInput.cfa (modified) (2 diffs)
-
tests/manipulatorsOutput1.cfa (modified) (6 diffs)
-
tests/manipulatorsOutput2.cfa (modified) (2 diffs)
-
tests/manipulatorsOutput3.cfa (added)
-
tests/math4.cfa (modified) (4 diffs)
-
tests/maybe.cfa (modified) (2 diffs)
-
tests/meta/.expect/archVast.nast.arm64.txt (added)
-
tests/meta/.expect/archVast.nast.x64.txt (added)
-
tests/meta/.expect/archVast.nast.x86.txt (added)
-
tests/meta/.expect/archVast.oast.arm64.txt (added)
-
tests/meta/.expect/archVast.oast.x64.txt (added)
-
tests/meta/.expect/archVast.oast.x86.txt (added)
-
tests/meta/archVast.cfa (added)
-
tests/minmax.cfa (modified) (3 diffs)
-
tests/nested-types.cfa (modified) (2 diffs)
-
tests/numericConstants.cfa (modified) (2 diffs)
-
tests/operators.cfa (modified) (1 diff)
-
tests/poly-d-cycle.cfa (added)
-
tests/poly-o-cycle.cfa (added)
-
tests/pybin/settings.py (modified) (6 diffs)
-
tests/pybin/test_run.py (modified) (4 diffs)
-
tests/pybin/tools.py (modified) (9 diffs)
-
tests/raii/.expect/ctor-autogen-ERR1.nast.txt (added)
-
tests/raii/.expect/ctor-autogen-ERR1.oast.txt (added)
-
tests/raii/.expect/ctor-autogen-ERR1.txt (deleted)
-
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 (added)
-
tests/raii/.expect/memberCtors-ERR1.oast.txt (added)
-
tests/raii/.expect/memberCtors-ERR1.txt (deleted)
-
tests/raii/ctor-autogen.cfa (modified) (1 diff)
-
tests/raii/init_once.cfa (modified) (2 diffs)
-
tests/references.cfa (modified) (1 diff)
-
tests/resolutionErrors.cfa (added)
-
tests/result.cfa (modified) (2 diffs)
-
tests/searchsort.cfa (modified) (1 diff)
-
tests/smart-pointers.cfa (added)
-
tests/stdincludes.cfa (modified) (2 diffs)
-
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/typedefRedef.cfa (modified) (2 diffs)
-
tests/typeof.cfa (modified) (1 diff)
-
tests/unified_locking/.expect/locks.txt (added)
-
tests/unified_locking/locks.cfa (added)
-
tests/unified_locking/timeout_lock.cfa (added)
-
tests/unified_locking/timeout_lock.txt (added)
-
tests/variableDeclarator.cfa (modified) (8 diffs)
-
tests/vector.cfa (modified) (2 diffs)
-
tests/vector_math/.expect/vec2_double.txt (added)
-
tests/vector_math/.expect/vec2_float.txt (added)
-
tests/vector_math/.expect/vec2_int.txt (added)
-
tests/vector_math/.expect/vec2_ldouble.txt (added)
-
tests/vector_math/.expect/vec2_uint.txt (added)
-
tests/vector_math/.expect/vec3_float.txt (added)
-
tests/vector_math/.expect/vec3_int.txt (added)
-
tests/vector_math/.expect/vec4_float.txt (added)
-
tests/vector_math/.expect/vec4_int.txt (added)
-
tests/vector_math/glm_equivalents/vec2_float.cc (added)
-
tests/vector_math/glm_equivalents/vec2_int.cc (added)
-
tests/vector_math/glm_equivalents/vec3_float.cc (added)
-
tests/vector_math/glm_equivalents/vec4_float.cc (added)
-
tests/vector_math/vec2_double.cfa (added)
-
tests/vector_math/vec2_float.cfa (added)
-
tests/vector_math/vec2_int.cfa (added)
-
tests/vector_math/vec2_ldouble.cfa (added)
-
tests/vector_math/vec2_uint.cfa (added)
-
tests/vector_math/vec3_float.cfa (added)
-
tests/vector_math/vec3_int.cfa (added)
-
tests/vector_math/vec4_float.cfa (added)
-
tests/vector_math/vec4_int.cfa (added)
-
tests/voidPtr.cfa (modified) (1 diff)
-
tests/warnings/.expect/self-assignment.nast.txt (added)
-
tests/warnings/.expect/self-assignment.oast.txt (added)
-
tests/warnings/.expect/self-assignment.txt (deleted)
-
tests/warnings/self-assignment.cfa (modified) (2 diffs)
-
tests/zombies/ArrayN.c (added)
-
tests/zombies/Initialization.c (added)
-
tests/zombies/Initialization2.c (added)
-
tests/zombies/Makefile.example (added)
-
tests/zombies/Members.c (added)
-
tests/zombies/Misc.c (added)
-
tests/zombies/MiscError.c (added)
-
tests/zombies/Rank2.c (added)
-
tests/zombies/Tuple.c (added)
-
tests/zombies/abstype.c (added)
-
tests/zombies/constructors.c (added)
-
tests/zombies/forward.c (added)
-
tests/zombies/gc_no_raii/.gitignore (added)
-
tests/zombies/gc_no_raii/bug-repro/assert.c (added)
-
tests/zombies/gc_no_raii/bug-repro/blockers.tar.gz (added)
-
tests/zombies/gc_no_raii/bug-repro/blockers/explicit_cast.c (added)
-
tests/zombies/gc_no_raii/bug-repro/blockers/file_scope.c (added)
-
tests/zombies/gc_no_raii/bug-repro/blockers/recursive_realloc.c (added)
-
tests/zombies/gc_no_raii/bug-repro/crash.c (added)
-
tests/zombies/gc_no_raii/bug-repro/deref.c (added)
-
tests/zombies/gc_no_raii/bug-repro/field.c (added)
-
tests/zombies/gc_no_raii/bug-repro/find.c (added)
-
tests/zombies/gc_no_raii/bug-repro/inline.c (added)
-
tests/zombies/gc_no_raii/bug-repro/malloc.c (added)
-
tests/zombies/gc_no_raii/bug-repro/not_equal.c (added)
-
tests/zombies/gc_no_raii/bug-repro/oddtype.c (added)
-
tests/zombies/gc_no_raii/bug-repro/push_back.c (added)
-
tests/zombies/gc_no_raii/bug-repro/push_back.h (added)
-
tests/zombies/gc_no_raii/bug-repro/realloc.c (added)
-
tests/zombies/gc_no_raii/bug-repro/return.c (added)
-
tests/zombies/gc_no_raii/bug-repro/return_template.c (added)
-
tests/zombies/gc_no_raii/bug-repro/slow_malloc.c (added)
-
tests/zombies/gc_no_raii/bug-repro/static_const_local.c (added)
-
tests/zombies/gc_no_raii/bug-repro/test-assert.cpp (added)
-
tests/zombies/gc_no_raii/bug-repro/void_pointer.c (added)
-
tests/zombies/gc_no_raii/bug-repro/while.c (added)
-
tests/zombies/gc_no_raii/bug-repro/zero.c (added)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-malign.c (added)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-malloc.c (added)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-mmap.c (added)
-
tests/zombies/gc_no_raii/pool-alloc/allocate-win-valloc.c (added)
-
tests/zombies/gc_no_raii/premake4.lua (added)
-
tests/zombies/gc_no_raii/src/allocate-pool.c (added)
-
tests/zombies/gc_no_raii/src/allocate-pool.h (added)
-
tests/zombies/gc_no_raii/src/gc.h (added)
-
tests/zombies/gc_no_raii/src/gcpointers.c (added)
-
tests/zombies/gc_no_raii/src/gcpointers.h (added)
-
tests/zombies/gc_no_raii/src/internal/card_table.h (added)
-
tests/zombies/gc_no_raii/src/internal/collector.c (added)
-
tests/zombies/gc_no_raii/src/internal/collector.h (added)
-
tests/zombies/gc_no_raii/src/internal/gc_tools.h (added)
-
tests/zombies/gc_no_raii/src/internal/globals.h (added)
-
tests/zombies/gc_no_raii/src/internal/memory_pool.c (added)
-
tests/zombies/gc_no_raii/src/internal/memory_pool.h (added)
-
tests/zombies/gc_no_raii/src/internal/object_header.c (added)
-
tests/zombies/gc_no_raii/src/internal/object_header.h (added)
-
tests/zombies/gc_no_raii/src/internal/state.c (added)
-
tests/zombies/gc_no_raii/src/internal/state.h (added)
-
tests/zombies/gc_no_raii/src/test_include.c (added)
-
tests/zombies/gc_no_raii/src/tools.h (added)
-
tests/zombies/gc_no_raii/src/tools/checks.h (added)
-
tests/zombies/gc_no_raii/src/tools/print.c (added)
-
tests/zombies/gc_no_raii/src/tools/print.h (added)
-
tests/zombies/gc_no_raii/src/tools/worklist.h (added)
-
tests/zombies/gc_no_raii/test/badlll.c (added)
-
tests/zombies/gc_no_raii/test/gctest.c (added)
-
tests/zombies/gc_no_raii/test/operators.c (added)
-
tests/zombies/hashtable.cfa (added)
-
tests/zombies/hashtable2.cfa (added)
-
tests/zombies/huge.c (added)
-
tests/zombies/includes.c (added)
-
tests/zombies/index.h (added)
-
tests/zombies/io_uring.txt (added)
-
tests/zombies/it_out.c (added)
-
tests/zombies/multicore.c (added)
-
tests/zombies/new.c (added)
-
tests/zombies/poly-bench.c (added)
-
tests/zombies/prolog.c (added)
-
tests/zombies/quad.c (added)
-
tests/zombies/s.c (added)
-
tests/zombies/simplePoly.c (added)
-
tests/zombies/simpler.c (added)
-
tests/zombies/specialize.c (added)
-
tests/zombies/square.c (added)
-
tests/zombies/structMember.cfa (modified) (2 diffs)
-
tests/zombies/twice.c (added)
-
tests/zombies/virtualCast.cfa (deleted)
-
tests/zombies/wrapper/.gitignore (added)
-
tests/zombies/wrapper/premake4.lua (added)
-
tests/zombies/wrapper/src/main.c (added)
-
tests/zombies/wrapper/src/pointer.h (added)
-
tests/zombies/zero_one.c (added)
-
tools/Makefile.am (modified) (1 diff)
-
tools/Makefile.in (deleted)
-
tools/auto-complete.md (added)
-
tools/build/cfa.m4 (added)
-
tools/build/cfa.make (added)
-
tools/build/push2dist.sh (modified) (2 diffs)
-
tools/busy (deleted)
-
tools/cfa.nanorc (modified) (3 diffs)
-
tools/error (deleted)
-
tools/gdb/.gdbinit (added)
-
tools/gdb/README (added)
-
tools/gdb/utils-gdb.gdb (added)
-
tools/gdb/utils-gdb.py (added)
-
tools/langserver/src/json.hpp (added)
-
tools/langserver/src/log.cpp (added)
-
tools/langserver/src/log.hpp (added)
-
tools/langserver/src/main.cpp (added)
-
tools/langserver/src/server.cpp (added)
-
tools/langserver/src/server.hpp (added)
-
tools/perf/process_halts.sh (added)
-
tools/perf/view_halts.py (added)
-
tools/prettyprinter/Makefile.am (modified) (1 diff)
-
tools/prettyprinter/Makefile.in (deleted)
-
tools/stat.py (modified) (2 diffs)
-
tools/vscode/uwaterloo.cforall-0.1.0/.gitignore (added)
-
tools/vscode/uwaterloo.cforall-0.1.0/client/main.js (added)
-
tools/vscode/uwaterloo.cforall-0.1.0/images/icon.png (added)
-
tools/vscode/uwaterloo.cforall-0.1.0/package.json (modified) (4 diffs)
-
tools/vscode/uwaterloo.cforall-0.1.0/server/out/server (added)
-
tools/vscode/uwaterloo.cforall-0.1.0/syntaxes/cfa.tmLanguage.json (added)
-
tools/watchdog (deleted)
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
r3c64c668 r58fe85a 4 4 5 5 # generated by configure 6 aclocal.m4 7 automake 6 8 autom4te.cache 7 9 config.h … … 9 11 config.log 10 12 config.py 13 configure 14 libtool 11 15 stamp-h1 12 libtool13 16 /Makefile 17 /Makefile.in 14 18 **/Makefile 19 **/Makefile.in 20 **/Makefile.dist.in 15 21 /version 16 22 … … 42 48 libcfa/x64-debug/ 43 49 libcfa/x64-nodebug/ 44 libcfa/x64-nolib/45 50 libcfa/x86-debug/ 46 51 libcfa/x86-nodebug/ 47 libcfa/x86-nolib/ 48 libcfa/arm-debug/ 49 libcfa/arm-nodebug/ 50 libcfa/arm-nolib/ 52 libcfa/arm64-debug/ 53 libcfa/arm64-nodebug/ 51 54 52 55 # generated by bison and lex from parser.yy and lex.ll … … 73 76 doc/user/pointer2.tex 74 77 doc/user/EHMHierarchy.tex 78 79 # generated by npm 80 package-lock.json -
Jenkins/FullBuild
r3c64c668 r58fe85a 8 8 def err = null 9 9 10 final scmVars = checkout scm 11 final commitId = scmVars.GIT_COMMIT 12 10 13 try { 11 14 //Wrap build to add timestamp to command line … … 14 17 stage('Build') { 15 18 16 results = [null, null] 19 parallel ( 20 gcc_8_x86_old: { trigger_build( 'gcc-8', 'x86', false ) }, 21 gcc_7_x86_old: { trigger_build( 'gcc-7', 'x86', false ) }, 22 gcc_6_x86_old: { trigger_build( 'gcc-6', 'x86', false ) }, 23 gcc_9_x64_old: { trigger_build( 'gcc-9', 'x64', false ) }, 24 gcc_8_x64_old: { trigger_build( 'gcc-8', 'x64', false ) }, 25 gcc_7_x64_old: { trigger_build( 'gcc-7', 'x64', false ) }, 26 gcc_6_x64_old: { trigger_build( 'gcc-6', 'x64', false ) }, 27 gcc_5_x64_old: { trigger_build( 'gcc-5', 'x64', false ) }, 28 clang_x64_old: { trigger_build( 'clang', 'x64', false ) }, 29 clang_x64_new: { trigger_build( 'clang', 'x64', true ) }, 30 ) 31 } 17 32 18 parallel ( 19 clang_x86: { trigger_build( 'gcc-8', 'x86' ) }, 20 gcc_5_x86: { trigger_build( 'gcc-7', 'x86' ) }, 21 gcc_6_x86: { trigger_build( 'gcc-6', 'x86' ) }, 22 gcc_9_x64: { trigger_build( 'gcc-9', 'x64' ) }, 23 gcc_8_x64: { trigger_build( 'gcc-8', 'x64' ) }, 24 gcc_7_x64: { trigger_build( 'gcc-7', 'x64' ) }, 25 gcc_6_x64: { trigger_build( 'gcc-6', 'x64' ) }, 26 gcc_5_x64: { trigger_build( 'gcc-5', 'x64' ) }, 27 clang_x64: { trigger_build( 'clang', 'x64' ) }, 28 ) 33 stage('Package') { 34 trigger_dist( commitId, currentBuild.number.toString() ) 29 35 } 30 36 } … … 59 65 //=========================================================================================================== 60 66 61 def trigger_build(String cc, String arch ) {67 def trigger_build(String cc, String arch, boolean new_ast) { 62 68 def result = build job: 'Cforall/master', \ 63 69 parameters: [ \ … … 68 74 name: 'Architecture', \ 69 75 value: arch], \ 76 [$class: 'BooleanParameterValue', \ 77 name: 'NewAST', \ 78 value: new_ast], \ 70 79 [$class: 'BooleanParameterValue', \ 71 80 name: 'RunAllTests', \ … … 79 88 [$class: 'BooleanParameterValue', \ 80 89 name: 'Publish', \ 81 value: true], \90 value: true], \ 82 91 [$class: 'BooleanParameterValue', \ 83 92 name: 'Silent', \ … … 94 103 } 95 104 96 //Helper routine to collect information about the git history 97 def collect_git_info() { 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 98 112 99 //create the temporary output directory in case it doesn't already exist 100 def out_dir = pwd tmp: true 101 sh "mkdir -p ${out_dir}" 113 echo(result.result) 102 114 103 //parse git logs to find what changed104 dir("../Cforall_Full_Build@script") {105 sh "git reflog > ${out_dir}/GIT_COMMIT"115 if(result.result != 'SUCCESS') { 116 sh("wget -q -O - https://cforall.uwaterloo.ca/jenkins/job/Cforall_Distribute_Ref/${result.number}/consoleText") 117 error(result.result) 106 118 } 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]110 119 } 111 120 -
Jenkinsfile
r3c64c668 r58fe85a 2 2 3 3 import groovy.transform.Field 4 5 // For skipping stages6 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils7 4 8 5 //=========================================================================================================== … … 15 12 SrcDir = pwd tmp: false 16 13 Settings = null 17 StageName = ''14 Tools = null 18 15 19 16 // Local variables … … 33 30 SrcDir = pwd tmp: false 34 31 35 clean()36 37 checkout()32 Tools.Clean() 33 34 Tools.Checkout() 38 35 39 36 build() … … 57 54 //attach the build log to the email 58 55 catch (Exception caughtError) { 59 //rethrow error later 56 // Store the result of the build log 57 currentBuild.result = "FAILURE" 58 59 // An error has occured, the build log is relevent 60 log_needed = true 61 62 // rethrow error later 60 63 err = caughtError 61 64 65 // print the error so it shows in the log 62 66 echo err.toString() 63 64 //An error has occured, the build log is relevent65 log_needed = true66 67 //Store the result of the build log68 currentBuild.result = "${StageName} FAILURE".trim()69 67 } 70 68 … … 84 82 // Main compilation routines 85 83 //=========================================================================================================== 86 def clean() {87 build_stage('Cleanup', true) {88 // clean the build by wipping the build directory89 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 loop96 def checkout() {97 build_stage('Checkout', true) {98 //checkout the source code and clean the repo99 final scmVars = checkout scm100 Settings.GitNewRef = scmVars.GIT_COMMIT101 Settings.GitOldRef = scmVars.GIT_PREVIOUS_COMMIT102 103 echo GitLogMessage()104 105 // This is a complete hack but it solves problems with automake thinking it needs to regenerate makefiles106 // We fudged automake/missing to handle that but automake stills bakes prints inside the makefiles107 // and these cause more problems.108 sh 'find . -name Makefile.in -exec touch {} +'109 }110 }111 112 84 def build() { 113 85 debug = true 114 86 release = Settings.RunAllTests || Settings.RunBenchmark 115 build_stage('Build : configure', true) { 87 Tools.BuildStage('Build : configure', true) { 88 // Configure must be run inside the tree 89 dir (SrcDir) { 90 // Generate the necessary build files 91 sh './autogen.sh' 92 } 93 116 94 // Build outside of the src tree to ease cleaning 117 95 dir (BuildDir) { 118 //Configure the co npilation (Output is not relevant)96 //Configure the compilation (Output is not relevant) 119 97 //Use the current directory as the installation target so nothing escapes the sandbox 120 98 //Also specify the compiler by hand … … 126 104 } 127 105 128 sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} ${targets} --quiet" 106 ast = Settings.NewAST ? "--enable-new-ast" : "--disable-new-ast" 107 108 sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} AR=gcc-ar RANLIB=gcc-ranlib ${targets} ${ast} --quiet --prefix=${BuildDir}" 129 109 130 110 // Configure libcfa … … 133 113 } 134 114 135 build_stage('Build : cfa-cpp', true) {115 Tools.BuildStage('Build : cfa-cpp', true) { 136 116 // Build outside of the src tree to ease cleaning 137 117 dir (BuildDir) { … … 144 124 } 145 125 146 build_stage('Build : libcfa(debug)', debug) {126 Tools.BuildStage('Build : libcfa(debug)', debug) { 147 127 // Build outside of the src tree to ease cleaning 148 128 dir (BuildDir) { … … 151 131 } 152 132 153 build_stage('Build : libcfa(nodebug)', release) {133 Tools.BuildStage('Build : libcfa(nodebug)', release) { 154 134 // Build outside of the src tree to ease cleaning 155 135 dir (BuildDir) { 156 136 sh "make -j 8 --no-print-directory -C libcfa/${Settings.Architecture.name}-nodebug" 137 } 138 } 139 140 Tools.BuildStage('Build : install', true) { 141 // Build outside of the src tree to ease cleaning 142 dir (BuildDir) { 143 sh "make -j 8 --no-print-directory install" 157 144 } 158 145 } … … 161 148 def test() { 162 149 try { 163 build_stage('Test: short', !Settings.RunAllTests) {150 Tools.BuildStage('Test: short', !Settings.RunAllTests) { 164 151 dir (BuildDir) { 165 152 //Run the tests from the tests directory … … 168 155 } 169 156 170 build_stage('Test: full', Settings.RunAllTests) {157 Tools.BuildStage('Test: full', Settings.RunAllTests) { 171 158 dir (BuildDir) { 172 159 //Run the tests from the tests directory … … 179 166 echo "Archiving core dumps" 180 167 dir (BuildDir) { 181 archiveArtifacts artifacts: "tests/crashes/**/* ", fingerprint: true168 archiveArtifacts artifacts: "tests/crashes/**/*,lib/**/lib*.so*", fingerprint: true 182 169 } 183 170 throw err … … 186 173 187 174 def benchmark() { 188 build_stage('Benchmark', Settings.RunBenchmark) {175 Tools.BuildStage('Benchmark', Settings.RunBenchmark) { 189 176 dir (BuildDir) { 190 177 //Append bench results … … 195 182 196 183 def build_doc() { 197 build_stage('Documentation', Settings.BuildDocumentation) {184 Tools.BuildStage('Documentation', Settings.BuildDocumentation) { 198 185 dir ('doc/user') { 199 186 make_doc() … … 207 194 208 195 def publish() { 209 build_stage('Publish', true) {196 Tools.BuildStage('Publish', true) { 210 197 211 198 if( Settings.Publish && !Settings.RunBenchmark ) { echo 'No results to publish!!!' } … … 229 216 //Routine responsible of sending the email notification once the build is completed 230 217 //=========================================================================================================== 231 @NonCPS232 def SplitLines(String text) {233 def list = []234 235 text.eachLine {236 list += it237 }238 239 return list240 }241 242 def GitLogMessage() {243 if (!Settings || !Settings.GitOldRef || !Settings.GitNewRef) return "\nERROR retrieveing git information!\n"244 245 def oldRef = Settings.GitOldRef246 def newRef = Settings.GitNewRef247 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 = oldRef258 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 294 218 //Standard build email notification 295 219 def email(boolean log) { … … 303 227 generated because of a git hooks/post-receive script following 304 228 a ref change which was pushed to the C\u2200 repository.</p> 305 """ + GitLogMessage()229 """ + Tools.GitLogMessage() 306 230 307 231 def email_to = !Settings.IsSandbox ? "cforall@lists.uwaterloo.ca" : "tdelisle@uwaterloo.ca" … … 325 249 public String CXX 326 250 public String CC 327 328 CC_Desc(String name, String CXX, String CC) { 251 public String lto 252 253 CC_Desc(String name, String CXX, String CC, String lto) { 329 254 this.name = name 330 255 this.CXX = CXX 331 this.CC = CC 256 this.CC = CC 257 this.lto = lto 332 258 } 333 259 } … … 349 275 public final CC_Desc Compiler 350 276 public final Arch_Desc Architecture 277 public final Boolean NewAST 351 278 public final Boolean RunAllTests 352 279 public final Boolean RunBenchmark … … 364 291 switch( param.Compiler ) { 365 292 case 'gcc-9': 366 this.Compiler = new CC_Desc('gcc-9', 'g++-9', 'gcc-9' )293 this.Compiler = new CC_Desc('gcc-9', 'g++-9', 'gcc-9', '-flto=auto') 367 294 break 368 295 case 'gcc-8': 369 this.Compiler = new CC_Desc('gcc-8', 'g++-8', 'gcc-8' )296 this.Compiler = new CC_Desc('gcc-8', 'g++-8', 'gcc-8', '-flto=auto') 370 297 break 371 298 case 'gcc-7': 372 this.Compiler = new CC_Desc('gcc-7', 'g++-7', 'gcc-7' )299 this.Compiler = new CC_Desc('gcc-7', 'g++-7', 'gcc-7', '-flto=auto') 373 300 break 374 301 case 'gcc-6': 375 this.Compiler = new CC_Desc('gcc-6', 'g++-6', 'gcc-6' )302 this.Compiler = new CC_Desc('gcc-6', 'g++-6', 'gcc-6', '-flto=auto') 376 303 break 377 304 case 'gcc-5': 378 this.Compiler = new CC_Desc('gcc-5', 'g++-5', 'gcc-5' )305 this.Compiler = new CC_Desc('gcc-5', 'g++-5', 'gcc-5', '-flto=auto') 379 306 break 380 307 case 'gcc-4.9': 381 this.Compiler = new CC_Desc('gcc-4.9', 'g++-4.9', 'gcc-4.9' )308 this.Compiler = new CC_Desc('gcc-4.9', 'g++-4.9', 'gcc-4.9', '-flto=auto') 382 309 break 383 310 case 'clang': 384 this.Compiler = new CC_Desc('clang', 'clang++- 6.0', 'gcc-6')311 this.Compiler = new CC_Desc('clang', 'clang++-10', 'gcc-9', '-flto=thin -flto-jobs=0') 385 312 break 386 313 default : … … 400 327 401 328 this.IsSandbox = (branch == "jenkins-sandbox") 329 this.NewAST = param.NewAST 402 330 this.RunAllTests = param.RunAllTests 403 331 this.RunBenchmark = param.RunBenchmark … … 409 337 this.DescShort = "${ this.Compiler.name }:${ this.Architecture.name }${full}" 410 338 339 final ast = this.NewAST ? "New AST" : "Old AST" 411 340 this.DescLong = """Compiler : ${ this.Compiler.name } (${ this.Compiler.CXX }/${ this.Compiler.CC }) 341 AST Version : ${ ast.toString() } 412 342 Architecture : ${ this.Architecture.name } 413 343 Arc Flags : ${ this.Architecture.flags } … … 439 369 // prepare the properties 440 370 properties ([ \ 371 buildDiscarder(logRotator( \ 372 artifactDaysToKeepStr: '', \ 373 artifactNumToKeepStr: '', \ 374 daysToKeepStr: '730', \ 375 numToKeepStr: '1000' \ 376 )), \ 441 377 [$class: 'ParametersDefinitionProperty', \ 442 378 parameterDefinitions: [ \ … … 444 380 description: 'Which compiler to use', \ 445 381 name: 'Compiler', \ 446 choices: 'gcc-9\ngcc-8\ngcc-7\ngcc-6\ngcc-5\ngcc-4.9\nclang', \382 choices: 'gcc-9\ngcc-8\ngcc-7\ngcc-6\ngcc-5\ngcc-4.9\nclang', \ 447 383 defaultValue: 'gcc-8', \ 448 384 ], \ … … 454 390 ], \ 455 391 [$class: 'BooleanParameterDefinition', \ 392 description: 'If true, build compiler using new AST', \ 393 name: 'NewAST', \ 394 defaultValue: true, \ 395 ], \ 396 [$class: 'BooleanParameterDefinition', \ 456 397 description: 'If false, only the quick test suite is ran', \ 457 398 name: 'RunAllTests', \ … … 481 422 ]]) 482 423 483 // It's unfortunate but it looks like we need to checkout the entire repo just to get the pretty git printer 424 // It's unfortunate but it looks like we need to checkout the entire repo just to get 425 // - the pretty git printer 426 // - Jenkins.tools 484 427 checkout scm 428 429 Tools = load "Jenkins/tools.groovy" 485 430 486 431 final settings = new BuildSettings(params, env.BRANCH_NAME) … … 490 435 491 436 return settings 492 }493 494 def build_stage(String name, boolean run, Closure block ) {495 StageName = name496 echo " -------- ${StageName} -------- "497 if(run) {498 stage(name, block)499 } else {500 stage(name) { Utils.markStageSkippedForConditional(STAGE_NAME) }501 }502 437 } 503 438 -
Makefile.am
r3c64c668 r58fe85a 19 19 20 20 MAINTAINERCLEANFILES = lib/* bin/* tests/.deps/* tests/.out/* # order important 21 DISTCLEANFILES = version 21 22 22 23 SUBDIRS = driver src . @LIBCFA_TARGET_DIRS@ 24 DIST_SUBDIRS = driver src . libcfa tests 23 25 24 26 @LIBCFA_TARGET_MAKEFILES@ : Makefile $(srcdir)/libcfa/configure … … 26 28 @ls $(config_file) || (echo "Missing config.data, re-run configure script again" && false) 27 29 @$(eval config_data = $(shell cat $(config_file))) 28 @echo "Configuring libcfa with '$(config_data)''"30 @echo "Configuring libcfa ($(abs_top_srcdir)/libcfa/configure) with '$(config_data)' from $(shell pwd) / $(dir $@)" 29 31 @cd $(dir $@) && $(abs_top_srcdir)/libcfa/configure $(config_data) 30 32 … … 32 34 33 35 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.sh 34 38 35 39 debug=yes … … 47 51 @./config.status --config | sed "s/ /\n\t/g; s/\t'/\t/g; s/'\n/\n/g; s/^'//g; s/'$$//g" 48 52 @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 done 58 59 clean-local: @LIBCFA_TARGET_MAKEFILES@ 60 for dir in @LIBCFA_TARGET_DIRS@; do \ 61 $(MAKE) -C $${dir} clean; \ 62 done 63 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
r3c64c668 r58fe85a 11 11 ## Created On : Sun May 31 09:08:15 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Sat Jan 25 09:20:44202014 ## Update Count : 25 513 ## Last Modified On : Tue Mar 10 11:41:18 2020 14 ## Update Count : 258 15 15 ############################################################################### 16 16 … … 19 19 20 20 # applies to both programs 21 include $(top_srcdir)/ src/cfa.make21 include $(top_srcdir)/tools/build/cfa.make 22 22 23 23 AM_CFLAGS = -O2 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror … … 66 66 # Dummy hack tricks 67 67 EXTRA_PROGRAMS = dummy # build but do not install 68 dummy_SOURCES = dummyC.c dummyCXX.cpp68 nodist_dummy_SOURCES = dummyC.c dummyCXX.cpp 69 69 70 70 dummyC.c: … … 80 80 ## ========================================================================================================= 81 81 82 all : basic$(EXEEXT) ctxswitch$(EXEEXT) mutex$(EXEEXT) schedint$(EXEEXT) schedext$(EXEEXT) creation$(EXEEXT) 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) 83 86 84 87 basic_loop_DURATION = 15000000000 … … 108 111 creation_cfa_coroutine_DURATION = 100000000 109 112 creation_cfa_coroutine_eager_DURATION = 10000000 113 creation_cfa_generator_DURATION = 1000000000 110 114 creation_upp_coroutine_DURATION = ${creation_cfa_coroutine_eager_DURATION} 111 creation_cfa_thread_DURATION = 10000000112 creation_upp_thread_DURATION = ${creation_cfa_thread_DURATION}113 115 creation_DURATION = 10000000 114 116 … … 144 146 145 147 cleancsv: 146 rm -f compile.csv basic.csv ctxswitch.csv mutex.csv sched uling.csv148 rm -f compile.csv basic.csv ctxswitch.csv mutex.csv schedint.csv 147 149 148 150 jenkins$(EXEEXT): cleancsv … … 155 157 +make mutex.csv 156 158 -+make mutex.diff.csv 157 +make sched uling.csv158 -+make sched uling.diff.csv159 +make schedint.csv 160 -+make schedint.diff.csv 159 161 @DOifskipcompile@ 160 162 cat compile.csv … … 165 167 cat mutex.csv 166 168 -cat mutex.diff.csv 167 cat sched uling.csv168 -cat sched uling.diff.csv169 cat schedint.csv 170 -cat schedint.diff.csv 169 171 170 172 compile.csv: … … 196 198 $(srcdir)/fixcsv.sh $@ 197 199 198 sched uling.csv:200 schedint.csv: 199 201 echo "building $@" 200 202 echo "schedint-1,schedint-2,schedext-1,schedext-2" > $@ … … 287 289 ctxswitch-python_coroutine$(EXEEXT): 288 290 $(BENCH_V_PY)echo "#!/bin/sh" > a.out 289 echo "python3 .7 $(srcdir)/ctxswitch/python_cor.py" >> a.out291 echo "python3 $(srcdir)/ctxswitch/python_cor.py \"$$""@\"" >> a.out 290 292 chmod a+x a.out 291 293 292 294 ctxswitch-nodejs_coroutine$(EXEEXT): 293 295 $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out 294 echo "nodejs $(srcdir)/ctxswitch/node_cor.js " >> a.out296 echo "nodejs $(srcdir)/ctxswitch/node_cor.js \"$$""@\"" >> a.out 295 297 chmod a+x a.out 296 298 297 299 ctxswitch-nodejs_await$(EXEEXT): 298 300 $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out 299 echo "nodejs $(srcdir)/ctxswitch/node_await.js " >> a.out301 echo "nodejs $(srcdir)/ctxswitch/node_await.js \"$$""@\"" >> a.out 300 302 chmod a+x a.out 301 303 … … 309 311 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/ctxswitch/JavaThread.java 310 312 echo "#!/bin/sh" > a.out 311 echo "java JavaThread " >> a.out313 echo "java JavaThread \"$$""@\"" >> a.out 312 314 chmod a+x a.out 313 315 … … 351 353 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/mutex/JavaThread.java 352 354 echo "#!/bin/sh" > a.out 353 echo "java JavaThread " >> a.out355 echo "java JavaThread \"$$""@\"" >> a.out 354 356 chmod a+x a.out 355 357 … … 383 385 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/schedint/JavaThread.java 384 386 echo "#!/bin/sh" > a.out 385 echo "java JavaThread " >> a.out387 echo "java JavaThread \"$$""@\"" >> a.out 386 388 chmod a+x a.out 387 389 … … 450 452 creation-python_coroutine$(EXEEXT): 451 453 $(BENCH_V_PY)echo "#!/bin/sh" > a.out 452 echo "python3 .7 $(srcdir)/creation/python_cor.py" >> a.out454 echo "python3 $(srcdir)/creation/python_cor.py \"$$""@\"" >> a.out 453 455 chmod a+x a.out 454 456 455 457 creation-nodejs_coroutine$(EXEEXT): 456 458 $(BENCH_V_NODEJS)echo "#!/bin/sh" > a.out 457 echo "nodejs $(srcdir)/creation/node_cor.js " >> a.out459 echo "nodejs $(srcdir)/creation/node_cor.js \"$$""@\"" >> a.out 458 460 chmod a+x a.out 459 461 … … 467 469 $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/creation/JavaThread.java 468 470 echo "#!/bin/sh" > a.out 469 echo "java JavaThread " >> a.out471 echo "java JavaThread \"$$""@\"" >> a.out 470 472 chmod a+x a.out 471 473 … … 475 477 ## ========================================================================================================= 476 478 477 compile$(EXEEXT) : \479 bcompile$(EXEEXT) : \ 478 480 compile-array.make \ 479 481 compile-attributes.make \ … … 488 490 489 491 compile-array$(EXEEXT): 490 $(CFACOMPILE) - fsyntax-only -w $(testdir)/array.cfa492 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/array.cfa 491 493 492 494 compile-attributes$(EXEEXT): 493 $(CFACOMPILE) - fsyntax-only -w $(testdir)/attributes.cfa495 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/attributes.cfa 494 496 495 497 compile-empty$(EXEEXT): 496 $(CFACOMPILE) - fsyntax-only -w $(srcdir)/compile/empty.cfa498 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(srcdir)/compile/empty.cfa 497 499 498 500 compile-expression$(EXEEXT): 499 $(CFACOMPILE) - fsyntax-only -w $(testdir)/expression.cfa501 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/expression.cfa 500 502 501 503 compile-io$(EXEEXT): 502 $(CFACOMPILE) - fsyntax-only -w $(testdir)/io1.cfa504 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/io1.cfa 503 505 504 506 compile-monitor$(EXEEXT): 505 $(CFACOMPILE) - fsyntax-only -w $(testdir)/concurrent/monitor.cfa507 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/concurrent/monitor.cfa 506 508 507 509 compile-operators$(EXEEXT): 508 $(CFACOMPILE) - fsyntax-only -w $(testdir)/operators.cfa510 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/operators.cfa 509 511 510 512 compile-thread$(EXEEXT): 511 $(CFACOMPILE) - fsyntax-only -w $(testdir)/concurrent/thread.cfa513 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/concurrent/thread.cfa 512 514 513 515 compile-typeof$(EXEEXT): 514 $(CFACOMPILE) - fsyntax-only -w $(testdir)/typeof.cfa516 $(CFACOMPILE) -DNO_COMPILED_PRAGMA -fsyntax-only -w $(testdir)/typeof.cfa 515 517 516 518 ## ========================================================================================================= … … 520 522 size-cfa$(EXEEXT): 521 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 $@) $@ -
benchmark/creation/JavaThread.java
r3c64c668 r58fe85a 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 int times = Integer.parseInt("10000") ;28 static private long times = Long.parseLong("10000") ; 29 29 30 30 public static class MyThread extends Thread { … … 33 33 } 34 34 public static void helper() throws InterruptedException { 35 for( inti = 1; i <= times; i += 1) {35 for(long 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 > 2) System.exit( 1 );50 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }49 if ( args.length > 1 ) System.exit( 1 ); 50 if ( args.length == 1 ) { times = Long.parseLong(args[0]); } 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
r3c64c668 r58fe85a 1 #include " bench.h"1 #include "../bench.h" 2 2 3 struct C{3 generator G { 4 4 volatile int restart; // ensure compiler does not optimize away all the code 5 5 }; 6 void ?{}( C & c ) { c.restart = 0; }7 void main( C& ) {}6 void ?{}( G & g ) { g.restart = 0; } 7 void main( G & ) {} 8 8 9 9 int main( int argc, char * argv[] ) { … … 11 11 BENCH( 12 12 for ( times ) { 13 C c;13 G g; 14 14 }, 15 15 result -
benchmark/ctxswitch/JavaThread.java
r3c64c668 r58fe85a 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 int times = Integer.parseInt("100000");28 static private long times = Long.parseLong("100000"); 29 29 30 30 public static void helper() { 31 for( inti = 1; i <= times; i += 1) {31 for(long 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 > 2) System.exit( 1 );43 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }42 if ( args.length > 1 ) System.exit( 1 ); 43 if ( args.length == 1 ) { times = Long.parseLong(args[0]); } 44 44 45 45 for (int i = Integer.parseInt("5"); --i >= 0 ; ) { -
benchmark/ctxswitch/cfa_cor.cfa
r3c64c668 r58fe85a 2 2 #include <thread.hfa> 3 3 4 #include " bench.h"4 #include "../bench.h" 5 5 6 coroutine C {} c;6 coroutine C {}; 7 7 void main( __attribute__((unused)) C & ) { 8 while() {9 suspend ();8 for () { 9 suspend; 10 10 } 11 11 } 12 12 int main( int argc, char * argv[] ) { 13 C c; 13 14 BENCH_START() 14 15 BENCH( -
benchmark/ctxswitch/cfa_gen.cfa
r3c64c668 r58fe85a 1 1 #include "../bench.h" 2 2 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; 3 generator G {}; 4 void main( G & ) { 10 5 for () { 11 return; 12 s1: ; 6 suspend; 13 7 } 14 8 } 15 9 16 10 int main( int argc, char * argv[] ) { 11 G g; 17 12 BENCH_START() 18 C c = { 0 };19 13 BENCH( 20 14 for ( times ) { 21 comain( &c);15 resume( g ); 22 16 }, 23 17 result -
benchmark/exclude
r3c64c668 r58fe85a 10 10 interrupt_linux.c 11 11 exclude 12 io 12 13 Monitor.c -
benchmark/mutex/JavaThread.java
r3c64c668 r58fe85a 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 int times = Integer.parseInt("100000000");28 static private long times = Long.parseLong("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( inti = 1; i <= times; i += 1) {36 x = (j.hashCode() ^ System.identityHashCode(j)) | 1 ; 37 for(long 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 > 2) System.exit( 1 );50 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }49 if ( args.length > 1 ) System.exit( 1 ); 50 if ( args.length == 1 ) { times = Long.parseLong(args[0]); } 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
r3c64c668 r58fe85a 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 int times = Integer.parseInt("10000000");49 static private long times = Long.parseLong("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 ( inti = 0; i < times; i += 1 ) {55 for ( long 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 = Integer.parseInt(args[1]); }73 if ( args.length == 2 ) { times = Long.parseLong(args[1]); } 74 74 75 if ( args.length > 2 ) System.exit( 1 ); 76 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); } 77 78 for ( int i = Integer.parseInt("5"); --i >= 0 ; ) { 75 for ( int i = Integer.parseInt("5"); --i >= 0 ; ) { 79 76 InnerMain(); 80 77 // Thread.sleep(2000); // 2 seconds -
benchmark/schedint/JavaThread.java
r3c64c668 r58fe85a 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 int times = Integer.parseInt("1000000");51 static private long times = Long.parseLong("1000000"); 52 52 53 53 public static void helper( Monitor m ) throws InterruptedException { 54 for( inti = 1; i <= times; i += 1) {54 for(long 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 > 2) System.exit( 1 );78 if ( args.length == 2 ) { times = Integer.parseInt(args[1]); }77 if ( args.length > 1 ) System.exit( 1 ); 78 if ( args.length == 1 ) { times = Long.parseLong(args[0]); } 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
r3c64c668 r58fe85a 3 3 4 4 AC_PREREQ([2.68]) 5 AC_INIT([cfa-cc],[1.0.0 .0],[cforall@plg.uwaterloo.ca])5 AC_INIT([cfa-cc],[1.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])9 8 AC_CONFIG_HEADERS([config.h:src/config.h.in]) 10 9 AM_SILENT_RULES([yes]) 11 10 12 m4_include([ automake/cfa.m4])11 m4_include([tools/build/cfa.m4]) 13 12 14 13 # don't use the default CFLAGS as they unconditonnaly add -O2 15 14 : ${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 support 29 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}" in 34 yes) newast=true ; DEFAULT_NEW_AST="True" ;; 35 no) newast=false; DEFAULT_NEW_AST="False" ;; 36 *) AC_MSG_ERROR([bad value ${enableval} for --enable-new-ast]) ;; 37 esac],[newast=true]) 38 AC_DEFINE_UNQUOTED([CFA_USE_NEW_AST], $newast) 39 AC_SUBST(DEFAULT_NEW_AST) 26 40 27 41 #============================================================================== … … 64 78 enable_distcc=$enableval, enable_distcc=no) 65 79 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 66 84 AM_CONDITIONAL([ENABLE_DISTCC], [test x$enable_distcc = xyes]) 67 85 HAS_DISTCC="False" … … 85 103 # Create variables for commonly used targets 86 104 87 TOP_SRCDIR="$(readlink - m $ac_confdir/)/"88 TOP_BUILDDIR="$(readlink - m$ac_pwd/)/"105 TOP_SRCDIR="$(readlink -e $ac_abs_confdir/)/" 106 TOP_BUILDDIR="$(readlink -e $ac_pwd/)/" 89 107 90 108 AC_DEFINE_UNQUOTED(TOP_SRCDIR, "$TOP_SRCDIR", [Top src directory]) … … 121 139 \'--enable-gprofiler=*) ;; 122 140 \'--disable-gprofiler) ;; 141 142 # skip the target hosts 143 \'--enable-new-ast=*) ;; 144 \'--disable-new-ast) ;; 145 146 # skip this, it only causes problems 147 \'--srcdir=*) ;; 123 148 124 149 # append all other arguments to the sub configure arguments … … 186 211 187 212 LIBCFA_TARGET_DIRS="${LIBCFA_TARGET_DIRS} ${lib_dir}" 213 LIBCFA_1TARGET_DIR="${lib_dir}" 188 214 LIBCFA_TARGET_MAKEFILES="${LIBCFA_TARGET_MAKEFILES} ${lib_dir}/Makefile" 189 215 … … 197 223 198 224 AC_SUBST(LIBCFA_TARGET_DIRS) 225 AC_SUBST(LIBCFA_1TARGET_DIR) 199 226 AC_SUBST(LIBCFA_TARGET_MAKEFILES) 200 227 … … 262 289 driver/Makefile 263 290 src/Makefile 264 benchmark/Makefile291 libcfa/Makefile:libcfa/Makefile.dist.in 265 292 tests/Makefile 266 longrun_tests/Makefile267 tools/Makefile268 tools/prettyprinter/Makefile269 293 ]) 294 295 # Some of our makefile don't need to be distributed 296 AM_CONDITIONAL([CFORALL_DISTRIBUTE], [test -e $TOP_SRCDIR/autogen.sh]) 297 AM_COND_IF([CFORALL_DISTRIBUTE], [ 298 AC_CONFIG_FILES([ 299 longrun_tests/Makefile 300 benchmark/Makefile 301 benchmark/io/http/Makefile 302 tools/Makefile 303 tools/prettyprinter/Makefile 304 ]) 305 306 AC_OUTPUT(benchmark/Cargo.toml) 307 ]) 270 308 271 309 AC_CONFIG_LINKS([tests/test.py:tests/test.py]) -
doc/LaTeXmacros/common.tex
r3c64c668 r58fe85a 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Fri May 24 07:59:54 201914 %% Update Count : 38213 %% Last Modified On : Mon Oct 5 09:34:46 2020 14 %% Update Count : 464 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 36 36 % Names used in the document. 37 37 38 \usepackage{xspace} 38 39 \newcommand{\CFAIcon}{\textsf{C}\raisebox{\depth}{\rotatebox{180}{\textsf{A}}}\xspace} % Cforall symbolic name 39 40 \newcommand{\CFA}{\protect\CFAIcon} % safe for section/caption … … 54 55 \newlength{\parindentlnth} 55 56 \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 correctly62 \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 lstinline69 %\usepackage{etoolbox}70 %\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}}71 57 72 58 \usepackage{pslatex} % reduce size of san serif font … … 241 227 }% 242 228 229 \usepackage{listings} % format program code 243 230 \usepackage{lstlang} 244 245 \newcommand{\CFADefaults}{% 231 \makeatletter 232 233 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}} 234 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}} 235 \newcommand{\LstCommentStyle}[1]{{\lst@basicstyle{\lst@commentstyle{#1}}}} 236 237 \newlength{\gcolumnposn} % temporary hack because lstlisting does not handle tabs correctly 238 \newlength{\columnposn} 239 \setlength{\gcolumnposn}{2.75in} 240 \setlength{\columnposn}{\gcolumnposn} 241 \newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@basicstyle{\LstCommentStyle{#2}}}} 242 \newcommand{\CRT}{\global\columnposn=\gcolumnposn} 243 244 % allow escape sequence in lstinline 245 %\usepackage{etoolbox} 246 %\patchcmd{\lsthk@TextStyle}{\let\lst@DefEsc\@empty}{}{}{\errmessage{failed to patch}} 247 248 % allow adding to lst literate 249 \def\addToLiterate#1{\protect\edef\lst@literate{\unexpanded\expandafter{\lst@literate}\unexpanded{#1}}} 250 \lst@Key{add to literate}{}{\addToLiterate{#1}} 251 \makeatother 252 253 \newcommand{\CFAStyle}{% 246 254 \lstset{ 247 language=CFA,248 255 columns=fullflexible, 249 256 basicstyle=\linespread{0.9}\sf, % reduce line spacing and use sanserif font … … 260 267 belowskip=3pt, 261 268 % replace/adjust listing characters that look bad in sanserif 262 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0. 8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1269 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1 263 270 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 264 271 {<-}{$\leftarrow$}2 {=>}{$\Rightarrow$}2 {->}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.8ex}{0.075ex}}}\kern-0.2ex\textgreater}2, 272 }% lstset 273 }% CFAStyle 274 275 \ifdefined\CFALatin% extra Latin-1 escape characters 276 \lstnewenvironment{cfa}[1][]{ 277 \lstset{ 278 language=CFA, 265 279 moredelim=**[is][\color{red}]{Ā®}{Ā®}, % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-. 266 280 moredelim=**[is][\color{blue}]{Ć}{Ć}, % blue highlighting Ć...Ć (sharp s symbol) emacs: C-q M-_ 267 281 moredelim=**[is][\color{OliveGreen}]{Ā¢}{Ā¢}, % green highlighting Ā¢...Ā¢ (cent symbol) emacs: C-q M-" 268 282 moredelim=[is][\lstset{keywords={}}]{¶}{¶}, % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 283 % replace/adjust listing characters that look bad in sanserif 284 add to literate={`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 269 285 }% lstset 270 }% CFADefaults 271 \newcommand{\CFAStyle}{% 272 \CFADefaults 286 \lstset{#1} 287 }{} 273 288 % inline code Ā©...Ā© (copyright symbol) emacs: C-q M-) 274 289 \lstMakeShortInlineĀ© % single-character for \lstinline 275 }% CFAStyle 276 277 \lstnewenvironment{cfa}[1][] 278 {\CFADefaults\lstset{#1}} 279 {} 290 \else% regular ASCI characters 291 \lstnewenvironment{cfa}[1][]{ 292 \lstset{ 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% 280 302 281 303 % Local Variables: % -
doc/LaTeXmacros/lstlang.sty
r3c64c668 r58fe85a 8 8 %% Created On : Sat May 13 16:34:42 2017 9 9 %% Last Modified By : Peter A. Buhr 10 %% Last Modified On : Tue Jan 8 14:40:33 201911 %% Update Count : 2 110 %% Last Modified On : Wed Sep 23 22:40:04 2020 11 %% Update Count : 24 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, _Generic, _Imaginary, __imag, __imag__,117 __float80, float80, __float128, float128, forall, ftype, generator, _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, thread,119 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, 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++}{} 127 \lstdefinelanguage{C++}[ANSI]{C++}{ 128 morekeywords={nullptr,} 129 } 128 130 129 131 % uC++ programming language, based on ANSI C++ -
doc/bibliography/pl.bib
r3c64c668 r58fe85a 9 9 % Predefined journal names: 10 10 % acmcs: Computing Surveys acta: Acta Infomatica 11 @string{acta="Acta Infomatica"}12 11 % cacm: Communications of the ACM 13 12 % ibmjrd: IBM J. Research & Development ibmsj: IBM Systems Journal … … 22 21 % tcs: Theoretical Computer Science 23 22 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}, 126 156 } 127 157 … … 398 428 journal = sigplan, 399 429 year = 1981, 400 month = feb, volume = 16, number = 2, pages = {48-52}, 430 month = feb, 431 volume = 16, 432 number = 2, 433 pages = {48-52}, 401 434 comment = { 402 435 A one-pass, top-down algorithm for overload resolution. Input is a … … 477 510 title = {An Alternative to Subclassing}, 478 511 journal = sigplan, 479 volume = {21}, number = {11}, 512 volume = {21}, 513 number = {11}, 480 514 pages = {424-428}, 481 month = nov, year = 1986, 515 month = nov, 516 year = 1986, 482 517 comment = { 483 518 The Smalltalk class hierarchy has three uses: factoring out code; … … 533 568 isbn = {3-540-66538-2}, 534 569 location = {Toulouse, France}, 535 doi = {http://doi.acm.org/10.1145/318773.319251},536 570 publisher = {Springer}, 537 571 address = {London, UK}, … … 631 665 year = 2010, 632 666 pages = {39--50}, 633 numpages = {12},634 667 publisher = {IEEE Computer Society}, 635 668 address = {Washington, DC, USA}, … … 922 955 } 923 956 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 924 969 @manual{C11, 925 970 keywords = {ISO/IEC C 11}, … … 928 973 title = {C Programming Language {ISO/IEC} 9889:2011-12}, 929 974 edition = {3rd}, 930 publisher= {International Standard Organization},931 address = { \href{https://www.iso.org/standard/57853.html}{https://\-www.iso.org/\-standard/\-57853.html}},975 organization= {International Standard Organization}, 976 address = {Geneva, Switzerland}, 932 977 year = 2012, 978 note = {\href{https://www.iso.org/standard/57853.html}{https://\-www.iso.org/\-standard/\-57853.html}}, 933 979 } 934 980 … … 938 984 key = {Concepts}, 939 985 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming language -- Extensions for concepts {ISO/IEC} {TS} 19217:2015}, 940 publisher= {International Standard Organization},941 address = { \href{https://www.iso.org/standard/64031.html}{https://\-www.iso.org/\-standard/\-64031.html}},986 organization= {International Standard Organization}, 987 address = {Geneva, Switzerland}, 942 988 year = 2015, 989 note = {\href{https://www.iso.org/standard/64031.html}{https://\-www.iso.org/\-standard/\-64031.html}}, 943 990 } 944 991 … … 957 1004 } 958 1005 959 @misc{Cforall BenchMarks,1006 @misc{CforallConcurrentBenchmarks, 960 1007 contributer = {pabuhr@plg}, 961 1008 key = {Cforall Benchmarks}, 962 1009 author = {{\textsf{C}{$\mathbf{\forall}$} Benchmarks}}, 963 howpublished= {\href{https:// plg.uwaterloo.ca/~cforall/benchmark.tar}{https://\-plg.uwaterloo.ca/\-$\sim$cforall/\-benchmark.tar}},1010 howpublished= {\href{https://github.com/cforall/ConcurrentBenchmarks_SPE20}{https://\-github.com/\-cforall/\-ConcurrentBenchmarks\_SPE20}}, 964 1011 } 965 1012 … … 1105 1152 title = {C\# Language Specification, Standard ECMA-334}, 1106 1153 organization= {ECMA International Standardizing Information and Communication Systems}, 1154 address = {Geneva, Switzerland}, 1107 1155 month = jun, 1108 1156 year = 2006, … … 1254 1302 title = {Programming Languages -- {Cobol} ISO/IEC 1989:2014}, 1255 1303 edition = {2nd}, 1256 institution= {International Standard Organization},1257 address = { \href{https://www.iso.org/standard/51416.html}{https://\-www.iso.org/\-standard/\-51416.html}},1304 organization= {International Standard Organization}, 1305 address = {Geneva, Switzerland}, 1258 1306 year = 2014, 1307 note = {\href{https://www.iso.org/standard/51416.html}{https://\-www.iso.org/\-standard/\-51416.html}}, 1259 1308 } 1260 1309 … … 1305 1354 location = {London, United Kingdom}, 1306 1355 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},1311 1356 publisher = {ACM}, 1312 1357 address = {New York, NY, USA}, … … 1614 1659 title = {$\mu${C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Annotated Reference Manual, Version 7.0.0}, 1615 1660 organization= {University of Waterloo}, 1661 address = {Waterloo Ontario, Canada}, 1616 1662 month = sep, 1617 1663 year = 2018, … … 1933 1979 title = {Cooperating Sequential Processes}, 1934 1980 institution = {Technological University}, 1935 address = {Eindhoven, Neth erlands},1981 address = {Eindhoven, Neth.}, 1936 1982 year = 1965, 1937 1983 note = {Reprinted in \cite{Genuys68} pp. 43--112.} … … 1942 1988 author = {Adya, Atul and Howell, Jon and Theimer, Marvin and Bolosky, William J. and Douceur, John R.}, 1943 1989 title = {Cooperative Task Management Without Manual Stack Management}, 1944 booktitle = {Proc eedings of the General Track of the Annual Conference on USENIX Annual Technical Conference},1990 booktitle = {Proc. of the General Track USENIX Tech. Conf.}, 1945 1991 series = {ATEC '02}, 1946 1992 year = {2002}, … … 2046 2092 author = {Walter Bright and Andrei Alexandrescu}, 2047 2093 organization= {Digital Mars}, 2094 address = {Vienna Virginia, U.S.A.}, 2048 2095 year = 2016, 2049 2096 note = {\href{http://dlang.org/spec/spec.html}{http://\-dlang.org/\-spec/\-spec.html}}, … … 2408 2455 year = 1993, 2409 2456 pages = {201--208}, 2410 url = {http://doi.acm.org/10.1145/155360.155580},2411 2457 publisher = {ACM}, 2412 2458 address = {New York, NY, USA}, … … 2606 2652 location = {Boulder, Colorado, USA}, 2607 2653 pages = {91--97}, 2608 numpages = {7},2609 2654 publisher = {ACM}, 2610 2655 address = {New York, NY, USA}, … … 2637 2682 issn = {0004-5411}, 2638 2683 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},2643 2684 publisher = {ACM}, 2644 2685 address = {New York, NY, USA}, … … 2708 2749 } 2709 2750 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 2710 2763 @misc{Turley99, 2711 2764 keywords = {embedded system, micrprocessor}, … … 2718 2771 howpublished= {\href{https://www.eetimes.com/author.asp?sectionid=36&doc_id=1287712} 2719 2772 {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}, 2720 2786 } 2721 2787 … … 3137 3203 } 3138 3204 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 3139 3220 @article{Lamport87, 3140 3221 keywords = {software solutions, mutual exclusion, fast}, … … 3258 3339 issn = {0001-0782}, 3259 3340 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},3264 3341 publisher = {ACM}, 3265 3342 address = {New York, NY, USA}, … … 3283 3360 title = {Programming Languages -- {Fortran} Part 1:Base Language ISO/IEC 1539-1:2010}, 3284 3361 edition = {3rd}, 3285 publisher= {International Standard Organization},3286 address = { \href{https://www.iso.org/standard/50459.html}{https://\-www.iso.org/\-standard/\-50459.html}},3362 organization= {International Standard Organization}, 3363 address = {Geneva, Switzerland}, 3287 3364 year = 2010, 3365 note = {\href{https://www.iso.org/standard/50459.html}{https://\-www.iso.org/\-standard/\-50459.html}}, 3288 3366 } 3289 3367 … … 3294 3372 title = {Programming Languages -- {Fortran} Part 1:Base Language ISO/IEC 1539-1:2018}, 3295 3373 edition = {4rd}, 3296 publisher= {International Standard Organization},3297 address = { \href{https://www.iso.org/standard/72320.html}{https://\-www.iso.org/\-standard/\-72320.html}},3374 organization= {International Standard Organization}, 3375 address = {Geneva, Switzerland}, 3298 3376 year = 2018, 3377 note = {\href{https://www.iso.org/standard/72320.html}{https://\-www.iso.org/\-standard/\-72320.html}}, 3299 3378 } 3300 3379 … … 3664 3743 } 3665 3744 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 3666 3754 @article{katzenelson83b, 3667 3755 contributer = {gjditchfield@plg}, … … 3697 3785 pages = {115-138}, 3698 3786 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}, 3699 3798 } 3700 3799 … … 4365 4464 } 4366 4465 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 4367 4477 @mastersthesis{Clarke90, 4368 4478 keywords = {concurrency, postponing requests}, … … 4423 4533 } 4424 4534 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 4425 4544 @article{Linda, 4426 4545 keywords = {Linda, concurrency}, … … 4456 4575 } 4457 4576 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 4458 4588 @article{Pierce00, 4459 keywords = {Scala },4589 keywords = {Scala, polymorphism, subtyping, type inference}, 4460 4590 contributer = {a3moss@uwaterloo.ca}, 4461 4591 author = {Pierce, Benjamin C. and Turner, David N.}, … … 4469 4599 issn = {0164-0925}, 4470 4600 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},4475 4601 publisher = {ACM}, 4476 4602 address = {New York, NY, USA}, 4477 keywords = {polymorphism, subtyping, type inference},4478 4603 } 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 } 4479 4619 4480 4620 @article{Sundell08, … … 4554 4694 journal = sigplan, 4555 4695 year = 1989, 4556 month = jun, volume = 24, number = 6, pages = {37-48}, 4696 month = jun, 4697 volume = 24, 4698 number = 6, 4699 pages = {37-48}, 4557 4700 abstract = { 4558 4701 This paper describes a scheme we have used to manage a large … … 4610 4753 address = {New York, NY, USA}, 4611 4754 } 4755 4612 4756 @techreport{Mesa, 4613 4757 keywords = {monitors, packages}, … … 4616 4760 title = {Mesa Language Manual}, 4617 4761 institution = {Xerox Palo Alto Research Center}, 4762 address = {Palo Alto, California, U.S.A.}, 4618 4763 number = {CSL--79--3}, 4619 4764 month = apr, … … 4625 4770 contributer = {pabuhr@plg}, 4626 4771 author = {Gregory R. Andrews}, 4627 title = {A Method for Solving Syn ronization Problems},4772 title = {A Method for Solving Synchronization Problems}, 4628 4773 journal = scp, 4629 4774 volume = 13, … … 4950 5095 title = {Multiple Inheritance for {C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}}}, 4951 5096 booktitle = {Proceedings of the Spring '87 EUUG Conference}, 4952 month = may, year = 1987 5097 month = may, 5098 year = 1987, 4953 5099 } 4954 5100 … … 4995 5141 year = 1986, 4996 5142 pages = {313--326}, 4997 numpages = {14},4998 5143 publisher = {ACM}, 4999 5144 address = {New York, NY, USA}, … … 5011 5156 year = 1986, 5012 5157 pages = {327--348}, 5013 numpages = {22},5014 5158 publisher = {ACM}, 5015 5159 address = {New York, NY, USA}, … … 5208 5352 year = 2005, 5209 5353 pages = {146-196}, 5210 numpages = {51},5211 5354 publisher = {ACM}, 5212 5355 address = {New York, NY, USA}, … … 5354 5497 year = 2000, 5355 5498 pages = {29-46}, 5356 note = {OOPSLA'00, Oct. 15--19, 2000, Minneapolis, Minn esota, U.S.A.},5499 note = {OOPSLA'00, Oct. 15--19, 2000, Minneapolis, Minn., U.S.A.}, 5357 5500 } 5358 5501 … … 5468 5611 location = {San Diego, California, USA}, 5469 5612 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},5474 5613 publisher = {ACM}, 5475 5614 address = {New York, NY, USA}, … … 5575 5714 issn = {0362-1340}, 5576 5715 pages = {30--42}, 5577 numpages = {13},5578 url = {http://doi.acm.org/10.1145/947586.947589},5579 doi = {10.1145/947586.947589},5580 5716 publisher = {ACM}, 5581 5717 address = {New York, NY, USA} … … 6114 6250 } 6115 6251 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 6116 6266 @book{Butenhof97, 6117 6267 keywords = {PThreads, concurrency}, … … 6162 6312 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:1998}, 6163 6313 edition = {1st}, 6164 publisher= {International Standard Organization},6165 address = { \href{https://www.iso.org/standard/25845.html}{https://\-www.iso.org/\-standard/\-25845.html}},6314 organization = {International Standard Organization}, 6315 address = {Geneva, Switzerland}, 6166 6316 year = 1998, 6317 note = {\href{https://www.iso.org/standard/25845.html}{https://\-www.iso.org/\-standard/\-25845.html}}, 6167 6318 } 6168 6319 … … 6173 6324 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:2014}, 6174 6325 edition = {4th}, 6175 publisher= {International Standard Organization},6176 address = { \href{https://www.iso.org/standard/64029.html}{https://\-www.iso.org/\-standard/\-64029.html}},6326 organization= {International Standard Organization}, 6327 address = {Geneva, Switzerland}, 6177 6328 year = 2014, 6329 note = {\href{https://www.iso.org/standard/64029.html}{https://\-www.iso.org/\-standard/\-64029.html}}, 6178 6330 } 6179 6331 … … 6184 6336 title = {{C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}} Programming Language ISO/IEC 14882:2017}, 6185 6337 edition = {5th}, 6186 publisher= {International Standard Organization},6187 address = { \href{https://www.iso.org/standard/68564.html}{https://\-www.iso.org/\-standard/\-68564.html}},6338 organization= {International Standard Organization}, 6339 address = {Geneva, Switzerland}, 6188 6340 year = 2017, 6341 note = {\href{https://www.iso.org/standard/68564.html}{https://\-www.iso.org/\-standard/\-68564.html}}, 6189 6342 } 6190 6343 … … 6318 6471 title = {The Programming Language Concurrent Pascal}, 6319 6472 journal = ieeese, 6320 volume = 2, 6473 volume = {SE-1}, 6474 number = 2, 6321 6475 month = jun, 6322 6476 year = 1975, 6323 pages = {199-20 6}6477 pages = {199-207} 6324 6478 } 6325 6479 … … 6499 6653 issn = {0164-0925}, 6500 6654 pages = {429-475}, 6501 url = {http://doi.acm.org/10.1145/1133651.1133653},6502 doi = {10.1145/1133651.1133653},6503 acmid = {1133653},6504 6655 publisher = {ACM}, 6505 6656 address = {New York, NY, USA}, … … 6531 6682 } 6532 6683 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 6533 6693 % R 6534 6694 … … 6574 6734 title = {Programming languages -- {Ada} ISO/IEC 8652:2012}, 6575 6735 edition = {3rd}, 6576 publisher= {International Standard Organization},6577 address = { \href{https://www.iso.org/standard/61507.html}{https://\-www.iso.org/\-standard/\-61507.html}},6736 organization= {International Standard Organization}, 6737 address = {Geneva, Switzerland}, 6578 6738 year = 2012, 6739 note = {\href{https://www.iso.org/standard/61507.html}{https://\-www.iso.org/\-standard/\-61507.html}}, 6579 6740 } 6580 6741 … … 6879 7040 issn = {0001-0782}, 6880 7041 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},6885 7042 publisher = {ACM}, 6886 7043 address = {New York, NY, USA} … … 6900 7057 issn = {0362-1340}, 6901 7058 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},6906 7059 publisher = {ACM}, 6907 7060 address = {New York, NY, USA}, … … 7006 7159 issn = {0362-1340}, 7007 7160 pages = {82--87}, 7008 numpages = {6},7009 url = {http://doi.acm.org/10.1145/947680.947688},7010 doi = {10.1145/947680.947688},7011 7161 publisher = {ACM}, 7012 7162 address = {New York, NY, USA}, … … 7153 7303 } 7154 7304 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 7155 7318 @article{Dijkstra65a, 7156 7319 keywords = {N-thread software-solution mutual exclusion}, … … 7363 7526 year = 1974, 7364 7527 pages = {261-301}, 7365 issn = {0360-0300},7366 doi = {http://doi.acm.org/10.1145/356635.356640},7367 7528 publisher = {ACM}, 7368 7529 address = {New York, NY, USA}, … … 7454 7615 publisher = {ACM Press}, 7455 7616 address = {New York, NY, USA}, 7456 doi = {http://doi.acm.org/10.1145/356586.356588},7457 7617 } 7458 7618 … … 7582 7742 title = {The Thoth System: Multi-Process Structuring and Portability}, 7583 7743 publisher = {American Elsevier}, 7744 address = {New York, New York, U.S.A.}, 7584 7745 year = 1982 7585 7746 } … … 7755 7916 howpublished= {\href{https://projects.eclipse.org/proposals/trace-compass}{https://\-projects.eclipse.org/\-proposals/\-trace-compass}}, 7756 7917 } 7757 7918 7919 @inproceedings{Boehm09, 7920 author = {Boehm, Hans-J.}, 7921 title = {Transactional Memory Should Be an Implementation Technique, Not a Programming Interface}, 7922 booktitle = {Proceedings of the First USENIX Conference on Hot Topics in Parallelism}, 7923 series = {HotPar'09}, 7924 year = {2009}, 7925 location = {Berkeley, California}, 7926 publisher = {USENIX Association}, 7927 address = {Berkeley, CA, USA}, 7928 } 7929 7758 7930 @article{Leroy00, 7759 7931 keywords = {type-systems, exceptions}, … … 7805 7977 number = {2}, 7806 7978 pages = {204-214}, 7807 month = apr, year = 1988, 7979 month = apr, 7980 year = 1988, 7808 7981 comment = { 7809 7982 Extended record types add fields to their base record. Assignment … … 7904 8077 } 7905 8078 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 7906 8095 @techreport{Harmony, 7907 8096 keywords = {messages, concurrency}, … … 7919 8108 contributer = {gjditchfield@plg}, 7920 8109 author = {Henry Lieverman}, 7921 title = {Using Prototypical Objects to Implement Shared Behavior in 7922 Object Oriented Systems}, 8110 title = {Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems}, 7923 8111 journal = sigplan, 7924 month = nov, year = 1986, 7925 volume = 21, number = 11, pages = {214-223} 8112 month = nov, 8113 year = 1986, 8114 volume = 21, 8115 number = 11, 8116 pages = {214-223} 7926 8117 } 7927 8118 … … 8110 8301 issn = {0004-5411}, 8111 8302 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},8116 8303 publisher = {ACM}, 8117 8304 address = {New York, NY, USA}, … … 8126 8313 contributer = {pabuhr@plg}, 8127 8314 author = {Boehm, Hans-J. and Adve, Sarita V.}, 8128 title = {You Don' TKnow Jack About Shared Variables or Memory Models},8315 title = {You Don't Know Jack About Shared Variables or Memory Models}, 8129 8316 journal = cacm, 8130 8317 volume = 55, -
doc/man/cfa.1
r3c64c668 r58fe85a 11 11 .\" Created On : Wed Jul 26 22:34:47 2017 12 12 .\" Last Modified By : Peter A. Buhr 13 .\" Last Modified On : Thu Jul 27 10:29:29 201714 .\" Update Count : 4413 .\" Last Modified On : Wed Sep 2 17:59:53 2020 14 .\" Update Count : 78 15 15 .\" 16 16 .\" nroff -man cfa.1 … … 23 23 .ds Cf "Cforall 24 24 .\" 25 .TH cfa 1 2017-07-27 cfa-\*(Mg25 .TH CFA 1 "2020-09-2" cfa-\*(Mg "\*(Cf Project" 26 26 .SH NAME 27 cfa \- \*(Cf Translator and Runtime Library27 cfa \- \*(Cf project translator and runtime library to enhance C 28 28 .SH SYNOPSIS 29 cfa [gcc-options] [C/\*(Cf source-files] [assembler/loader files] 29 cfa [cfa/gcc-options] 30 [cfa/c source-files] 31 [assembler/loader files] 30 32 .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 31 35 The cfa command compiles C and \*(Cf source files and links C/\*(Cf object 32 36 files named on the command line. … … 34 38 The cfa command introduces a translator pass over the specified source files 35 39 after the C preprocessor but before the C compilation. The translator converts 36 new \*(Cf constructs into C statements. The cfa command also provides the 37 runtime library, which is linked with each \*(Cf application. 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. 38 43 39 44 The command line options depend on the particular C compiler used (gcc/clang 40 45 supported). As with most C compilers, the output is sent to the file a.out(5) 41 46 unless the -o option is present on the command line. See the reference pages 42 for gcc(1) for more information .47 for gcc(1) for more information on command line options. 43 48 .SH OPTIONS 44 49 When multiple conflicting options appear on the command line, e.g., … … 50 55 All of the options available to the gcc compiler are available to the cfa 51 56 translator. The following gcc flags are implicitly turned on: 52 .IP -std=gnu99 3 53 The 1999 C standard plus GNU extensions. 54 .IP -fgnu89-inline 55 Use the traditional GNU semantics for inline routines in C99 mode, which allows inline routines in header files. 57 .IP "-std=gnu11" 3 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. 56 65 .LP 57 66 The following additional options are available: 58 .IP -CFA367 .IP "-CFA" 3 59 68 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. 60 69 The generated code starts with the standard \*(Cf prelude. 61 .IP -debug70 .IP "-debug" 62 71 The program is linked with the debugging version of the runtime system. 63 72 The debug version performs runtime checks to help during the debugging phase of a \*(Cf program, but can substantially slow program execution. 64 73 The runtime checks should only be removed after the program is completely debugged. 65 74 .B This option is the default. 66 .IP -nodebug75 .IP "-nodebug" 67 76 The program is linked with the non-debugging version of the runtime system, so the execution of the program is faster. 68 77 .I However, no runtime checks or asserts are performed so errors usually result in abnormal program behaviour or termination. 69 .IP -help78 .IP "-help" 70 79 Information about the set of \*(Cf compilation flags is printed. 71 .IP -nohelp80 .IP "-nohelp" 72 81 Information about the set of \*(Cf compilation flags is not printed. 73 82 .B This option is the default. 74 .IP -quiet83 .IP "-quiet" 75 84 The \*(Cf compilation message is not printed at the beginning of a compilation. 76 .IP -noquiet85 .IP "-noquiet" 77 86 The \*(Cf compilation message is printed at the beginning of a compilation. 78 87 .B This option is the default. … … 81 90 available. These variables allow conditional compilation of programs that must 82 91 work differently in these situations. 83 .IP __CFA_MAJOR__392 .IP "__CFA_MAJOR__" 3 84 93 is available during preprocessing and its value is the major version number of \*(Cf. 85 .IP __CFA_MINOR__94 .IP "__CFA_MINOR__" 86 95 is available during preprocessing and its value is the minor version number of \*(Cf. 87 .IP __CFA_PATCH__96 .IP "__CFA_PATCH__" 88 97 is available during preprocessing and its value is the patch level number of \*(Cf. 89 98 .IP "__CFA__, __CFORALL__, and __cforall" 90 99 are always available during preprocessing and have no value. 91 .IP __CFA_DEBUG__100 .IP "__CFA_DEBUG__" 92 101 is available during preprocessing if the -debug compilation option is 93 102 specified. … … 116 125 .SH REFERENCES 117 126 .HP 3 118 \*(Cf Reference and Rational Manual 127 .I \*(Cf Home Page 119 128 .br 120 http ://plg.uwaterloo.ca/~cforall/refrat.pdf129 https://cforall.uwaterloo.ca 121 130 .HP 122 131 .I \*(Cf User Manual 123 132 .br 124 http://plg.uwaterloo.ca/~cforall/user.pdf 133 https://cforall.uwaterloo.ca/doc/user.pdf 134 .SH BUILDS 135 Nightly builds are available here https://cforall.uwaterloo.ca/jenkins 125 136 .SH BUGS 126 Bugs should be reported to trac@plg.cs.uwaterloo.ca.137 Bugs reportss are available here https://cforall.uwaterloo.ca/trac 127 138 .SH COPYRIGHT 128 139 \*(Cf is covered under the licence agreement in the distribution. 129 140 .SH AUTHORS 130 141 Andrew Beach, Richard Bilson, Peter A. Buhr, Thierry Delisle, Glen Ditchfield, 131 Rodolfo G. Esteves, Aaron Moss, Rob Schluntz 142 Rodolfo G. Esteves, Aaron Moss, Rob Schluntz, Mubeen Zulfiqar -
doc/papers/AMA/AMA-stix/ama/WileyNJD-v2.cls
r3c64c668 r58fe85a 2444 2444 \@afterheading} 2445 2445 2446 \renewcommand\section{\@startsection{section}{1}{\z@}{-2 5pt \@plus -2pt \@minus -2pt}{12\p@}{\sectionfont}}%2447 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{- 22pt \@plus -2pt \@minus -2pt}{5\p@}{\subsectionfont}}%2448 \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{- 20pt \@plus -2pt \@minus -2pt}{2\p@}{\subsubsectionfont}}%2446 \renewcommand\section{\@startsection{section}{1}{\z@}{-20pt \@plus -2pt \@minus -2pt}{7\p@}{\sectionfont}}% 2447 \renewcommand\subsection{\@startsection{subsection}{2}{\z@}{-18pt \@plus -2pt \@minus -2pt}{5\p@}{\subsectionfont}}% 2448 \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}{-16pt \@plus -2pt \@minus -2pt}{2\p@}{\subsubsectionfont}}% 2449 2449 % 2450 2450 \newskip\secruleskip\secruleskip8.5\p@% -
doc/papers/concurrency/Paper.tex
r3c64c668 r58fe85a 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, e.g.,\newterm{abc}.102 % The option parameter provides an index term different from the new term, e.g.,\newterm[\texttt{abc}]{abc}101 % Denote newterms in particular font and index them without particular font and in lowercase, \eg \newterm{abc}. 102 % The option parameter provides an index term different from the new term, \eg \newterm[\texttt{abc}]{abc} 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}} 112 %\newcommand{\EG}{\abbrevFont{e}\abbrevFont{g}} 113 \newcommand{\EG}{for example} 113 114 \newcommand*{\eg}{% 114 115 \@ifnextchar{,}{\EG}% … … 117 118 }}{}% 118 119 \@ifundefined{ie}{ 119 \newcommand{\IE}{\abbrevFont{i}\abbrevFont{e}} 120 %\newcommand{\IE}{\abbrevFont{i}\abbrevFont{e}} 121 \newcommand{\IE}{that is} 120 122 \newcommand*{\ie}{% 121 123 \@ifnextchar{,}{\IE}% … … 127 129 \newcommand*{\etc}{% 128 130 \@ifnextchar{.}{\ETC}% 129 {\ETC.\xspace}%131 {\ETC.\xspace}% 130 132 }}{}% 131 133 \@ifundefined{etal}{ 132 134 \newcommand{\ETAL}{\abbrevFont{et}~\abbrevFont{al}} 133 135 \newcommand*{\etal}{% 134 \@ifnextchar{.}{\ protect\ETAL}%135 {\ protect\ETAL.\xspace}%136 \@ifnextchar{.}{\ETAL}% 137 {\ETAL.\xspace}% 136 138 }}{}% 137 139 \@ifundefined{viz}{ … … 163 165 __float80, float80, __float128, float128, forall, ftype, generator, _Generic, _Imaginary, __imag, __imag__, 164 166 inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or, 165 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, thread,167 otype, restrict, resume, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, thread, 166 168 _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__, 167 169 virtual, __volatile, __volatile__, waitfor, when, with, zero_t}, 168 170 moredirectives={defined,include_next}, 169 171 % replace/adjust listing characters that look bad in sanserif 170 literate={-}{\makebox[1ex][c]{\raisebox{0. 4ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1172 literate={-}{\makebox[1ex][c]{\raisebox{0.5ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1 171 173 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 % {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 172 174 {<}{\textrm{\textless}}1 {>}{\textrm{\textgreater}}1 … … 197 199 _Else, _Enable, _Event, _Finally, _Monitor, _Mutex, _Nomutex, _PeriodicTask, _RealTimeTask, 198 200 _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]{`}{`},213 201 } 214 202 … … 238 226 {} 239 227 \lstnewenvironment{C++}[1][] % use C++ style 240 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{`}{`} ,#1}\lstset{#1}}228 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 241 229 {} 242 230 \lstnewenvironment{uC++}[1][] 243 {\lstset{ #1}}231 {\lstset{language=uC++,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 244 232 {} 245 233 \lstnewenvironment{Go}[1][] 246 {\lstset{language=Golang,moredelim=**[is][\protect\color{red}]{`}{`} ,#1}\lstset{#1}}234 {\lstset{language=Golang,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 247 235 {} 248 236 \lstnewenvironment{python}[1][] 249 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 237 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 238 {} 239 \lstnewenvironment{java}[1][] 240 {\lstset{language=java,moredelim=**[is][\protect\color{red}]{`}{`}}\lstset{#1}} 250 241 {} 251 242 … … 262 253 } 263 254 264 \new box\myboxA265 \new box\myboxB266 \new box\myboxC267 \new box\myboxD255 \newsavebox{\myboxA} 256 \newsavebox{\myboxB} 257 \newsavebox{\myboxC} 258 \newsavebox{\myboxD} 268 259 269 260 \title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}} … … 275 266 \address[1]{\orgdiv{Cheriton School of Computer Science}, \orgname{University of Waterloo}, \orgaddress{\state{Waterloo, ON}, \country{Canada}}} 276 267 277 \corres{*Peter A. Buhr, Cheriton School of Computer Science, University of Waterloo, 200 University Avenue West, Waterloo, ON ,N2L 3G1, Canada. \email{pabuhr{\char`\@}uwaterloo.ca}}268 \corres{*Peter A. Buhr, Cheriton School of Computer Science, University of Waterloo, 200 University Avenue West, Waterloo, ON N2L 3G1, Canada. \email{pabuhr{\char`\@}uwaterloo.ca}} 278 269 279 270 % \fundingInfo{Natural Sciences and Engineering Research Council of Canada} 280 271 281 272 \abstract[Summary]{ 282 \CFA is a polymorphic, non -object-oriented, concurrent, backwards-compatible extension of the C programming language.273 \CFA is a polymorphic, nonobject-oriented, concurrent, backwards compatible extension of the C programming language. 283 274 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. 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.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. 285 276 \CFA introduces modern language-level control-flow mechanisms, like generators, coroutines, user-level threading, and monitors for mutual exclusion and synchronization. 286 277 % Library extension for executors, futures, and actors are built on these basic mechanisms. 287 278 The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and monitor barging. 288 The runtime also ensures multiple monitors can be safely acquired \emph{simultaneously} (deadlock free), and this feature is fully integrated with all monitor synchronization mechanisms.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. 289 280 All control-flow features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers. 290 281 Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming languages. 291 282 }% 292 283 293 \keywords{ generator, coroutine, concurrency, parallelism, thread, monitor, runtime, C, \CFA (Cforall)}284 \keywords{C \CFA (Cforall) coroutine concurrency generator monitor parallelism runtime thread} 294 285 295 286 296 287 \begin{document} 297 \linenumbers% comment out to turn off line numbering288 %\linenumbers % comment out to turn off line numbering 298 289 299 290 \maketitle … … 302 293 \section{Introduction} 303 294 304 This paper discusses the design philosophy and implementation of advanced language-level control-flow and concurrent/parallel features in \CFA~\cite{Moss18,Cforall} and its runtime, which is written entirely in \CFA. 305 \CFA is a modern, polymorphic, non-object-oriented\footnote{ 306 \CFA has features often associated with object-oriented programming languages, such as constructors, destructors, virtuals and simple inheritance. 307 However, functions \emph{cannot} be nested in structures, so there is no lexical binding between a structure and set of functions (member/method) implemented by an implicit \lstinline@this@ (receiver) parameter.}, 308 backwards-compatible extension of the C programming language. 309 In many ways, \CFA is to C as Scala~\cite{Scala} is to Java, providing a \emph{research vehicle} for new typing and control-flow capabilities on top of a highly popular programming language allowing immediate dissemination. 310 Within the \CFA framework, new control-flow features are created from scratch because ISO \Celeven defines only a subset of the \CFA extensions, where the overlapping features are concurrency~\cite[\S~7.26]{C11}. 311 However, \Celeven concurrency is largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}, and \Celeven and pthreads concurrency is simple, based on thread fork/join in a function and mutex/condition locks, which is low-level and error-prone; 312 no high-level language concurrency features are defined. 313 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-9 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C11 concurrency approach (possibly because the effort to add concurrency to \CC). 314 Finally, while the \Celeven standard does not state a threading model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}. 315 295 \CFA~\cite{Moss18,Cforall} is a modern, polymorphic, nonobject-oriented\footnote{ 296 \CFA has object-oriented features, such as constructors, destructors, and simple trait/interface inheritance. 297 % Go interfaces, Rust traits, Swift Protocols, Haskell Type Classes and Java Interfaces. 298 % "Trait inheritance" works for me. "Interface inheritance" might also be a good choice, and distinguish clearly from implementation inheritance. 299 % You'll want to be a little bit careful with terms like "structural" and "nominal" inheritance as well. CFA has structural inheritance (I think Go as well) -- it's inferred based on the structure of the code. 300 % Java, Rust, and Haskell (not sure about Swift) have nominal inheritance, where there needs to be a specific statement that "this type inherits from this type". 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. 316 326 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. 317 As multi -core hardware became available in the 1980/90s, both user and kernel threading were examined.327 As multicore hardware became available in the 1980/1990s, both user and kernel threading were examined. 318 328 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}. 319 329 Libraries like pthreads were developed for C, and the Solaris operating-system switched from user (JDK 1.1~\cite{JDK1.1}) to kernel threads. 320 As a result, languages like Java, Scala, Objective-C~\cite{obj-c-book}, \CCeleven~\cite{C11}, and C\#~\cite{Csharp} adopt the 1:1 kernel-threading model, with a variety of presentation mechanisms.321 From 2000 onward s, 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 unitsto facilitate load balancing by the runtime~\cite{Verch12}.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 work to facilitate load balancing by the runtime~\cite{Verch12}. 323 333 As well, user-threading facilitates a simpler concurrency approach using thread objects that leverage sequential patterns versus events with call-backs~\cite{Adya02,vonBehren03}. 324 Finally, performant user-threading implementations (both time and space) meet or exceed direct kernel-threading implementations, while achieving the programming advantages of high concurrency levels and safety. 325 326 A further effort over the past two decades is the development of language memory models to deal with the conflict between language features and compiler/hardware optimizations, \ie some language features are unsafe in the presence of aggressive sequential optimizations~\cite{Buhr95a,Boehm05}. 327 The consequence is that a language must provide sufficient tools to program around safety issues, as inline and library code is all sequential to the compiler. 328 One solution is low-level qualifiers and functions (\eg @volatile@ and atomics) allowing \emph{programmers} to explicitly write safe (race-free~\cite{Boehm12}) programs. 329 A safer solution is high-level language constructs so the \emph{compiler} knows the optimization boundaries, and hence, provides implicit safety. 330 This problem is best known with respect to concurrency, but applies to other complex control-flow, like exceptions\footnote{ 331 \CFA exception handling will be presented in a separate paper. 332 The key feature that dovetails with this paper is nonlocal exceptions allowing exceptions to be raised across stacks, with synchronous exceptions raised among coroutines and asynchronous exceptions raised among threads, similar to that in \uC~\cite[\S~5]{uC++} 333 } and coroutines. 334 Finally, language solutions allow matching constructs with language paradigm, \ie imperative and functional languages often have different presentations of the same concept to fit their programming model. 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. 335 342 336 343 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. 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. 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. 356 356 The main contributions of this work are: 357 \begin{itemize}[topsep=3pt,itemsep= 1pt]357 \begin{itemize}[topsep=3pt,itemsep=0pt] 358 358 \item 359 language-level generators, coroutines and user-level threading, which respect the expectations of C programmers. 359 a set of fundamental execution properties that dictate which language-level control-flow features need to be supported, 360 360 361 \item 361 monitor synchronization without barging, and the ability to safely acquiring multiple monitors \emph{simultaneously} (deadlock free), while seamlessly integrating these capabilities with all monitor synchronization mechanisms. 362 integration of these language-level control-flow features, while respecting the style and expectations of C programmers, 363 362 364 \item 363 providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features. 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 364 370 % \item 365 371 % library extensions for executors, futures, and actors built on the basic mechanisms. 372 366 373 \item 367 a runtime system with no spurious wakeup. 374 a runtime system without spurious wake-up and no performance loss, 375 368 376 \item 369 a dynamic partitioning mechanism to segregate the execution environment for specialized requirements. 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 370 379 % \item 371 % a non-blocking I/O library 380 % a nonblocking I/O library 381 372 382 \item 373 experimental results showing comparable performance of the new features with similar mechanisms in other programminglanguages.383 experimental results showing comparable performance of the \CFA features with similar mechanisms in other languages. 374 384 \end{itemize} 375 385 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). 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. 379 390 Section~\ref{s:Monitor} shows how both mutual exclusion and synchronization are safely embedded in the @monitor@ and @thread@ constructs. 380 Section~\ref{s:CFARuntimeStructure} describes the large-scale mechanism to structure (cluster) threads and virtual processors (kernel threads). 381 Section~\ref{s:Performance} uses a series of microbenchmarks to compare \CFA threading with pthreads, Java OpenJDK-9, Go 1.12.6 and \uC 7.0.0. 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. 382 548 383 549 … … 385 551 \label{s:StatefulFunction} 386 552 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. 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 }% 399 628 400 629 \begin{figure} … … 410 639 411 640 641 642 412 643 int fn = f->fn; f->fn = f->fn1; 413 644 f->fn1 = f->fn + fn; 414 645 return fn; 415 416 646 } 417 647 int main() { … … 432 662 void `main(Fib & fib)` with(fib) { 433 663 664 434 665 [fn1, fn] = [1, 0]; 435 666 for () { … … 451 682 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 452 683 typedef struct { 453 int fn1, fn; void * `next`;684 int `restart`, fn1, fn; 454 685 } Fib; 455 #define FibCtor { 1, 0, NULL}686 #define FibCtor { `0`, 1, 0 } 456 687 Fib * comain( Fib * f ) { 457 if ( f->next ) goto *f->next; 458 f->next = &&s1; 688 `static void * states[] = {&&s0, &&s1};` 689 `goto *states[f->restart];` 690 s0: f->`restart` = 1; 459 691 for ( ;; ) { 460 692 return f; 461 693 s1:; int fn = f->fn + f->fn1; 462 f->fn1 = f->fn; f->fn = fn;694 f->fn1 = f->fn; f->fn = fn; 463 695 } 464 696 } … … 472 704 \end{lrbox} 473 705 474 \subfloat[C asymmetric generator]{\label{f:CFibonacci}\usebox\myboxA}706 \subfloat[C]{\label{f:CFibonacci}\usebox\myboxA} 475 707 \hspace{3pt} 476 708 \vrule 477 709 \hspace{3pt} 478 \subfloat[\CFA asymmetric generator]{\label{f:CFAFibonacciGen}\usebox\myboxB}710 \subfloat[\CFA]{\label{f:CFAFibonacciGen}\usebox\myboxB} 479 711 \hspace{3pt} 480 712 \vrule 481 713 \hspace{3pt} 482 \subfloat[C generat or implementation]{\label{f:CFibonacciSim}\usebox\myboxC}483 \caption{Fibonacci (output)asymmetric generator}714 \subfloat[C generated code for \CFA version]{\label{f:CFibonacciSim}\usebox\myboxC} 715 \caption{Fibonacci output asymmetric generator} 484 716 \label{f:FibonacciAsymmetricGenerator} 485 717 … … 493 725 }; 494 726 void ?{}( Fmt & fmt ) { `resume(fmt);` } // constructor 495 void ^?{}( Fmt & f ) with(f) { $\C[ 1.75in]{// destructor}$727 void ^?{}( Fmt & f ) with(f) { $\C[2.25in]{// destructor}$ 496 728 if ( g != 0 || b != 0 ) sout | nl; } 497 729 void `main( Fmt & f )` with(f) { … … 499 731 for ( ; g < 5; g += 1 ) { $\C{// groups}$ 500 732 for ( ; b < 4; b += 1 ) { $\C{// blocks}$ 501 `suspend;` $\C{// wait for character}$502 while ( ch == '\n' ) `suspend;` // ignore503 sout | ch; // newline504 } sout | " "; // block spacer505 } sout | nl; // group newline733 do { `suspend;` $\C{// wait for character}$ 734 while ( ch == '\n' ); // ignore newline 735 sout | ch; $\C{// print character}$ 736 } sout | " "; $\C{// block separator}$ 737 } sout | nl; $\C{// group separator}$ 506 738 } 507 739 } … … 521 753 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 522 754 typedef struct { 523 void * next;755 int `restart`, g, b; 524 756 char ch; 525 int g, b;526 757 } Fmt; 527 758 void comain( Fmt * f ) { 528 if ( f->next ) goto *f->next; 529 f->next = &&s1; 759 `static void * states[] = {&&s0, &&s1};` 760 `goto *states[f->restart];` 761 s0: f->`restart` = 1; 530 762 for ( ;; ) { 531 763 for ( f->g = 0; f->g < 5; f->g += 1 ) { 532 764 for ( f->b = 0; f->b < 4; f->b += 1 ) { 533 return;534 s1:; while ( f->ch == '\n' ) return;765 do { return; s1: ; 766 } while ( f->ch == '\n' ); 535 767 printf( "%c", f->ch ); 536 768 } printf( " " ); … … 539 771 } 540 772 int main() { 541 Fmt fmt = { NULL}; comain( &fmt ); // prime773 Fmt fmt = { `0` }; comain( &fmt ); // prime 542 774 for ( ;; ) { 543 775 scanf( "%c", &fmt.ch ); … … 550 782 \end{lrbox} 551 783 552 \subfloat[\CFA asymmetric generator]{\label{f:CFAFormatGen}\usebox\myboxA}553 \hspace{3 pt}784 \subfloat[\CFA]{\label{f:CFAFormatGen}\usebox\myboxA} 785 \hspace{35pt} 554 786 \vrule 555 787 \hspace{3pt} 556 \subfloat[C generat or simulation]{\label{f:CFormatSim}\usebox\myboxB}788 \subfloat[C generated code for \CFA version]{\label{f:CFormatGenImpl}\usebox\myboxB} 557 789 \hspace{3pt} 558 \caption{Formatter (input)asymmetric generator}790 \caption{Formatter input asymmetric generator} 559 791 \label{f:FormatterAsymmetricGenerator} 560 792 \end{figure} 561 793 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. 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. 606 795 This generator is an \emph{output generator}, producing a new result on each resumption. 607 796 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. 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;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; 609 798 hence, state is retained in a closure between calls. 610 799 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. 611 800 The C version only has the middle execution state because the top execution state is declaration initialization. 612 801 Figure~\ref{f:CFAFibonacciGen} shows the \CFA approach, which also has a manual closure, but replaces the structure with a custom \CFA @generator@ type. 613 This generator type is then connected to a function that \emph{must be named \lstinline|main|},\footnote{ 614 The name \lstinline|main| has special meaning in C, specifically the function where a program starts execution. 615 Hence, overloading this name for other starting points (generator/coroutine/thread) is a logical extension.} 616 called a \emph{generator main},which takes as its only parameter a reference to the generator type. 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. 617 807 The generator main contains @suspend@ statements that suspend execution without ending the generator versus @return@. 618 For the Fibonacci generator-main,\footnote{ 619 The \CFA \lstinline|with| opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics. 620 Multiple aggregates may be opened.} 621 the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@. 808 For the Fibonacci generator-main, the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@. 622 809 Any local variables in @main@ \emph{are not retained} between calls; 623 810 hence local variables are only for temporary computations \emph{between} suspends. … … 627 814 Resuming an ended (returned) generator is undefined. 628 815 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. 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 }% 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. 634 823 \begin{cfa} 635 824 int ?()( Fib & fib ) { return `resume( fib )`.fn; } $\C[3.9in]{// function-call interface}$ 636 int ?()( Fib & fib, int N ) { for ( N - 1 ) `fib()`; return `fib()`; } $\C{// use function-call interface to skip N values}$ 637 double ?()( Fib & fib ) { return (int)`fib()` / 3.14159; } $\C{// different return type, cast prevents recursive call}\CRT$ 638 sout | (int)f1() | (double)f1() | f2( 2 ); // alternative interface, cast selects call based on return type, step 2 values 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$ 639 829 \end{cfa} 640 830 Now, the generator can be a separately compiled opaque-type only accessed through its interface functions. 641 831 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. 642 832 643 Having to manually create the generator closure by moving local-state variables into the generator type is an additional programmer burden. 644 (This restriction is removed by the coroutine in Section~\ref{s:Coroutine}.) 645 This requirement follows from the generality of variable-size local-state, \eg local state with a variable-length array requires dynamic allocation because the array size is unknown at compile time. 833 \begin{figure} 834 %\centering 835 \newbox\myboxA 836 \begin{lrbox}{\myboxA} 837 \begin{python}[aboveskip=0pt,belowskip=0pt] 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. 646 897 However, dynamic allocation significantly increases the cost of generator creation/destruction and is a showstopper for embedded real-time programming. 647 898 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. 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 generatorproblems 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.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 the problems 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 and fast generators. 651 902 652 903 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. … … 669 920 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. 670 921 The destructor provides a newline, if formatted text ends with a full line. 671 Figure~\ref{f:CFormatSim} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@. 672 For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the Fibonacci generator. 673 674 Figure~\ref{f:DeviceDriverGen} shows a \emph{killer} asymmetric generator, a device-driver, because device drivers caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}. 675 Device drives follow the pattern of simple data state but complex execution state, \ie finite state-machine (FSM) parsing a protocol. 676 For example, the following protocol: 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: 677 934 \begin{center} 678 935 \ldots\, STX \ldots\, message \ldots\, ESC ETX \ldots\, message \ldots\, ETX 2-byte crc \ldots 679 936 \end{center} 680 is a network message beginning with the control character STX, ending with an ETX, and followed by a 2-byte cyclic-redundancy check.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. 681 938 Control characters may appear in a message if preceded by an ESC. 682 939 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. 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. 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. 692 944 693 945 \begin{figure} 694 946 \centering 695 \newbox\myboxA696 \begin{lrbox}{\myboxA}697 \begin{python}[aboveskip=0pt,belowskip=0pt]698 def Fib():699 fn1, fn = 0, 1700 while True:701 `yield fn1`702 fn1, fn = fn, fn1 + fn703 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\myboxB717 \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 prewritten732 for i in range( 41 ):733 `fmt.send( 'a' );` # send to yield734 \end{python}735 \end{lrbox}736 \subfloat[Fibonacci]{\label{f:PythonFibonacci}\usebox\myboxA}737 \hspace{3pt}738 \vrule739 \hspace{3pt}740 \subfloat[Formatter]{\label{f:PythonFormatter}\usebox\myboxB}741 \caption{Python generator}742 \label{f:PythonGenerator}743 744 \bigskip745 746 947 \begin{tabular}{@{}l|l@{}} 747 948 \begin{cfa}[aboveskip=0pt,belowskip=0pt] … … 750 951 `generator` Driver { 751 952 Status status; 752 unsignedchar byte, * msg; // communication753 unsignedint lnth, sum; // local state754 unsignedshort int crc;953 char byte, * msg; // communication 954 int lnth, sum; // local state 955 short int crc; 755 956 }; 756 957 void ?{}( Driver & d, char * m ) { d.msg = m; } … … 797 998 \end{figure} 798 999 799 Figure~\ref{f:CFAPingPongGen} shows a symmetric generator, where the generator resumes another generator, forming a resume/resume cycle.1000 Generators can also have symmetric activation using resume/resume to create control-flow cycles among generators. 800 1001 (The trivial cycle is a generator resuming itself.) 801 1002 This control flow is similar to recursion for functions but without stack growth. 802 The steps for symmetric control-flow are creating, executing, and terminating the cycle. 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. 803 1005 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. 804 1006 (This issue occurs for any cyclic data structure.) 805 % The example creates all the generators and then assigns the partners that form the cycle. 806 % Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle. 807 Once the cycle is formed, the program main resumes one of the generators, and the generators can then traverse an arbitrary cycle using @resume@ to activate partner generator(s). 1007 % (Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle.) 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). 808 1009 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. 809 1011 The starting stack-frame is below the last active generator because the resume/resume cycle does not grow the stack. 810 Also, since local variables are not retained in the generator function, it does not contain any objects with destructors that must be called, so the cost is the same as a function return. 811 Destructor cost occurs when the generator instance is deallocated, which is easily controlled by the programmer. 812 813 Figure~\ref{f:CPingPongSim} shows the implementation of the symmetric generator, where the complexity is the @resume@, which needs an extension to the calling convention to perform a forward rather than backward jump. 814 This jump-starts at the top of the next generator main to re-execute the normal calling convention to make space on the stack for its local variables. 815 However, before the jump, the caller must reset its stack (and any registers) equivalent to a @return@, but subsequently jump forward. 816 This semantics is basically a tail-call optimization, which compilers already perform. 817 The example shows the assembly code to undo the generator's entry code before the direct jump. 818 This assembly code depends on what entry code is generated, specifically if there are local variables and the level of optimization. 819 To provide this new calling convention requires a mechanism built into the compiler, which is beyond the scope of \CFA at this time. 820 Nevertheless, it is possible to hand generate any symmetric generators for proof of concept and performance testing. 821 A compiler could also eliminate other artifacts in the generator simulation to further increase performance, \eg LLVM has various coroutine support~\cite{CoroutineTS}, and \CFA can leverage this support should it fork @clang@. 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} 822 1022 823 1023 \begin{figure} … … 826 1026 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 827 1027 `generator PingPong` { 1028 int N, i; // local state 828 1029 const char * name; 829 int N;830 int i; // local state831 1030 PingPong & partner; // rebindable reference 832 1031 }; 833 1032 834 1033 void `main( PingPong & pp )` with(pp) { 1034 1035 835 1036 for ( ; i < N; i += 1 ) { 836 1037 sout | name | i; … … 850 1051 \begin{cfa}[escapechar={},aboveskip=0pt,belowskip=0pt] 851 1052 typedef struct PingPong { 1053 int restart, N, i; 852 1054 const char * name; 853 int N, i;854 1055 struct PingPong * partner; 855 void * next;856 1056 } PingPong; 857 #define PPCtor(name, N) { name,N,0,NULL,NULL}1057 #define PPCtor(name, N) {0, N, 0, name, NULL} 858 1058 void comain( PingPong * pp ) { 859 if ( pp->next ) goto *pp->next; 860 pp->next = &&cycle; 1059 static void * states[] = {&&s0, &&s1}; 1060 goto *states[pp->restart]; 1061 s0: pp->restart = 1; 861 1062 for ( ; pp->i < pp->N; pp->i += 1 ) { 862 1063 printf( "%s %d\n", pp->name, pp->i ); 863 1064 asm( "mov %0,%%rdi" : "=m" (pp->partner) ); 864 1065 asm( "mov %rdi,%rax" ); 865 asm( "popq %rbx" ); 1066 asm( "add $16, %rsp" ); 1067 asm( "popq %rbp" ); 866 1068 asm( "jmp comain" ); 867 cycle: ;1069 s1: ; 868 1070 } 869 1071 } … … 881 1083 \end{figure} 882 1084 883 Finally, part of this generator work was inspired by the recent \CCtwenty generator proposal~\cite{C++20Coroutine19} (which they call coroutines). 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. 884 1094 Our work provides the same high-performance asymmetric generators as \CCtwenty, and extends their work with symmetric generators. 885 1095 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: … … 896 1106 \label{s:Coroutine} 897 1107 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. 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. 901 1115 A series of different kinds of coroutines and their implementations demonstrate how coroutines extend generators. 902 1116 903 1117 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. 904 \begin{description} 905 \item[Fibonacci] 906 Move the declaration of @fn1@ to the start of 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 907 1123 \begin{cfa}[xleftmargin=0pt] 908 void main( Fib & fib ) with(fib) {1124 void main( Fib & fib ) ... 909 1125 `int fn1;` 910 \end{cfa} 911 \item[Formatter] 912 Move the declaration of @g@ and @b@ to the for loops in the coroutine main. 1126 1127 1128 \end{cfa} 1129 & 913 1130 \begin{cfa}[xleftmargin=0pt] 914 1131 for ( `g`; 5 ) { 915 1132 for ( `b`; 4 ) { 916 \end{cfa} 917 \item[Device Driver] 918 Move the declaration of @lnth@ and @sum@ to their points of initialization. 1133 1134 1135 \end{cfa} 1136 & 919 1137 \begin{cfa}[xleftmargin=0pt] 920 status = CONT; 921 `unsigned int lnth = 0, sum = 0;` 922 ... 923 `unsigned short int crc = byte << 8;` 924 \end{cfa} 925 \item[PingPong] 926 Move the declaration of @i@ to the for loop in the coroutine main. 1138 status = CONT; 1139 `int lnth = 0, sum = 0;` 1140 ... 1141 `short int crc = byte << 8;` 1142 \end{cfa} 1143 & 927 1144 \begin{cfa}[xleftmargin=0pt] 928 void main( PingPong & pp ) with(pp) {1145 void main( PingPong & pp ) ... 929 1146 for ( `i`; N ) { 930 \end{cfa} 931 \end{description} 1147 1148 1149 \end{cfa} 1150 \end{tabular} 1151 \end{center} 932 1152 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. 933 1153 \begin{cfa} 934 unsigned int Crc() { 935 `suspend;` 936 unsigned short int crc = byte << 8; 937 `suspend;` 938 status = (crc | byte) == sum ? MSG : ECRC; 1154 int Crc() { 1155 `suspend;` short int crc = byte << 8; 1156 `suspend;` status = (crc | byte) == sum ? MSG : ECRC; 939 1157 return crc; 940 1158 } 941 1159 \end{cfa} 942 A call to this function is placed at the end of the d river's coroutine-main.943 For complex finite-state machines, refactoring is part of normal program abstraction, especially when code is used in multiple places.1160 A call to this function is placed at the end of the device driver's coroutine-main. 1161 For complex FSMs, refactoring is part of normal program abstraction, especially when code is used in multiple places. 944 1162 Again, this complexity is usually associated with execution state rather than data state. 945 1163 946 1164 \begin{comment} 947 Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface functions, \eg @ next@.948 Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the (overloaded)coroutine main.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 overloaded coroutine main. 949 1167 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@. 950 The interface function @ next@, takes a Fibonacci instance and context switches to it using @resume@;1168 The interface function @restart@, takes a Fibonacci instance and context switches to it using @resume@; 951 1169 on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned. 952 1170 The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack; … … 1114 1332 \begin{figure} 1115 1333 \centering 1116 \lstset{language=CFA,escapechar={},moredelim=**[is][\protect\color{red}]{`}{`}}% allow $1117 1334 \begin{tabular}{@{}l@{\hspace{2\parindentlnth}}l@{}} 1118 1335 \begin{cfa} 1119 1336 `coroutine` Prod { 1120 Cons & c; // communication1337 Cons & c; $\C[1.5in]{// communication}$ 1121 1338 int N, money, receipt; 1122 1339 }; 1123 1340 void main( Prod & prod ) with( prod ) { 1124 // 1st resume starts here 1125 for ( i; N ) { 1341 for ( i; N ) { $\C{// 1st resume}\CRT$ 1126 1342 int p1 = random( 100 ), p2 = random( 100 ); 1127 sout | p1 | " " | p2;1128 1343 int status = delivery( c, p1, p2 ); 1129 sout | " $" | money | nl | status;1130 1344 receipt += 1; 1131 1345 } 1132 1346 stop( c ); 1133 sout | "prod stops";1134 1347 } 1135 1348 int payment( Prod & prod, int money ) { … … 1152 1365 \begin{cfa} 1153 1366 `coroutine` Cons { 1154 Prod & p; // communication1367 Prod & p; $\C[1.5in]{// communication}$ 1155 1368 int p1, p2, status; 1156 1369 bool done; 1157 1370 }; 1158 1371 void ?{}( Cons & cons, Prod & p ) { 1159 &cons.p = &p; // reassignable reference1372 &cons.p = &p; $\C{// reassignable reference}$ 1160 1373 cons.[status, done ] = [0, false]; 1161 1374 } 1162 1375 void main( Cons & cons ) with( cons ) { 1163 // 1st resume starts here 1164 int money = 1, receipt; 1376 int money = 1, receipt; $\C{// 1st resume}\CRT$ 1165 1377 for ( ; ! done; ) { 1166 sout | p1 | " " | p2 | nl | " $" | money;1167 1378 status += 1; 1168 1379 receipt = payment( p, money ); 1169 sout | " #" | receipt;1170 1380 money += 1; 1171 1381 } 1172 sout | "cons stops";1173 1382 } 1174 1383 int delivery( Cons & cons, int p1, int p2 ) { … … 1189 1398 1190 1399 Figure~\ref{f:ProdCons} shows the ping-pong example in Figure~\ref{f:CFAPingPongGen} extended into a producer/consumer symmetric-coroutine performing bidirectional communication. 1191 This example is illustrative because both producer /consumer have two interface functions with @resume@s that suspend execution in these interface (helper)functions.1400 This example is illustrative because both producer and consumer have two interface functions with @resume@s that suspend execution in these interface functions. 1192 1401 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. 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. 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. 1207 1409 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. 1208 1411 1209 1412 \begin{figure} … … 1214 1417 \caption{Producer / consumer runtime stacks} 1215 1418 \label{f:ProdConsRuntimeStacks} 1216 1217 \medskip1218 1219 \begin{center}1220 \input{FullCoroutinePhases.pstex_t}1221 \end{center}1222 \vspace*{-10pt}1223 \caption{Ping / Pong coroutine steps}1224 \label{f:PingPongFullCoroutineSteps}1225 1419 \end{figure} 1226 1420 1227 1421 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. 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. 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. 1231 1429 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. 1232 1430 Unfortunately, it is impossible to determine statically if a coroutine is in a cycle and unrealistic to check dynamically (graph-cycle problem). 1233 1431 Hence, a compromise solution is necessary that works for asymmetric (acyclic) and symmetric (cyclic) coroutines. 1234 1235 Our solution is to context switch back to the first resumer (starter) once the coroutine ends.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}). 1236 1434 This semantics works well for the most common asymmetric and symmetric coroutine usage patterns. 1237 For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer. 1238 All previous generators converted to coroutines have this property. 1239 For symmetric coroutines, it is common for the cycle creator to persist for the lifetime of the cycle. 1240 Hence, the starter coroutine is remembered on the first resume and ending the coroutine resumes the starter. 1241 Figure~\ref{f:ProdConsRuntimeStacks} shows this semantic by the dashed lines from the end of the coroutine mains: @prod@ starts @cons@ so @cons@ resumes @prod@ at the end, and the program main starts @prod@ so @prod@ resumes the program main at the end. 1242 For other scenarios, it is always possible to devise a solution with additional programming effort, such as forcing the cycle forward (backward) to a safe point before starting termination. 1243 1244 The producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first. 1245 Assume generator @PingPong@ is converted to a coroutine. 1246 Figure~\ref{f:PingPongFullCoroutineSteps} shows the creation, starter, and cyclic execution steps of the coroutine version. 1247 The program main creates (declares) coroutine instances @ping@ and @pong@. 1248 Next, program main resumes @ping@, making it @ping@'s starter, and @ping@'s main resumes @pong@'s main, making it @pong@'s starter. 1249 Execution forms a cycle when @pong@ resumes @ping@, and cycles $N$ times. 1250 By adjusting $N$ for either @ping@/@pong@, it is possible to have either one finish first, instead of @pong@ always ending first. 1251 If @pong@ ends first, it resumes its starter @ping@ in its coroutine main, then @ping@ ends and resumes its starter the program main in function @start@. 1252 If @ping@ ends first, it resumes its starter the program main in function @start@. 1253 Regardless of the cycle complexity, the starter stack always leads back to the program main, but the stack can be entered at an arbitrary point. 1254 Once back at the program main, coroutines @ping@ and @pong@ are deallocated. 1255 For generators, deallocation runs the destructors for all objects in the generator type. 1256 For coroutines, deallocation deals with objects in the coroutine type and must also run the destructors for any objects pending on the coroutine's stack for any unterminated coroutine. 1257 Hence, if a coroutine's destructor detects the coroutine is not ended, it implicitly raises a cancellation exception (uncatchable exception) at the coroutine and resumes it so the cancellation exception can propagate to the root of the coroutine's stack destroying all local variable on the stack. 1258 So the \CFA semantics for the generator and coroutine, ensure both can be safely deallocated at any time, regardless of their current state, like any other aggregate object. 1259 Explicitly raising normal exceptions at another coroutine can replace flag variables, like @stop@, \eg @prod@ raises a @stop@ exception at @cons@ after it finishes generating values and resumes @cons@, which catches the @stop@ exception to terminate its loop. 1260 1261 Finally, there is an interesting effect for @suspend@ with symmetric coroutines. 1262 A coroutine must retain its last resumer to suspend back because the resumer is on a different stack. 1263 These reverse pointers allow @suspend@ to cycle \emph{backwards}, which may be useful in certain cases. 1264 However, there is an anomaly if a coroutine resumes itself, because it overwrites its last resumer with itself, losing the ability to resume the last external resumer. 1265 To prevent losing this information, a self-resume does not overwrite the last resumer. 1266 1267 1268 \subsection{Generator / Coroutine Implementation} 1269 1270 A significant implementation challenge for generators/coroutines (and threads in Section~\ref{s:threads}) is adding extra fields to the custom types and related functions, \eg inserting code after/before the coroutine constructor/destructor and @main@ to create/initialize/de-initialize/destroy any extra fields, \eg stack. 1271 There are several solutions to these problem, which follow from the object-oriented flavour of adopting custom types. 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. 1272 1454 1273 1455 For object-oriented languages, inheritance is used to provide extra fields and code via explicit inheritance: … … 1276 1458 \end{cfa} 1277 1459 % The problem is that the programming language and its tool chain, \eg debugger, @valgrind@, need to understand @baseCoroutine@ because it infers special property, so type @baseCoroutine@ becomes a de facto keyword and all types inheriting from it are implicitly custom types. 1278 The problem is that some special properties are not handled by existing language semantics, \eg the execution of constructors /destructors is in the wrong order to implicitly start threads because the thread must start \emph{after} all constructors as it relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived.1460 The problem is that some special properties are not handled by existing language semantics, \eg the execution of constructors and destructors is in the wrong order to implicitly start threads because the thread must start \emph{after} all constructors as it relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived. 1279 1461 Alternatives, such as explicitly starting threads as in Java, are repetitive and forgetting to call start is a common source of errors. 1280 1462 An alternative is composition: … … 1294 1476 Users wanting to extend custom types or build their own can only do so in ways offered by the language. 1295 1477 Furthermore, implementing custom types without language support may display the power of a programming language. 1296 \CFA blends the two approaches, providing custom type for idiomatic \CFA code, while extending and building new custom types is still possible, similar to Java concurrency with builtin and library .1478 \CFA blends the two approaches, providing custom type for idiomatic \CFA code, while extending and building new custom types is still possible, similar to Java concurrency with builtin and library (@java.util.concurrent@) monitors. 1297 1479 1298 1480 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. … … 1304 1486 forall( `dtype` T | is_coroutine(T) ) void $suspend$( T & ), resume( T & ); 1305 1487 \end{cfa} 1306 Note, copying generators/coroutines/threads is not meaningful. 1307 For example, both the resumer and suspender descriptors can have bidirectional pointers; 1308 copying these coroutines does not update the internal pointers so behaviour of both copies would be difficult to understand. 1309 Furthermore, two coroutines cannot logically execute on the same stack. 1310 A deep coroutine copy, which copies the stack, is also meaningless in an unmanaged language (no garbage collection), like C, because the stack may contain pointers to object within it that require updating for the copy. 1311 The \CFA @dtype@ property provides no \emph{implicit} copying operations and the @is_coroutine@ trait provides no \emph{explicit} copying operations, so all coroutines must be passed by reference (pointer). 1312 The function definitions ensure there is a statically typed @main@ function that is the starting point (first stack frame) of a coroutine, and a mechanism to get (read) the coroutine descriptor from its handle. 1313 The @main@ function has no return value or additional parameters because the coroutine type allows an arbitrary number of interface functions with corresponding arbitrary typed input/output values versus fixed ones. 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. 1314 1492 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@. 1315 1493 … … 1352 1530 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. 1353 1531 1354 Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a t askis similar).1355 The coroutine handle is the @coroutine@ instance containing programmer specified type global /communication variables across interface functions.1532 Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a thread is similar). 1533 The coroutine handle is the @coroutine@ instance containing programmer specified type global and communication variables across interface functions. 1356 1534 The coroutine descriptor contains all implicit declarations needed by the runtime, \eg @suspend@/@resume@, and can be part of the coroutine handle or separate. 1357 1535 The coroutine stack can appear in a number of locations and be fixed or variable sized. 1358 Hence, the coroutine's stack could be a VLS\footnote{ 1359 We are examining variable-sized structures (VLS), where fields can be variable-sized structures or arrays. 1360 Once allocated, a VLS is fixed sized.} 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.} 1361 1540 on the allocating stack, provided the allocating stack is large enough. 1362 For a VLS stack allocation /deallocation is an inexpensive adjustment of the stack pointer, modulo any stack constructor costs (\eg initial frame setup).1363 For heap stack allocation, allocation/deallocation is an expensive heap allocation (where the heap can be a shared resource), modulo any stack constructor costs.1364 With heap stack allocation, it is also possible to use a split (segmented) stack calling convention, available with gcc and clang, so the stack is variable sized.1365 Currently, \CFA supports stack /heap allocated descriptors but only fixed-sized heap allocated stacks.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 and heap allocated descriptors but only fixed-sized heap allocated stacks. 1366 1545 In \CFA debug-mode, the fixed-sized stack is terminated with a write-only page, which catches most stack overflows. 1367 1546 Experience teaching concurrency with \uC~\cite{CS343} shows fixed-sized stacks are rarely an issue for students. 1368 Split-stack allocation is under development but requires recompilation of legacy code, which may be impossible.1547 Split-stack allocation is under development but requires recompilation of legacy code, which is not always possible. 1369 1548 1370 1549 \begin{figure} … … 1380 1559 1381 1560 Concurrency is nondeterministic scheduling of independent sequential execution paths (threads), where each thread has its own stack. 1382 A single thread with multiple call stacks, \newterm{coroutining}~\cite{Conway63,Marlin80}, does \emph{not} imply concurrency~\cite[\S~2]{Buhr05a}.1383 In coroutining, coroutinesself-schedule the thread across stacks so execution is deterministic.1561 A single thread with multiple stacks, \ie coroutining, does \emph{not} imply concurrency~\cite[\S~3]{Buhr05a}. 1562 Coroutining self-schedule the thread across stacks so execution is deterministic. 1384 1563 (It is \emph{impossible} to generate a concurrency error when coroutining.) 1385 However, coroutines are a stepping stone towards concurrency. 1386 1387 The transition to concurrency, even for a single thread with multiple stacks, occurs when coroutines context switch to a \newterm{scheduling coroutine}, introducing non-determinism from the coroutine perspective~\cite[\S~3,]{Buhr05a}. 1564 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}. 1388 1566 Therefore, a minimal concurrency system requires coroutines \emph{in conjunction with a nondeterministic scheduler}. 1389 The resulting execution system now follows a cooperative threading model~\cite{Adya02,libdill}, called \newterm{non-preemptive scheduling}. 1390 Adding \newterm{preemption} introduces non-cooperative scheduling, where context switching occurs randomly between any two instructions often based on a timer interrupt, called \newterm{preemptive scheduling}. 1391 While a scheduler introduces uncertain execution among explicit context switches, preemption introduces uncertainty by introducing implicit context switches. 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. 1392 1569 Uncertainty gives the illusion of parallelism on a single processor and provides a mechanism to access and increase performance on multiple processors. 1393 The reason is that the scheduler /runtime have complete knowledge about resources and how to best utilized them.1394 However, the introduction of unrestricted nondeterminism results in the need for \newterm{mutual exclusion} and \newterm{synchronization} , which restrict nondeterminism for correctness;1570 The reason is that the scheduler and runtime 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; 1395 1572 otherwise, it is impossible to write meaningful concurrent programs. 1396 1573 Optimal concurrent performance is often obtained by having as much nondeterminism as mutual exclusion and synchronization correctness allow. 1397 1574 1398 A scheduler can either be astackless or stackful.1575 A scheduler can also be stackless or stackful. 1399 1576 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. 1400 1577 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. … … 1405 1582 \label{s:threads} 1406 1583 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(...); 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(...); 1415 1593 `t.start();` // start 1416 1594 // concurrency … … 1419 1597 & 1420 1598 \begin{cfa} 1421 class MyT ask{ ... } // functor1422 MyT ask mytask;1423 `thread t( myt ask, ... );` // start1599 class MyThread { ... } // functor 1600 MyThread mythread; 1601 `thread t( mythread, ... );` // start 1424 1602 // concurrency 1425 1603 `t.join();` // wait … … 1434 1612 \end{cfa} 1435 1613 \end{tabular} 1436 \end{cquote} 1437 \CFA has a simpler approach using a custom @thread@ type and leveraging declaration semantics (allocation/deallocation), where threads implicitly @fork@ after construction and @join@ before destruction. 1438 \begin{cfa} 1439 thread MyTask {}; 1440 void main( MyTask & this ) { ... } 1614 \vspace{1pt} 1615 \par\noindent 1616 \CFA has a simpler approach using a custom @thread@ type and leveraging declaration semantics, allocation and deallocation, where threads implicitly @fork@ after construction and @join@ before destruction. 1617 \begin{cfa} 1618 thread MyThread {}; 1619 void main( MyThread & this ) { ... } 1441 1620 int main() { 1442 MyT askteam`[10]`; $\C[2.5in]{// allocate stack-based threads, implicit start after construction}$1621 MyThread team`[10]`; $\C[2.5in]{// allocate stack-based threads, implicit start after construction}$ 1443 1622 // concurrency 1444 1623 } $\C{// deallocate stack-based threads, implicit joins before destruction}$ 1445 1624 \end{cfa} 1446 This semantic ensures a thread is started and stopped exactly once, eliminating some programming error, and scales to multiple threads for basic (termination)synchronization.1447 For block allocation to arbitrary depth, including recursion, threads are created /destroyed in a lattice structure (tree with top and bottom).1625 This semantic ensures a thread is started and stopped exactly once, eliminating some programming error, and scales to multiple threads for basic termination synchronization. 1626 For block allocation to arbitrary depth, including recursion, threads are created and destroyed in a lattice structure (tree with top and bottom). 1448 1627 Arbitrary topologies are possible using dynamic allocation, allowing threads to outlive their declaration scope, identical to normal dynamic allocation. 1449 1628 \begin{cfa} 1450 MyT ask* factory( int N ) { ... return `anew( N )`; } $\C{// allocate heap-based threads, implicit start after construction}$1629 MyThread * factory( int N ) { ... return `anew( N )`; } $\C{// allocate heap-based threads, implicit start after construction}$ 1451 1630 int main() { 1452 MyT ask* team = factory( 10 );1631 MyThread * team = factory( 10 ); 1453 1632 // concurrency 1454 ` delete( team );` $\C{// deallocate heap-based threads, implicit joins before destruction}\CRT$1633 `adelete( team );` $\C{// deallocate heap-based threads, implicit joins before destruction}\CRT$ 1455 1634 } 1456 1635 \end{cfa} … … 1493 1672 1494 1673 1495 \subsection{Thread Implementation}1674 \subsection{Thread implementation} 1496 1675 1497 1676 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. 1498 Like coroutines, and for the same design reasons, \CFA provides a custom @thread@ type and a @trait@ to enforce and restrict the t ask-interface functions.1677 Like coroutines, and for the same design reasons, \CFA provides a custom @thread@ type and a @trait@ to enforce and restrict the thread-interface functions. 1499 1678 \begin{cquote} 1500 1679 \begin{tabular}{@{}c@{\hspace{3\parindentlnth}}c@{}} … … 1516 1695 \end{tabular} 1517 1696 \end{cquote} 1518 Like coroutines, the @dtype@ property prevents \emph{implicit} copy operations and the @is_thread@ trait provides no \emph{explicit} copy operations, so threads must be passed by reference (pointer).1519 Similarly, the function definitions ensure there is a statically typed @main@ function that is the thread starting point (first stack frame), a mechanism to get (read)the thread descriptor from its handle, and a special destructor to prevent deallocation while the thread is executing.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 read the thread descriptor from its handle, and a special destructor to prevent deallocation while the thread is executing. 1520 1699 (The qualifier @mutex@ for the destructor parameter is discussed in Section~\ref{s:Monitor}.) 1521 1700 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; 1522 1701 whereas, a thread is scheduling for execution in @main@ immediately after its constructor is run. 1523 No return value or additional parameters are necessary for this function because the @thread@ type allows an arbitrary number of interface functions with corresponding arbitrary typed input /output values.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 and output values. 1524 1703 1525 1704 … … 1527 1706 \label{s:MutualExclusionSynchronization} 1528 1707 1529 Unrestricted nondeterminism is meaningless as there is no way to know when the result is completed without synchronization.1708 Unrestricted nondeterminism is meaningless as there is no way to know when a result is completed and safe to access. 1530 1709 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}. 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). 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}. 1718 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. 1720 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). 1532 1722 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.1723 Hence, a programmer must learn and manipulate two sets of design and programming patterns. 1534 1724 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}. 1538 However, for productivity it is always desirable to use the highest-level construct that provides the necessary efficiency~\cite{Hochstein05}. 1539 A newer approach for restricting non-determinism is transactional memory~\cite{Herlihy93}. 1540 While this approach is pursued in hardware~\cite{Nakaike15} and system languages, like \CC~\cite{Cpp-Transactions}, the performance and feature set is still too restrictive to be the main concurrency paradigm for system languages, which is why it is rejected as the core paradigm for concurrency in \CFA. 1541 1542 One of the most natural, elegant, and efficient mechanisms for mutual exclusion and synchronization for shared-memory systems is the \emph{monitor}. 1543 First proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}, many concurrent programming languages provide monitors as an explicit language construct: \eg Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}. 1544 In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as mutex locks or semaphores to simulate monitors. 1545 For these reasons, \CFA selected monitors as the core high-level concurrency construct, upon which higher-level approaches can be easily constructed. 1546 1547 1548 \subsection{Mutual Exclusion} 1549 1550 A group of instructions manipulating a specific instance of shared data that must be performed atomically is called a \newterm{critical section}~\cite{Dijkstra65}, which is enforced by \newterm{simple mutual-exclusion}. 1551 The generalization is called a \newterm{group critical-section}~\cite{Joung00}, where multiple tasks with the same session use the resource simultaneously and different sessions are segregated, which is enforced by \newterm{complex mutual-exclusion} providing the correct kind and number of threads using a group critical-section. 1552 The readers/writer problem~\cite{Courtois71} is an instance of a group critical-section, where readers share a session but writers have a unique session. 1553 1554 However, many solutions exist for mutual exclusion, which vary in terms of performance, flexibility and ease of use. 1555 Methods range from low-level locks, which are fast and flexible but require significant attention for correctness, to higher-level concurrency techniques, which sacrifice some performance to improve ease of use. 1556 Ease of use comes by either guaranteeing some problems cannot occur, \eg deadlock free, or by offering a more explicit coupling between shared data and critical section. 1557 For example, the \CC @std::atomic<T>@ offers an easy way to express mutual-exclusion on a restricted set of operations, \eg reading/writing, for numerical types. 1558 However, a significant challenge with locks is composability because it takes careful organization for multiple locks to be used while preventing deadlock. 1559 Easing composability is another feature higher-level mutual-exclusion mechanisms can offer. 1560 1561 1562 \subsection{Synchronization} 1563 1564 Synchronization enforces relative ordering of execution, and synchronization tools provide numerous mechanisms to establish these timing relationships. 1565 Low-level synchronization primitives offer good performance and flexibility at the cost of ease of use; 1566 higher-level mechanisms often simplify usage by adding better coupling between synchronization and data, \eg receive-specific versus receive-any thread in message passing or offering specialized solutions, \eg barrier lock. 1567 Often synchronization is used to order access to a critical section, \eg ensuring a waiting writer thread enters the critical section before a calling reader thread. 1568 If the calling reader is scheduled before the waiting writer, the reader has barged. 1569 Barging can result in staleness/freshness problems, where a reader barges ahead of a writer and reads temporally stale data, or a writer barges ahead of another writer overwriting data with a fresh value preventing the previous value from ever being read (lost computation). 1570 Preventing or detecting barging is an involved challenge with low-level locks, which is made easier through higher-level constructs. 1571 This challenge is often split into two different approaches: barging avoidance and prevention. 1572 Algorithms that unconditionally releasing a lock for competing threads to acquire use barging avoidance during synchronization to force a barging thread to wait; 1573 algorithms that conditionally hold locks during synchronization, \eg baton-passing~\cite{Andrews89}, prevent barging completely. 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. 1574 1728 1575 1729 … … 1577 1731 \label{s:Monitor} 1578 1732 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} 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 1598 1758 int i = 0, j = 0, k = 5; 1599 Aint x = { 0 }, y = { 0 }, z = { 5 }; $\C{// no mutex required}$ 1600 ++x; ++y; ++z; $\C{// safe increment by multiple threads}$ 1601 x = 2; y = i; z = k; $\C{// conversions}$ 1602 i = x; j = y; k = z; 1603 \end{cfa} 1604 1605 \CFA monitors have \newterm{multi-acquire} semantics so the thread in the monitor may acquire it multiple times without deadlock, allowing recursion and calling other interface functions. 1606 \begin{cfa} 1607 monitor M { ... } m; 1608 void foo( M & mutex m ) { ... } $\C{// acquire mutual exclusion}$ 1609 void bar( M & mutex m ) { $\C{// acquire mutual exclusion}$ 1610 ... `bar( m );` ... `foo( m );` ... $\C{// reacquire mutual exclusion}$ 1611 } 1612 \end{cfa} 1613 \CFA monitors also ensure the monitor lock is released regardless of how an acquiring function ends (normal or exceptional), and returning a shared variable is safe via copying before the lock is released. 1614 Similar safety is offered by \emph{explicit} mechanisms like \CC RAII; 1615 monitor \emph{implicit} safety ensures no programmer usage errors. 1616 Furthermore, RAII mechanisms cannot handle complex synchronization within a monitor, where the monitor lock may not be released on function exit because it is passed to an unblocking thread; 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; 1617 1803 RAII is purely a mutual-exclusion mechanism (see Section~\ref{s:Scheduling}). 1618 1804 1619 1620 \subsection{Monitor Implementation} 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} 1621 1814 1622 1815 For the same design reasons, \CFA provides a custom @monitor@ type and a @trait@ to enforce and restrict the monitor-interface functions. … … 1638 1831 \end{tabular} 1639 1832 \end{cquote} 1640 The @dtype@ property prevents \emph{implicit} copy operations and the @is_monitor@ trait provides no \emph{explicit} copy operations, so monitors must be passed by reference (pointer). 1641 % Copying a lock is insecure because it is possible to copy an open lock and then use the open copy when the original lock is closed to simultaneously access the shared data. 1642 % Copying a monitor is secure because both the lock and shared data are copies, but copying the shared data is meaningless because it no longer represents a unique entity. 1643 Similarly, the function definitions ensures there is a mechanism to get (read) the monitor descriptor from its handle, and a special destructor to prevent deallocation if a thread using the shared data. 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. 1644 1835 The custom monitor type also inserts any locks needed to implement the mutual exclusion semantics. 1645 1646 1647 \subsection{Mutex Acquisition} 1836 \CFA relies heavily on traits as an abstraction mechanism, so the @mutex@ qualifier prevents coincidentally matching of a monitor trait with a type that is not a monitor, similar to coincidental inheritance where a shape and playing card can both be drawable. 1837 1838 1839 \subsection{Mutex acquisition} 1648 1840 \label{s:MutexAcquisition} 1649 1841 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 { ... } 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 { ... }; 1666 1846 int f1( M & mutex m ); $\C{// single parameter object}$ 1667 1847 int f2( M * mutex m ); $\C{// single or multiple parameter object}$ … … 1669 1849 int f4( stack( M * ) & mutex m ); $\C{// multiple parameters object}$ 1670 1850 \end{cfa} 1671 Function @f1@ has a single parameter object, while @f2@'s indirection could be a single or multi-element array, where static array size is often unknown in C. 1672 Function @f3@ has a multiple object matrix, and @f4@ a multiple object data structure. 1673 While shown shortly, multiple object acquisition is possible, but the number of objects must be statically known. 1674 Therefore, \CFA only acquires one monitor per parameter with at most one level of indirection, excluding pointers as it is impossible to statically determine the size. 1675 1676 For object-oriented monitors, \eg Java, calling a mutex member \emph{implicitly} acquires mutual exclusion of the receiver object, @`rec`.foo(...)@. 1677 \CFA has no receiver, and hence, the explicit @mutex@ qualifier is used to specify which objects acquire mutual exclusion. 1678 A positive consequence of this design decision is the ability to support multi-monitor functions,\footnote{ 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{ 1679 1855 While object-oriented monitors can be extended with a mutex qualifier for multiple-monitor members, no prior example of this feature could be found.} 1680 called \newterm{bulk acquire}.1681 \CFA guarantees acquisition order is consistent across calls to @mutex@ functions using the same monitors as arguments, so acquiring multiple monitorsis safe from deadlock.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 acquire is safe from deadlock. 1682 1858 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. 1683 1859 A \CFA programmer only has to manage when to acquire mutual exclusion; … … 1699 1875 void transfer( BankAccount & `mutex` my, 1700 1876 BankAccount & `mutex` your, int me2you ) { 1701 1877 // bulk acquire 1702 1878 deposit( my, -me2you ); // debit 1703 1879 deposit( your, me2you ); // credit … … 1729 1905 void transfer( BankAccount & my, 1730 1906 BankAccount & your, int me2you ) { 1731 `scoped_lock lock( my.m, your.m );` 1907 `scoped_lock lock( my.m, your.m );` // bulk acquire 1732 1908 deposit( my, -me2you ); // debit 1733 1909 deposit( your, me2you ); // credit … … 1757 1933 \end{figure} 1758 1934 1759 Users can still force the acquiring order by using @mutex@/\lstinline[morekeywords=nomutex]@nomutex@.1935 Users can still force the acquiring order by using or not using @mutex@. 1760 1936 \begin{cfa} 1761 1937 void foo( M & mutex m1, M & mutex m2 ); $\C{// acquire m1 and m2}$ 1762 void bar( M & mutex m1, M & /* nomutex */ m2 ) { $\C{//acquire m1}$1938 void bar( M & mutex m1, M & m2 ) { $\C{// only acquire m1}$ 1763 1939 ... foo( m1, m2 ); ... $\C{// acquire m2}$ 1764 1940 } 1765 void baz( M & /* nomutex */ m1, M & mutex m2 ) { $\C{//acquire m2}$1941 void baz( M & m1, M & mutex m2 ) { $\C{// only acquire m2}$ 1766 1942 ... foo( m1, m2 ); ... $\C{// acquire m1}$ 1767 1943 } … … 1806 1982 % 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. 1807 1983 % Different forms of scheduling include access to processors by threads (see Section~\ref{s:RuntimeStructureCluster}), another is access to a shared resource by a lock or monitor. 1808 This section discusses monitor scheduling for waiting threads eligible for entry, \ie which thread gets the shared resource next. (See Section~\ref{s:RuntimeStructureCluster} for scheduling threads on virtual processors.) 1809 While monitor mutual-exclusion provides safe access to shared data, the monitor data may indicate that a thread accessing it cannot proceed, \eg a bounded buffer may be full/empty so produce/consumer threads must block. 1810 Leaving the monitor and trying again (busy waiting) is impractical for high-level programming. 1811 Monitors eliminate busy waiting by providing synchronization to schedule threads needing access to the shared data, where threads block versus spinning. 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. 1812 1990 Synchronization is generally achieved with internal~\cite{Hoare74} or external~\cite[\S~2.9.2]{uC++} scheduling. 1813 \newterm{Internal scheduling} is characterized by each thread entering the monitor and making an individual decision about proceeding or blocking, while \newterm{external scheduling} is characterized by an entering thread making a decision about proceeding for itself and on behalf of other threads attempting entry. 1814 Finally, \CFA monitors do not allow calling threads to barge ahead of signalled threads, which simplifies synchronization among threads in the monitor and increases correctness. 1815 If barging is allowed, synchronization between a signaller and signallee is difficult, often requiring additional flags and multiple unblock/block cycles. 1816 In fact, signals-as-hints is completely opposite from that proposed by Hoare in the seminal paper on monitors~\cite[p.~550]{Hoare74}. 1991 \newterm{Internal} largely schedules threads located \emph{inside} the monitor and is accomplished using condition variables with signal and wait. 1992 \newterm{External} largely schedules threads located \emph{outside} the monitor and is accomplished with the @waitfor@ statement. 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}. 1817 1999 % \begin{cquote} 1818 2000 % However, we decree that a signal operation be followed immediately by resumption of a waiting program, without possibility of an intervening procedure call from yet a third program. 1819 % It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signal ling program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74}2001 % It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signaling program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74} 1820 2002 % \end{cquote} 1821 Furthermore, \CFA concurrency has no spurious wakeup~\cite[\S~9]{Buhr05a}, which eliminates an implicit form of self barging. 1822 Hence, a \CFA @wait@ statement is not enclosed in a @while@ loop retesting a blocking predicate, which can cause thread starvation due to barging. 1823 1824 Figure~\ref{f:MonitorScheduling} shows general internal/external scheduling (for the bounded-buffer example in Figure~\ref{f:InternalExternalScheduling}). 1825 External calling threads block on the calling queue, if the monitor is occupied, otherwise they enter in FIFO order. 1826 Internal threads block on condition queues via @wait@ and reenter from the condition in FIFO order. 1827 Alternatively, internal threads block on urgent from the @signal_block@ or @waitfor@, and reenter implicitly when the monitor becomes empty, \ie, the thread in the monitor exits or waits. 1828 1829 There are three signalling mechanisms to unblock waiting threads to enter the monitor. 1830 Note, signalling cannot have the signaller and signalled thread in the monitor simultaneously because of the mutual exclusion, so either the signaller or signallee can proceed. 1831 For internal scheduling, threads are unblocked from condition queues using @signal@, where the signallee is moved to urgent and the signaller continues (solid line). 1832 Multiple signals move multiple signallees to urgent until the condition is empty. 1833 When the signaller exits or waits, a thread blocked on urgent is processed before calling threads to prevent barging. 1834 (Java conceptually moves the signalled thread to the calling queue, and hence, allows barging.) 1835 The alternative unblock is in the opposite order using @signal_block@, where the signaller is moved to urgent and the signallee continues (dashed line), and is implicitly unblocked from urgent when the signallee exits or waits. 1836 1837 For external scheduling, the condition queues are not used; 1838 instead threads are unblocked directly from the calling queue using @waitfor@ based on function names requesting mutual exclusion. 1839 (The linear search through the calling queue to locate a particular call can be reduced to $O(1)$.) 1840 The @waitfor@ has the same semantics as @signal_block@, where the signalled thread executes before the signallee, which waits on urgent. 1841 Executing multiple @waitfor@s from different signalled functions causes the calling threads to move to urgent. 1842 External scheduling requires urgent to be a stack, because the signaller expects to execute immediately after the specified monitor call has exited or waited. 1843 Internal scheduling behaves the same for an urgent stack or queue, except for multiple signalling, where the threads unblock from urgent in reverse order from signalling. 1844 If the restart order is important, multiple signalling by a signal thread can be transformed into daisy-chain signalling among threads, where each thread signals the next thread. 1845 We tried both a stack for @waitfor@ and queue for signalling, but that resulted in complex semantics about which thread enters next. 1846 Hence, \CFA uses a single urgent stack to correctly handle @waitfor@ and adequately support both forms of signalling. 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. 1847 2016 1848 2017 \begin{figure} … … 1862 2031 \end{figure} 1863 2032 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 % \item1875 % The signalling thread returns immediately and the signalled thread continues.1876 % \item1877 % The signalling thread continues and the signalled thread is marked for urgent unblocking at the next scheduling point (exit/wait).1878 % \item1879 % 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 1884 2033 \begin{figure} 1885 2034 \centering … … 1893 2042 T elements[10]; 1894 2043 }; 1895 void ?{}( Buffer(T) & buf fer ) with(buffer) {2044 void ?{}( Buffer(T) & buf ) with(buf) { 1896 2045 front = back = count = 0; 1897 2046 } 1898 void insert( Buffer(T) & mutex buffer, T elem ) 1899 with(buffer){1900 if ( count == 10 ) `wait( empty )`; 1901 // insert el em into buffer2047 2048 void insert(Buffer(T) & mutex buf, T elm) with(buf){ 2049 if ( count == 10 ) `wait( empty )`; // full ? 2050 // insert elm into buf 1902 2051 `signal( full )`; 1903 2052 } 1904 T remove( Buffer(T) & mutex buf fer ) with(buffer) {1905 if ( count == 0 ) `wait( full )`; 1906 // remove el em from buffer2053 T remove( Buffer(T) & mutex buf ) with(buf) { 2054 if ( count == 0 ) `wait( full )`; // empty ? 2055 // remove elm from buf 1907 2056 `signal( empty )`; 1908 return el em;2057 return elm; 1909 2058 } 1910 2059 } 1911 2060 \end{cfa} 1912 2061 \end{lrbox} 1913 1914 % \newbox\myboxB1915 % \begin{lrbox}{\myboxB}1916 % \begin{cfa}[aboveskip=0pt,belowskip=0pt]1917 % forall( otype T ) { // distribute forall1918 % 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 ); // forward1927 % void insert( Buffer(T) & mutex buffer, T elem )1928 % with(buffer) {1929 % if ( count == 10 ) `waitfor( remove, buffer )`;1930 % // insert elem into buffer1931 %1932 % }1933 % T remove( Buffer(T) & mutex buffer ) with(buffer) {1934 % if ( count == 0 ) `waitfor( insert, buffer )`;1935 % // remove elem from buffer1936 %1937 % return elem;1938 % }1939 % }1940 % \end{cfa}1941 % \end{lrbox}1942 2062 1943 2063 \newbox\myboxB 1944 2064 \begin{lrbox}{\myboxB} 1945 2065 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2066 forall( otype T ) { // distribute forall 2067 monitor Buffer { 2068 2069 int front, back, count; 2070 T elements[10]; 2071 }; 2072 void ?{}( Buffer(T) & buf ) with(buf) { 2073 front = back = count = 0; 2074 } 2075 T remove( Buffer(T) & mutex buf ); // forward 2076 void insert(Buffer(T) & mutex buf, T elm) with(buf){ 2077 if ( count == 10 ) `waitfor( remove : buf )`; 2078 // insert elm into buf 2079 2080 } 2081 T remove( Buffer(T) & mutex buf ) with(buf) { 2082 if ( count == 0 ) `waitfor( insert : buf )`; 2083 // remove elm from buf 2084 2085 return elm; 2086 } 2087 } 2088 \end{cfa} 2089 \end{lrbox} 2090 2091 \subfloat[Internal scheduling]{\label{f:BBInt}\usebox\myboxA} 2092 \hspace{1pt} 2093 \vrule 2094 \hspace{3pt} 2095 \subfloat[External scheduling]{\label{f:BBExt}\usebox\myboxB} 2096 2097 \caption{Generic bounded buffer} 2098 \label{f:GenericBoundedBuffer} 2099 \end{figure} 2100 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 \centering 2127 \newbox\myboxA 2128 \begin{lrbox}{\myboxA} 2129 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2130 enum RW { READER, WRITER }; 1946 2131 monitor ReadersWriter { 1947 int rcnt, wcnt; // readers/writer using resource 2132 int rcnt, wcnt; // readers/writer using resource 2133 `condition RWers;` 1948 2134 }; 1949 2135 void ?{}( ReadersWriter & rw ) with(rw) { … … 1952 2138 void EndRead( ReadersWriter & mutex rw ) with(rw) { 1953 2139 rcnt -= 1; 2140 if ( rcnt == 0 ) `signal( RWers )`; 1954 2141 } 1955 2142 void EndWrite( ReadersWriter & mutex rw ) with(rw) { 1956 2143 wcnt = 0; 2144 `signal( RWers );` 1957 2145 } 1958 2146 void StartRead( ReadersWriter & mutex rw ) with(rw) { 1959 if ( wcnt > 0 ) `waitfor( EndWrite, rw );` 2147 if ( wcnt !=0 || ! empty( RWers ) ) 2148 `wait( RWers, READER )`; 1960 2149 rcnt += 1; 2150 if ( ! empty(RWers) && `front(RWers) == READER` ) 2151 `signal( RWers )`; // daisy-chain signaling 1961 2152 } 1962 2153 void StartWrite( ReadersWriter & mutex rw ) with(rw) { 1963 if ( wcnt > 0 ) `waitfor( EndWrite, rw );`1964 else while ( rcnt > 0 ) `waitfor( EndRead, rw );` 2154 if ( wcnt != 0 || rcnt != 0 ) `wait( RWers, WRITER )`; 2155 1965 2156 wcnt = 1; 1966 2157 } 1967 1968 2158 \end{cfa} 1969 2159 \end{lrbox} 1970 2160 1971 \subfloat[Generic bounded buffer, internal scheduling]{\label{f:BBInt}\usebox\myboxA} 1972 \hspace{3pt} 2161 \newbox\myboxB 2162 \begin{lrbox}{\myboxB} 2163 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2164 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} 1973 2197 \vrule 1974 2198 \hspace{3pt} 1975 \subfloat[ Readers / writer lock, external scheduling]{\label{f:RWExt}\usebox\myboxB}1976 1977 \caption{ Internal / external scheduling}1978 \label{f: InternalExternalScheduling}2199 \subfloat[External scheduling]{\label{f:RWExt}\usebox\myboxB} 2200 2201 \caption{Readers / writer lock} 2202 \label{f:ReadersWriterLock} 1979 2203 \end{figure} 1980 2204 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. 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}.) 2008 2211 2009 2212 \begin{figure} … … 2019 2222 }; 2020 2223 int girl( DS & mutex ds, int phNo, int ccode ) { 2021 if ( is_empty( Boys[ccode] ) ) {2224 if ( empty( Boys[ccode] ) ) { 2022 2225 wait( Girls[ccode] ); 2023 2226 GirlPhNo = phNo; … … 2046 2249 }; 2047 2250 int girl( DS & mutex ds, int phNo, int ccode ) { 2048 if ( is_empty( Boys[ccode] ) ) { // no compatible2251 if ( empty( Boys[ccode] ) ) { // no compatible 2049 2252 wait( Girls[ccode] ); // wait for boy 2050 2253 GirlPhNo = phNo; // make phone number available … … 2066 2269 \qquad 2067 2270 \subfloat[\lstinline@signal_block@]{\label{f:DatingSignalBlock}\usebox\myboxB} 2068 \caption{Dating service }2069 \label{f:DatingService }2271 \caption{Dating service Monitor} 2272 \label{f:DatingServiceMonitor} 2070 2273 \end{figure} 2071 2274 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. 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. 2080 2298 \begin{cquote} 2081 \begin{tabular}{@{}l@{\hspace{ 3\parindentlnth}}l@{}}2299 \begin{tabular}{@{}l@{\hspace{2\parindentlnth}}l@{}} 2082 2300 \begin{cfa} 2083 2301 monitor M { `condition e`; ... }; … … 2090 2308 & 2091 2309 \begin{cfa} 2092 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); 2310 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); // overload rtn 2093 2311 void rtn$\(_2\)$( M & mutex m1 ); 2094 2312 void bar( M & mutex m1, M & mutex m2 ) { 2095 ... waitfor( `rtn` ); ... // $\LstCommentStyle{waitfor( rtn\(_1\),m1, m2 )}$2096 ... waitfor( `rtn , m1` ); ... // $\LstCommentStyle{waitfor( rtn\(_2\), m1 )}$2313 ... waitfor( `rtn`${\color{red}\(_1\)}$ ); ... // $\LstCommentStyle{waitfor( rtn\(_1\) : m1, m2 )}$ 2314 ... waitfor( `rtn${\color{red}\(_2\)}$ : m1` ); ... 2097 2315 } 2098 2316 \end{cfa} … … 2100 2318 \end{cquote} 2101 2319 For @wait( e )@, the default semantics is to atomically block the signaller and release all acquired mutex parameters, \ie @wait( e, m1, m2 )@. 2102 To override the implicit multi -monitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@.2103 Wait cannot statically verif iesthe 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.2320 To override the implicit multimonitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@. 2321 Wait cannot statically verify the 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. 2105 2323 Finally, a signaller, 2106 2324 \begin{cfa} … … 2109 2327 } 2110 2328 \end{cfa} 2111 must have acquired at least the same locks as the waiting thread signal led 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.2329 must have acquired at least the same locks as the waiting thread signaled from a condition queue to allow the locks to be passed, and hence, prevent barging. 2330 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 multimonitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn : m1 )@. 2333 @waitfor@ does statically verify the monitor types passed are the same as the acquired mutex-parameters of the given function or function pointer, hence the prototype must be accessible. 2116 2334 % When an overloaded function appears in an @waitfor@ statement, calls to any function with that name are accepted. 2117 % The rationale is that members with the same name should perform a similar function, and therefore, all should be eligible to accept a call.2335 % The rationale is that functions with the same name should perform a similar actions, and therefore, all should be eligible to accept a call. 2118 2336 Overloaded functions can be disambiguated using a cast 2119 2337 \begin{cfa} 2120 2338 void rtn( M & mutex m ); 2121 2339 `int` rtn( M & mutex m ); 2122 waitfor( (`int` (*)( M & mutex ))rtn ,m );2123 \end{cfa} 2124 2125 The ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock .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}). 2126 2344 \begin{cfa} 2127 2345 void foo( M & mutex m1, M & mutex m2 ) { 2128 ... wait( `e, m1` ); ... $\C{// release m1, keeping m2 acquired )}$2129 void bar( M & mutex m1, M & mutex m2 ) { $\C{// must acquire m1 and m2 )}$2346 ... wait( `e, m1` ); ... $\C{// release m1, keeping m2 acquired}$ 2347 void bar( M & mutex m1, M & mutex m2 ) { $\C{// must acquire m1 and m2}$ 2130 2348 ... signal( `e` ); ... 2131 2349 \end{cfa} 2132 The @wait@ only releases @m1@ so the signalling thread cannot acquire @m1@ and @m2@ to enter @bar@ and @signal@ the condition. 2133 While deadlock can occur with multiple/nesting acquisition, this is a consequence of locks, and by extension monitors, not being perfectly composable. 2134 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. 2135 2352 2136 2353 2137 2354 \subsection{\texorpdfstring{Extended \protect\lstinline@waitfor@}{Extended waitfor}} 2355 \label{s:ExtendedWaitfor} 2138 2356 2139 2357 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. 2140 For a @waitfor@ clause to be executed, its @when@ must be true and an outstanding call to its corresponding member(s) must exist.2358 For a @waitfor@ clause to be executed, its @when@ must be true and an outstanding call to its corresponding function(s) must exist. 2141 2359 The \emph{conditional-expression} of a @when@ may call a function, but the function must not block or context switch. 2142 If there are multiple acceptable mutex calls, selection occurs top-to-bottom (prioritized)among the @waitfor@ clauses, whereas some programming languages with similar mechanisms accept nondeterministically for this case, \eg Go \lstinline[morekeywords=select]@select@.2143 If some accept guards are true and there are no outstanding calls to these members, the acceptor is blocked until a call to one of these members is made.2360 If there are multiple acceptable mutex calls, selection is prioritized top-to-bottom among the @waitfor@ clauses, whereas some programming languages with similar mechanisms accept nondeterministically for this case, \eg Go \lstinline[morekeywords=select]@select@. 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. 2144 2362 If there is a @timeout@ clause, it provides an upper bound on waiting. 2145 2363 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. 2146 2364 Hence, the terminating @else@ clause allows a conditional attempt to accept a call without blocking. 2147 2365 If both @timeout@ and @else@ clause are present, the @else@ must be conditional, or the @timeout@ is never triggered. 2148 There is also a traditional future wait queue (not shown) (\eg Microsoft (@WaitForMultipleObjects@)), to wait for a specified number of future elements in the queue. 2366 % There is also a traditional future wait queue (not shown) (\eg Microsoft @WaitForMultipleObjects@), to wait for a specified number of future elements in the queue. 2367 Finally, there is a shorthand for specifying multiple functions using the same set of monitors: @waitfor( f, g, h : m1, m2, m3 )@. 2149 2368 2150 2369 \begin{figure} … … 2152 2371 \begin{cfa} 2153 2372 `when` ( $\emph{conditional-expression}$ ) $\C{// optional guard}$ 2154 waitfor( $\emph{mutex- member-name}$ ) $\emph{statement}$ $\C{// action after call}$2373 waitfor( $\emph{mutex-function-name}$ ) $\emph{statement}$ $\C{// action after call}$ 2155 2374 `or` `when` ( $\emph{conditional-expression}$ ) $\C{// any number of functions}$ 2156 waitfor( $\emph{mutex- member-name}$ ) $\emph{statement}$2375 waitfor( $\emph{mutex-function-name}$ ) $\emph{statement}$ 2157 2376 `or` ... 2158 2377 `when` ( $\emph{conditional-expression}$ ) $\C{// optional guard}$ … … 2172 2391 The left example only accepts @mem1@ if @C1@ is true or only @mem2@ if @C2@ is true. 2173 2392 The right example accepts either @mem1@ or @mem2@ if @C1@ and @C2@ are true. 2174 2175 An interesting use of @waitfor@ is accepting the @mutex@ destructor to know when an object is deallocated, \eg assume the bounded buffer is restructred from a monitor to a thread with the following @main@. 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@. 2176 2396 \begin{cfa} 2177 2397 void main( Buffer(T) & buffer ) with(buffer) { 2178 2398 for () { 2179 `waitfor( ^?{} ,buffer )` break;2180 or when ( count != 20 ) waitfor( insert ,buffer ) { ... }2181 or when ( count != 0 ) waitfor( remove ,buffer ) { ... }2399 `waitfor( ^?{} : buffer )` break; 2400 or when ( count != 20 ) waitfor( insert : buffer ) { ... } 2401 or when ( count != 0 ) waitfor( remove : buffer ) { ... } 2182 2402 } 2183 2403 // clean up … … 2192 2412 2193 2413 2194 \subsection{Bulk Barging Prevention}2195 2196 Figure~\ref{f:BulkBargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signal ling semantics.2414 \subsection{Bulk barging prevention} 2415 2416 Figure~\ref{f:BulkBargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signaling semantics. 2197 2417 The complexity begins at the end of the inner @mutex@ statement, where the semantics of internal scheduling need to be extended for multiple monitors. 2198 2418 The problem is that bulk acquire is used in the inner @mutex@ statement where one of the monitors is already acquired. 2199 When the signal ling 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 signal ling and waiting threads W1 and W2 need some subset of monitors @m1@ and @m2@.2419 When the signaling thread reaches the end of the inner @mutex@ statement, it should transfer ownership of @m1@ and @m2@ to the waiting threads to prevent barging into the outer @mutex@ statement by another thread. 2420 However, both the signaling and waiting threads W1 and W2 need some subset of monitors @m1@ and @m2@. 2201 2421 \begin{cquote} 2202 2422 condition c: (order 1) W2(@m2@), W1(@m1@,@m2@)\ \ \ or\ \ \ (order 2) W1(@m1@,@m2@), W2(@m2@) \\ … … 2265 2485 \end{figure} 2266 2486 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 behavio ur of single-monitor scheduling.2268 However, this solution is inefficient if W2 waited first and can beimmediate passed @m2@ when released, while S retains @m1@ until completion of the outer mutex statement.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 behavior 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. 2269 2489 If W1 waited first, the signaller must retain @m1@ amd @m2@ until completion of the outer mutex statement and then pass both to W1. 2270 2490 % Furthermore, there is an execution sequence where the signaller always finds waiter W2, and hence, waiter W1 starves. 2271 To support th is 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 m onitor function/statement, the front waiter on urgent is unblocked if all its monitors are released.2273 Implementing a fast subset check for the necessar y released monitors is important.2491 To support these 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 mutex function or statement, the front waiter on urgent is unblocked if all its monitors are released. 2493 Implementing a fast subset check for the necessarily released monitors is important and discussed in the following sections. 2274 2494 % 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. 2275 2495 2276 2496 2277 \subsection{Loose Object Definitions} 2278 \label{s:LooseObjectDefinitions} 2279 2280 In an object-oriented programming language, a class includes an exhaustive list of operations. 2281 A new class can add members via static inheritance but the subclass still has an exhaustive list of operations. 2282 (Dynamic member adding, \eg JavaScript~\cite{JavaScript}, is not considered.) 2283 In the object-oriented scenario, the type and all its operators are always present at compilation (even separate compilation), so it is possible to number the operations in a bit mask and use an $O(1)$ compare with a similar bit mask created for the operations specified in a @waitfor@. 2284 2285 However, in \CFA, monitor functions can be statically added/removed in translation units, making a fast subset check difficult. 2286 \begin{cfa} 2287 monitor M { ... }; // common type, included in .h file 2288 translation unit 1 2289 void `f`( M & mutex m ); 2290 void g( M & mutex m ) { waitfor( `f`, m ); } 2291 translation unit 2 2292 void `f`( M & mutex m ); $\C{// replacing f and g for type M in this translation unit}$ 2293 void `g`( M & mutex m ); 2294 void h( M & mutex m ) { waitfor( `f`, m ) or waitfor( `g`, m ); } $\C{// extending type M in this translation unit}$ 2295 \end{cfa} 2296 The @waitfor@ statements in each translation unit cannot form a unique bit-mask because the monitor type does not carry that information. 2497 \subsection{\texorpdfstring{\protect\lstinline@waitfor@ Implementation}{waitfor Implementation}} 2498 \label{s:waitforImplementation} 2499 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. 2549 (A possible way to construct a dense mapping is at link or load-time.) 2297 2550 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. 2300 (A possible way to construct a dense mapping is at link or load-time.) 2301 2302 2303 \subsection{Multi-Monitor Scheduling} 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} 2304 2556 \label{s:Multi-MonitorScheduling} 2305 2557 2306 External scheduling, like internal scheduling, becomes significantly more complex for multi -monitor semantics.2558 External scheduling, like internal scheduling, becomes significantly more complex for multimonitor semantics. 2307 2559 Even in the simplest case, new semantics need to be established. 2308 2560 \begin{cfa} … … 2313 2565 The solution is for the programmer to disambiguate: 2314 2566 \begin{cfa} 2315 waitfor( f ,`m2` ); $\C{// wait for call to f with argument m2}$2567 waitfor( f : `m2` ); $\C{// wait for call to f with argument m2}$ 2316 2568 \end{cfa} 2317 2569 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@. 2318 This behavio ur can be extended to the multi-monitor @waitfor@ statement.2570 This behavior can be extended to the multimonitor @waitfor@ statement. 2319 2571 \begin{cfa} 2320 2572 monitor M { ... }; 2321 2573 void f( M & mutex m1, M & mutex m2 ); 2322 void g( M & mutex m1, M & mutex m2 ) { waitfor( f ,`m1, m2` ); $\C{// wait for call to f with arguments m1 and m2}$2574 void g( M & mutex m1, M & mutex m2 ) { waitfor( f : `m1, m2` ); $\C{// wait for call to f with arguments m1 and m2}$ 2323 2575 \end{cfa} 2324 2576 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. 2325 Also, the order of the monitors in a @waitfor@ statement is unimportant.2326 2327 Figure~\ref{f:UnmatchedMutexSets} shows an example where, for internal and external scheduling with multiple monitors, a signalling or accepting thread must match exactly, \ie partial matching results in waiting.2328 For both examples, the set of monitors is disjoint so unblocking is impossible.2577 % Also, the order of the monitors in a @waitfor@ statement must match the order of the mutex parameters. 2578 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. 2329 2581 2330 2582 \begin{figure} … … 2355 2607 } 2356 2608 void g( M1 & mutex m1, M2 & mutex m2 ) { 2357 waitfor( f ,m1, m2 );2609 waitfor( f : m1, m2 ); 2358 2610 } 2359 2611 g( `m11`, m2 ); // block on accept … … 2370 2622 \end{figure} 2371 2623 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 2382 2624 \begin{figure} 2383 2625 \centering … … 2386 2628 2387 2629 struct Msg { int i, j; }; 2388 thread GoRtn { int i; float f; Msg m; };2630 mutex thread GoRtn { int i; float f; Msg m; }; 2389 2631 void mem1( GoRtn & mutex gortn, int i ) { gortn.i = i; } 2390 2632 void mem2( GoRtn & mutex gortn, float f ) { gortn.f = f; } … … 2392 2634 void ^?{}( GoRtn & mutex ) {} 2393 2635 2394 void main( GoRtn & gortn ) with( gortn ) {// thread starts2636 void main( GoRtn & mutex gortn ) with(gortn) { // thread starts 2395 2637 2396 2638 for () { 2397 2639 2398 `waitfor( mem1 ,gortn )` sout | i; // wait for calls2399 or `waitfor( mem2 ,gortn )` sout | f;2400 or `waitfor( mem3 ,gortn )` sout | m.i | m.j;2401 or `waitfor( ^?{} , gortn )` break;2640 `waitfor( mem1 : gortn )` sout | i; // wait for calls 2641 or `waitfor( mem2 : gortn )` sout | f; 2642 or `waitfor( mem3 : gortn )` sout | m.i | m.j; 2643 or `waitfor( ^?{} : gortn )` break; // low priority 2402 2644 2403 2645 } … … 2453 2695 \hspace{3pt} 2454 2696 \subfloat[Go]{\label{f:Gochannel}\usebox\myboxB} 2455 \caption{Direct communication} 2456 \label{f:DirectCommunication} 2697 \caption{Direct versus indirect communication} 2698 \label{f:DirectCommunicationComparison} 2699 2700 \medskip 2701 2702 \begin{cfa} 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} 2457 2729 \end{figure} 2458 2730 … … 2469 2741 void main( Ping & pi ) { 2470 2742 for ( 10 ) { 2471 `waitfor( ping ,pi );`2743 `waitfor( ping : pi );` 2472 2744 `pong( po );` 2473 2745 } … … 2482 2754 for ( 10 ) { 2483 2755 `ping( pi );` 2484 `waitfor( pong ,po );`2756 `waitfor( pong : po );` 2485 2757 } 2486 2758 } … … 2493 2765 % \label{f:pingpong} 2494 2766 % \end{figure} 2495 Note, the ping/pong threads are globally declared, @pi@/@po@, and hence, start (and possibly complete)before the program main starts.2767 Note, the ping/pong threads are globally declared, @pi@/@po@, and hence, start and possibly complete before the program main starts. 2496 2768 \end{comment} 2497 2769 2498 2770 2499 \subsection{Execution Properties} 2500 2501 Table~\ref{t:ObjectPropertyComposition} shows how the \CFA high-level constructs cover 3 fundamental execution properties: thread, stateful function, and mutual exclusion. 2502 Case 1 is a basic object, with none of the new execution properties. 2503 Case 2 allows @mutex@ calls to Case 1 to protect shared data. 2504 Case 3 allows stateful functions to suspend/resume but restricts operations because the state is stackless. 2505 Case 4 allows @mutex@ calls to Case 3 to protect shared data. 2506 Cases 5 and 6 are the same as 3 and 4 without restriction because the state is stackful. 2507 Cases 7 and 8 are rejected because a thread cannot execute without a stackful state in a preemptive environment when context switching from the signal handler. 2508 Cases 9 and 10 have a stackful thread without and with @mutex@ calls. 2509 For situations where threads do not require direct communication, case 9 provides faster creation/destruction by eliminating @mutex@ setup. 2510 2511 \begin{table} 2512 \caption{Object property composition} 2513 \centering 2514 \label{t:ObjectPropertyComposition} 2515 \renewcommand{\arraystretch}{1.25} 2516 %\setlength{\tabcolsep}{5pt} 2517 \begin{tabular}{c|c||l|l} 2518 \multicolumn{2}{c||}{object properties} & \multicolumn{2}{c}{mutual exclusion} \\ 2519 \hline 2520 thread & stateful & \multicolumn{1}{c|}{No} & \multicolumn{1}{c}{Yes} \\ 2521 \hline 2522 \hline 2523 No & No & \textbf{1}\ \ \ aggregate type & \textbf{2}\ \ \ @monitor@ aggregate type \\ 2524 \hline 2525 No & Yes (stackless) & \textbf{3}\ \ \ @generator@ & \textbf{4}\ \ \ @monitor@ @generator@ \\ 2526 \hline 2527 No & Yes (stackful) & \textbf{5}\ \ \ @coroutine@ & \textbf{6}\ \ \ @monitor@ @coroutine@ \\ 2528 \hline 2529 Yes & No / Yes (stackless) & \textbf{7}\ \ \ {\color{red}rejected} & \textbf{8}\ \ \ {\color{red}rejected} \\ 2530 \hline 2531 Yes & Yes (stackful) & \textbf{9}\ \ \ @thread@ & \textbf{10}\ \ @monitor@ @thread@ \\ 2532 \end{tabular} 2533 \end{table} 2771 \subsection{\texorpdfstring{\protect\lstinline@mutex@ Generators / coroutines / threads}{monitor Generators / coroutines / threads}} 2772 2773 \CFA generators, coroutines, and threads can also be @mutex@ (Table~\ref{t:ExecutionPropertyComposition} cases 4, 6, 12) allowing safe \emph{direct communication} with threads, \ie the custom types can have mutex functions that are called by other threads. 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. 2534 2794 2535 2795 … … 2537 2797 2538 2798 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. 2539 Some of these low-level mechanism are used in the \CFA runtime, but we stronglyadvocate using high-level mechanisms whenever possible.2799 Some of these low-level mechanisms are used to build the \CFA runtime, but we always advocate using high-level mechanisms whenever possible. 2540 2800 2541 2801 … … 2551 2811 % 2552 2812 % 2553 % \subsection{User Threads}2813 % \subsection{User threads} 2554 2814 % 2555 2815 % A direct improvement on kernel threads is user threads, \eg Erlang~\cite{Erlang} and \uC~\cite{uC++book}. … … 2566 2826 2567 2827 \begin{comment} 2568 \subsection{Thread Pools}2828 \subsection{Thread pools} 2569 2829 2570 2830 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. 2571 If the jobs are dependent, \ie interact, there is an implicit /explicitdependency graph that ties them together.2831 If the jobs are dependent, \ie interact, there is an implicit dependency graph that ties them together. 2572 2832 While removing direct concurrency, and hence the amount of context switching, thread pools significantly limit the interaction that can occur among jobs. 2573 2833 Indeed, jobs should not block because that also blocks the underlying thread, which effectively means the CPU utilization, and therefore throughput, suffers. … … 2580 2840 \begin{cfa} 2581 2841 struct Adder { 2582 int * row, cols;2842 int * row, cols; 2583 2843 }; 2584 2844 int operator()() { … … 2639 2899 \label{s:RuntimeStructureCluster} 2640 2900 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). 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 2642 2905 The purpose of a cluster is to control the amount of parallelism that is possible among threads, plus scheduling and other execution defaults. 2643 2906 The default cluster-scheduler is single-queue multi-server, which provides automatic load-balancing of threads on processors. 2644 However, the design allows changing the scheduler, \eg multi-queue multi -server with work-stealing/sharing across the virtual processors.2907 However, the design allows changing the scheduler, \eg multi-queue multiserver with work-stealing/sharing across the virtual processors. 2645 2908 If several clusters exist, both threads and virtual processors, can be explicitly migrated from one cluster to another. 2646 2909 No automatic load balancing among clusters is performed by \CFA. … … 2652 2915 2653 2916 2654 \subsection{Virtual Processor}2917 \subsection{Virtual processor} 2655 2918 \label{s:RuntimeStructureProcessor} 2656 2919 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.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. 2658 2921 Programs may use more virtual processors than hardware processors. 2659 2922 On a multiprocessor, kernel threads are distributed across the hardware processors resulting in virtual processors executing in parallel. 2660 (It is possible to use affinity to lock a virtual processor onto a particular hardware processor~\cite{affinityLinux, affinityWindows, affinityFreebsd, affinityNetbsd, affinityMacosx}, which is used when caching issues occur or for heterogeneous hardware processors.)2923 (It is possible to use affinity to lock a virtual processor onto a particular hardware processor~\cite{affinityLinux,affinityWindows}, which is used when caching issues occur or for heterogeneous hardware processors.) %, affinityFreebsd, affinityNetbsd, affinityMacosx 2661 2924 The \CFA runtime attempts to block unused processors and unblock processors as the system load increases; 2662 balancing the workload with processors is difficult because it requires future knowledge, \ie what will the applicat on workload do next.2925 balancing the workload with processors is difficult because it requires future knowledge, \ie what will the application workload do next. 2663 2926 Preemption occurs on virtual processors rather than user threads, via operating-system interrupts. 2664 2927 Thus virtual processors execute user threads, where preemption frequency applies to a virtual processor, so preemption occurs randomly across the executed user threads. … … 2670 2933 \label{s:Implementation} 2671 2934 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.2935 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. 2673 2936 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. 2674 2937 Furthermore, several bulk-acquire operations need a variable amount of memory. 2675 2938 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. 2676 2939 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.2940 In \CFA, ordering of monitor acquisition relies on memory ordering to prevent deadlock~\cite{Havender68}, because all objects have distinct nonoverlapping memory layouts, and mutual-exclusion for a monitor is only defined for its lifetime. 2678 2941 When a mutex call is made, pointers to the concerned monitors are aggregated into a variable-length array and sorted. 2679 2942 This array persists for the entire duration of the mutual exclusion and is used extensively for synchronization operations. … … 2694 2957 2695 2958 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. 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 Microsoftruntime does not support interrupts and on Linux systems, interrupts are complex (see below).2959 This atomic reliance can fail on multicore machines, because execution across cores is nondeterministic. 2960 A different reason for not supporting preemption is that it significantly complicates the runtime system, \eg Windows runtime does not support interrupts and on Linux systems, interrupts are complex (see below). 2698 2961 Preemption is normally handled by setting a countdown timer on each virtual processor. 2699 When the timer expires, an interrupt is delivered, and the interrupthandler 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.2962 When the timer expires, an interrupt is delivered, and its signal handler resets the countdown timer, and if the virtual processor is executing in user code, the signal handler performs a user-level context-switch, or if executing in the language runtime kernel, the preemption is ignored or rolled forward to the point where the runtime kernel context switches back to user code. 2700 2963 Multiple signal handlers may be pending. 2701 2964 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. 2702 2965 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; 2703 2966 therefore, the same signal mask is required for all virtual processors in a cluster. 2704 Because preemption frequency is usually long (1 millisecond) performance cost is negligible. 2705 2706 Linux switched a decade ago from specific to arbitrary process signal-delivery for applications with multiple kernel threads. 2707 \begin{cquote} 2708 A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. 2709 If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which it will deliver the signal. 2710 SIGNAL(7) - Linux Programmer's Manual 2711 \end{cquote} 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. 2712 2971 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). 2713 2972 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. … … 2717 2976 2718 2977 2719 \subsection{Debug Kernel}2720 2721 There are two versions of the \CFA runtime kernel: debug and non -debug.2722 The debugging version has many runtime checks and internal assertions, \eg stack (non-writable)guard page, and checks for stack overflow whenever context switches occur among coroutines and threads, which catches most stack overflows.2723 After a program is debugged, the non -debugging version can be used to significantly decrease space and increase performance.2978 \subsection{Debug kernel} 2979 2980 There are two versions of the \CFA runtime kernel: debug and nondebug. 2981 The debugging version has many runtime checks and internal assertions, \eg stack nonwritable guard page, and checks for stack overflow whenever context switches occur among coroutines and threads, which catches most stack overflows. 2982 After a program is debugged, the nondebugging version can be used to significantly decrease space and increase performance. 2724 2983 2725 2984 … … 2727 2986 \label{s:Performance} 2728 2987 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. 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. 2750 3011 2751 3012 \begin{multicols}{2} 2752 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2753 \begin{cfa} 2754 @thread@ MyThread {}; 2755 void @main@( MyThread & ) {} 3013 \begin{cfa}[xleftmargin=0pt] 3014 `coroutine` MyCoroutine {}; 3015 void ?{}( MyCoroutine & this ) { 3016 #ifdef EAGER 3017 resume( this ); 3018 #endif 3019 } 3020 void main( MyCoroutine & ) {} 2756 3021 int main() { 2757 BENCH( for ( N ) { @MyThread m;@} )2758 sout | result `ns;2759 } 2760 \end{cfa} 2761 \captionof{figure}{\CFA object-creation benchmark}3022 BENCH( for ( N ) { `MyCoroutine c;` } ) 3023 sout | result; 3024 } 3025 \end{cfa} 3026 \captionof{figure}{\CFA creation benchmark} 2762 3027 \label{f:creation} 2763 3028 … … 2765 3030 2766 3031 \vspace*{-16pt} 2767 \captionof{table}{ Object creation comparison (nanoseconds)}2768 \label{t ab:creation}3032 \captionof{table}{Creation comparison (nanoseconds)} 3033 \label{t:creation} 2769 3034 2770 3035 \begin{tabular}[t]{@{}r*{3}{D{.}{.}{5.2}}@{}} 2771 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2772 \CFA Coroutine Lazy & 13.2 & 13.1 & 0.44 \\ 2773 \CFA Coroutine Eager & 531.3 & 536.0 & 26.54 \\ 2774 \CFA Thread & 2074.9 & 2066.5 & 170.76 \\ 2775 \uC Coroutine & 89.6 & 90.5 & 1.83 \\ 2776 \uC Thread & 528.2 & 528.5 & 4.94 \\ 2777 Goroutine & 4068.0 & 4113.1 & 414.55 \\ 2778 Java Thread & 103848.5 & 104295.4 & 2637.57 \\ 2779 Pthreads & 33112.6 & 33127.1 & 165.90 3036 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3037 \CFA generator (1B) & 0.6 & 0.6 & 0.0 \\ 3038 \CFA coroutine lazy (100M) & 13.4 & 13.1 & 0.5 \\ 3039 \CFA coroutine eager (10M) & 144.7 & 143.9 & 1.5 \\ 3040 \CFA thread (10M) & 466.4 & 468.0 & 11.3 \\ 3041 \uC coroutine (10M) & 155.6 & 155.7 & 1.7 \\ 3042 \uC thread (10M) & 523.4 & 523.9 & 7.7 \\ 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 2780 3051 \end{tabular} 2781 3052 \end{multicols} 2782 3053 2783 2784 \paragraph{Context-Switching} 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. 3062 3063 \begin{multicols}{2} 3064 \setlength{\tabcolsep}{3pt} 3065 \begin{cfa}[xleftmargin=0pt] 3066 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*/` ) { 3073 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; 3081 } 3082 int main() { 3083 T t; 3084 wait( m1/*, m2, m3, m4*/ ); 3085 } 3086 \end{cfa} 3087 \vspace*{-8pt} 3088 \captionof{figure}{\CFA Internal-scheduling benchmark} 3089 \label{f:schedint} 3090 3091 \columnbreak 3092 3093 \vspace*{-16pt} 3094 \captionof{table}{Internal-scheduling comparison (nanoseconds)} 3095 \label{t:schedint} 3096 \bigskip 3097 3098 \begin{tabular}{@{}r*{3}{D{.}{.}{5.2}}@{}} 3099 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 3100 \CFA @signal@, 1 monitor (10M) & 364.4 & 364.2 & 4.4 \\ 3101 \CFA @signal@, 2 monitor (10M) & 484.4 & 483.9 & 8.8 \\ 3102 \CFA @signal@, 4 monitor (10M) & 709.1 & 707.7 & 15.0 \\ 3103 \uC @signal@ monitor (10M) & 328.3 & 327.4 & 2.4 \\ 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 3108 \end{tabular} 3109 \end{multicols} 3110 3111 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} 2785 3197 2786 3198 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.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. 2789 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@). 2790 3204 The thread test is using yield to enter and return from the runtime kernel, which is two context switches. 2791 3205 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}. 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. 2793 3219 2794 3220 \begin{multicols}{2} 2795 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2796 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 2797 @coroutine@ C {} c; 2798 void main( C & ) { for ( ;; ) { @suspend;@ } } 3221 \begin{cfa}[xleftmargin=0pt] 3222 `coroutine` C {}; 3223 void main( C & ) { for () { `suspend;` } } 2799 3224 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; 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; 2806 3232 } 2807 3233 \end{cfa} … … 2813 3239 \vspace*{-16pt} 2814 3240 \captionof{table}{Context switch comparison (nanoseconds)} 2815 \label{t ab:ctx-switch}3241 \label{t:ctx-switch} 2816 3242 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 2817 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2818 C function & 1.8 & 1.8 & 0.01 \\ 2819 \CFA generator & 2.4 & 2.2 & 0.25 \\ 2820 \CFA Coroutine & 36.2 & 36.2 & 0.25 \\ 2821 \CFA Thread & 93.2 & 93.5 & 2.09 \\ 2822 \uC Coroutine & 52.0 & 52.1 & 0.51 \\ 2823 \uC Thread & 96.2 & 96.3 & 0.58 \\ 2824 Goroutine & 141.0 & 141.3 & 3.39 \\ 2825 Java Thread & 374.0 & 375.8 & 10.38 \\ 2826 Pthreads Thread & 361.0 & 365.3 & 13.19 3243 \multicolumn{1}{@{}r}{Object(N)\hspace*{10pt}} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 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 2827 3261 \end{tabular} 2828 3262 \end{multicols} 2829 3263 2830 3264 2831 \paragraph{Mutual-Exclusion} 2832 2833 Uncontented mutual exclusion, which frequently occurs, is measured by entering/leaving a critical section. 2834 For monitors, entering and leaving a monitor function is measured. 2835 To put the results in context, the cost of entering a non-inline function and the cost of acquiring and releasing a @pthread_mutex@ lock is also measured. 2836 Figure~\ref{f:mutex} shows the code for \CFA with all results in Table~\ref{tab:mutex}. 2837 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 2838 2839 \begin{multicols}{2} 2840 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2841 \begin{cfa} 2842 @monitor@ M {} m1/*, m2, m3, m4*/; 2843 void __attribute__((noinline)) 2844 do_call( M & @mutex m/*, m2, m3, m4*/@ ) {} 2845 int main() { 2846 BENCH( 2847 for( N ) do_call( m1/*, m2, m3, m4*/ ); 2848 ) 2849 sout | result`ns; 2850 } 2851 \end{cfa} 2852 \captionof{figure}{\CFA acquire/release mutex benchmark} 2853 \label{f:mutex} 2854 2855 \columnbreak 2856 2857 \vspace*{-16pt} 2858 \captionof{table}{Mutex comparison (nanoseconds)} 2859 \label{tab:mutex} 2860 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 2861 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2862 test and test-and-test lock & 19.1 & 18.9 & 0.40 \\ 2863 \CFA @mutex@ function, 1 arg. & 45.9 & 46.6 & 1.45 \\ 2864 \CFA @mutex@ function, 2 arg. & 105.0 & 104.7 & 3.08 \\ 2865 \CFA @mutex@ function, 4 arg. & 165.0 & 167.6 & 5.65 \\ 2866 \uC @monitor@ member rtn. & 54.0 & 53.7 & 0.82 \\ 2867 Java synchronized method & 31.0 & 31.1 & 0.50 \\ 2868 Pthreads Mutex Lock & 33.6 & 32.6 & 1.14 2869 \end{tabular} 2870 \end{multicols} 2871 2872 2873 \paragraph{External Scheduling} 2874 2875 External scheduling is measured using a cycle of two threads calling and accepting the call using the @waitfor@ statement. 2876 Figure~\ref{f:ext-sched} shows the code for \CFA, with results in Table~\ref{tab:ext-sched}. 2877 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 2878 2879 \begin{multicols}{2} 2880 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2881 \vspace*{-16pt} 2882 \begin{cfa} 2883 volatile int go = 0; 2884 @monitor@ M {} m; 2885 thread T {}; 2886 void __attribute__((noinline)) 2887 do_call( M & @mutex@ ) {} 2888 void main( T & ) { 2889 while ( go == 0 ) { yield(); } 2890 while ( go == 1 ) { do_call( m ); } 2891 } 2892 int __attribute__((noinline)) 2893 do_wait( M & @mutex@ m ) { 2894 go = 1; // continue other thread 2895 BENCH( for ( N ) { @waitfor( do_call, m );@ } ) 2896 go = 0; // stop other thread 2897 sout | result`ns; 2898 } 2899 int main() { 2900 T t; 2901 do_wait( m ); 2902 } 2903 \end{cfa} 2904 \captionof{figure}{\CFA external-scheduling benchmark} 2905 \label{f:ext-sched} 2906 2907 \columnbreak 2908 2909 \vspace*{-16pt} 2910 \captionof{table}{External-scheduling comparison (nanoseconds)} 2911 \label{tab:ext-sched} 2912 \begin{tabular}{@{}r*{3}{D{.}{.}{3.2}}@{}} 2913 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} &\multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2914 \CFA @waitfor@, 1 @monitor@ & 376.4 & 376.8 & 7.63 \\ 2915 \CFA @waitfor@, 2 @monitor@ & 491.4 & 492.0 & 13.31 \\ 2916 \CFA @waitfor@, 4 @monitor@ & 681.0 & 681.7 & 19.10 \\ 2917 \uC @_Accept@ & 331.1 & 331.4 & 2.66 2918 \end{tabular} 2919 \end{multicols} 2920 2921 2922 \paragraph{Internal Scheduling} 2923 2924 Internal scheduling is measured using a cycle of two threads signalling and waiting. 2925 Figure~\ref{f:int-sched} shows the code for \CFA, with results in Table~\ref{tab:int-sched}. 2926 Note, the incremental cost of bulk acquire for \CFA, which is largely a fixed cost for small numbers of mutex objects. 2927 Java scheduling is significantly greater because the benchmark explicitly creates multiple thread in order to prevent the JIT from making the program sequential, \ie removing all locking. 2928 2929 \begin{multicols}{2} 2930 \lstset{language=CFA,moredelim=**[is][\color{red}]{@}{@},deletedelim=**[is][]{`}{`}} 2931 \begin{cfa} 2932 volatile int go = 0; 2933 @monitor@ M { @condition c;@ } m; 2934 void __attribute__((noinline)) 2935 do_call( M & @mutex@ a1 ) { @signal( c );@ } 2936 thread T {}; 2937 void main( T & this ) { 2938 while ( go == 0 ) { yield(); } 2939 while ( go == 1 ) { do_call( m ); } 2940 } 2941 int __attribute__((noinline)) 2942 do_wait( M & mutex m ) with(m) { 2943 go = 1; // continue other thread 2944 BENCH( for ( N ) { @wait( c );@ } ); 2945 go = 0; // stop other thread 2946 sout | result`ns; 2947 } 2948 int main() { 2949 T t; 2950 do_wait( m ); 2951 } 2952 \end{cfa} 2953 \captionof{figure}{\CFA Internal-scheduling benchmark} 2954 \label{f:int-sched} 2955 2956 \columnbreak 2957 2958 \vspace*{-16pt} 2959 \captionof{table}{Internal-scheduling comparison (nanoseconds)} 2960 \label{tab:int-sched} 2961 \bigskip 2962 2963 \begin{tabular}{@{}r*{3}{D{.}{.}{5.2}}@{}} 2964 \multicolumn{1}{@{}c}{} & \multicolumn{1}{c}{Median} & \multicolumn{1}{c}{Average} & \multicolumn{1}{c@{}}{Std Dev} \\ 2965 \CFA @signal@, 1 @monitor@ & 372.6 & 374.3 & 14.17 \\ 2966 \CFA @signal@, 2 @monitor@ & 492.7 & 494.1 & 12.99 \\ 2967 \CFA @signal@, 4 @monitor@ & 749.4 & 750.4 & 24.74 \\ 2968 \uC @signal@ & 320.5 & 321.0 & 3.36 \\ 2969 Java @notify@ & 10160.5 & 10169.4 & 267.71 \\ 2970 Pthreads Cond. Variable & 4949.6 & 5065.2 & 363 2971 \end{tabular} 2972 \end{multicols} 2973 2974 2975 \section{Conclusion} 3265 \subsection{Discussion} 3266 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} 2976 3279 2977 3280 Advanced control-flow will always be difficult, especially when there is temporal ordering and nondeterminism. 2978 3281 However, many systems exacerbate the difficulty through their presentation mechanisms. 2979 This paper shows it is possible to present a hierarchy of control-flow features, generator, coroutine, thread, and monitor, providing an integrated set of high-level, efficient, and maintainable control-flow features. 2980 Eliminated from \CFA are spurious wakeup and barging, which are nonintuitive and lead to errors, and having to work with a bewildering set of low-level locks and acquisition techniques. 2981 \CFA high-level race-free monitors and tasks provide the core mechanisms for mutual exclusion and synchronization, without having to resort to magic qualifiers like @volatile@/@atomic@. 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@. 2982 3286 Extending these mechanisms to handle high-level deadlock-free bulk acquire across both mutual exclusion and synchronization is a unique contribution. 2983 3287 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. 2984 3288 The M:N model is judged to be efficient and provide greater flexibility than a 1:1 threading model. 2985 3289 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. 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} 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. 2991 3292 2992 3293 While control flow in \CFA has a strong start, development is still underway to complete a number of missing features. 2993 3294 2994 \paragraph{Flexible Scheduling} 2995 \label{futur:sched} 2996 3295 \medskip 3296 \textbf{Flexible scheduling:} 2997 3297 An important part of concurrency is scheduling. 2998 Different scheduling algorithms can affect performance (both in terms of average and variation).3298 Different scheduling algorithms can affect performance, both in terms of average and variation. 2999 3299 However, no single scheduler is optimal for all workloads and therefore there is value in being able to change the scheduler for given programs. 3000 3300 One solution is to offer various tuning options, allowing the scheduler to be adjusted to the requirements of the workload. 3001 3301 However, to be truly flexible, a pluggable scheduler is necessary. 3002 Currently, the \CFA pluggable scheduler is too simple to handle complex scheduling, \eg quality of service and real-time, where the scheduler must interact with mutex objects to deal with issues like priority inversion~\cite{Buhr00b}. 3003 3004 \paragraph{Non-Blocking I/O} 3005 \label{futur:nbio} 3006 3007 Many modern workloads are not bound by computation but IO operations, a common case being web servers and XaaS~\cite{XaaS} (anything as a service). 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). 3008 3307 These types of workloads require significant engineering to amortizing costs of blocking IO-operations. 3009 At its core, non -blocking I/O is an operating-system level feature queuing IO operations, \eg network operations, and registering for notifications instead of waiting for requests to complete.3308 At its core, nonblocking I/O is an operating-system level feature queuing IO operations, \eg network operations, and registering for notifications instead of waiting for requests to complete. 3010 3309 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. 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 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:} 3018 3316 While monitors offer flexible and powerful concurrency for \CFA, other concurrency tools are also necessary for a complete multi-paradigm concurrency package. 3019 3317 Examples of such tools can include futures and promises~\cite{promises}, executors and actors. … … 3021 3319 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. 3022 3320 3023 \paragraph{Implicit Threading} 3024 \label{futur:implcit} 3025 3026 Basic concurrent (embarrassingly parallel) applications can benefit greatly from implicit concurrency, where sequential programs are converted to concurrent, possibly with some help from pragmas to guide the conversion. 3321 \smallskip 3322 \textbf{Implicit threading:} 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. 3027 3324 This type of concurrency can be achieved both at the language level and at the library level. 3028 3325 The canonical example of implicit concurrency is concurrent nested @for@ loops, which are amenable to divide and conquer algorithms~\cite{uC++book}. 3029 The \CFA language features should make it possible to develop a reasonable number of implicit concurrency mechanism to solve basic HPC data-concurrency problems.3326 The \CFA language features should make it possible to develop a reasonable number of implicit concurrency mechanisms to solve basic HPC data-concurrency problems. 3030 3327 However, implicit concurrency is a restrictive solution with significant limitations, so it can never replace explicit concurrent programming. 3031 3328 … … 3033 3330 \section{Acknowledgements} 3034 3331 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.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. 3037 3334 3038 3335 {% 3039 \fontsize{9bp}{1 2bp}\selectfont%3336 \fontsize{9bp}{11.5bp}\selectfont% 3040 3337 \bibliography{pl,local} 3041 3338 }% -
doc/papers/concurrency/annex/local.bib
r3c64c668 r58fe85a 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, 31 33 year = {2005}, 32 34 pages = {35-35}, 33 month = nov,34 35 } 35 36 … … 58 59 59 60 @manual{Cpp-Transactions, 60 keywords = {C++, Transactional Memory},61 title = {Technical Specification for C++ Extensions for Transactional Memory},62 organization= {International Standard ISO/IEC TS 19841:2015},63 publisher = {American National Standards Institute},64 address = {http://www.iso.org},65 year = 2015,61 keywords = {C++, Transactional Memory}, 62 title = {Tech. Spec. for C++ Extensions for Transactional Memory {ISO/IEC} {TS} 19841:2015}, 63 organization= {International Standard Organization}, 64 address = {Geneva, Switzerland}, 65 year = 2015, 66 note = {\href{https://www.iso.org/standard/66343.html}{https://\-www.iso.org/\-standard/\-66343.html}}, 66 67 } 67 68 … … 109 110 @manual{affinityLinux, 110 111 key = {TBB}, 111 title = "{Linux man page - sched\_setaffinity(2)}" 112 title = "{Linux man page - sched\_setaffinity(2)}", 113 note = {\href{https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html}{https://\-man7.org/\-linux/man-pages/\-man2/sched\_setaffinity.2.html}}, 112 114 } 113 115 114 116 @manual{affinityWindows, 115 title = "{Windows (vs.85) - SetThreadAffinityMask function}" 117 title = "{Windows documentation - SetThreadAffinityMask function}", 118 note = {\href{https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadaffinitymask}{https://\-docs.microsoft.com/\-en-us/\-windows/\-win32/api/\-winbase/\-nf-winbase-setthreadaffinitymask}} 116 119 } 117 120 -
doc/papers/concurrency/examples/Fib.py
r3c64c668 r58fe85a 4 4 while True: 5 5 fn = fn1 + fn2; fn2 = fn1; fn1 = fn; yield fn 6 7 8 6 9 7 f1 = Fib() … … 14 12 # Local Variables: # 15 13 # tab-width: 4 # 16 # compile-command: "python3. 5Fib.py" #14 # compile-command: "python3.7 Fib.py" # 17 15 # End: # -
doc/papers/concurrency/examples/Fib2.c
r3c64c668 r58fe85a 1 1 #include <stdio.h> 2 2 3 void mary() {4 printf( "MARY\n" );5 }6 7 3 #define FIB_INIT { 0 } 8 typedef struct { int next; int fn1, fn2; } Fib;4 typedef struct { int restart; int fn1, fn2; } Fib; 9 5 int fib( Fib * f ) { 10 static void * states[] = { &&s1, &&s2, &&s3 }; 11 goto *states[f->next]; 6 static void * states[] = { &&s0, &&s1, &&s2 }; 7 goto *states[f->restart]; 8 s0: 9 f->fn1 = 0; 10 f->restart = 1; 11 return f->fn1; 12 12 s1: 13 mary();14 f->fn1 = 0;15 f->next = 1;16 return f->fn1;17 s2:18 mary();19 13 f->fn2 = f->fn1; 20 14 f->fn1 = 1; 21 f-> next = 2;15 f->restart = 2; 22 16 return f->fn1; 23 s3:; 24 mary(); 17 s2:; 25 18 int fn = f->fn1 + f->fn2; 26 19 f->fn2 = f->fn1; -
doc/papers/concurrency/examples/Fib2.py
r3c64c668 r58fe85a 1 1 def Fib(): 2 fn1, fn = 0, 12 fn1, fn = 1, 0 3 3 while True: 4 yield fn 14 yield fn 5 5 fn1, fn = fn, fn1 + fn 6 6 … … 12 12 # Local Variables: # 13 13 # tab-width: 4 # 14 # compile-command: "python3. 5Fib2.py" #14 # compile-command: "python3.7 Fib2.py" # 15 15 # End: # -
doc/papers/concurrency/examples/Fib3.c
r3c64c668 r58fe85a 2 2 3 3 typedef struct { 4 int fn1, fn; 5 void * next; 4 int restart, fn1, fn; 6 5 } Fib; 7 #define FibCtor { 1, 0, NULL}6 #define FibCtor { 0, 1, 0 } 8 7 9 8 Fib * comain( Fib * f ) { 10 if ( __builtin_expect(f->next != 0, 1) ) goto *f->next; 11 f->next = &&s1; 9 static void * states[] = {&&s0, &&s1}; 10 goto *states[f->restart]; 11 s0: f->restart = 1; 12 12 for ( ;; ) { 13 13 return f; -
doc/papers/concurrency/examples/FibRefactor.py
r3c64c668 r58fe85a 22 22 # Local Variables: # 23 23 # tab-width: 4 # 24 # compile-command: "python3. 5FibRefactor.py" #24 # compile-command: "python3.7 FibRefactor.py" # 25 25 # End: # -
doc/papers/concurrency/examples/Format.c
r3c64c668 r58fe85a 2 2 3 3 typedef struct { 4 void * next;4 int restart, g, b; 5 5 char ch; 6 int g, b;7 6 } Fmt; 8 7 9 8 void comain( Fmt * f ) { 10 if ( __builtin_expect(f->next != 0, 1) ) goto *f->next; 11 f->next = &&s1; 9 static void * states[] = {&&s0, &&s1}; 10 goto *states[f->restart]; 11 s0: f->restart = 1; 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 return; 16 s1:; while ( f->ch == '\n' ) return; // ignore 15 do { 16 return; s1: ; 17 } while ( f->ch == '\n' ); // ignore 17 18 printf( "%c", f->ch ); // print character 18 19 } … … 24 25 25 26 int main() { 26 Fmt fmt = { NULL};27 Fmt fmt = { 0 }; 27 28 comain( &fmt ); // prime 28 29 for ( ;; ) { -
doc/papers/concurrency/examples/Format.cc
r3c64c668 r58fe85a 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 bg Format.cc" //33 // compile-command: "u++-work -O2 -nodebug Format.cc" // 34 34 // End: // -
doc/papers/concurrency/examples/Format.cfa
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 1 abcdefghijklmnopqrstuvwxyzxxxxxxxxxxxxxx 1 abcdefghijklmnop 2 qrstuvwxyzx 3 xxxxxxxxxxxxx -
doc/papers/concurrency/examples/Format.py
r3c64c668 r58fe85a 4 4 for g in range( 5 ): # groups of 5 blocks 5 5 for b in range( 4 ): # blocks of 4 characters 6 print( (yield), end='' ) # receive from send 6 while True: 7 ch = (yield) # receive from send 8 if '\n' not in ch: 9 break 10 print( ch, end='' ) # receive from send 7 11 print( ' ', end='' ) # block separator 8 12 print() # group separator … … 11 15 print() 12 16 17 input = "abcdefghijklmnop\nqrstuvwx\nyzxxxxxxxxxxxxxx\n" 18 13 19 fmt = Format() 14 20 next( fmt ) # prime generator 15 for i in range( 41 ):16 fmt.send( 'a'); # send to yield21 for i in input: 22 fmt.send( i ); # send to yield 17 23 18 24 # Local Variables: # 19 25 # tab-width: 4 # 20 # compile-command: "python3. 5Format.py" #26 # compile-command: "python3.7 Format.py" # 21 27 # End: # -
doc/papers/concurrency/examples/Format1.c
r3c64c668 r58fe85a 2 2 3 3 typedef struct { 4 void * next;4 int restart, g, b; 5 5 char ch; 6 int g, b;7 6 } Fmt; 8 7 9 8 void format( Fmt * f ) { 10 if ( __builtin_expect(f->next != 0, 1) ) goto *f->next; 11 f->next = &&s1; 9 static void * states[] = {&&s0, &&s1}; 10 goto *states[f->restart]; 11 s0: f->restart = 1; 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: ; 17 if ( f->ch == '\0' ) goto fini; // EOF ? 16 s1: if ( f->ch == '\0' ) goto fini; // EOF ? 18 17 while ( f->ch == '\n' ) return; // ignore 19 printf( "%c", f->ch ); // print character18 // printf( "%c", f->ch ); // print character 20 19 } 21 printf( " " ); // block separator20 // printf( " " ); // block separator 22 21 } 23 printf( "\n" ); // group separator22 // printf( "\n" ); // group separator 24 23 } 25 fini: 26 if ( f->g != 0 || f->b != 0 ) printf( "\n" );24 fini:; 25 // if ( f->g != 0 || f->b != 0 ) printf( "\n" ); 27 26 } 28 27 29 28 int main() { 30 Fmt fmt = { NULL};29 Fmt fmt = { 0 }; 31 30 format( &fmt ); // prime 32 for ( ;; ) { 33 scanf( "%c", &fmt.ch ); // direct read into communication variable 34 if ( feof( stdin ) ) break; 31 fmt.ch = 'a'; 32 for ( long int i = 0; i < 1000000000; i += 1 ) { 33 // scanf( "%c", &fmt.ch ); // direct read into communication variable 34 // if ( feof( stdin ) ) break; 35 35 format( &fmt ); 36 36 } 37 fmt.ch = '\0'; 37 fmt.ch = '\0'; // sentential (EOF) 38 38 format( &fmt ); 39 39 } -
doc/papers/concurrency/examples/PingPong.c
r3c64c668 r58fe85a 2 2 3 3 typedef struct PingPong { 4 int restart; // style 1 5 int N, i; 4 6 const char * name; 5 int N, i;6 7 struct PingPong * partner; 7 void * next; 8 void * next; // style 2 8 9 } PingPong; 9 #define PPCtor( name, N ) { name, N, 0, NULL, NULL } 10 #define PPCtor( name, N ) { 0, N, 0, name, NULL, NULL } 11 10 12 void comain( PingPong * pp ) __attribute__(( noinline )); 11 13 void comain( PingPong * pp ) { 14 #if 0 12 15 if ( __builtin_expect(pp->next != 0, 1) ) goto *pp->next; 13 #if 014 pp->next = &&here;15 asm( "mov %0,%%rdi" : "=m" (pp) );16 asm( "mov %rdi,%rax" );17 #ifndef OPT18 #ifdef PRINT19 asm( "add $16, %rsp" );20 #endif // PRINT21 asm( "popq %rbp" );22 #endif // ! OPT23 24 #ifdef OPT25 #ifdef PRINT26 asm( "popq %rbx" );27 #endif // PRINT28 #endif // OPT29 asm( "jmp comain" );30 here: ;31 #endif // 032 33 16 pp->next = &&cycle; 34 17 for ( ; pp->i < pp->N; pp->i += 1 ) { … … 53 36 cycle: ; 54 37 } // for 38 #endif // 0 39 40 #if 1 41 static void * states[] = {&&s0, &&s1}; 42 goto *states[pp->restart]; 43 s0: pp->restart = 1; 44 for ( ; pp->i < pp->N; pp->i += 1 ) { 45 #ifdef PRINT 46 printf( "%s %d\n", pp->name, pp->i ); 47 #endif // PRINT 48 asm( "mov %0,%%rdi" : "=m" (pp->partner) ); 49 asm( "mov %rdi,%rax" ); 50 #ifndef OPT 51 #ifdef PRINT 52 asm( "add $16, %rsp" ); 53 #endif // PRINT 54 asm( "popq %rbp" ); 55 #endif // ! OPT 56 57 #ifdef OPT 58 #ifdef PRINT 59 asm( "popq %rbx" ); 60 #endif // PRINT 61 #endif // OPT 62 asm( "jmp comain" ); 63 s1: ; 64 } // for 65 #endif // 0 55 66 } 56 67 … … 70 81 // Local Variables: // 71 82 // tab-width: 4 // 72 // compile-command: "gcc- 8-g -DPRINT PingPong.c" //83 // compile-command: "gcc-9 -g -DPRINT PingPong.c" // 73 84 // End: // -
doc/papers/concurrency/examples/Pingpong.py
r3c64c668 r58fe85a 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 while True: 12 n = next( n ) # schedule coroutine 10 n = yield # starting coroutine 11 try: 12 while True: 13 n = next( n ) # schedule coroutine 14 except StopIteration: 15 pass 13 16 14 17 pi = PingPong( "ping", 5 ) 15 18 po = PingPong( "pong", 5 ) 16 next( pi ) # prime17 pi.send( po ) # send partner18 next( po ) # prime19 po.send( pi ) # send partner19 next( pi ) # prime 20 pi.send( po ) # send partner 21 next( po ) # prime 22 po.send( pi ) # send partner 20 23 21 24 s = Scheduler(); 22 next( s ) # prime25 next( s ) # prime 23 26 try: 24 27 s.send( pi ) # start cycle 25 except StopIteration: 26 p rint( "scheduler stop" )28 except StopIteration: # scheduler stopped 29 pass 27 30 print( "stop" ) 28 31 29 32 # Local Variables: # 30 33 # tab-width: 4 # 31 # compile-command: "python3. 5Pingpong.py" #34 # compile-command: "python3.7 Pingpong.py" # 32 35 # End: # -
doc/papers/concurrency/examples/ProdCons.py
r3c64c668 r58fe85a 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 while True: 20 n = next( n ) # schedule coroutine 18 n = yield # starting coroutine 19 try: 20 while True: 21 n = next( n ) # schedule coroutine 22 except StopIteration: 23 pass 21 24 22 25 prod = Prod( 5 ) 23 26 cons = Cons( 5 ) 24 next( prod ) # prime25 prod.send( cons ) # send cons26 next( cons ) # prime27 cons.send( prod ) # send prod27 next( prod ) # prime 28 prod.send( cons ) # send cons 29 next( cons ) # prime 30 cons.send( prod ) # send prod 28 31 29 32 s = Scheduler(); 30 next( s ) # prime33 next( s ) # prime 31 34 try: 32 35 s.send( prod ) # start cycle 33 except StopIteration: 34 p rint( "scheduler stop" )36 except StopIteration: # scheduler stopped 37 pass 35 38 print( "stop" ) 36 39 37 40 # Local Variables: # 38 41 # tab-width: 4 # 39 # compile-command: "python3. 5ProdCons.py" #42 # compile-command: "python3.7 ProdCons.py" # 40 43 # End: # -
doc/papers/concurrency/examples/Refactor.py
r3c64c668 r58fe85a 26 26 # Local Variables: # 27 27 # tab-width: 4 # 28 # compile-command: "python3. 5Refactor.py" #28 # compile-command: "python3.7 Refactor.py" # 29 29 # End: # -
doc/papers/concurrency/figures/FullCoroutinePhases.fig
r3c64c668 r58fe85a 8 8 -2 9 9 1200 2 10 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 4575.000 2437.500 4275 1875 4575 1800 4875 187510 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 5175.000 2437.500 4875 1875 5175 1800 5475 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 4575.000 1537.500 4875 2100 4575 2175 4275 210012 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 5175.000 1537.500 5475 2100 5175 2175 4875 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 207.500 1642.500 4125 1425 3975 1650 4200 187514 5 1 0 1 0 7 50 -1 -1 0.000 0 1 1 0 4807.500 1642.500 4725 1425 4575 1650 4800 1875 15 15 1 1 1.00 45.00 90.00 16 6 1575 1575 2700 2025 16 17 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 17 18 1 1 1.00 45.00 90.00 … … 20 21 1 1 1.00 45.00 90.00 21 22 2175 1575 2400 1800 23 4 1 0 100 0 4 10 0.0000 2 165 300 1725 1950 ping\001 24 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 22 28 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 23 29 1 1 1.00 45.00 90.00 24 3 3001575 3300 180030 3525 1575 3300 1800 25 31 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 26 32 1 1 1.00 45.00 90.00 27 3300 2025 3300 2250 28 4 1 0 100 0 0 10 0.0000 2 105 555 2100 1200 creation\001 29 4 1 0 100 0 4 10 0.0000 2 165 300 1725 1950 ping\001 30 4 1 0 100 0 4 10 0.0000 2 135 360 2475 1950 pong\001 31 4 1 0 100 0 4 10 0.0000 2 165 300 3300 1950 ping\001 32 4 1 0 100 0 4 10 0.0000 2 135 360 3300 2400 pong\001 33 4 1 0 100 0 0 10 0.0000 2 105 675 4575 1200 execution\001 34 4 1 0 100 0 4 10 0.0000 2 165 300 4275 2025 ping\001 35 4 1 0 100 0 4 10 0.0000 2 135 360 4875 2025 pong\001 36 4 1 0 100 0 0 10 0.0000 2 90 420 3300 1200 starter\001 33 3675 1575 3900 1800 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 37 38 4 1 0 100 0 4 10 0.0000 2 165 705 2100 1500 pgm main\001 38 4 1 0 100 0 4 10 0.0000 2 165 705 3300 1500 pgm main\001 39 4 1 0 100 0 4 10 0.0000 2 165 705 4500 1500 pgm main\001 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 -
doc/papers/concurrency/figures/RunTimeStructure.fig
r3c64c668 r58fe85a 8 8 -2 9 9 1200 2 10 6 3 855 2775 4155 292511 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3 930 2850 30 30 3930 2850 3960 288012 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4035 2850 30 30 4035 2850 4065 288010 6 3255 2475 3555 2625 11 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3330 2550 30 30 3330 2550 3360 2580 12 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3435 2550 30 30 3435 2550 3465 2580 13 13 -6 14 6 4 755 3525 5055 367515 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4 830 3600 30 30 4830 3600 4860 363016 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4 935 3600 30 30 4935 3600 4965 363014 6 4155 3225 4455 3375 15 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4230 3300 30 30 4230 3300 4260 3330 16 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 4335 3300 30 30 4335 3300 4365 3330 17 17 -6 18 6 4 650 2775 4950 292519 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4 725 2850 15 15 4725 2850 4740 286520 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4 800 2850 15 15 4800 2850 4815 286521 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4 875 2850 15 15 4875 2850 4890 286518 6 4050 2475 4350 2625 19 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4125 2550 15 15 4125 2550 4140 2565 20 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4200 2550 15 15 4200 2550 4215 2565 21 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4275 2550 15 15 4275 2550 4290 2565 22 22 -6 23 6 3225 2400 3525 255024 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3300 2475 15 15 3300 2475 3315 249025 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3375 2475 15 15 3375 2475 3390 249026 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3450 2475 15 15 3450 2475 3465 249023 6 2625 2100 2925 2250 24 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2700 2175 15 15 2700 2175 2715 2190 25 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2775 2175 15 15 2775 2175 2790 2190 26 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 2850 2175 15 15 2850 2175 2865 2190 27 27 -6 28 6 5475 3450 5625 375029 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3525 15 15 5550 3525 5535 354030 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3600 15 15 5550 3600 5535 361531 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 5550 3675 15 15 5550 3675 5535 369028 6 4875 3150 5025 3450 29 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3225 15 15 4950 3225 4935 3240 30 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3300 15 15 4950 3300 4935 3315 31 1 3 0 1 -1 -1 0 0 20 0.000 1 4.7120 4950 3375 15 15 4950 3375 4935 3390 32 32 -6 33 6 4275 3525 4575 367534 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4350 3600 15 15 4350 3600 4365 361535 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4425 3600 15 15 4425 3600 4440 361536 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 4500 3600 15 15 4500 3600 4515 361533 6 3675 3225 3975 3375 34 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3750 3300 15 15 3750 3300 3765 3315 35 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3825 3300 15 15 3825 3300 3840 3315 36 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3900 3300 15 15 3900 3300 3915 3315 37 37 -6 38 6 2175 4650 7050 4950 39 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 2250 4830 30 30 2250 4830 2280 4860 40 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4200 4800 150 75 4200 4800 4350 4875 41 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3275 4800 100 100 3275 4800 3375 4800 42 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 43 5400 4950 5400 4725 5175 4725 5175 4950 5400 4950 44 2 2 1 1 -1 -1 0 0 -1 3.000 0 0 0 0 0 5 45 6525 4950 6300 4950 6300 4725 6525 4725 6525 4950 46 4 0 -1 0 0 0 10 0.0000 2 105 450 6600 4875 cluster\001 47 4 0 -1 0 0 0 10 0.0000 2 105 660 5475 4875 processor\001 48 4 0 -1 0 0 0 10 0.0000 2 105 555 4425 4875 monitor\001 49 4 0 -1 0 0 0 10 0.0000 2 120 270 3450 4875 task\001 50 4 0 -1 0 0 0 10 0.0000 2 105 660 2325 4875 coroutine\001 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 51 43 -6 52 6 3450 1275 3750 1425 53 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3525 1350 15 15 3525 1350 3540 1365 54 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3600 1350 15 15 3600 1350 3615 1365 55 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3675 1350 15 15 3675 1350 3690 1365 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 56 46 -6 57 6 5550 1275 5850 1425 58 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5625 1350 15 15 5625 1350 5640 1365 59 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5700 1350 15 15 5700 1350 5715 1365 60 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 5775 1350 15 15 5775 1350 5790 1365 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 61 52 -6 62 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 2625 150 150 5550 2625 5700 2625 63 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 3225 150 150 5550 3225 5700 3225 64 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5550 3975 150 150 5550 3975 5700 3975 65 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3525 2850 150 150 3525 2850 3675 2850 66 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4200 2475 150 150 4200 2475 4350 2475 67 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4425 2850 150 150 4425 2850 4575 2850 68 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4650 2475 150 150 4650 2475 4800 2475 69 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3525 3600 150 150 3525 3600 3675 3600 70 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3975 3600 150 150 3975 3600 4125 3600 71 1 3 0 1 0 0 0 0 0 0.000 1 0.0000 3525 3600 30 30 3525 3600 3555 3630 72 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3750 2475 150 150 3750 2475 3900 2625 73 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4875 3600 150 150 4875 3600 5025 3750 74 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 3975 2850 150 150 3975 2850 4125 2850 75 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 7200 2775 150 150 7200 2775 7350 2775 76 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4650 1350 225 150 4650 1350 4875 1500 77 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 5250 1350 225 150 5250 1350 5475 1500 78 1 1 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 1350 225 150 4050 1350 4275 1500 79 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 80 2400 4200 2400 3750 1950 3750 1950 4200 2400 4200 81 2 2 1 1 -1 -1 0 0 -1 4.000 0 0 0 0 0 5 82 6300 4500 6300 1800 3000 1800 3000 4500 6300 4500 83 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 84 5775 2850 5775 2400 5325 2400 5325 2850 5775 2850 85 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 86 5775 4200 5775 3750 5325 3750 5325 4200 5775 4200 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 87 56 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 88 57 1 1 1.00 45.00 90.00 89 5175 3975 5325 397558 6075 3450 6375 3450 90 59 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 91 60 1 1 1.00 45.00 90.00 92 5175 3225 5325 3225 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 93 64 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 94 65 1 1 1.00 45.00 90.00 95 5175 2625 5325 2625 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 96 94 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 97 95 1 1 1.00 45.00 90.00 98 5775 3975 5925 397596 4575 3675 4725 3675 99 97 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 100 98 1 1 1.00 45.00 90.00 101 5775 3225 5925 322599 4575 2925 4725 2925 102 100 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 103 101 1 1 1.00 45.00 90.00 104 5775 2625 5925 2625 105 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 106 5175 3975 5175 2625 102 4575 2325 4725 2325 107 103 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 108 104 1 1 1.00 45.00 90.00 109 5 925 3975 5925 2025105 5175 3675 5325 3675 110 106 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 111 107 1 1 1.00 45.00 90.00 112 5 925 3750 6225 3750108 5175 2925 5325 2925 113 109 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 114 110 1 1 1.00 45.00 90.00 115 3450 2625 3225 2625 111 5175 2325 5325 2325 112 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 113 4575 3675 4575 2325 114 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 115 1 1 1.00 45.00 90.00 116 5325 3675 5325 1725 117 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 118 1 1 1.00 45.00 90.00 119 5325 3450 5625 3450 120 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 121 1 1 1.00 45.00 90.00 122 2850 2325 2625 2325 116 123 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 3 117 124 1 1 1.00 45.00 90.00 118 5 925 2025 4200 2025 4200 2250125 5325 1725 3600 1725 3600 1950 119 126 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 120 3225 2625 3225 3600127 2625 2325 2625 3300 121 128 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 122 129 1 1 1.00 45.00 90.00 123 3075 3600 3375 3600130 2475 3300 2775 3300 124 131 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 125 132 1 1 1.00 45.00 90.00 126 3 675 3600 3825 3600133 3075 3300 3225 3300 127 134 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 128 135 1 1 1.00 45.00 90.00 129 4125 3600 4275 3600136 3525 3300 3675 3300 130 137 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 131 138 1 1 1.00 45.00 90.00 132 4575 3600 4725 3600139 3975 3300 4125 3300 133 140 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 134 141 1 1 1.00 45.00 90.00 135 5025 3600 5175 3600142 4425 3300 4575 3300 136 143 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 137 5 775 3450 5775 3000 5325 3000 5325 3450 5775 3450144 5175 3150 5175 2700 4725 2700 4725 3150 5175 3150 138 145 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 4500146 7500 4200 7500 1500 6000 1500 6000 4200 7500 4200 140 147 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 141 148 1 1 1.00 45.00 90.00 142 6675 3975 6975 3975 143 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 144 1 1 1.00 45.00 90.00 145 7050 2775 6825 2775 149 6450 2475 6225 2475 146 150 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 0 0 2 147 6825 2775 6825 3975 148 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 149 1 1 1.00 45.00 90.00 150 7125 3975 7350 3975 151 2 2 0 1 -1 -1 0 0 -1 0.000 0 0 0 0 0 5 152 7800 4200 7800 3750 7350 3750 7350 4200 7800 4200 153 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 2 154 1 1 1.00 45.00 90.00 155 7800 3975 8025 3975 151 6225 2475 6225 3450 156 152 2 1 0 1 -1 -1 0 0 -1 0.000 0 0 -1 1 0 4 157 153 1 1 1.00 45.00 90.00 158 7875 3975 7875 2325 7200 2325 7200 2550 159 4 1 -1 0 0 0 10 0.0000 2 105 720 5550 4425 Processors\001 160 4 1 -1 0 0 0 10 0.0000 2 120 1005 4200 3225 Blocked Tasks\001 161 4 1 -1 0 0 0 10 0.0000 2 150 870 4200 3975 Ready Tasks\001 162 4 1 -1 0 0 0 10 0.0000 2 135 1095 7350 1725 Other Cluster(s)\001 163 4 1 -1 0 0 0 10 0.0000 2 105 840 4650 1725 User Cluster\001 164 4 1 -1 0 0 0 10 0.0000 2 150 615 2175 3675 Manager\001 165 4 1 -1 0 0 0 10 0.0000 2 105 990 2175 3525 Discrete-event\001 166 4 1 -1 0 0 0 10 0.0000 2 135 795 2175 4350 preemption\001 154 7275 3450 7275 2025 6600 2025 6600 2250 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 -
doc/papers/concurrency/mail
r3c64c668 r58fe85a 10 10 Dear Dr Buhr, 11 11 12 Your manuscript entitled "Concurrency in C ā" has been received by Software:12 Your manuscript entitled "Concurrency in Cforall" 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 ā" to Software: Practice and Experience.43 Many thanks for submitting SPE-18-0205 entitled "Concurrency in Cforall" 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
r3c64c668 r58fe85a 22 22 Software: Practice and Experience Editorial Office 23 23 24 25 26 Date: Tue, 12 Nov 2019 22:25:17 +0000 27 From: Richard Jones <onbehalfof@manuscriptcentral.com> 28 Reply-To: R.E.Jones@kent.ac.uk 29 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 30 Subject: Software: Practice and Experience - Decision on Manuscript ID 31 SPE-19-0219 32 33 12-Nov-2019 34 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 Jones 59 Software: Practice and Experience 60 R.E.Jones@kent.ac.uk 61 62 63 Referee(s)' Comments to Author: 64 65 Reviewing: 1 66 67 Comments to the Author 68 This article presents the design and rationale behind the various 69 threading and synchronization mechanisms of C-forall, a new low-level 70 programming language. This paper is very similar to a companion paper 71 which I have also received: as the papers are similar, so will these 72 reviews be --- in particular any general comments from the other 73 review apply to this paper also. 74 75 As far as I can tell, the article contains three main ideas: an 76 asynchronous execution / threading model; a model for monitors to 77 provide mutual exclusion; and an implementation. The first two ideas 78 are drawn together in Table 1: unfortunately this is on page 25 of 30 79 pages of text. Implementation choices and descriptions are scattered 80 throughout the paper - and the sectioning of the paper seems almost 81 arbitrary. 82 83 The article is about its contributions. Simply adding feature X to 84 language Y isn't by itself a contribution, (when feature X isn't 85 already a contribution). The contribution can be in the design: the 86 motivation, the space of potential design options, the particular 87 design chosen and the rationale for that choice, or the resulting 88 performance. For example: why support two kinds of generators as well 89 as user-level threads? Why support both low and high level 90 synchronization constructs? Similarly I would have found the article 91 easier to follow if it was written top down, presenting the design 92 principles, present the space of language features, justify chosen 93 language features (and rationale) and those excluded, and then present 94 implementation, and performance. 95 96 Then the writing of the article is often hard to follow, to say the 97 least. Two examples: section 3 "stateful functions" - I've some idea 98 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. The 100 top of page 3 throws a whole lot of defintions at the reader 101 "generator" "coroutine" "stackful" "stackless" "symmetric" 102 "asymmetric" without every stopping to define each one --- but then in 103 footnote "C" takes the time to explain what C's "main" function is? I 104 cannot imagine a reader of this paper who doesn't know what "main" is 105 in C; especially if they understand the other concepts already 106 presented in the paper. The start of section 3 then does the same 107 thing: putting up a whole lot of definitions, making distinctions and 108 comparisons, even talking about some runtime details, but the critical 109 definition of a monitor doesn't appear until three pages later, at the 110 start of section 5 on p15, lines 29-34 are a good, clear, description 111 of what a monitor actually is. That needs to come first, rather than 112 being buried again after two sections of comparisons, discussions, 113 implementations, and options that are ungrounded because they haven't 114 told the reader what they are actually talking about. First tell the 115 reader what something is, then how they might use it (as programmers: 116 what are the rules and restrictions) and only then start comparison 117 with other things, other approaches, other languages, or 118 implementations. 119 120 The description of the implementation is similarly lost in the trees 121 without ever really seeing the wood. Figure 19 is crucial here, but 122 it's pretty much at the end of the paper, and comments about 123 implementations are threaded throughout the paper without the context 124 (fig 19) to understand what's going on. The protocol for performance 125 testing may just about suffice for C (although is N constantly ten 126 million, or does it vary for each benchmark) but such evaluation isn't 127 appropriate for garbage-collected or JITTed languages like Java or Go. 128 129 other comments working through the paper - these are mostly low level 130 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 programming 135 languages, such as constructors, destructors, virtuals and simple 136 inheritance." There's no need to quibble about this. Once a language 137 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 a 143 C-forall generator. Fig 1(b) might do: but put it inline instead of 144 the python example - and explain the key rules and restrictions on the 145 construct. Then don't even start to compare with coroutines until 146 you've presented, described and explained your coroutines... 147 p3 I'd probably leave out the various "C" versions unless there are 148 key points to make you can't make in C-forall. All the alternatives 149 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 any 159 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 suspend 163 back because the resumer is on a different stack. These reverse 164 pointers allow suspend to cycle backwards, " I've no idea what is 165 going on here? why should I care? Shouldn't I just be using threads 166 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 a 171 paper about that, and only about that. 172 173 p23 "Loose Object Definitions" - no idea what that means. in that 174 section: you can't leave out JS-style dynamic properties. Even in 175 OOLs that (one way or another) allow separate definitions of methods 176 (like Objective-C, Swift, Ruby, C#) at any time a runtime class has a 177 fixed definition. Quite why the detail about bit mask implementation 178 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: 2 186 187 Comments to the Author 188 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 meant 209 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 oblique 213 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 meant 221 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 means 247 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 example 293 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 better 303 304 p23: line 18: "urgent stack" -- back-reference to where this was explained before 305 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 frequency 321 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 intended 325 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 inlining 329 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 cases 333 334 335 Reviewing: 3 336 337 Comments to the Author 338 # Cforall review 339 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 ## Summary 343 344 * Expand on the motivations for including both generator and coroutines, vs trying to build one atop the other 345 * Expand on the motivations for having Why both symmetric and asymettric coroutines? 346 * Comparison to async-await model adopted by other languages 347 * C#, JS 348 * Rust and its async/await model 349 * Consider performance comparisons against node.js and Rust frameworks 350 * Discuss performance of monitors vs finer-grained memory models and atomic operations found in other languages 351 * Why both internal/external scheduling for synchronization? 352 353 ## Generator/coroutines 354 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 threading 366 367 ### Comparison to atomics overlooks performance 368 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 lacking 374 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 confusing 382 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 work 400 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 ## Performance 404 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 review 412 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 ## Links 416 417 [aa]: https://blog.rust-lang.org/2019/09/30/Async-await-hits-beta.html 418 [zc]: https://aturon.github.io/blog/2016/08/11/futures/ 419 [sq]: https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=db 420 [pt]: https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=plaintext 421 422 423 424 Subject: Re: manuscript SPE-19-0219 425 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 +0000 428 429 Dear Dr Buhr 430 431 Your should have received a decision letter on this today. I am sorry that this 432 has taken so long. Unfortunately SP&E receives a lot of submissions and getting 433 reviewers is a perennial problem. 434 435 Regards 436 Richard 437 438 Peter A. Buhr wrote on 11/11/2019 13:10: 439 > 26-Jun-2019 440 > Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" 441 > has been received by Software: Practice and Experience. It will be given 442 > full consideration for publication in the journal. 443 > 444 > Hi, it has been over 4 months since submission of our manuscript SPE-19-0219 445 > with no response. 446 > 447 > Currently, I am refereeing a paper for IEEE that already cites our prior SP&E 448 > paper and the Master's thesis forming the bases of the SP&E paper under 449 > review. Hence our work is apropos and we want to get it disseminates as soon as 450 > possible. 451 > 452 > [3] A. Moss, R. Schluntz, and P. A. Buhr, "Cforall: Adding modern programming 453 > language features to C," Software - Practice and Experience, vol. 48, 454 > no. 12, pp. 2111-2146, 2018. 455 > 456 > [4] T. Delisle, "Concurrency in C for all," Master's thesis, University of 457 > Waterloo, 2018. [Online]. Available: 458 > https://uwspace.uwaterloo.ca/bitstream/handle/10012/12888 459 460 461 462 Date: Mon, 13 Jan 2020 05:33:15 +0000 463 From: Richard Jones <onbehalfof@manuscriptcentral.com> 464 Reply-To: R.E.Jones@kent.ac.uk 465 To: pabuhr@uwaterloo.ca 466 Subject: Revision reminder - SPE-19-0219 467 468 13-Jan-2020 469 Dear Dr Buhr 470 SPE-19-0219 471 472 This is a reminder that your opportunity to revise and re-submit your 473 manuscript will expire 28 days from now. If you require more time please 474 contact me directly and I may grant an extension to this deadline, otherwise 475 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 Jones 482 Editor, Software: Practice and Experience 483 https://mc.manuscriptcentral.com/spe 484 485 486 487 Date: Wed, 5 Feb 2020 04:22:18 +0000 488 From: Aaron Thomas <onbehalfof@manuscriptcentral.com> 489 Reply-To: speoffice@wiley.com 490 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 491 Subject: SPE-19-0219.R1 successfully submitted 492 493 04-Feb-2020 494 495 Dear Dr Buhr, 496 497 Your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" has 498 been successfully submitted online and is presently being given full 499 consideration for publication in Software: Practice and Experience. 500 501 Your manuscript number is SPE-19-0219.R1. Please mention this number in all 502 future correspondence regarding this submission. 503 504 You can view the status of your manuscript at any time by checking your Author 505 Center after logging into https://mc.manuscriptcentral.com/spe. If you have 506 difficulty using this site, please click the 'Get Help Now' link at the top 507 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 Office 513 514 515 516 Date: Sat, 18 Apr 2020 10:42:13 +0000 517 From: Richard Jones <onbehalfof@manuscriptcentral.com> 518 Reply-To: R.E.Jones@kent.ac.uk 519 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 520 Subject: Software: Practice and Experience - Decision on Manuscript ID 521 SPE-19-0219.R1 522 523 18-Apr-2020 524 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 Richard 547 548 Prof. Richard Jones 549 Software: Practice and Experience 550 R.E.Jones@kent.ac.uk 551 552 553 Referee(s)' Comments to Author: 554 555 Reviewing: 1 556 557 Comments to the Author 558 (A relatively short second review) 559 560 I thank the authors for their revisions and comprehensive response to 561 reviewers' comments --- many of my comments have been successfully 562 addressed by the revisions. Here I'll structure my comments around 563 the main salient points in that response which I consider would 564 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 thus 570 addresses my main concerns about the paper. 571 572 I still have a couple of issues --- perhaps the largest is that it's 573 still not clear at this point in the paper what some of these options 574 are, or crucially how they would be used. I don't know if it's 575 possbile to give high-level examples or use cases to be clear about 576 these up front - or if that would duplicate too much information from 577 later in the paper - either way expanding out the discussion - even if 578 just two a couple of sentences for each row - would help me more. The 579 point is not just to define these categories but to ensure the 580 readers' understanding of these definitons agrees with that used in 581 the paper. 582 583 in a little more detail: 584 585 * 1st para section 2 begs the question: why not support each 586 dimension independently, and let the programmer or library designer 587 combiine features? 588 589 * "execution state" seems a relatively low-level description here. 590 I don't think of e.g. the lambda calculus that way. Perhaps it's as 591 good a term as any. 592 593 * Why must there "be language mechanisms to create, block/unblock, 594 and join with a thread"? There aren't in Smalltalk (although there 595 are in the runtime). Especially given in Cforall those mechanisms 596 are *implicit* on thread creation and destruction? 597 598 * "Case 1 is a function that borrows storage for its state (stack 599 frame/activation) and a thread from its invoker" 600 601 this much makes perfect sense to me, but I don't understand how a 602 non-stateful, non-theaded function can then retain 603 604 "this state across callees, ie, function local-variables are 605 retained on the stack across calls." 606 607 how can it retain function-local values *across calls* when it 608 doesn't have any functional-local state? 609 610 I'm not sure if I see two separate cases here - rougly equivalent 611 to C functions without static storage, and then C functions *with* 612 static storage. I assumed that was the distinction between cases 1 613 & 3; but perhpas the actual distinction is that 3 has a 614 suspend/resume point, and so the "state" in figure 1 is this 615 component of execution state (viz figs 1 & 2), not the state 616 representing the cross-call variables? 617 618 > but such evaluation isn't appropriate for garbage-collected or JITTed 619 languages like Java or Go. 620 621 For JITTed languages in particular, reporting peak performance needs 622 to "warm up" the JIT with a number of iterators before beginning 623 measurement. Actually for JIT's its even worse: see Edd Barrett et al 624 OOPSLA 2017. 625 626 627 628 minor issues: 629 630 * footnote A - I've looked at various other papers & the website to 631 try to understand how "object-oriented" Cforall is - I'm still not 632 sure. This footnote says Cforall has "virtuals" - presumably 633 virtual functions, i.e. dynamic dispatch - and inheritance: that 634 really is OO as far as I (and most OO people) are concerned. For 635 example Haskell doesn't have inheritance, so it's not OO; while 636 CLOS (the Common Lisp *Object* System) or things like Cecil and 637 Dylan are considered OO even though they have "multiple function 638 parameters as receivers", lack "lexical binding between a structure 639 and set of functions", and don't have explicit receiver invocation 640 syntax. Python has receiver syntax, but unlike Java or Smalltalk 641 or C++, method declarations still need to have an explicit "self" 642 receiver parameter. Seems to me that Go, for example, is 643 more-or-less OO with interfaces, methods, and dynamic dispatch (yes 644 also and an explicit receiver syntax but that's not 645 determiniative); while Rust lacks dynamic dispatch built-in. C is 646 not OO as a language, but as you say given it supports function 647 pointers with structures, it does support an OO programm style. 648 649 This is why I again recommend just not buying into this fight: not 650 making any claims about whether Cforall is OO or is not - because 651 as I see it, the rest of the paper doesn't depend on whether 652 Cforall is OO or not. That said: this is just a recommendation, 653 and I won't quibble over this any further. 654 655 * is a "monitor function" the same as a "mutex function"? 656 if so the paper should pick one term; if not, make the distinction clear. 657 658 659 * "As stated on line 1 because state declarations from the generator 660 type can be moved out of the coroutine type into the coroutine main" 661 662 OK sure, but again: *why* would a programmer want to do that? 663 (Other than, I guess, to show the difference between coroutines & 664 generators?) Perhaps another way to put this is that the first 665 para of 3.2 gives the disadvantages of coroutines vs-a-vs 666 generators, briefly describes the extended semantics, but never 667 actualy says why a programmer may want those extended semantics, 668 or how they would benefit. I don't mean to belabour the point, 669 but (generalist?) readers like me would generally benefit from 670 those kinds of discussions about each feature throughout the 671 paper: why might a programmer want to use them? 672 673 674 > p17 if the multiple-monitor entry procedure really is novel, write a paper 675 > about that, and only about that. 676 677 > We do not believe this is a practical suggestion. 678 679 * I'm honestly not trying to be snide here: I'm not an expert on 680 monitor or concurrent implementations. Brinch Hansen's original 681 monitors were single acquire; this draft does not cite any other 682 previous work that I could see. I'm not suggesting that the brief 683 mention of this mechanism necessarily be removed from this paper, 684 but if this is novel (and a clear advance over a classical OO 685 monitor a-la Java which only acquires the distinguished reciever) 686 then that would be worth another paper in itself. 687 688 > * conclusion should conclude the paper, not the related. 689 > We do not understand this comment.if ithis 690 691 My typo: the paper's conclusion should come at the end, after the 692 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: 2 702 703 Comments to the Author 704 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: 3 734 735 Comments to the Author 736 This is the second round of reviewing. 737 738 As in the first review, I found that the paper (and Cforall) contains 739 a lot of really interesting ideas, but it remains really difficult to 740 have a good sense of which idea I should use and when. This applies in 741 different ways to different features from the language: 742 743 * coroutines/generators/threads: here there is 744 some discussion, but it can be improved. 745 * interal/external scheduling: I didn't find any direct comparison 746 between these features, except by way of example. 747 748 I requested similar things in my previous review and I see that 749 content was added in response to those requests. Unfortunately, I'm 750 not sure that I can say it improved the paper's overall read. I think 751 in some sense the additions were "too much" -- I would have preferred 752 something more like a table or a few paragraphs highlighting the key 753 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 quite 757 rushed. 758 759 ## Summary 760 761 I make a number of suggestions below but the two most important 762 I think are: 763 764 * Recommend to shorten the comparison on coroutine/generator/threads 765 in Section 2 to a paragraph with a few examples, or possibly a table 766 explaining the trade-offs between the constructs 767 * Recommend to clarify the relationship between internal/external 768 scheduling -- is one more general but more error-prone or low-level? 769 770 ## Coroutines/generators/threads 771 772 There is obviously a lot of overlap between these features, and in 773 particular between coroutines and generators. As noted in the previous 774 review, many languages have chosen to offer *only* generators, and to 775 build coroutines by stacks of generators invoking one another. 776 777 I believe the newly introduced Section 2 of the paper is trying to 778 motivate why each of these constructs exist, but I did not find it 779 effective. It was dense and difficult to understand. I think the 780 problem is that Section 2 seems to be trying to derive "from first 781 principles" why each construct exists, but I think that a more "top 782 down" approach would be easier to understand. 783 784 In fact, the end of Section 2.1 (on page 5) contains a particular 785 paragraph that embodies this "top down" approach. It starts, 786 "programmers can now answer three basic questions", and thus gives 787 some practical advice for which construct you should use and when. I 788 think giving some examples of specific applications that this 789 paragraph, combined with some examples of cases where each construct 790 was needed, would be a better approach. 791 792 I don't think this compariosn needs to be very long. It seems clear 793 enough that one would 794 795 * prefer generators for simple computations that yield up many values, 796 * prefer coroutines for more complex processes that have significant 797 internal structure, 798 * prefer threads for cases where parallel execution is desired or 799 needed. 800 801 I did appreciate the comparison in Section 2.3 between async-await in 802 JS/Java and generators/coroutines. I agree with its premise that those 803 mechanisms are a poor replacement for generators (and, indeed, JS has 804 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 having 806 read it, I wonder if it is really necessary, since those mechanisms 807 are so different in purpose. 808 809 ## Internal vs external scheduling 810 811 I find the motivation for supporting both internal and external 812 scheduling to be fairly implicit. After several reads through the 813 section, I came to the conclusion that internal scheduling is more 814 expressive than external scheduling, but sometimes less convenient or 815 clear. Is this correct? If not, it'd be useful to clarify where 816 external scheduling is more expressive. 817 818 The same is true, I think, of the `signal_block` function, which I 819 have not encountered before; it seems like its behavior can be modeled 820 with multiple condition variables, but that's clearly more complex. 821 822 One question I had about `signal_block`: what happens if one signals 823 but no other thread is waiting? Does it block until some other thread 824 waits? Or is that user error? 825 826 I would find it very interesting to try and capture some of the 827 properties that make internal vs external scheduling the better 828 choice. 829 830 For example, it seems to me that external scheduling works well if 831 there are only a few "key" operations, but that internal scheduling 832 might be better otherwise, simply because it would be useful to have 833 the ability to name a signal that can be referenced by many 834 methods. Consider the bounded buffer from Figure 13: if it had 835 multiple methods for removing elements, and not just `remove`, then 836 the `waitfor(remove)` call in `insert` might not be sufficient. 837 838 ## Comparison of external scheduling to messaging 839 840 I did enjoy the section comparing external scheduling to Go's 841 messaging mechanism, which I believe is a new addition. 842 843 I believe that one difference between the Go program and the Cforall 844 equivalent is that the Goroutine has an associated queue, so that 845 multiple messages could be enqueued, whereas the Cforall equivalent is 846 effectively a "bounded buffer" of length 1. Is that correct? I think 847 this should be stated explicitly. (Presumably, one could modify the 848 Cforall program to include an explicit vector of queued messages if 849 desired, but you would also be reimplementing the channel 850 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 main 866 function for a `monitor thread`. 867 868 ## Atomic operations and race freedom 869 870 I was glad to see that the paper acknowledged that Cforall still had 871 low-level atomic operations, even if their use is discouraged in favor 872 of higher-level alternatives. 873 874 However, I still feel that the conclusion overstates the value of the 875 contribution here when it says that "Cforall high-level race-free 876 monitors and threads provide the core mechanisms for mutual exclusion 877 and synchronization, without the need for volatile and atomics". I 878 feel confident that Java programmers, for example, would be advised to 879 stick with synchronized methods whenever possible, and it seems to me 880 that they offer similar advantages -- but they sometimes wind up using 881 volatiles for performance reasons. 882 883 I was also confused by the term "race-free" in that sentence. In 884 particular, I don't think that Cforall has any mechanisms for 885 preventing *data races*, and it clearly doesn't prevent "race 886 conditions" (which would bar all sorts of useful programs). I suppose 887 that "race free" here might be referring to the improvements such as 888 removing barging behavior. 889 890 ## Performance comparisons 891 892 In my previous review, I requested comparisons against Rust and 893 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 comparing 897 against the threads found in Rust's standard library, which are 898 essentially a shallow wrapper around pthreads, and hence the 899 performance is quite close to pthread performance (as one would 900 expect). It would perhaps be more interesting to see a comparison 901 built using [tokio] or [async-std], two of the more prominent 902 user-space threading libraries that build on Rust's async-await 903 feature (which operates quite differently than Javascript's 904 async-await, in that it doesn't cause every aync function call to 905 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 in 911 the current revision. 912 913 ## Minor notes and typos 914 915 Several figures used the `with` keyword. I deduced that `with(foo)` 916 permits one to write `bar` instead of `foo.bar`. It seems worth 917 introducing. Apologies if this is stated in the paper, if so I missed 918 it. 919 920 On page 20, section 6.3, "external scheduling and vice versus" should be 921 "external scheduling and vice versa". 922 923 On page 5, section 2.3, the paper states "we content" but it should be 924 "we contend". 925 926 Reviewing: Editor 927 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 using 935 936 937 938 Date: Tue, 16 Jun 2020 13:45:03 +0000 939 From: Aaron Thomas <onbehalfof@manuscriptcentral.com> 940 Reply-To: speoffice@wiley.com 941 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 942 Subject: SPE-19-0219.R2 successfully submitted 943 944 16-Jun-2020 945 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 Office 960 961 962 963 Date: Wed, 2 Sep 2020 20:55:34 +0000 964 From: Richard Jones <onbehalfof@manuscriptcentral.com> 965 Reply-To: R.E.Jones@kent.ac.uk 966 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 967 Subject: Software: Practice and Experience - Decision on Manuscript ID 968 SPE-19-0219.R2 969 970 02-Sep-2020 971 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/3133876 979 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.2464160 982 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 Richard 996 997 Prof. Richard Jones 998 Editor, Software: Practice and Experience 999 R.E.Jones@kent.ac.uk 1000 1001 Referee(s)' Comments to Author: 1002 1003 Reviewing: 1 1004 1005 Comments to the Author 1006 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: 2 1012 1013 Comments to the Author 1014 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 points 1021 * 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 +0000 1027 From: Richard Jones <onbehalfof@manuscriptcentral.com> 1028 Reply-To: R.E.Jones@kent.ac.uk 1029 To: pabuhr@uwaterloo.ca 1030 Subject: Revision reminder - SPE-19-0219.R2 1031 1032 01-Oct-2020 1033 1034 Dear Dr Buhr 1035 1036 SPE-19-0219.R2 1037 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 Jones 1047 Editor, Software: Practice and Experience 1048 1049 https://mc.manuscriptcentral.com/spe 1050 1051 1052 1053 Date: Tue, 6 Oct 2020 15:29:41 +0000 1054 From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com> 1055 Reply-To: speoffice@wiley.com 1056 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 1057 Subject: SPE-19-0219.R3 successfully submitted 1058 1059 06-Oct-2020 1060 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 Office 1075 1076 1077 1078 Date: Thu, 15 Oct 2020 13:48:52 +0000 1079 From: Richard Jones <onbehalfof@manuscriptcentral.com> 1080 Reply-To: R.E.Jones@kent.ac.uk 1081 To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca 1082 Subject: Software: Practice and Experience - Decision on Manuscript ID 1083 SPE-19-0219.R3 1084 1085 15-Oct-2020 1086 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 Richard 1099 1100 Prof. Richard Jones 1101 Editor, Software: Practice and Experience 1102 R.E.Jones@kent.ac.uk 1103 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 +0000 1111 From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com> 1112 Reply-To: speoffice@wiley.com 1113 To: pabuhr@uwaterloo.ca 1114 Subject: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c] 1115 1116 16-Oct-2020 1117 1118 Dear Dr. Buhr, 1119 1120 Manuscript id: SPE-19-0219.R3 1121 Manuscript title: Advanced Control-flow and Concurrency in Cforall 1122 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 document 1129 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 Office 1146 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 +0000 1153 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 Regards 1163 1164 Mayank Roy Chowdhury 1165 Editorial Assistant 1166 Software practice and Experience 1167 ________________________________ 1168 From: Peter A. Buhr <pabuhr@uwaterloo.ca> 1169 Sent: Sunday, October 18, 2020 2:00 PM 1170 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 the 1187 final version of the PDF and source files using the mechanism on the "File 1188 Upload" page and submitted that. 1189 1190 1191 1192 Date: Tue, 20 Oct 2020 13:28:37 +0530 1193 To: "Dr. Peter Buhr" <pabuhr@uwaterloo.ca> 1194 From: jpcms@spi-global.com 1195 Subject: Information: Production Editor Contact Software:Practice and Experience | Advanced Control-flow and Concurrency in C A 1196 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 Wiley 1206 1207 Article ID: SPE_2925 1208 Article DOI: 10.1002/SPE.2925 1209 1210 1211 1212 Date: Tue, 20 Oct 2020 10:33:04 +0000 1213 From: <cs-author@wiley.com> 1214 To: <pabuhr@uwaterloo.ca> 1215 Subject: In Production: Your article accepted in Software: Practice and Experience 1216 1217 Dear Peter Buhr, 1218 1219 Article ID: SPE2925 1220 Article DOI: 10.1002/spe.2925 1221 Internal Article ID: 16922213 1222 Article: Advanced Control-flow and Concurrency in C A 1223 Journal: Software: Practice and Experience 1224 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-new 1232 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 article 1236 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 Services 1240 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 +0000 1246 From: <cs-author@wiley.com> 1247 To: <pabuhr@uwaterloo.ca> 1248 Subject: You have actions to complete in Author Services 1249 1250 Dear Peter Buhr, 1251 1252 Article ID: SPE2925 1253 Article DOI: 10.1002/spe.2925 1254 Internal Article ID: 16922213 1255 Article: Advanced Control-flow and Concurrency in C A 1256 Journal: Software: Practice and Experience 1257 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 Services 1266 1267 1268 1269 Date: Thu, 22 Oct 2020 23:13:07 +0000 1270 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: SPE2925 1277 Article DOI: 10.1002/spe.2925 1278 Internal Article ID: 16922213 1279 Article: Advanced Control-flow and Concurrency in C A 1280 Journal: Software: Practice and Experience 1281 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 Services 1287 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 review 1294 Date: Thu, 5 Nov 2020 02:03:27 +0000 1295 1296 Dear Dr Buhr, 1297 1298 Thank you for letting me know. We will wait for your corrections then. 1299 1300 Best regards, 1301 Joel 1302 1303 Joel Q. Pacaanas 1304 Production Editor 1305 On behalf of Wiley 1306 Manila 1307 We partner with global experts to further innovative research. 1308 1309 E-mail: jpacaanas@wiley.com 1310 Tel: +632 88558618 1311 Fax: +632 5325 0768 1312 1313 -----Original Message----- 1314 From: Peter A. Buhr [mailto:pabuhr@uwaterloo.ca] 1315 Sent: Thursday, November 5, 2020 5:57 AM 1316 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 review 1319 1320 This is an external email. 1321 1322 We appreciate that the COVID-19 pandemic may create conditions for you that 1323 make it difficult for you to review your proof within standard time 1324 frames. If you have any problems keeping to this schedule, please reach out 1325 to me at (SPEproofs@wiley.com) to discuss alternatives. 1326 1327 Hi, 1328 1329 We are in the middle of reading the proofs but it will take a little more 1330 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 review 1338 Date: Fri, 20 Nov 2020 05:27:18 +0000 1339 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=ab7739d5678447fbbe5036f3bcba2445081500061 1345 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 Joel 1352 1353 Joel Q. Pacaanas 1354 Production Editor 1355 On behalf of Wiley 1356 Manila 1357 We partner with global experts to further innovative research. 1358 1359 E-mail: jpacaanas@wiley.com 1360 Tel: +632 88558618 1361 Fax: +632 5325 0768 1362 1363 1364 1365 From: "Wiley Online Proofing" <notifications@eproofing.in> 1366 To: pabuhr@uwaterloo.ca 1367 Cc: SPEproofs@wiley.com 1368 Reply-To: eproofing@wiley.com 1369 Date: 26 Nov 2020 18:57:27 +0000 1370 Subject: Corrections successfully submitted for SPE_EV_SPE2925, Advanced control-flow in Cforall. 1371 1372 Corrections successfully submitted 1373 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=ab7739d5678447fbbe5036f3bcba2445081500061 1379 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
r3c64c668 r58fe85a 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 Access 241 This requires that the resolution scope (see below) is at the type level or 242 has explicate points with names. These are the tables and table names used 243 here. 244 245 The system already knows where to find the virtual table and the object. If 246 the tables have particular identities, or on the user side names, then it is 247 meaningful to check if a binding virtual table is the same* as another. The 248 main use of this is virtual table declarations also give the type they bind 249 and if a binding table matches a known table then the underlyind object in the 250 trait object must be of that type. 251 252 * By identity, by value would work and in some senses be more flexiable. But 253 it would be slower and refering to further away functions would be harder. 254 255 This gives one of the main new features of the hierarchical use of virtual 256 tables (see below); the ability to recover the underlying object. Or a pointer 257 of the approprate type it which both reflects the implementation and gives a 258 convenent way to encode the boolean/conditional aspect of the operation which 259 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. The 262 cast is traditional and would definitely fit if a single pointer repersents 263 a trait object with the virtual table as part of the object. However for a 264 double pointer field access might be more approprate. By this system though 265 it is not the type that is used as the identifier but the virtual table. If 266 there is one table per type than it becomes equivilant again. Otherwise the 267 table has to be used as the identifier and the type is just a result of that 268 which seems important for syntax. 239 269 240 270 Hierarchy … … 482 512 possibly like the one used to create the assertion. 483 513 514 ### Extension: Associated Types Use 515 If the `associated_types.md` proposal is accepted the following trait could 516 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 find 525 the (possibly default) virtual table. It is required to construct instances 526 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 the 535 user has to explicately provide the table's name as it doesn't really have its 536 own type name. If it does it is probably mangled. 537 484 538 ### Virtual Tables as Types 485 539 Here we consider encoding plus the implementation of functions on it to be a … … 560 614 be used in only some of the declarations. 561 615 562 trait combiner fee = (summation_instance, sum);616 trait combiner fee = {summation_instance, sum}; 563 617 trait combiner foe = summation_instance; 564 618 -
doc/refrat/refrat.tex
r3c64c668 r58fe85a 11 11 %% Created On : Wed Apr 6 14:52:25 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Wed Jan 31 17:30:23 201814 %% Update Count : 1 0813 %% Last Modified On : Mon Oct 5 09:02:53 2020 14 %% Update Count : 110 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 30 30 \usepackage{upquote} % switch curled `'" to straight 31 31 \usepackage{calc} 32 \usepackage{xspace}33 32 \usepackage{varioref} % extended references 34 \usepackage{listings} % format program code35 33 \usepackage[flushmargin]{footmisc} % support label/reference in footnote 36 34 \usepackage{latexsym} % \Box glyph 37 35 \usepackage{mathptmx} % better math font with "times" 38 36 \usepackage[usenames]{color} 39 \input{common} % common CFA document macros 40 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 41 \usepackage{breakurl} 42 \renewcommand{\UrlFont}{\small\sf} 43 44 \usepackage[pagewise]{lineno} 45 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 46 \usepackage[firstpage]{draftwatermark} 47 \SetWatermarkLightness{0.9} 48 49 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 50 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 51 % AFTER HYPERREF. 52 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 53 54 \setlength{\topmargin}{-0.45in} % move running title into header 55 \setlength{\headsep}{0.25in} 56 57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 58 59 \CFAStyle % use default CFA format-style 60 \lstnewenvironment{C++}[1][] % use C++ style 61 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®}#1}} 62 {} 63 37 \newcommand{\CFALatin}{} 64 38 % inline code Ā©...Ā© (copyright symbol) emacs: C-q M-) 65 39 % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-. … … 69 43 % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 70 44 % math escape $...$ (dollar symbol) 45 \input{common} % common CFA document macros 46 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 47 \usepackage{breakurl} 48 \renewcommand{\UrlFont}{\small\sf} 49 50 \usepackage[pagewise]{lineno} 51 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 52 \usepackage[firstpage]{draftwatermark} 53 \SetWatermarkLightness{0.9} 54 55 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 56 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 57 % AFTER HYPERREF. 58 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 59 60 \setlength{\topmargin}{-0.45in} % move running title into header 61 \setlength{\headsep}{0.25in} 71 62 72 63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 73 64 65 \CFAStyle % use default CFA format-style 66 \lstnewenvironment{C++}[1][] % use C++ style 67 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}} 68 {} 69 70 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 71 74 72 % Names used in the document. 75 \newcommand{\Version}{\input{ ../../version}}73 \newcommand{\Version}{\input{build/version}} 76 74 \newcommand{\Textbf}[2][red]{{\color{#1}{\textbf{#2}}}} 77 75 \newcommand{\Emph}[2][red]{{\color{#1}\textbf{\emph{#2}}}} -
doc/theses/thierry_delisle_PhD/.gitignore
r3c64c668 r58fe85a 8 8 9 9 comp_II/build/ 10 comp_II/img/*.fig.bak 10 11 comp_II/comp_II.pdf 11 12 comp_II/comp_II.ps 13 comp_II/presentation.pdf 14 15 thesis/build/ 16 thesis/fig/*.fig.bak 17 thesis/thesis.pdf 18 thesis/thesis.ps 12 19 13 20 !Makefile -
doc/theses/thierry_delisle_PhD/comp_II/Makefile
r3c64c668 r58fe85a 2 2 3 3 Build = build 4 Figures = figures4 Figures = img 5 5 Macros = ../../../LaTeXmacros 6 6 TeXLIB = .:${Macros}:${Build}:../../../bibliography: … … 12 12 13 13 ## Define the text source files. 14 15 SOURCES = ${addsuffix .tex, \16 comp_II \17 }18 19 14 FIGURES = ${addsuffix .tex, \ 15 emptybit \ 16 emptytree \ 17 emptytls \ 18 resize \ 20 19 } 21 20 22 21 PICTURES = ${addsuffix .pstex, \ 22 base \ 23 empty \ 24 system \ 23 25 } 24 26 … … 30 32 31 33 ## Define the documents that need to be made. 34 all: comp_II.pdf presentation.pdf 35 comp_II.pdf: ${FIGURES} ${PICTURES} 36 presentation.pdf: presentationstyle.sty base.dark.pstex empty.dark.pstex system.dark.pstex 32 37 33 DOCUMENT = comp_II.pdf 38 DOCUMENT = comp_II.pdf presentation.pdf 34 39 BASE = ${basename ${DOCUMENT}} 35 40 … … 45 50 # File Dependencies # 46 51 47 ${DOCUMENT} : ${BASE}.ps 52 %.pdf : build/%.ps | ${Build} 48 53 ps2pdf $< 49 54 50 ${BASE}.ps : ${BASE}.dvi 51 dvips $ {Build}/$< -o $@55 build/%.ps : build/%.dvi | ${Build} 56 dvips $< -o $@ 52 57 53 ${BASE}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} \ 54 ${Macros}/common.tex ${Macros}/indexstyle ../../../bibliography/pl.bib \ 55 local.bib glossary.tex | ${Build} 58 build/%.dvi : %.tex Makefile | ${Build} 56 59 # Must have *.aux file containing citations for bibtex 57 if [ ! -r ${basename $@}.aux ] ; then ${LaTeX} $ {basename $@}.tex; fi58 -${BibTeX} ${ Build}/${basename $@}60 if [ ! -r ${basename $@}.aux ] ; then ${LaTeX} $< ; fi 61 -${BibTeX} ${basename $@} 59 62 # Some citations reference others so run again to resolve these citations 60 ${LaTeX} $ {basename $@}.tex61 -${BibTeX} ${ Build}/${basename $@}63 ${LaTeX} $< 64 -${BibTeX} ${basename $@} 62 65 # Make index from *.aux entries and input index at end of document 63 makeglossaries -q -s ${Build}/${basename $@}.ist ${Build}/${basename $@}66 -makeglossaries -q -s ${basename $@}.ist ${basename $@} 64 67 # Run again to finish citations 65 ${LaTeX} $ {basename $@}.tex68 ${LaTeX} $< 66 69 67 70 ## Define the default recipes. … … 70 73 mkdir -p ${Build} 71 74 72 %.tex : %.fig${Build}75 %.tex : img/%.fig | ${Build} 73 76 fig2dev -L eepic $< > ${Build}/$@ 74 77 75 %.ps : %.fig | ${Build}78 %.ps : img/%.fig | ${Build} 76 79 fig2dev -L ps $< > ${Build}/$@ 77 80 78 %.pstex : %.fig | ${Build}81 %.pstex : img/%.fig | ${Build} 79 82 fig2dev -L pstex $< > ${Build}/$@ 83 fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t 84 85 ## pstex with inverted colors 86 %.dark.pstex : img/%.fig Makefile | ${Build} 87 fig2dev -L pstex $< > ${Build}/$@ 88 sed -i 's/\/col-1 {0 setgray} bind def/\/col-1 {1 setgray} bind def/g' ${Build}/$@ 89 sed -i 's/\/col0 {0.000 0.000 0.000 srgb} bind def/\/col0 {1.000 1.000 1.000 srgb} bind def/g' ${Build}/$@ 90 sed -i 's/\/col7 {1.000 1.000 1.000 srgb} bind def/\/col7 {0.000 0.000 0.000 srgb} bind def/g' ${Build}/$@ 80 91 fig2dev -L pstex_t -p ${Build}/$@ $< > ${Build}/$@_t 81 92 -
doc/theses/thierry_delisle_PhD/comp_II/comp_II.tex
r3c64c668 r58fe85a 1 \documentclass[11pt,fullpage]{article} 1 \documentclass[11pt]{article} 2 \usepackage{fullpage} 2 3 \usepackage[T1]{fontenc} 3 4 \usepackage[utf8]{inputenc} 4 \usepackage{listings} % for code listings5 5 \usepackage{xspace} 6 6 \usepackage{xcolor} 7 7 \usepackage{graphicx} 8 \usepackage[hidelinks]{hyperref} 8 \usepackage{epic,eepic} 9 \usepackage{listings} % for code listings 9 10 \usepackage{glossaries} 10 11 \usepackage{textcomp} 11 \usepackage{geometry}12 13 12 % cfa macros used in the document 14 13 \input{common} 14 15 \setlist{topsep=6pt,parsep=0pt} % global reduce spacing between points 16 \newcommand{\uC}{$\mu$\CC} 17 \usepackage[hidelinks]{hyperref} 18 \setlength{\abovecaptionskip}{5pt plus 3pt minus 2pt} 19 \lstMakeShortInline$% % single-character for \lstinline 20 %\usepackage[margin=1in]{geometry} 21 %\usepackage{float} 22 15 23 \input{glossary} 16 24 … … 24 32 25 33 \author{ 26 \huge Thierry Delisle \ \27 \Large \ vspace*{0.1in} \texttt{tdelisle@uwaterloo.ca} \\34 \huge Thierry Delisle \vspace*{5pt} \\ 35 \Large \texttt{tdelisle@uwaterloo.ca} \vspace*{5pt} \\ 28 36 \Large Cheriton School of Computer Science \\ 29 37 \Large University of Waterloo … … 36 44 \begin{document} 37 45 \maketitle 46 \thispagestyle{empty} 38 47 \cleardoublepage 39 48 40 49 \newcommand{\cit}{\textsuperscript{[Citation Needed]}\xspace} 41 \newcommand{\TODO}{ ~\newline{\large\bf\color{red} TODO :}\xspace}50 \newcommand{\TODO}{{\large\bf\color{red} TODO: }\xspace} 42 51 43 52 % =============================================================================== … … 51 60 \section{Introduction} 52 61 \subsection{\CFA and the \CFA concurrency package} 53 \CFA\cit is a modern, polymorphic, non-object-oriented, backwards-compatible extension of the C programming language. It aims to add high productivity features while maintaning the predictible performance of C. As such concurrency in \CFA\cit aims to offer simple and safe high-level tools while still allowing performant code. Concurrent code is written in the syncrhonous programming paradigm but uses \glspl{uthrd} in order to achieve the simplicity and maintainability of synchronous programming without sacrificing the efficiency of asynchronous programing. As such the \CFA scheduler is a user-level scheduler that maps \glspl{uthrd} onto \glspl{kthrd}. 54 55 The goal of this research is to produce a scheduler that is simple to use and offers acceptable performance in all cases. Here simplicity does not refer to the API but to how much scheduling concerns programmers need to take into account when using the \CFA concurrency package. Therefore, the main goal of this proposal is as follows : 62 \CFA~\cite{Moss18} is a modern, polymorphic, non-object-oriented, concurrent, backwards-compatible extension of the C programming language. 63 It aims to add high-productivity features while maintaining the predictable performance of C. 64 As such, concurrency in \CFA~\cite{Delisle19} aims to offer simple and safe high-level tools while still allowing performant code. 65 \CFA concurrent code is written in the synchronous programming paradigm but uses \glspl{uthrd} to achieve the simplicity and maintainability of synchronous programming without sacrificing the efficiency of asynchronous programming. 66 As such, the \CFA \newterm{scheduler} is a preemptive user-level scheduler that maps \glspl{uthrd} onto \glspl{kthrd}. 67 68 \subsection{Scheduling} 69 \newterm{Scheduling} occurs when execution switches from one thread to another, where the second thread is implicitly chosen by the scheduler. 70 This scheduling is an indirect handoff, as opposed to generators and coroutines that explicitly switch to the next generator and coroutine respectively. 71 The cost of switching between two threads for an indirect handoff has two components: 72 \begin{enumerate} 73 \item 74 the cost of actually context-switching, \ie changing the relevant registers to move execution from one thread to the other, 75 \item 76 and the cost of scheduling, \ie deciding which thread to run next among all the threads ready to run. 77 \end{enumerate} 78 The first cost is generally constant\footnote{Affecting the constant context-switch cost is whether it is done in one step, where the first thread schedules the second, or in two steps, where the first thread context switches to a third scheduler thread.}, while the scheduling cost can vary based on the system state. 79 Adding multiple \glspl{kthrd} does not fundamentally change the scheduler semantics or requirements, it simply adds new correctness requirements, \ie \newterm{linearizability}\footnote{Meaning however fast the CPU threads run, there is an equivalent sequential order that gives the same result.}, and a new dimension to performance: scalability, where scheduling cost also depends on contention. 80 The more threads switch, the more the administration cost of scheduling becomes noticeable. 81 It is therefore important to build a scheduler with the lowest possible cost and latency. 82 Another important consideration is \newterm{fairness}. 83 In principle, scheduling should give the illusion of perfect fairness, where all threads ready to run are running \emph{simultaneously}. 84 In practice, there can be advantages to unfair scheduling, similar to the express cash register at a grocery store. 85 While the illusion of simultaneity is easier to reason about, it can break down if the scheduler allows too much unfairness. 86 Therefore, the scheduler should offer as much fairness as needed to guarantee eventual progress, but use unfairness to help performance. 87 88 \subsection{Research Goal} 89 The goal of this research is to produce a scheduler that is simple for programmers to understand and offers good general performance. 90 Here understandability does not refer to the API but to how much scheduling concerns programmers need to take into account when writing a \CFA concurrent package. 91 Therefore, the main consequence of this goal is : 56 92 \begin{quote} 57 The \CFA scheduler should be \emph{viable} for anyworkload.93 The \CFA scheduler should be \emph{viable} for \emph{any} workload. 58 94 \end{quote} 59 95 60 This objective includes producing a scheduling strategy with minimal fairness guarantees, creating an abstraction layer over the operating system to handle kernel-threads spinning unnecessarily and hide blocking I/O operations and, writing sufficient library tools to allow developpers to properly use the scheduler. 61 62 % =============================================================================== 63 % =============================================================================== 64 65 \section{Scheduling for \CFA} 66 While the \CFA concurrency package doesn't have any particular scheduling needs beyond those of any concurrency package which uses \glspl{uthrd}, it is important that the default \CFA Scheduler be viable in general. Indeed, since the \CFA Scheduler does not target any specific workloads, it is unrealistic to demand that it use the best scheduling strategy in all cases. However, it should offer a viable ``out of the box'' solution for most scheduling problems so that programmers can quickly write performant concurrent without needed to think about which scheduling strategy is more appropriate for their workload. Indeed, only programmers with exceptionnaly high performance requirements should need to write their own scheduler. More specifically, two broad types of schedulering strategies should be avoided in order to avoid penalizing certain types of workloads : feedback-based and priority schedulers. 96 For a general-purpose scheduler, it is impossible to produce an optimal algorithm as that requires knowledge of the future behaviour of threads. 97 As such, scheduling performance is generally either defined by a best-case scenario, \ie a workload to which the scheduler is tailored, or a worst-case scenario, \ie the scheduler behaves no worse than \emph{X}. 98 For this proposal, the performance is evaluated using the second approach to allow \CFA programmers to rely on scheduling performance. 99 Because there is no optimal scheduler, ultimately \CFA may allow programmers to write their own scheduler; but that is not the subject of this proposal, which considers only the default scheduler. 100 As such, it is important that only programmers with exceptionally high performance requirements should need to write their own scheduler and replace the scheduler in this proposal. 101 102 To achieve the \CFA scheduling goal includes: 103 \begin{enumerate} 104 \item producing a scheduling strategy with sufficient fairness guarantees, 105 \item creating an abstraction layer over the operating system to handle kernel-threads spinning unnecessarily, 106 \item scheduling blocking I/O operations, 107 \item and writing sufficient library tools to allow developers to indirectly use the scheduler, either through tuning knobs in the default scheduler or replacing the default scheduler. 108 \end{enumerate} 109 110 % =============================================================================== 111 % =============================================================================== 112 113 \section{\CFA Scheduling} 114 To schedule user-level threads across all workloads, the scheduler has a number of requirements: 115 116 \paragraph{Correctness} As with any other concurrent data structure or algorithm, the correctness requirement is paramount. 117 The scheduler cannot allow threads to be dropped from the ready queue, \ie scheduled but never run, or be executed multiple times when only being scheduled once. 118 Since \CFA concurrency has no spurious wake up, this definition of correctness also means the scheduler should have no spurious wake up. 119 The \CFA scheduler must be correct. 120 121 \paragraph{Performance} The performance of a scheduler can generally be measured in terms of scheduling cost, scalability and latency. 122 \newterm{Scheduling cost} is the cost to switch from one thread to another, as mentioned above. 123 For compute-bound concurrent applications with little context switching, the scheduling cost is negligible. 124 For applications with high context-switch rates, scheduling cost can begin to dominating the cost. 125 \newterm{Scalability} is the cost of adding multiple kernel threads. 126 It can increase the time for scheduling because of contention from the multiple threads accessing shared resources, \eg a single ready queue. 127 Finally, \newterm{tail latency} is service delay and relates to thread fairness. 128 Specifically, latency measures how long a thread waits to run once scheduled and is evaluated by the worst case. 129 The \CFA scheduler should offer good performance for all three metrics. 130 131 \paragraph{Fairness} Like performance, this requirement has several aspects : eventual progress, predictability and performance reliability. 132 \newterm{Eventual progress} guarantees every scheduled thread is eventually run, \ie prevent starvation. 133 As a hard requirement, the \CFA scheduler must guarantee eventual progress, otherwise the above-mentioned illusion of simultaneous execution is broken and the scheduler becomes much more complex to reason about. 134 \newterm{Predictability} and \newterm{reliability} mean similar workloads achieve similar performance so programmer execution intuition is respected. 135 For example, a thread that yields aggressively should not run more often than other threads. 136 While this is intuitive, it does not hold true for many work-stealing or feedback based schedulers. 137 The \CFA scheduler must guarantee eventual progress, should be predictable, and offer reliable performance. 138 139 \paragraph{Efficiency} Finally, efficient usage of CPU resources is also an important requirement and is discussed in depth towards the end of the proposal. 140 \newterm{Efficiency} means avoiding using CPU cycles when there are no threads to run (to conserve energy), and conversely, using as many available CPU cycles when the workload can benefit from it. 141 Balancing these two states is where the complexity lies. 142 The \CFA scheduler should be efficient with respect to the underlying (shared) computer. 143 144 \bigskip To achieve these requirements, I can reject two broad types of scheduling strategies : feedback-based and priority schedulers. 67 145 68 146 \subsection{Feedback-Based Schedulers} 69 Many operating systems use schedulers based on feadback loops in some form, they measure how much CPU a particular thread has used\footnote{Different metrics can be used to here but it is not relevant to the discussion.} and schedule threads based on this metric. These strategies are sensible for operating systems but rely on two assumptions on the workload : 70 71 \begin{enumerate} 72 \item Threads live long enough to be scheduled many times. 73 \item Cooperation among all threads is not simply infeasible, it is a security risk. 74 \end{enumerate} 75 76 While these two assumptions generally hold for operating systems, they may not for \CFA programs. In fact, \CFA uses \glspl{uthrd} which have the explicit goal of reducing the cost of threading primitives to allow many smaller threads. This can naturally lead to have threads with much shorter lifetime and only being scheduled a few times. Scheduling strategies based on feadback loops cannot be effective in these cases because they will not have the opportunity to measure the metrics that underlay the algorithm. Note that the problem of feadback loop convergence (reacting too slowly to scheduling events) is not specific to short lived threads but can also occur with threads that show drastic changes in scheduling event, e.g., threads running for long periods of time and then suddenly blocking and unblocking quickly and repeatedly. 77 78 In the context of operating systems, these concerns can be overshadowed by a more pressing concern : security. When multiple users are involved, it is possible that some users are malevolent and try to exploit the scheduling strategy in order to achieve some nefarious objective. Security concerns mean that more precise and robust fairness metrics must be used. In the case of the \CFA scheduler, every thread runs in the same user-space and are controlled from the same user. It is then possible to safely ignore the possibility that threads are malevolent and assume that all threads will ignore or cooperate with each other. This allows for a much simpler fairness metric and in this proposal ``fairness'' will be considered as equal opportunities to run once scheduled. 79 80 Since feadback is not necessarily feasible within the lifetime of all threads and a simple fairness metric can be used, the scheduling strategy proposed for the \CFA runtime does not user per-threads feedback. Feedback loops in general are not rejected for secondary concerns like idle sleep, but no feedback loop is used to decide which thread to run next. 147 Many operating systems use schedulers based on feedback in some form, \eg measuring how much CPU a particular thread has used\footnote{Different metrics can be measured but it is not relevant to the discussion.} and schedule threads based on this metric. 148 These strategies are sensible for operating systems but rely on two assumptions for the workload: 149 150 \begin{enumerate} 151 \item Threads live long enough for useful feedback information to be gathered. 152 \item Threads belong to multiple users so fairness across users is important. 153 \end{enumerate} 154 155 While these two assumptions generally hold for operating systems, they may not for user-level threading. 156 Since \CFA has the explicit goal of allowing many smaller threads, this can naturally lead to threads with much shorter lifetimes that are only scheduled a few times. 157 Scheduling strategies based on feedback cannot be effective in these cases because there is no opportunity to measure the metrics that underlie the algorithm. 158 Note, the problem of \newterm{feedback convergence} (reacting too slowly to scheduling events) is not specific to short-lived threads but can also occur with threads that show drastic changes in scheduling, \eg threads running for long periods of time and then suddenly blocking and unblocking quickly and repeatedly. 159 160 In the context of operating systems, these concerns can be overshadowed by a more pressing concern : security. 161 When multiple users are involved, it is possible some users are malevolent and try to exploit the scheduling strategy to achieve some nefarious objective. 162 Security concerns mean more precise and robust fairness metrics must be used to guarantee fairness across processes created by users as well as threads created within a process. 163 In the case of the \CFA scheduler, every thread runs in the same user space and is controlled by the same user. 164 Fairness across users is therefore a given and it is then possible to safely ignore the possibility that threads are malevolent. 165 This approach allows for a much simpler fairness metric, and in this proposal, \emph{fairness} is defined as: 166 \begin{quote} 167 When multiple threads are cycling through the system, the total ordering of threads being scheduled, \ie pushed onto the ready queue, should not differ much from the total ordering of threads being executed, \ie popped from the ready queue. 168 \end{quote} 169 170 Since feedback is not necessarily feasible within the lifetime of all threads and a simple fairness metric can be used, the scheduling strategy proposed for the \CFA runtime does not use per-threads feedback. 171 Feedback in general is not rejected for secondary concerns like idle sleep for kernel threads, but no feedback is used to decide which thread to run next. 81 172 82 173 \subsection{Priority Schedulers} 83 Another broad category of schedulers are priority schedulers. In these scheduling strategies threads have priorities and the runtime schedules the threads with the highest priority before scheduling other threads. Threads with equal priority are scheduled using a secondary strategy, often something simple like round-robin or FIFO. These priority mean that, as long as there is a thread with a higher priority that desires to run, a thread with a lower priority will not run. This possible starving of threads can dramatically increase programming complexity since starving threads and priority inversion (prioritising a lower priority thread) can both lead to serious problems, leaving programmers between a rock and a hard place. 84 85 An important observation to make is that threads do not need to have explicit priorities for problems to be possible. Indeed, any system with multiple ready-queues and attempts to exhaust one queue before accessing the other queues, could encounter starvation problems. A popular scheduling strategy that suffers from implicit priorities is work-stealing. Work-stealing is generally presented as follows : 86 87 \begin{itemize} 88 \item Each processor has a list of threads. 89 \end{itemize} 90 \begin{enumerate} 91 \item Run threads from ``this'' processor's list. 92 \item If ``this'' processor's list is empty, run threads from some other processor's list. 93 \end{enumerate} 94 95 In a loaded system\footnote{A loaded system is a system where threads are being run at the same rate they are scheduled}, if a thread does not yield or block for an extended period of time, threads on the same processor list will starve if no other processors can exhaust their list. 96 97 Since priorities can be complex to handle for programmers, the scheduling strategy proposed for the \CFA runtime does not use a strategy with either implicit or explicit thread priorities. 98 99 \subsection{Schedulers without feadback or priorities} 100 I claim that the ideal default scheduler for the \CFA runtime is a scheduler that offers good scalability and a simple fairness guarantee that is easy for programmers to reason about. The simplest fairness guarantee is to guarantee FIFO ordering, i.e., threads scheduled first will run first. However, enforcing FIFO ordering generally conflicts with scalability across multiple processors because of the additionnal synchronization. Thankfully, strict FIFO is not needed for scheduling. Since concurrency is inherently non-deterministic, fairness concerns in scheduling are only a problem if a thread repeatedly runs before another thread can run\footnote{This is because the non-determinism means that programmers must already handle ordering problems in order to produce correct code and already must rely on weak guarantees, for example that a specific thread will \emph{eventually} run.}. This need for unfairness to persist before problems occur means that the FIFO fairness guarantee can be significantly relaxed without causing problems. For this proposal, the target guarantee is that the \CFA scheduler guarantees \emph{probable} FIFO ordering, which is defined as follows : 101 \begin{itemize} 102 \item Given two threads $X$ and $Y$, the odds that thread $X$ runs $N$ times \emph{after} thread $Y$ is scheduled but \emph{before} it is run, decreases exponentially with regards to $N$. 103 \end{itemize} 104 105 While this is not a strong guarantee, the probability that problems persist for long period of times decreases exponentially, making persisting problems virtually impossible. 106 107 \subsection{Real-Time} 108 While the objective of this proposed scheduler is similar to the objective of real-time scheduling, this proposal is not a proposal for real-time scheduler and as such makes no attempt to offer either soft or hard guarantees on scheduling delays. 109 110 % =============================================================================== 111 % =============================================================================== 112 \section{Proposal} 113 114 \subsection{Ready-Queue} 115 Using trevor's paper\cit as basis, it is simple to build a relaxed FIFO list that is fast and scalable for loaded or overloaded systems. The described queue uses an array of underlying strictly FIFO queue. Pushing new data is done by selecting one of these underlying queues at random, recording a timestamp for the push and pushing to the selected queue. Popping is done by selecting two queues at random and popping from the queue for which the head has the oldest timestamp. In loaded or overloaded systems, it is higly likely that the queues is far from empty, e.i., several tasks are on each of the underlying queues. This means that selecting a queue at random to pop from is higly likely to yield a queue that is not empty. 116 117 When the ready queue is "more empty", i.e., several of the inner queues are empty, selecting a random queue for popping is less likely to yield a valid selection and more attempts need to be made, resulting in a performance degradation. In cases, with few elements on the ready queue and few processors running, performance can be improved by adding information to help processors find which inner queues are used. Preliminary performance tests indicate that with few processors, a bitmask can be used to identify which inner queues are currently in use. This is especially effective in the single-thread case, where the bitmask will always be up-to-date. Furthermore, modern x86 CPUs have a BMI2 extension which allow using the bitmask with very little overhead over directly accessing the readyqueue offerring decent performance even in cases with many empty inner queues. This technique does not solve the problem completely, it randomly attempts to find a block of 64 queues where at least one is used, instead of attempting to find a used queue. For systems with a large number of cores this does not completely solve the problem, but it is a fixed improvement. The size of the blocks are limited by the maximum size atomic instruction can operate on, therefore atomic instructions on large words would increase the 64 queues per block limit. 118 119 \TODO double check the next sentence 120 Preliminary result indicate that the bitmask approach with the BMI2 extension can lead to multi-threaded performance that is contention agnostic in the worst case. 121 This result suggests that the contention penalty and the increase performance for additionnal thread cancel each other exactly. This may indicate that a relatively small reduction in contention may tip the performance into positive scalling even for the worst case. It can be noted that in cases of high-contention, the use of the bitmask to find queues that are not empty is much less reliable. Indeed, if contention on the bitmask is high, it means it probably changes significantly between the moment it is read and the actual operation on the queues it represents. Furthermore, the objective of the bitmask is to avoid probing queues that are empty. Therefore, in cases where the bitmask is highly contented, it may be preferrable to probe queues randomly, either until contention decreases or until a prior prefetch of the bitmask completes. Ideally, the scheduler would be able to observe that the bitmask is highly contented and adjust its behaviour appropriately. However, I am not aware of any mechanism to query whether a cacheline is in cache or to run other instructions until a cacheline is fetch without blocking on the cacheline. As such, an alternative that may have a similar impact would be for each thread to have their own bitmask, which would be updated both after each scheduler action and after a certain number of failed probing. If the bitmask has little contention, the local bitmask will be mostly up-to-date and several threads won't need to contend as much on the global bitmask. If the bitmask has significant contention, then fetching it becomes more expensive and threads may as well probe randomly. This solution claims that probing randomly or against an out-of-date bitmask is equivalent. 122 123 In cases where this is insufficient, another approach is to use a hiearchical data structure. Creating a tree of nodes to reduce contention has been shown to work in similar cases\cit(SNZI: Scalable NonZero Indicators)\footnote{This particular paper seems to be patented in the US. How does that affect \CFA? Can I use it in my work?}. However, this approach may lead to poorer single-threaded performance due to the inherent pointer chasing, as such, it was not considered as the first approach but as a fallback in case the bitmask approach does not satisfy the performance goals. 124 125 Part of this performance relies on contention being low when there are few threads on the readyqueue. However, this can be assumed reliably if the system handles putting idle processors to sleep, which is addressed in section \ref{sleep}. 174 Another broad category of schedulers are priority schedulers. 175 In these scheduling strategies, threads have priorities and the runtime schedules the threads with the highest priority before scheduling other threads. 176 Threads with equal priority are scheduled using a secondary strategy, often something simple like round robin or FIFO. 177 A consequence of priority is that, as long as there is a thread with a higher priority that desires to run, a thread with a lower priority does not run. 178 The potential for thread starvation dramatically increases programming complexity since starving threads and priority inversion (prioritizing a lower priority thread) can both lead to serious problems. 179 180 An important observation is that threads do not need to have explicit priorities for problems to occur. 181 Indeed, any system with multiple ready queues that attempts to exhaust one queue before accessing the other queues, essentially provides implicit priority, which can encounter starvation problems. 182 For example, a popular scheduling strategy that suffers from implicit priorities is work stealing. 183 \newterm{Work stealing} is generally presented as follows: 184 \begin{enumerate} 185 \item Each processor has a list of ready threads. 186 \item Each processor runs threads from its ready queue first. 187 \item If a processor's ready queue is empty, attempt to run threads from some other processor's ready queue. 188 \end{enumerate} 189 In a loaded system\footnote{A \newterm{loaded system} is a system where threads are being run at the same rate they are scheduled.}, if a thread does not yield, block, or preempt for an extended period of time, threads on the same processor's list starve if no other processors exhaust their list. 190 191 Since priorities can be complex for programmers to incorporate into their execution intuition, the \CFA scheduling strategy does not provided explicit priorities and attempts to eliminate implicit priorities. 192 193 \subsection{Schedulers without feedback or priorities} 194 This proposal conjectures that it is possible to construct a default scheduler for the \CFA runtime that offers good scalability and a simple fairness guarantee that is easy for programmers to reason about. 195 The simplest fairness guarantee is FIFO ordering, \ie threads scheduled first run first. 196 However, enforcing FIFO ordering generally conflicts with scalability across multiple processors because of the additional synchronization. 197 Thankfully, strict FIFO is not needed for sufficient fairness. 198 Since concurrency is inherently non-deterministic, fairness concerns in scheduling are only a problem if a thread repeatedly runs before another thread can run. 199 Some relaxation is possible because non-determinism means programmers already handle ordering problems to produce correct code and hence rely on weak guarantees, \eg that a thread \emph{eventually} runs. 200 Since some reordering does not break correctness, the FIFO fairness guarantee can be significantly relaxed without causing problems. 201 For this proposal, the target guarantee is that the \CFA scheduler provides \emph{probable} FIFO ordering, which allows reordering but makes it improbable that threads are reordered far from their position in total ordering. 202 203 The \CFA scheduler fairness is defined as follows: 204 \begin{quote} 205 Given two threads $X$ and $Y$, the odds that thread $X$ runs $N$ times \emph{after} thread $Y$ is scheduled but \emph{before} it is run, decreases exponentially with regard to $N$. 206 \end{quote} 207 While this is not a bounded guarantee, the probability that unfairness persist for long periods of times decreases exponentially, making persisting unfairness virtually impossible. 208 209 % =============================================================================== 210 % =============================================================================== 211 \section{Proposal Details} 212 213 \subsection{Central Ready Queue} \label{sec:queue} 214 A central ready queue can be built from a FIFO queue, where user threads are pushed onto the queue when they are ready to run, and processors (kernel-threads acting as virtual processors) pop the user threads from the queue and execute them. 215 Alistarh \etal~\cite{alistarh2018relaxed} show it is straightforward to build a relaxed FIFO list that is fast and scalable for loaded or overloaded systems. 216 The described queue uses an array of underlying strictly FIFO queues as shown in Figure~\ref{fig:base}\footnote{For this section, the number of underlying queues is assumed to be constant. 217 Section~\ref{sec:resize} discusses resizing the array.}. 218 Pushing new data is done by selecting one of the underlying queues at random, recording a timestamp for the operation, and pushing to the selected queue. 219 Popping is done by selecting two queues at random and popping from the queue with the oldest timestamp. 220 A higher number of underlying queues leads to less contention on each queue and therefore better performance. 221 In a loaded system, it is highly likely the queues are non-empty, \ie several threads are on each of the underlying queues. 222 For this case, selecting a queue at random to pop from is highly likely to yield a queue with available items. 223 In Figure~\ref{fig:base}, ignoring the ellipsis, the chances of getting an empty queue is 2/7 per pick, meaning two random picks yield an item approximately 9 times out of 10. 224 225 \begin{figure} 226 \begin{center} 227 \input{base.pstex_t} 228 \end{center} 229 \caption{Loaded relaxed FIFO list base on an array of strictly FIFO lists. 230 A timestamp appears in each node and array cell.} 231 \label{fig:base} 232 \end{figure} 233 234 \begin{figure} 235 \begin{center} 236 \input{empty.pstex_t} 237 \end{center} 238 \caption{Underloaded relaxed FIFO list where the array contains many empty cells.} 239 \label{fig:empty} 240 \end{figure} 241 242 In an underloaded system, several of the queues are empty, so selecting a random queue for popping is less likely to yield a successful selection and more attempts are needed, resulting in a performance degradation. 243 Figure~\ref{fig:empty} shows an example with fewer elements, where the chances of getting an empty queue is 5/7 per pick, meaning two random picks yield an item only half the time. 244 Since the ready queue is not empty, the pop operation \emph{must} find an element before returning and therefore must retry. 245 Note, the popping kernel thread has no work to do, but CPU cycles are wasted both for available user and kernel threads during the pop operation as the popping thread is using a CPU. 246 Overall performance is therefore influenced by the contention on the underlying queues and pop performance is influenced by the item density. 247 248 This leads to four performance cases for the centralized ready queue, as depicted in Table~\ref{tab:perfcases}. 249 The number of processors (many or few) refers to the number of kernel threads \emph{actively} attempting to pop user threads from the queues, not the total number of kernel threads. 250 The number of threads (many or few) refers to the number of user threads ready to be run. 251 Many threads means they outnumber processors significantly and most underlying queues have items, few threads mean there are barely more threads than processors and most underlying queues are empty. 252 Cases with fewer threads than processors are discussed in Section~\ref{sec:sleep}. 253 254 \begin{table} 255 \begin{center} 256 \begin{tabular}{|r|l|l|} 257 \cline{2-3} 258 \multicolumn{1}{r|}{} & \multicolumn{1}{c|}{Many Processors} & \multicolumn{1}{c|}{Few Processors} \\ 259 \hline 260 Many Threads & A: good performance & B: good performance \\ 261 \hline 262 Few Threads & C: worst performance & D: poor performance \\ 263 \hline 264 \end{tabular} 265 \end{center} 266 \caption{Expected performance of the relaxed FIFO list in different cases.} 267 \label{tab:perfcases} 268 \end{table} 269 270 Performance can be improved in Table~\ref{tab:perfcases} case~D by adding information to help processors find which inner queues are used. 271 This addition aims to avoid the cost of retrying the pop operation but does not affect contention on the underlying queues and can incur some management cost for both push and pop operations. 272 The approach used to encode this information can vary in density and be either global or local. 273 \newterm{Density} means the information is either packed in a few cachelines or spread across several cachelines, and \newterm{local information} means each thread uses an independent copy instead of a single global, \ie common, source of information. 274 275 For example, Figure~\ref{fig:emptybit} shows a dense bitmask to identify which inner queues are currently in use. 276 This approach means processors can often find user threads in constant time, regardless of how many underlying queues are empty. 277 Furthermore, modern x86 CPUs have extended bit manipulation instructions (BMI2) that allow using the bitmask with very little overhead compared to the randomized selection approach for a filled ready queue, offering good performance even in cases with many empty inner queues. 278 However, this technique has its limits: with a single word\footnote{Word refers here to however many bits can be written atomically.} bitmask, the total number of underlying queues in the ready queue is limited to the number of bits in the word. 279 With a multi-word bitmask, this maximum limit can be increased arbitrarily, but it is not possible to check if the queue is empty by reading the bitmask atomically. 280 281 Finally, a dense bitmap, either single or multi-word, causes additional problems in Table~\ref{tab:perfcases} case C, because many processors are continuously scanning the bitmask to find the few available threads. 282 This increased contention on the bitmask(s) reduces performance because of cache misses after updates and the bitmask is updated more frequently by the scanning processors racing to read and/or update that information. 283 This increased update frequency means the information in the bitmask is more often stale before a processor can use it to find an item, \ie mask read says there are available user threads but none on queue. 284 285 \begin{figure} 286 \begin{center} 287 {\resizebox{0.73\textwidth}{!}{\input{emptybit}}} 288 \end{center} 289 \vspace*{-5pt} 290 \caption{Underloaded queue with added bitmask to indicate which array cells have items.} 291 \label{fig:emptybit} 292 \begin{center} 293 {\resizebox{0.73\textwidth}{!}{\input{emptytree}}} 294 \end{center} 295 \vspace*{-5pt} 296 \caption{Underloaded queue with added binary search tree indicate which array cells have items.} 297 \label{fig:emptytree} 298 \begin{center} 299 {\resizebox{0.9\textwidth}{!}{\input{emptytls}}} 300 \end{center} 301 \vspace*{-5pt} 302 \caption{Underloaded queue with added per processor bitmask to indicate which array cells have items.} 303 \label{fig:emptytls} 304 \end{figure} 305 306 Figure~\ref{fig:emptytree} shows an approach using a hierarchical tree data-structure to reduce contention and has been shown to work in similar cases~\cite{ellen2007snzi}. 307 However, this approach may lead to poorer performance in Table~\ref{tab:perfcases} case~B due to the inherent pointer chasing cost and already low contention cost in that case. 308 309 Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but have each thread keep its own independent copy of it. 310 While this approach can offer good scalability \emph{and} low latency, the liveliness of the information can become a problem. 311 In the simple cases, local copies can become stale and end-up not being useful for the pop operation. 312 A more serious problem is that reliable information is necessary for some parts of this algorithm to be correct. 313 As mentioned in this section, processors must know \emph{reliably} whether the list is empty or not to decide if they can return \texttt{NULL} or if they must keep looking during a pop operation. 314 Section~\ref{sec:sleep} discusses another case where reliable information is required for the algorithm to be correct. 315 316 There is a fundamental tradeoff among these approach. 317 Dense global information about empty underlying queues helps zero-contention cases at the cost of the high-contention case. 318 Sparse global information helps high-contention cases but increases latency in zero-contention cases to read and ``aggregate'' the information\footnote{Hierarchical structures, \eg binary search tree, effectively aggregate information but follow pointer chains, learning information at each node. 319 Similarly, other sparse schemes need to read multiple cachelines to acquire all the information needed.}. 320 Finally, dense local information has both the advantages of low latency in zero-contention cases and scalability in high-contention cases. 321 However, the information can become stale making it difficult to use to ensure correctness. 322 The fact that these solutions have these fundamental limits suggest to me a better solution that attempts to combine these properties in an interesting way. 323 Also, the lock discussed in Section~\ref{sec:resize} allows for solutions that adapt to the number of processors, which could also prove useful. 126 324 127 325 \paragraph{Objectives and Existing Work} 128 How much scalability is actually needed is highly debatable, libfibre\cit is has compared favorably to other schedulers in webserver tests\cit and uses a single atomic counter in its scheduling algorithm similarly to the proposed bitmask. As such the single atomic instruction on a shared cacheline may be sufficiently performant. 129 130 I have built a prototype of this ready-queue (including the bitmask and BMI2 usage, but not the sharded bitmask) and ran performance experiments on it but it is difficult to compare this prototype to a thread scheduler as the prototype is used as a data-queue. I have also integrated this prototype into the \CFA runtime, but have not yet created performance experiments to compare results. I believe that the bitmask approach is currently one of the larger risks of the proposal, early tests lead me to believe it may work but it is not clear that the contention problem can be overcome. The worst-case scenario is a case where the number of processors and the number of ready threads are similar, yet scheduling events are very frequent. Fewer threads should lead to the Idle Sleep mechanism reducing contention while having many threads ready leads to optimal performance. It is difficult to evaluate the likeliness of this worst-case scenario in real workloads. I believe, frequent scheduling events suggest a more ``bursty'' workload where new work is finely divided among many threads which race to completion. This type of workload would only see a peek of contention close to the end of the work, but no sustained contention. Very fine-grained pipelines are less ``bursty'', these may lead to more sustained contention. However, they could also easily benefit from a direct hand-off strategy which would circumvent the problem entirely. 131 132 \subsection{Dynamic Resizing} 133 The \CFA runtime system currently handles dynamically adding and removing processors from clusters at any time. Since this is part of the existing design, the proposed scheduler must also support this behaviour. However, dynamicly resizing the clusters is considered a rare event associated with setup, teardown and major configuration changes. This assumptions is made both in the design of the proposed scheduler as well as in the original design of the \CFA runtime system. As such, the proposed scheduler must honor the correctness of these behaviour but does not have any performance objectives with regards to resizing a cluster. How long adding or removing processors take and how much this disrupts the performance of other threads is considered a secondary concern since it should be amortized over long period of times. This description effectively matches with te description of a Reader-Writer lock, in frequent but invasive updates among frequent (mostly) read operations. In the case of the Ready-Queue described above, read operations are operations that push or pop from the ready-queue but do not invalidate any references to the ready queue data structures. Writes on the other-hand would add or remove inner queues, invalidating references to the array of inner queues in the process. Therefore, the current proposed approach to this problem is the add a per-cluster Reader Writer lock around the ready queue to prevent restructuring of the ready-queue data structure while threads are being pushed or popped. 134 135 There are possible alternatives to the Reader Writer lock solution. This problem is effectively a memory reclamation problem and as such there is a large body of research on the subject. However, the RWlock solution is simple and can be leveraged to solve other problems (e.g. processor ordering and memory reclamation of threads) which makes it an attractive solution. 326 327 How much scalability is actually needed is highly debatable. 328 \emph{libfibre}~\cite{libfibre} has compared favourably to other schedulers in webserver tests~\cite{Karsten20} and uses a single atomic counter in its scheduling algorithm similarly to the proposed bitmask. 329 As such, the single atomic instruction on a shared cacheline may be sufficiently performant. 330 331 I have built a prototype of this ready queue in the shape of a data queue, \ie nodes on the queue are structures with a single $int$ representing a thread and intrusive data fields. 332 Using this prototype, preliminary performance experiments confirm the expected performance in Table~\ref{tab:perfcases}. 333 However, these experiments only offer a hint at the actual performance of the scheduler since threads are involved in more complex operations, \eg threads are not independent of each other: when a thread blocks some other thread must intervene to wake it. 334 335 I have also integrated this prototype into the \CFA runtime, but have not yet created performance experiments to compare results, as creating one-to-one comparisons between the prototype and the \CFA runtime will be complex. 336 337 \subsection{Dynamic Resizing} \label{sec:resize} 338 339 \begin{figure} 340 \begin{center} 341 \input{system.pstex_t} 342 \end{center} 343 \caption{Global structure of the \CFA runtime system.} 344 \label{fig:system} 345 \end{figure} 346 347 The \CFA runtime system groups processors together as \newterm{clusters}, as shown in Figure~\ref{fig:system}. 348 Threads on a cluster are always scheduled on one of the processors of the cluster. 349 Currently, the runtime handles dynamically adding and removing processors from clusters at any time. 350 Since this feature is part of the existing design, the proposed scheduler must also support this behaviour. 351 However, dynamically resizing a cluster is considered a rare event associated with setup, tear down and major configuration changes. 352 This assumption is made both in the design of the proposed scheduler as well as in the original design of the \CFA runtime system. 353 As such, the proposed scheduler must honour the correctness of this behaviour but does not have any performance objectives with regard to resizing a cluster. 354 That is, the time to add or remove processors and how much this disrupts the performance of other threads is considered a secondary concern since it should be amortized over long periods of times. 355 However, as mentioned in Section~\ref{sec:queue}, contention on the underlying queues can have a direct impact on performance. 356 The number of underlying queues must therefore be adjusted as the number of processors grows or shrinks. 357 Since the underlying queues are stored in a dense array, changing the number of queues requires resizing the array and expanding the array requires moving it, which can introduce memory reclamation problems if not done correctly. 358 359 \begin{figure} 360 \begin{center} 361 \input{resize} 362 \end{center} 363 \caption{Copy of data structure shown in Figure~\ref{fig:base}.} 364 \label{fig:base2} 365 \end{figure} 366 367 It is important to note how the array is used in this case. 368 While the array cells are modified by every push and pop operation, the array itself, \ie the pointer that would change when resized, is only read during these operations. 369 Therefore the use of this pointer can be described as frequent reads and infrequent writes. 370 This description effectively matches with the description of a reader-writer lock, infrequent but invasive updates among frequent read operations. 371 In the case of the ready queue described above, read operations are operations that push or pop from the ready queue but do not invalidate any references to the ready queue data structures. 372 Writes, on the other hand, would add or remove inner queues, invalidating references to the array of inner queues in a process. 373 Therefore, the current proposed approach to this problem is to add a per-cluster reader-writer lock around the ready queue to prevent restructuring of the ready-queue data-structure while threads are being pushed or popped. 374 375 There are possible alternatives to the reader-writer lock solution. 376 This problem is effectively a memory reclamation problem and as such there is a large body of research on the subject~\cite{brown2015reclaiming, michael2004hazard}. 377 However, the reader-write lock-solution is simple and can be leveraged to solve other problems (\eg processor ordering and memory reclamation of threads), which makes it an attractive solution. 136 378 137 379 \paragraph{Objectives and Existing Work} 138 The lock must offer scalability and performance on par with the actual ready-queue in order not to introduce a new bottle neck. I have already built a lock that fits the desired requirements and preliminary testing show scalability and performance that exceed the target. As such, I do not consider this lock to be a risk on this project. 139 140 \subsection{Idle Sleep} \label{sleep} 141 As mentionned above, idle sleep is the process of putting processors to sleep while they do not have threads to execute. In this context processors are kernel-threads and sleeping refers to asking the kernel to block a thread. This can be achieved with either thread synchronization operations like pthread\_cond\_wait or using signal operations like sigsuspend. 142 143 Support for idle sleep broadly involves calling the operating system to block the kernel thread but also handling the race between the sleeping and the waking up, and handling which kernel thread should sleep or wake-up. 144 145 When a processor decides to sleep, there is a race that occurs between it signalling that it will go to sleep (so other processors can find sleeping processors) and actually blocking the kernel thread. This is equivalent to the classic problem of missing signals when using condition variables, the ``sleepy'' processor indicates that it will sleep but has not yet gone to sleep, if another processor attempts to wake it up, the waking-up operation may claim nothing needs to be done and the signal will have been missed. In cases where threads are scheduled from processors on the current cluster, loosing signals is not necessarily critical, because at least some processors on the cluster are awake. Individual processors always finish shceduling threads before looking for new work, which means that the last processor to go to sleep cannot miss threads scheduled from inside the cluster (if they do, that demonstrates the ready-queue is not linearizable). However, this guarantee does not hold if threads are shceduled from outside the cluster, either due to an external event like timers and I/O, or due to a thread migrating from a different cluster. In this case, missed signals can lead to the cluster deadlocking where it should not\footnote{Clusters ``should'' never deadlock, but for this proposal, cases where \CFA users \emph{actually} wrote \CFA code that leads to a deadlock it is considered as a deadlock that ``should'' happen. }. Therefore, it is important that the scheduling of threads include a mechanism where signals \emph{cannot} be missed. For performance reasons, it can be advantageous to have a secondary mechanism that allows signals to be missed in cases where it cannot lead to a deadlock. To be safe, this process must include a ``handshake'' where it is guaranteed that either~: the sleepy processor notices that a thread was scheduled after it signalled its intent to block or code scheduling threads well see the intent to sleep before scheduling and be able to wake-up the processor. This matter is complicated by the fact that pthread offers few tools to implement this solution and offers no guarantee of ordering of threads waking up for most of these tools. 146 147 Another issues is trying to avoid kernel sleeping and waking frequently. A possible partial solution is to order the processors so that the one which most recently went to sleep is woken up. This allows other sleeping processors to reach deeper sleep state (when these are available) while keeping ``hot'' processors warmer. Note that while this generally means organising the processors in a stack, I believe that the unique index provided by the ReaderWriter lock can be reused to strictly order the waking order of processors, causing a LIFO like waking order. While a strict LIFO stack is probably better, using the processor index could proove useful and offer a sufficiently LIFO ordering. 148 149 Finally, another important aspect of Idle Sleep is when should processors make the decision to sleep and when it is appropriate for sleeping processors to be woken up. Processors that are unnecessarily awake lead to unnecessary contention and power consumption, while too many sleeping processors can lead to sub-optimal throughput. Furthermore, transitions from sleeping to awake and vice-versa also add unnecessary latency. There is already a wealth of research on the subject and I do not plan to implement a novel idea for the Idle Sleep heuristic in this project. 380 The lock must offer scalability and performance on par with the actual ready queue in order not to introduce a new bottleneck. 381 I have already built a lock that fits the desired requirements and preliminary testing show scalability and performance that exceed the target. 382 As such, I do not consider this lock to be a risk for this project. 383 384 \subsection{Idle Sleep} \label{sec:sleep} 385 386 \newterm{Idle sleep} is the process of putting processors to sleep when they have no threads to execute. 387 In this context, processors are kernel threads and sleeping refers to asking the kernel to block a thread. 388 This operation can be achieved with either thread synchronization operations like $pthread_cond_wait$ or using signal operations like $sigsuspend$. 389 The goal of putting idle processors to sleep is: 390 \begin{enumerate} 391 \item 392 reduce contention on the ready queue, since the otherwise idle processors generally contend trying to pop items from the queue, 393 \item 394 give back unneeded CPU time associated with a process to other user processors executing on the computer, 395 \item 396 and reduce energy consumption in cases where more idle kernel-threads translate into idle CPUs, which can cycle down. 397 \end{enumerate} 398 Support for idle sleep broadly involves calling the operating system to block the kernel thread and handling the race between a blocking thread and the waking thread, and handling which kernel thread should sleep or wake up. 399 400 When a processor decides to sleep, there is a race that occurs between it signalling that is going to sleep (so other processors can find sleeping processors) and actually blocking the kernel thread. 401 This operation is equivalent to the classic problem of missing signals when using condition variables: the ``sleepy'' processor indicates its intention to block but has not yet gone to sleep when another processor attempts to wake it up. 402 The waking-up operation sees the blocked process and signals it, but the blocking process is racing to sleep so the signal is missed. 403 In cases where kernel threads are managed as processors on the current cluster, losing signals is not necessarily critical, because at least some processors on the cluster are awake and may check for more processors eventually. 404 Individual processors always finish scheduling user threads before looking for new work, which means that the last processor to go to sleep cannot miss threads scheduled from inside the cluster (if they do, that demonstrates the ready queue is not linearizable). 405 However, this guarantee does not hold if threads are scheduled from outside the cluster, either due to an external event like timers and I/O, or due to a user (or kernel) thread migrating from a different cluster. 406 In this case, missed signals can lead to the cluster deadlocking\footnote{Clusters should only deadlock in cases where a \CFA programmer \emph{actually} writes \CFA code that leads to a deadlock.}. 407 Therefore, it is important that the scheduling of threads include a mechanism where signals \emph{cannot} be missed. 408 For performance reasons, it can be advantageous to have a secondary mechanism that allows signals to be missed in cases where it cannot lead to a deadlock. 409 To be safe, this process must include a ``handshake'' where it is guaranteed that either: 410 \begin{enumerate} 411 \item 412 the sleeping processor notices that a user thread is scheduled after the sleeping processor signalled its intent to block or 413 \item 414 code scheduling threads sees the intent to sleep before scheduling and be able to wake-up the processor. 415 \end{enumerate} 416 This matter is complicated by the fact that pthreads and Linux offer few tools to implement this solution and no guarantee of ordering of threads waking up for most of these tools. 417 418 Another important issue is avoiding kernel threads sleeping and waking frequently because there is a significant operating-system cost. 419 This scenario happens when a program oscillates between high and low activity, needing most and then few processors. 420 A possible partial solution is to order the processors so that the one which most recently went to sleep is woken up. 421 This allows other sleeping processors to reach deeper sleep state (when these are available) while keeping ``hot'' processors warmer. 422 Note that while this generally means organizing the processors in a stack, I believe that the unique index provided in my reader-writer lock can be reused to strictly order the waking processors, causing a mostly LIFO order. 423 While a strict LIFO stack is probably better, the processor index could prove useful for other reasons, while still offering a sufficiently LIFO ordering. 424 425 A final important aspect of idle sleep is when should processors make the decision to sleep and when is it appropriate for sleeping processors to be woken up. 426 Processors that are unnecessarily unblocked lead to unnecessary contention, CPU usage, and power consumption, while too many sleeping processors can lead to suboptimal throughput. 427 Furthermore, transitions from sleeping to awake and vice versa also add unnecessary latency. 428 There is already a wealth of research on the subject~\cite{schillings1996engineering, wiki:thunderherd} and I may use an existing approach for the idle-sleep heuristic in this project, \eg~\cite{Karsten20}. 150 429 151 430 \subsection{Asynchronous I/O} 152 The final aspect of this proposal is asynchronous I/O. Without it, user threads that execute I/O operations will block the underlying kernel thread. This leads to poor throughput, it would be preferrable to block the user-thread and reuse the underlying kernel-thread to run other ready threads. This requires intercepting the user-threads' calls to I/O operations, redirecting them to an asynchronous I/O interface and handling the multiplexing between the synchronous and asynchronous API. As such, these are the three components needed to implemented to support asynchronous I/O : an OS abstraction layer over the asynchronous interface, an event-engine to (de)multiplex the operations and a synchronous interface for users to use. None of these components currently exist in \CFA and I will need to build all three for this project. 153 154 \paragraph{OS Abstraction} 155 One of the fundamental part of this converting blocking I/O operations into non-blocking ones. This relies on having an underlying asynchronous I/O interface to which to direct the I/O operations. While there exists many different APIs for asynchronous I/O, it is not part of this proposal to create a novel API, simply to use an existing one that is sufficient. uC++ uses the \texttt{select} as its interface, which handles pipes and sockets. It entails significant complexity and has performances problems which make it a less interesting alternative. Another interface which is becoming popular recently\cit is \texttt{epoll}. However, epoll also does not handle file system and seems to have problem to linux pipes and \texttt{TTY}s\cit. A very recent alternative that must still be investigated is \texttt{io\_uring}. It claims to address some of the issues with \texttt{epoll} but is too recent to be confident that it does. Finally, a popular cross-platform alternative is \texttt{libuv}, which offers asynchronous sockets and asynchronous file system operations (among other features). However, as a full-featured library it includes much more than what is needed and could conflict with other features of \CFA unless significant efforts are made to merge them together. 156 157 \paragraph{Event-Engine} 158 Laying on top of the asynchronous interface layer is the event-engine. This engine is responsible for multiplexing (batching) the synchronous I/O requests into an asynchronous I/O request and demultiplexing the results onto appropriate blocked threads. This can be straightforward for the simple cases, but can become quite complex. Decisions that will need to be made include : whether to poll from a seperate kernel thread or a regularly scheduled user thread, what should be the ordering used when results satisfy many requests, how to handle threads waiting for multiple operations, etc. 431 432 The final aspect of this proposal is asynchronous I/O. 433 Without it, user threads that execute I/O operations block the underlying kernel thread, which leads to poor throughput. 434 It is preferable to block the user thread performing the I/O and reuse the underlying kernel-thread to run other ready user threads. 435 This approach requires intercepting user-thread calls to I/O operations, redirecting them to an asynchronous I/O interface, and handling the multiplexing/demultiplexing between the synchronous and asynchronous API. 436 As such, there are three components needed to implement support for asynchronous I/O: 437 \begin{enumerate} 438 \item 439 an OS abstraction layer over the asynchronous interface, 440 \item 441 an event-engine to (de)multiplex the operations, 442 \item 443 and a synchronous interface for users. 444 \end{enumerate} 445 None of these components currently exist in \CFA and I will need to build all three for this project. 446 447 \paragraph{OS Asynchronous Abstraction} 448 One fundamental part for converting blocking I/O operations into non-blocking is having an underlying asynchronous I/O interface to direct the I/O operations. 449 While there exists many different APIs for asynchronous I/O, it is not part of this proposal to create a novel API. 450 It is sufficient to make one work in the complex context of the \CFA runtime. 451 \uC uses the $select$~\cite{select} as its interface, which handles ttys, pipes and sockets, but not disk. 452 $select$ entails significant complexity and is being replaced in UNIX operating systems, which make it a less interesting alternative. 453 Another popular interface is $epoll$~\cite{epoll}, which is supposed to be cheaper than $select$. 454 However, $epoll$ also does not handle the file system and anecdotal evidence suggest it has problems with Linux pipes and ttys. 455 A popular cross-platform alternative is $libuv$~\cite{libuv}, which offers asynchronous sockets and asynchronous file system operations (among other features). 456 However, as a full-featured library it includes much more than I need and could conflict with other features of \CFA unless significant effort is made to merge them together. 457 A very recent alternative that I am investigating is $io_uring$~\cite{io_uring}. 458 It claims to address some of the issues with $epoll$ and my early investigating suggests that the claim is accurate. 459 $io_uring$ uses a much more general approach where system calls are registered to a queue and later executed by the kernel, rather than relying on system calls to support returning an error instead of blocking. 460 I believe this approach allows for fewer problems, \eg the manpage for $open$~\cite{open} states: 461 \begin{quote} 462 Note that [the $O_NONBLOCK$ flag] has no effect for regular files and block devices; 463 that is, I/O operations will (briefly) block when device activity is required, regardless of whether $O_NONBLOCK$ is set. 464 Since $O_NONBLOCK$ semantics might eventually be implemented, applications should not depend upon blocking behaviour when specifying this flag for regular files and block devices. 465 \end{quote} 466 This makes approaches based on $select$/$epoll$ less reliable since they may not work for every file descriptors. 467 For this reason, I plan to use $io_uring$ as the OS abstraction for the \CFA runtime unless further work encounters a fatal problem. 468 However, only a small subset of the features are available in Ubuntu as of April 2020~\cite{wiki:ubuntu-linux}, which will limit performance comparisons. 469 I do not believe this will affect the comparison result. 470 471 \paragraph{Event Engine} 472 Above the OS asynchronous abstraction is the event engine. 473 This engine is responsible for multiplexing (batching) the synchronous I/O requests into asynchronous I/O requests and demultiplexing the results to appropriate blocked user threads. 474 This step can be straightforward for simple cases, but becomes quite complex when there are thousands of user threads performing both reads and writes, possibly on overlapping file descriptors. 475 Decisions that need to be made include: 476 \begin{enumerate} 477 \item 478 whether to poll from a separate kernel thread or a regularly scheduled user thread, 479 \item 480 what should be the ordering used when results satisfy many requests, 481 \item 482 how to handle threads waiting for multiple operations, etc. 483 \end{enumerate} 159 484 160 485 \paragraph{Interface} 161 Finally, for these components to be available, it is necessary to expose them through a synchronous interface. This can be a novel interface but it is preferrable to attempt to intercept the existing POSIX interface in order to be compatible with existing code. This will allow C programs written using this interface to be transparently converted to \CFA with minimal effeort. Where this is not applicable, a novel interface will be created to fill the gaps. 486 Finally, for these non-blocking I/O components to be available, it is necessary to expose them through a synchronous interface because that is the \CFA concurrent programming style. 487 The interface can be novel but it is preferable to match the existing POSIX interface when possible to be compatible with existing code. 488 Matching allows C programs written using this interface to be transparently converted to \CFA with minimal effort. 489 Where new functionality is needed, I will add novel interface extensions to fill gaps and provide advanced features. 162 490 163 491 … … 165 493 % =============================================================================== 166 494 \section{Discussion} 167 495 I believe that runtime system and scheduling are still open topics. 496 Many ``state of the art'' production frameworks still use single-threaded event loops because of performance considerations, \eg~\cite{nginx-design}, and, to my knowledge, no widely available system language offers modern threading facilities. 497 I believe the proposed work offers a novel runtime and scheduling package, where existing work only offers fragments that users must assemble themselves when possible. 168 498 169 499 % =============================================================================== 170 500 % =============================================================================== 171 501 \section{Timeline} 172 173 174 \cleardoublepage 502 \begin{center} 503 \begin{tabular}{ | r @{--} l | p{4in} | } 504 \hline May 2020 & October 2020 & Creation of the performance benchmark. \\ 505 \hline November 2020 & March 2021 & Completion of the implementation. \\ 506 \hline March 2021 & April 2021 & Final Performance experiments. \\ 507 \hline May 2021 & August 2021 & Thesis writing and defence. \\ 508 \hline 509 \end{tabular} 510 \end{center} 175 511 176 512 % B I B L I O G R A P H Y 177 513 % ----------------------------- 178 \addcontentsline{toc}{chapter}{Bibliography} 514 \cleardoublepage 515 \phantomsection % allows hyperref to link to the correct page 516 \addcontentsline{toc}{section}{\refname} 179 517 \bibliographystyle{plain} 180 518 \bibliography{pl,local} 519 520 % G L O S S A R Y 521 % ----------------------------- 181 522 \cleardoublepage 182 523 \phantomsection % allows hyperref to link to the correct page 183 184 % G L O S S A R Y 185 % ----------------------------- 186 \addcontentsline{toc}{chapter}{Glossary} 524 \addcontentsline{toc}{section}{Glossary} 187 525 \printglossary 188 \cleardoublepage189 \phantomsection % allows hyperref to link to the correct page190 526 191 527 \end{document} -
doc/theses/thierry_delisle_PhD/comp_II/local.bib
r3c64c668 r58fe85a 76 76 77 77 @article{finkel1987dib, 78 title={DIB āa distributed implementation of backtracking},78 title={DIB-a distributed implementation of backtracking}, 79 79 author={Finkel, Raphael and Manber, Udi}, 80 80 journal={ACM Transactions on Programming Languages and Systems (TOPLAS)}, … … 221 221 organization={ACM} 222 222 } 223 224 % =============================================================================== 225 % Algorithms 226 % =============================================================================== 227 @article{michael2004hazard, 228 title={Hazard pointers: Safe memory reclamation for lock-free objects}, 229 author={Michael, Maged M}, 230 journal={IEEE Transactions on Parallel and Distributed Systems}, 231 volume={15}, 232 number={6}, 233 pages={491--504}, 234 year={2004}, 235 publisher={IEEE} 236 } 237 238 @inproceedings{brown2015reclaiming, 239 title={Reclaiming memory for lock-free data structures: There has to be a better way}, 240 author={Brown, Trevor Alexander}, 241 booktitle={Proceedings of the 2015 ACM Symposium on Principles of Distributed Computing}, 242 pages={261--270}, 243 year={2015} 244 } 245 246 % Trevor's relaxed FIFO list 247 @inproceedings{alistarh2018relaxed, 248 title={Relaxed schedulers can efficiently parallelize iterative algorithms}, 249 author={Alistarh, Dan and Brown, Trevor and Kopinsky, Justin and Nadiradze, Giorgi}, 250 booktitle={Proceedings of the 2018 ACM Symposium on Principles of Distributed Computing}, 251 pages={377--386}, 252 year={2018} 253 } 254 255 % Scalable counters which only support is !0 256 @inproceedings{ellen2007snzi, 257 title={SNZI: Scalable nonzero indicators}, 258 author={Ellen, Faith and Lev, Yossi and Luchangco, Victor and Moir, Mark}, 259 booktitle={Proceedings of the twenty-sixth annual ACM symposium on Principles of distributed computing}, 260 pages={13--22}, 261 year={2007} 262 } 263 264 % =============================================================================== 265 % Linux Man Pages 266 % =============================================================================== 267 @manual{open, 268 key = "open", 269 title = "open(2) Linux User's Manual", 270 year = "2020", 271 month = "February", 272 } 273 274 @manual{epoll, 275 key = "epoll", 276 title = "epoll(7) Linux User's Manual", 277 year = "2019", 278 month = "March", 279 } 280 281 @manual{select, 282 key = "select", 283 title = "select(7) Linux User's Manual", 284 year = "2019", 285 month = "March", 286 } 287 288 @misc{io_uring, 289 title = {Efficient IO with io\_uring}, 290 author = {Axboe, Jens}, 291 year = "2019", 292 month = "March", 293 version = {0,4}, 294 howpublished = {\url{https://kernel.dk/io_uring.pdf}} 295 } 296 297 @misc{libuv, 298 key = "libuv", 299 title = {libuv}, 300 howpublished = {\url{https://github.com/libuv/libuv}} 301 } 302 303 % =============================================================================== 304 % MISC 305 % =============================================================================== 306 307 @misc{nginx-design, 308 key = "nginx", 309 title={Inside {NGINX}: How We Designed for Performance \& Scale}, 310 howpublished= {\href{https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale} 311 {https://\-www.nginx.com/\-blog/\-inside\--nginx\--how\--we\--designed\--for\--performance\--scale}}, 312 } 313 314 @article{schillings1996engineering, 315 title={Be engineering insights: Benaphores}, 316 author={Schillings, Benoit}, 317 journal={Be Newsletters}, 318 volume={1}, 319 number={26}, 320 year={1996} 321 } 322 323 @misc{wiki:thunderherd, 324 author = "{Wikipedia contributors}", 325 title = "Thundering herd problem --- {W}ikipedia{,} The Free Encyclopedia", 326 year = "2020", 327 howpublished = {\href{https://en.wikipedia.org/wiki/Thundering_herd_problem} 328 {https://\-en.wikipedia.org/\-wiki/\-Thundering\_herd\_problem}},}, 329 note = "[Online; accessed 14-April-2020]" 330 } 331 332 @misc{wiki:ubuntu-linux, 333 author = "{Wikipedia contributors}", 334 title = "Ubuntu version history : Table of versions --- {W}ikipedia{,} The Free Encyclopedia", 335 year = "2020", 336 howpublished = {\href{https://en.wikipedia.org/wiki/Ubuntu_version_history\#Table_of_versions} 337 {https://\-en.wikipedia.org/\-wiki/\-Ubuntu\_version\_history\#Table\_of\_versions}}, 338 note = "[Online; accessed 15-April-2020]" 339 } -
doc/user/Makefile
r3c64c668 r58fe85a 55 55 56 56 ${DOCUMENT} : ${BASE}.ps 57 ps2pdf $<57 ps2pdf -dPDFSETTINGS=/prepress $< 58 58 59 59 ${BASE}.ps : ${BASE}.dvi -
doc/user/user.tex
r3c64c668 r58fe85a 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Sat Jul 13 18:36:18 201914 %% Update Count : 3 87613 %% Last Modified On : Mon Oct 5 08:57:29 2020 14 %% Update Count : 3998 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 30 30 \usepackage{upquote} % switch curled `'" to straight 31 31 \usepackage{calc} 32 \usepackage{xspace}33 32 \usepackage{varioref} % extended references 34 \usepackage{listings} % format program code 33 \usepackage[labelformat=simple,aboveskip=0pt,farskip=0pt]{subfig} 34 \renewcommand{\thesubfigure}{\alph{subfigure})} 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 \input{common} % common CFA document macros 40 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 41 \usepackage{breakurl} 42 43 \usepackage[pagewise]{lineno} 44 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 45 \usepackage[firstpage]{draftwatermark} 46 \SetWatermarkLightness{0.9} 47 48 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 49 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 50 % AFTER HYPERREF. 51 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 52 53 \setlength{\topmargin}{-0.45in} % move running title into header 54 \setlength{\headsep}{0.25in} 55 56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 57 58 \CFAStyle % use default CFA format-style 59 \lstnewenvironment{C++}[1][] % use C++ style 60 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}} 61 {} 62 39 \newcommand{\CFALatin}{} 63 40 % inline code Ā©...Ā© (copyright symbol) emacs: C-q M-) 64 41 % red highlighting Ā®...Ā® (registered trademark symbol) emacs: C-q M-. … … 68 45 % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ 69 46 % math escape $...$ (dollar symbol) 47 \input{common} % common CFA document macros 48 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 49 \usepackage{breakurl} 50 51 \renewcommand\footnoterule{\kern -3pt\rule{0.3\linewidth}{0.15pt}\kern 2pt} 52 53 \usepackage[pagewise]{lineno} 54 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 55 \usepackage[firstpage]{draftwatermark} 56 \SetWatermarkLightness{0.9} 57 58 % Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore 59 % removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR 60 % AFTER HYPERREF. 61 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 62 63 \setlength{\topmargin}{-0.45in} % move running title into header 64 \setlength{\headsep}{0.25in} 65 66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 67 68 \CFAStyle % use default CFA format-style 69 \lstnewenvironment{C++}[1][] % use C++ style 70 {\lstset{language=C++,moredelim=**[is][\protect\color{red}]{Ā®}{Ā®},#1}} 71 {} 72 73 \newsavebox{\myboxA} 74 \newsavebox{\myboxB} 70 75 71 76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 79 84 \newcommand{\G}[1]{{\Textbf[OliveGreen]{#1}}} 80 85 \newcommand{\KWC}{K-W C\xspace} 81 82 \newsavebox{\LstBox}83 86 84 87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 211 214 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. 212 215 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. 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 30years are: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 35 years are: 215 218 \begin{center} 216 219 \setlength{\tabcolsep}{10pt} 217 \begin{tabular}{@{}rccccccc@{}} 218 & 2018 & 2013 & 2008 & 2003 & 1998 & 1993 & 1988 \\ \hline 219 Java & 1 & 2 & 1 & 1 & 16 & - & - \\ 220 \R{C} & \R{2} & \R{1} & \R{2} & \R{2} & \R{1} & \R{1} & \R{1} \\ 221 \CC & 3 & 4 & 3 & 3 & 2 & 2 & 5 \\ 220 \begin{tabular}{@{}rcccccccc@{}} 221 & 2020 & 2015 & 2010 & 2005 & 2000 & 1995 & 1990 & 1985 \\ \hline 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 \\ 222 226 \end{tabular} 223 227 \end{center} … … 252 256 253 257 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): 254 \begin{ lstlisting}258 \begin{cfa} 255 259 Ā®forall( otype T )Ā® T identity( T val ) { return val; } 256 260 int forty_two = identity( 42 ); §\C{// T is bound to int, forty\_two == 42}§ 257 \end{ lstlisting}261 \end{cfa} 258 262 % extending the C type system with parametric polymorphism and overloading, as opposed to the \Index*[C++]{\CC{}} approach of object-oriented extensions. 259 263 \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}. … … 274 278 \begin{comment} 275 279 A simple example is leveraging the existing type-unsafe (Ā©void *Ā©) C Ā©bsearchĀ© to binary search a sorted floating array: 276 \begin{ lstlisting}280 \begin{cfa} 277 281 void * bsearch( const void * key, const void * base, size_t dim, size_t size, 278 282 int (* compar)( const void *, const void * )); … … 283 287 double key = 5.0, vals[10] = { /* 10 sorted floating values */ }; 284 288 double * val = (double *)bsearch( &key, vals, 10, sizeof(vals[0]), comp ); §\C{// search sorted array}§ 285 \end{ lstlisting}289 \end{cfa} 286 290 which can be augmented simply with a polymorphic, type-safe, \CFA-overloaded wrappers: 287 \begin{ lstlisting}291 \begin{cfa} 288 292 forall( otype T | { int ?<?( T, T ); } ) T * bsearch( T key, const T * arr, size_t size ) { 289 293 int comp( const void * t1, const void * t2 ) { /* as above with double changed to T */ } … … 296 300 double * val = bsearch( 5.0, vals, 10 ); §\C{// selection based on return type}§ 297 301 int posn = bsearch( 5.0, vals, 10 ); 298 \end{ lstlisting}302 \end{cfa} 299 303 The nested function Ā©compĀ© provides the hidden interface from typed \CFA to untyped (Ā©void *Ā©) C, plus the cast of the result. 300 304 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. … … 304 308 \CFA has replacement libraries condensing hundreds of existing C functions into tens of \CFA overloaded functions, all without rewriting the actual computations. 305 309 For example, it is possible to write a type-safe \CFA wrapper Ā©mallocĀ© based on the C Ā©mallocĀ©: 306 \begin{ lstlisting}310 \begin{cfa} 307 311 forall( dtype T | sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); } 308 312 int * ip = malloc(); §\C{// select type and size from left-hand side}§ 309 313 double * dp = malloc(); 310 314 struct S {...} * sp = malloc(); 311 \end{ lstlisting}315 \end{cfa} 312 316 where the return type supplies the type/size of the allocation, which is impossible in most type systems. 313 317 \end{comment} … … 512 516 Keyword clashes are accommodated by syntactic transformations using the \CFA backquote escape-mechanism: 513 517 \begin{cfa} 514 int Ā®` Ā®otypeĀ®`Ā®= 3; §\C{// make keyword an identifier}§515 double Ā®` Ā®forallĀ®`Ā®= 3.5;518 int Ā®``Ā®otype = 3; §\C{// make keyword an identifier}§ 519 double Ā®``Ā®forall = 3.5; 516 520 \end{cfa} 517 521 … … 524 528 // include file uses the CFA keyword "with". 525 529 #if ! defined( with ) §\C{// nesting ?}§ 526 #define with Ā®` Ā®withĀ®`®§\C{// make keyword an identifier}§530 #define with Ā®``Ā®with §\C{// make keyword an identifier}§ 527 531 #define __CFA_BFD_H__ 528 532 #endif 529 530 Ā®#include_next <bfdlink.h> §\C{// must have internal check for multiple expansion}§ 531 Ā® 533 §{\color{red}\#\textbf{include\_next} <bfdlink.h>}§ §\C{// must have internal check for multiple expansion}§ 532 534 #if defined( with ) && defined( __CFA_BFD_H__ ) §\C{// reset only if set}§ 533 535 #undef with … … 576 578 \section{Exponentiation Operator} 577 579 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$.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$. 580 582 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)Ā©. 581 583 582 As for \Index{division}, there are exponentiation operators for integral and floating types, including the builtin \Index{complex} types.584 There are exponentiation operators for integral and floating types, including the builtin \Index{complex} types. 583 585 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). 584 Overflow f rom large exponents or negative exponents returnzero.586 Overflow for a large exponent or negative exponent returns zero. 585 587 Floating exponentiation\index{exponentiation!floating} is performed using \Index{logarithm}s\index{exponentiation!logarithm}, so the exponent cannot be negative. 586 588 \begin{cfa} … … 589 591 1 1 256 -64 125 Ā®0Ā® 3273344365508751233 Ā®0Ā® Ā®0Ā® -0.015625 18.3791736799526 0.264715-1.1922i 590 592 \end{cfa} 591 Note, Ā©5 Ā®\Ā® 32Ā© and Ā©5L Ā®\Ā® 64Ā© overflow, and Ā©-4 Ā®\Ā®-3Ā© is a fraction but stored in an integer so all three computations generate an integral zero.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. 592 594 Parenthesis are necessary for complex constants or the expression is parsed as Ā©1.0f+Ā®(Ā®2.0fi \ 3.0fĀ®)Ā®+2.0fiĀ©. 593 595 The exponentiation operator is available for all the basic types, but for user-defined types, only the integral-computation version is available. … … 598 600 OT ?Ā®\Ā®?( OT ep, unsigned long int y ); 599 601 \end{cfa} 600 The user type Ā©TĀ© must define multiplication, one , Ā©1Ā©, and,Ā©*Ā©.602 The user type Ā©TĀ© must define multiplication, one (Ā©1Ā©), and Ā©*Ā©. 601 603 602 604 … … 626 628 627 629 628 \subsection{Loop Control} 629 630 The Ā©forĀ©/Ā©whileĀ©/Ā©do-whileĀ© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}). 631 \begin{itemize} 632 \item 633 An empty conditional implies Ā©1Ā©. 634 \item 635 The up-to range Ā©~Ā©\index{~@Ā©~Ā©} means exclusive range [M,N). 636 \item 637 The up-to range Ā©~=Ā©\index{~=@Ā©~=Ā©} means inclusive range [M,N]. 638 \item 639 The down-to range Ā©-~Ā©\index{-~@Ā©-~Ā©} means exclusive range [N,M). 640 \item 641 The down-to range Ā©-~=Ā©\index{-~=@Ā©-~=Ā©} means inclusive range [N,M]. 642 \item 643 Ā©@Ā© means put nothing in this field. 644 \item 645 Ā©0Ā© is the implicit start value; 646 \item 647 Ā©1Ā© is the implicit increment value. 648 \item 649 The up-to range uses Ā©+=Ā© for increment; 650 \item 651 The down-to range uses Ā©-=Ā© for decrement. 652 \item 653 The loop index is polymorphic in the type of the start value or comparison value when start is implicitly Ā©0Ā©. 654 \end{itemize} 655 656 \begin{figure} 630 %\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}} 631 \subsection{\texorpdfstring{\LstKeywordStyle{case} Clause}{case Clause}} 632 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: 657 637 \begin{cquote} 658 \begin{tabular}{@{}l|l@{}} 659 \multicolumn{1}{c|}{loop control} & \multicolumn{1}{c}{output} \\ 660 \hline 661 \begin{cfa} 662 sout | nlOff; 663 while Ā®()Ā® { sout | "empty"; break; } sout | nl; 664 do { sout | "empty"; break; } while Ā®()Ā®; sout | nl; 665 for Ā®()Ā® { sout | "empty"; break; } sout | nl; 666 for ( Ā®0Ā® ) { sout | "A"; } sout | "zero" | nl; 667 for ( Ā®1Ā® ) { sout | "A"; } sout | nl; 668 for ( Ā®10Ā® ) { sout | "A"; } sout | nl; 669 for ( Ā®1 ~= 10 ~ 2Ā® ) { sout | "B"; } sout | nl; 670 for ( Ā®10 -~= 1 ~ 2Ā® ) { sout | "C"; } sout | nl; 671 for ( Ā®0.5 ~ 5.5Ā® ) { sout | "D"; } sout | nl; 672 for ( Ā®5.5 -~ 0.5Ā® ) { sout | "E"; } sout | nl; 673 for ( Ā®i; 10Ā® ) { sout | i; } sout | nl; 674 for ( Ā®i; 1 ~= 10 ~ 2Ā® ) { sout | i; } sout | nl; 675 for ( Ā®i; 10 -~= 1 ~ 2Ā® ) { sout | i; } sout | nl; 676 for ( Ā®i; 0.5 ~ 5.5Ā® ) { sout | i; } sout | nl; 677 for ( Ā®i; 5.5 -~ 0.5Ā® ) { sout | i; } sout | nl; 678 for ( Ā®ui; 2u ~= 10u ~ 2uĀ® ) { sout | ui; } sout | nl; 679 for ( Ā®ui; 10u -~= 2u ~ 2uĀ® ) { sout | ui; } sout | nl; 680 enum { N = 10 }; 681 for ( Ā®NĀ® ) { sout | "N"; } sout | nl; 682 for ( Ā®i; NĀ® ) { sout | i; } sout | nl; 683 for ( Ā®i; N -~ 0Ā® ) { sout | i; } sout | nl; 684 const int start = 3, comp = 10, inc = 2; 685 for ( Ā®i; start ~ comp ~ inc + 1Ā® ) { sout | i; } sout | nl; 686 for ( Ā®i; 1 ~ @Ā® ) { if ( i > 10 ) break; 687 sout | i; } sout | nl; 688 for ( Ā®i; 10 -~ @Ā® ) { if ( i < 0 ) break; 689 sout | i; } sout | nl; 690 for ( Ā®i; 2 ~ @ ~ 2Ā® ) { if ( i > 10 ) break; 691 sout | i; } sout | nl; 692 for ( Ā®i; 2.1 ~ @ ~ @Ā® ) { if ( i > 10.5 ) break; 693 sout | i; i += 1.7; } sout | nl; 694 for ( Ā®i; 10 -~ @ ~ 2Ā® ) { if ( i < 0 ) break; 695 sout | i; } sout | nl; 696 for ( Ā®i; 12.1 ~ @ ~ @Ā® ) { if ( i < 2.5 ) break; 697 sout | i; i -= 1.7; } sout | nl; 698 for ( Ā®i; 5 : j; -5 ~ @Ā® ) { sout | i | j; } sout | nl; 699 for ( Ā®i; 5 : j; -5 -~ @Ā® ) { sout | i | j; } sout | nl; 700 for ( Ā®i; 5 : j; -5 ~ @ ~ 2Ā® ) { sout | i | j; } sout | nl; 701 for ( Ā®i; 5 : j; -5 -~ @ ~ 2Ā® ) { sout | i | j; } sout | nl; 702 for ( Ā®j; -5 ~ @ : i; 5Ā® ) { sout | i | j; } sout | nl; 703 for ( Ā®j; -5 -~ @ : i; 5Ā® ) { sout | i | j; } sout | nl; 704 for ( Ā®j; -5 ~ @ ~ 2 : i; 5Ā® ) { sout | i | j; } sout | nl; 705 for ( Ā®j; -5 -~ @ ~ 2 : i; 5Ā® ) { sout | i | j; } sout | nl; 706 for ( Ā®j; -5 -~ @ ~ 2 : i; 5 : k; 1.5 ~ @Ā® ) { 707 sout | i | j | k; } sout | nl; 708 for ( Ā®j; -5 -~ @ ~ 2 : k; 1.5 ~ @ : i; 5Ā® ) { 709 sout | i | j | k; } sout | nl; 710 for ( Ā®k; 1.5 ~ @ : j; -5 -~ @ ~ 2 : i; 5Ā® ) { 711 sout | i | j | k; } sout | nl; 638 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}} 639 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{C}} \\ 640 \begin{cfa} 641 switch ( i ) { 642 case Ā®1, 3, 5Ā®: 643 ... 644 case Ā®2, 4, 6Ā®: 645 ... 646 } 712 647 \end{cfa} 713 648 & 714 649 \begin{cfa} 715 716 empty 717 empty 718 empty 719 zero 720 A 721 A A A A A A A A A A 722 B B B B B 723 C C C C C 724 D D D D D 725 E E E E E 726 0 1 2 3 4 5 6 7 8 9 727 1 3 5 7 9 728 10 8 6 4 2 729 0.5 1.5 2.5 3.5 4.5 730 5.5 4.5 3.5 2.5 1.5 731 2 4 6 8 10 732 10 8 6 4 2 733 734 N N N N N N N N N N 735 0 1 2 3 4 5 6 7 8 9 736 10 9 8 7 6 5 4 3 2 1 737 738 3 6 9 739 740 1 2 3 4 5 6 7 8 9 10 741 742 10 9 8 7 6 5 4 3 2 1 0 743 744 2 4 6 8 10 745 746 2.1 3.8 5.5 7.2 8.9 747 748 10 8 6 4 2 0 749 750 12.1 10.4 8.7 7 5.3 3.6 751 0 -5 1 -4 2 -3 3 -2 4 -1 752 0 -5 1 -6 2 -7 3 -8 4 -9 753 0 -5 1 -3 2 -1 3 1 4 3 754 0 -5 1 -7 2 -9 3 -11 4 -13 755 0 -5 1 -4 2 -3 3 -2 4 -1 756 0 -5 1 -6 2 -7 3 -8 4 -9 757 0 -5 1 -3 2 -1 3 1 4 3 758 0 -5 1 -7 2 -9 3 -11 4 -13 759 760 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 761 762 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 763 764 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 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 765 665 \end{cfa} 766 666 \end{tabular} 767 667 \end{cquote} 768 \caption{Loop Control Examples} 769 \label{f:LoopControlExamples} 770 \end{figure} 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} 771 682 772 683 … … 977 888 978 889 979 %\section{\texorpdfstring{\protect\lstinline@case@ Clause}{case Clause}} 980 \subsection{\texorpdfstring{\LstKeywordStyle{case} Statement}{case Statement}} 981 982 C restricts the Ā©caseĀ© clause of a Ā©switchĀ© statement to a single value. 983 For multiple Ā©caseĀ© clauses associated with the same statement, it is necessary to have multiple Ā©caseĀ© clauses rather than multiple values. 984 Requiring a Ā©caseĀ© clause for each value does not seem to be in the spirit of brevity normally associated with C. 985 Therefore, the Ā©caseĀ© clause is extended with a list of values, as in: 986 \begin{cquote} 987 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{2em}}l@{}} 988 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{C}} \\ 989 \begin{cfa} 990 switch ( i ) { 991 case Ā®1, 3, 5Ā®: 890 \subsection{Non-terminating and Labelled \texorpdfstring{\LstKeywordStyle{fallthrough}}{Non-terminating and Labelled fallthrough}} 891 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 909 \end{cfa} 910 & 911 \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 925 \end{cfa} 926 & 927 \begin{cfa} 928 choose ( ... ) { 929 case 3: 930 choose ( ... ) { 931 case 4: 932 for ( ... ) { 933 // multi-level transfer 934 ... Ā®fallthru common;Ā® 935 } 936 ... 937 } 992 938 ... 993 case Ā®2, 4, 6Ā®: 994 ... 995 } 939 Ā®common:Ā® // below fallthrough 940 // at case-clause level 941 \end{cfa} 942 \end{tabular} 943 \end{center} 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 996 \end{cfa} 997 997 & 998 998 \begin{cfa} 999 switch ( i ) { 1000 case 1: case 3 : case 5: 1001 ... 1002 case 2: case 4 : case 6: 1003 ... 1004 } 1005 \end{cfa} 1006 & 1007 \begin{cfa} 1008 1009 // odd values 1010 1011 // even values 1012 1013 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 1014 1041 \end{cfa} 1015 1042 \end{tabular} 1016 \end{cquote} 1017 In addition, subranges are allowed to specify case values.\footnote{ 1018 gcc has the same mechanism but awkward syntax, \lstinline@2 ...42@, because a space is required after a number, otherwise the period is a decimal point.} 1019 \begin{cfa} 1020 switch ( i ) { 1021 case Ā®1~5:Ā® §\C{// 1, 2, 3, 4, 5}§ 1022 ... 1023 case Ā®10~15:Ā® §\C{// 10, 11, 12, 13, 14, 15}§ 1024 ... 1025 } 1026 \end{cfa} 1027 Lists of subranges are also allowed. 1028 \begin{cfa} 1029 case Ā®1~5, 12~21, 35~42Ā®: 1030 \end{cfa} 1031 1043 \caption{Loop Control Examples} 1044 \label{f:LoopControlExamples} 1045 \end{figure} 1032 1046 1033 1047 % for () => for ( ;; ) … … 1040 1054 1041 1055 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 \item 1061 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 \item 1063 An empty conditional implies comparison value of Ā©1Ā© (true). 1064 \item 1065 A comparison N is implicit up-to exclusive range [0,N©®)®©. 1066 \item 1067 A comparison Ā©=Ā© N is implicit up-to inclusive range [0,N©®]®©. 1068 \item 1069 The up-to range M Ā©~Ā©\index{~@Ā©~Ā©} N means exclusive range [M,N©®)®©. 1070 \item 1071 The up-to range M Ā©~=Ā©\index{~=@Ā©~=Ā©} N means inclusive range [M,N©®]®©. 1072 \item 1073 The down-to range M Ā©-~Ā©\index{-~@Ā©-~Ā©} N means exclusive range [N,M©®)®©. 1074 \item 1075 The down-to range M Ā©-~=Ā©\index{-~=@Ā©-~=Ā©} N means inclusive range [N,M©®]®©. 1076 \item 1077 Ā©0Ā© is the implicit start value; 1078 \item 1079 Ā©1Ā© is the implicit increment value. 1080 \item 1081 The up-to range uses operator Ā©+=Ā© for increment; 1082 \item 1083 The down-to range uses operator Ā©-=Ā© for decrement. 1084 \item 1085 Ā©@Ā© means put nothing in this field. 1086 \item 1087 Ā©:Ā© means start another index. 1088 \end{itemize} 1089 1090 1042 1091 %\subsection{\texorpdfstring{Labelled \protect\lstinline@continue@ / \protect\lstinline@break@}{Labelled continue / break}} 1043 1092 \subsection{\texorpdfstring{Labelled \LstKeywordStyle{continue} / \LstKeywordStyle{break} Statement}{Labelled continue / break Statement}} … … 1049 1098 for Ā©breakĀ©, the target label can also be associated with a Ā©switchĀ©, Ā©ifĀ© or compound (Ā©{}Ā©) statement. 1050 1099 \VRef[Figure]{f:MultiLevelExit} shows Ā©continueĀ© and Ā©breakĀ© indicating the specific control structure, and the corresponding C program using only Ā©gotoĀ© and labels. 1051 The innermost loop has 7exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s.1100 The innermost loop has 8 exit points, which cause continuation or termination of one or more of the 7 \Index{nested control-structure}s. 1052 1101 1053 1102 \begin{figure} 1054 \begin{tabular}{@{\hspace{\parindentlnth}}l@{\hspace{\parindentlnth}}l@{\hspace{\parindentlnth}}l@{}} 1055 \multicolumn{1}{@{\hspace{\parindentlnth}}c@{\hspace{\parindentlnth}}}{\textbf{\CFA}} & \multicolumn{1}{@{\hspace{\parindentlnth}}c}{\textbf{C}} \\ 1056 \begin{cfa} 1057 Ā®LC:Ā® { 1058 ... §declarations§ ... 1059 Ā®LS:Ā® switch ( ... ) { 1060 case 3: 1061 Ā®LIF:Ā® if ( ... ) { 1062 Ā®LF:Ā® for ( ... ) { 1063 Ā®LW:Ā® while ( ... ) { 1064 ... break Ā®LCĀ®; ... 1065 ... break Ā®LSĀ®; ... 1066 ... break Ā®LIFĀ®; ... 1067 ... continue Ā®LF;Ā® ... 1068 ... break Ā®LFĀ®; ... 1069 ... continue Ā®LWĀ®; ... 1070 ... break Ā®LWĀ®; ... 1071 } // while 1072 } // for 1073 } else { 1074 ... break Ā®LIFĀ®; ... 1075 } // if 1076 } // switch 1103 \centering 1104 \begin{lrbox}{\myboxA} 1105 \begin{cfa}[tabsize=3] 1106 Ā®Compound:Ā® { 1107 Ā®Try:Ā® try { 1108 Ā®For:Ā® for ( ... ) { 1109 Ā®While:Ā® while ( ... ) { 1110 Ā®Do:Ā® do { 1111 Ā®If:Ā® if ( ... ) { 1112 Ā®Switch:Ā® switch ( ... ) { 1113 case 3: 1114 Ā®break CompoundĀ®; 1115 Ā®break TryĀ®; 1116 Ā®break ForĀ®; /* or */ Ā®continue ForĀ®; 1117 Ā®break WhileĀ®; /* or */ Ā®continue WhileĀ®; 1118 Ā®break DoĀ®; /* or */ Ā®continue DoĀ®; 1119 Ā®break IfĀ®; 1120 Ā®break SwitchĀ®; 1121 } // switch 1122 } else { 1123 ... Ā®break IfĀ®; ... // terminate if 1124 } // if 1125 } while ( ... ); // do 1126 } // while 1127 } // for 1128 } Ā®finallyĀ® { // always executed 1129 } // try 1077 1130 } // compound 1078 1131 \end{cfa} 1079 & 1080 \begin{cfa} 1132 \end{lrbox} 1133 1134 \begin{lrbox}{\myboxB} 1135 \begin{cfa}[tabsize=3] 1081 1136 { 1082 ... §declarations§ ... 1083 switch ( ... ) { 1084 case 3: 1085 if ( ... ) { 1086 for ( ... ) { 1087 while ( ... ) { 1088 ... goto Ā®LCĀ®; ... 1089 ... goto Ā®LSĀ®; ... 1090 ... goto Ā®LIFĀ®; ... 1091 ... goto Ā®LFCĀ®; ... 1092 ... goto Ā®LFBĀ®; ... 1093 ... goto Ā®LWCĀ®; ... 1094 ... goto Ā®LWBĀ®; ... 1095 Ā®LWCĀ®: ; } Ā®LWB:Ā® ; 1096 Ā®LFC:Ā® ; } Ā®LFB:Ā® ; 1097 } else { 1098 ... goto Ā®LIFĀ®; ... 1099 } Ā®L3:Ā® ; 1100 } Ā®LS:Ā® ; 1101 } Ā®LC:Ā® ; 1102 \end{cfa} 1103 & 1104 \begin{cfa} 1105 1106 1107 1108 1109 1110 1111 1112 // terminate compound 1113 // terminate switch 1114 // terminate if 1115 // continue loop 1116 // terminate loop 1117 // continue loop 1118 // terminate loop 1119 1120 1121 1122 // terminate if 1123 1124 1125 1126 \end{cfa} 1127 \end{tabular} 1137 1138 Ā®ForC:Ā® for ( ... ) { 1139 Ā®WhileC:Ā® while ( ... ) { 1140 Ā®DoC:Ā® do { 1141 if ( ... ) { 1142 switch ( ... ) { 1143 case 3: 1144 Ā®goto CompoundĀ®; 1145 Ā®goto TryĀ®; 1146 Ā®goto ForBĀ®; /* or */ Ā®goto ForCĀ®; 1147 Ā®goto WhileBĀ®; /* or */ Ā®goto WhileCĀ®; 1148 Ā®goto DoBĀ®; /* or */ Ā®goto DoCĀ®; 1149 Ā®goto IfĀ®; 1150 Ā®goto SwitchĀ®; 1151 } Ā®Switch:Ā® ; 1152 } else { 1153 ... Ā®goto IfĀ®; ... // terminate if 1154 } Ā®If:Ā®; 1155 } while ( ... ); Ā®DoB:Ā® ; 1156 } Ā®WhileB:Ā® ; 1157 } Ā®ForB:Ā® ; 1158 1159 1160 } Ā®Compound:Ā® ; 1161 \end{cfa} 1162 \end{lrbox} 1163 1164 \subfloat[\CFA]{\label{f:CFibonacci}\usebox\myboxA} 1165 \hspace{2pt} 1166 \vrule 1167 \hspace{2pt} 1168 \subfloat[C]{\label{f:CFAFibonacciGen}\usebox\myboxB} 1128 1169 \caption{Multi-level Exit} 1129 1170 \label{f:MultiLevelExit} … … 1380 1421 try { 1381 1422 f(...); 1382 } catch( E e ; §boolean-predicate§ ) { §\C [8cm]{// termination handler}§1423 } catch( E e ; §boolean-predicate§ ) { §\C{// termination handler}§ 1383 1424 // recover and continue 1384 } catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler} \CRT§1425 } catchResume( E e ; §boolean-predicate§ ) { §\C{// resumption handler}§ 1385 1426 // repair and return 1386 1427 } finally { … … 3445 3486 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. 3446 3487 \begin{cquote} 3447 \begin{lrbox}{\ LstBox}3488 \begin{lrbox}{\myboxA} 3448 3489 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 3449 3490 int x; double y char z; … … 3451 3492 \end{lrbox} 3452 3493 \begin{tabular}{@{}l@{\hspace{3em}}l@{\hspace{3em}}l@{}} 3453 \multicolumn{1}{@{}l@{}}{\usebox\ LstBox} \\3494 \multicolumn{1}{@{}l@{}}{\usebox\myboxA} \\ 3454 3495 \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}} & \multicolumn{1}{c}{\textbf{Python}} \\ 3455 3496 \begin{cfa}[aboveskip=0pt,belowskip=0pt] … … 6547 6588 hence, names in these include files are not mangled\index{mangling!name} (see~\VRef{s:Interoperability}). 6548 6589 All other C header files must be explicitly wrapped in Ā©extern "C"Ā© to prevent name mangling. 6549 For \Index*[C++]{\CC{}}, the name-mangling issue is often handled internally in manyC header-files through checks for preprocessor variable Ā©__cplusplusĀ©, which adds appropriate Ā©extern "C"Ā© qualifiers.6590 This approach is different from \Index*[C++]{\CC{}} where the name-mangling issue is handled internally in C header-files through checks for preprocessor variable Ā©__cplusplusĀ©, which adds appropriate Ā©extern "C"Ā© qualifiers. 6550 6591 6551 6592 … … 6561 6602 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. 6562 6603 6563 Storage management provides the following capabilities:6604 C storage management provides the following capabilities: 6564 6605 \begin{description} 6565 \item[fill ]6566 after allocation the storage is filled with a specified character.6606 \item[filled] 6607 after allocation with a specified character or value. 6567 6608 \item[resize] 6568 an existing allocation is decreased or increased insize.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 .6609 an existing allocation to decreased or increased its size. 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. 6570 6611 For an increase in storage size, new storage after the copied data may be filled. 6571 \item[align ment]6572 an allocation startson a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes.6612 \item[align] 6613 an allocation on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes. 6573 6614 \item[array] 6574 6615 the allocation size is scaled to the specified number of array elements. 6575 6616 An array may be filled, resized, or aligned. 6576 6617 \end{description} 6577 The table shows allocation routines supporting different combinations of storage-management capabilities: 6578 \begin{center} 6579 \begin{tabular}{@{}r|r|l|l|l|l@{}} 6618 \VRef[Table]{t:AllocationVersusCapabilities} shows allocation routines supporting different combinations of storage-management capabilities. 6619 \begin{table} 6620 \centering 6621 \begin{minipage}{0.75\textwidth} 6622 \begin{tabular}{@{}r|l|l|l|l|l@{}} 6580 6623 \multicolumn{1}{c}{}& & \multicolumn{1}{c|}{fill} & resize & alignment & array \\ 6581 6624 \hline 6582 6625 C & Ā©mallocĀ© & no & no & no & no \\ 6583 6626 & Ā©callocĀ© & yes (0 only) & no & no & yes \\ 6584 & Ā©reallocĀ© & no/copy& yes & no & no \\6627 & Ā©reallocĀ© & copy & yes & no & no \\ 6585 6628 & Ā©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 \\ 6586 6631 & Ā©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 \\ 6587 6635 \hline 6588 C11 & Ā©aligned_allocĀ© & no & no & yes & no \\ 6589 \hline 6590 \CFA & Ā©allocĀ© & no/copy/yes & no/yes & no & yes \\ 6591 & Ā©align_allocĀ© & no/yes & no & yes & yes \\ 6636 \CFA & Ā©cmemalignĀ© & yes (0 only) & no & yes & yes \\ 6637 & Ā©reallocĀ© & copy & yes & yes & no \\ 6638 & Ā©allocĀ© & no & yes & no & yes \\ 6639 & Ā©alloc_setĀ© & yes & yes & no & yes \\ 6640 & Ā©alloc_alignĀ© & no & yes & yes & yes \\ 6641 & Ā©alloc_align_setĀ© & yes & yes & yes & yes \\ 6592 6642 \end{tabular} 6593 \end{center} 6594 It is impossible to resize with alignment because the underlying Ā©reallocĀ© allocates storage if more space is needed, and it does not honour alignment from the original allocation. 6643 \end{minipage} 6644 \caption{Allocation Routines versus Storage-Management Capabilities} 6645 \label{t:AllocationVersusCapabilities} 6646 \end{table} 6647 6648 \CFA memory management extends the type safety of all allocations by using the type of the left-hand-side type to determine the allocation size and return a matching type for the new storage. 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Ā©. 6595 6690 6596 6691 \leavevmode 6597 6692 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 6598 // C unsafe allocation6599 6693 extern "C" { 6600 void * malloc( size_t size );§\indexc{memset}§ 6601 void * calloc( size_t dim, size_t size );§\indexc{calloc}§ 6602 void * realloc( void * ptr, size_t size );§\indexc{realloc}§ 6603 void * memalign( size_t align, size_t size );§\indexc{memalign}§ 6604 int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§ 6605 6606 // C unsafe initialization/copy 6607 void * memset( void * dest, int c, size_t size ); 6608 void * memcpy( void * dest, const void * src, size_t size ); 6609 } 6694 // C unsafe allocation 6695 void * malloc( size_t size );§\indexc{malloc}§ 6696 void * calloc( size_t dim, size_t size );§\indexc{calloc}§ 6697 void * realloc( void * ptr, size_t size );§\indexc{realloc}§ 6698 void * memalign( size_t align, size_t size );§\indexc{memalign}§ 6699 void * aligned_alloc( size_t align, size_t size );§\indexc{aligned_alloc}§ 6700 int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§ 6701 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize );§\indexc{cmemalign}§ // CFA 6702 6703 // C unsafe initialization/copy 6704 void * memset( void * dest, int c, size_t size );§\indexc{memset}§ 6705 void * memcpy( void * dest, const void * src, size_t size );§\indexc{memcpy}§ 6706 } 6707 6708 void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap 6610 6709 6611 6710 forall( dtype T | sized(T) ) { 6612 // §\CFA§ safe equivalents, i.e., implicit size specification6711 // §\CFA§ safe equivalents, i.e., implicit size specification 6613 6712 T * malloc( void ); 6614 6713 T * calloc( size_t dim ); 6615 6714 T * realloc( T * ptr, size_t size ); 6616 6715 T * memalign( size_t align ); 6716 T * cmemalign( size_t align, size_t dim ); 6617 6717 T * aligned_alloc( size_t align ); 6618 6718 int posix_memalign( T ** ptr, size_t align ); 6619 6719 6620 // §\CFA§ safe general allocation, fill, resize, array 6621 T * alloc( void );§\indexc{alloc}§ 6622 T * alloc( char fill ); 6623 T * alloc( size_t dim ); 6624 T * alloc( size_t dim, char fill ); 6625 T * alloc( T ptr[], size_t dim ); 6626 T * alloc( T ptr[], size_t dim, char fill ); 6627 6628 // §\CFA§ safe general allocation, align, fill, array 6629 T * align_alloc( size_t align ); 6630 T * align_alloc( size_t align, char fill ); 6631 T * align_alloc( size_t align, size_t dim ); 6632 T * align_alloc( size_t align, size_t dim, char fill ); 6633 6634 // §\CFA§ safe initialization/copy, i.e., implicit size specification 6635 T * memset( T * dest, char c );§\indexc{memset}§ 6720 // §\CFA§ safe general allocation, fill, resize, alignment, array 6721 T * alloc( void );§\indexc{alloc}§ §\C[3.5in]{// variable, T size}§ 6722 T * alloc( size_t dim ); §\C{// array[dim], T size elements}§ 6723 T * alloc( T ptr[], size_t dim ); §\C{// realloc array[dim], T size elements}§ 6724 6725 T * alloc_set( char fill );§\indexc{alloc_set}§ §\C{// variable, T size, fill bytes with value}§ 6726 T * alloc_set( T fill ); §\C{// variable, T size, fill with value}§ 6727 T * alloc_set( size_t dim, char fill ); §\C{// array[dim], T size elements, fill bytes with value}§ 6728 T * alloc_set( size_t dim, T fill ); §\C{// array[dim], T size elements, fill elements with value}§ 6729 T * alloc_set( size_t dim, const T fill[] ); §\C{// array[dim], T size elements, fill elements with array}§ 6730 T * alloc_set( T ptr[], size_t dim, char fill ); §\C{// realloc array[dim], T size elements, fill bytes with value}§ 6731 6732 T * alloc_align( size_t align ); §\C{// aligned variable, T size}§ 6733 T * alloc_align( size_t align, size_t dim ); §\C{// aligned array[dim], T size elements}§ 6734 T * alloc_align( T ptr[], size_t align ); §\C{// realloc new aligned array}§ 6735 T * alloc_align( T ptr[], size_t align, size_t dim ); §\C{// realloc new aligned array[dim]}§ 6736 6737 T * alloc_align_set( size_t align, char fill ); §\C{// aligned variable, T size, fill bytes with value}§ 6738 T * alloc_align_set( size_t align, T fill ); §\C{// aligned variable, T size, fill with value}§ 6739 T * alloc_align_set( size_t align, size_t dim, char fill ); §\C{// aligned array[dim], T size elements, fill bytes with value}§ 6740 T * alloc_align_set( size_t align, size_t dim, T fill ); §\C{// aligned array[dim], T size elements, fill elements with value}§ 6741 T * alloc_align_set( size_t align, size_t dim, const T fill[] ); §\C{// aligned array[dim], T size elements, fill elements with array}§ 6742 T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); §\C{// realloc new aligned array[dim], fill new bytes with value}§ 6743 6744 // §\CFA§ safe initialization/copy, i.e., implicit size specification 6745 T * memset( T * dest, char fill );§\indexc{memset}§ 6636 6746 T * memcpy( T * dest, const T * src );§\indexc{memcpy}§ 6637 6747 6638 // §\CFA§ safe initialization/copy array 6639 T * amemset( T dest[], char c, size_t dim );6748 // §\CFA§ safe initialization/copy, i.e., implicit size specification, array types 6749 T * amemset( T dest[], char fill, size_t dim ); 6640 6750 T * amemcpy( T dest[], const T src[], size_t dim ); 6641 6751 } 6642 6752 6643 // §\CFA§ allocation/deallocation and constructor/destructor 6644 forall( dtype T | sized(T), ttype Params | { void ?{}( T *, Params ); } ) T * new( Params p );§\indexc{new}§6645 forall( dtype T | { void ^?{}( T *); } ) void delete( T * ptr );§\indexc{delete}§6646 forall( dtype T, ttype Params | { void ^?{}( T *); void delete( Params ); } )6753 // §\CFA§ allocation/deallocation and constructor/destructor, non-array types 6754 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 ); } ) 6647 6757 void delete( T * ptr, Params rest ); 6648 6758 6649 // §\CFA§ allocation/deallocation and constructor/destructor, array 6650 forall( dtype T | sized(T), ttype Params | { void ?{}( T *, Params ); } ) T * anew( size_t dim, Params p );§\indexc{anew}§6651 forall( dtype T | sized(T) | { void ^?{}( T *); } ) void adelete( size_t dim, T arr[] );§\indexc{adelete}§6652 forall( dtype T | sized(T) | { void ^?{}( T *); }, ttype Params | { void adelete( Params ); } )6759 // §\CFA§ allocation/deallocation and constructor/destructor, array types 6760 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 ); } ) 6653 6763 void adelete( size_t dim, T arr[], Params rest ); 6654 6764 \end{cfa} -
driver/Makefile.am
r3c64c668 r58fe85a 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);\ 30 31 $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) cfa $(CFA_BINDIR)/$(CFA_NAME) || exit $$? 31 32 -
driver/cc1.cc
r3c64c668 r58fe85a 10 10 // Created On : Fri Aug 26 14:23:51 2005 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 20 08:14:33 201913 // Update Count : 38512 // Last Modified On : Tue Nov 17 14:27:08 2020 13 // Update Count : 414 14 14 // 15 15 … … 24 24 #include <unistd.h> // execvp, fork, unlink 25 25 #include <sys/wait.h> // wait 26 #include <fcntl.h> 26 #include <fcntl.h> // creat 27 27 28 28 … … 38 38 static string o_file; 39 39 static string bprefix; 40 static string lang; // -x flag 40 41 41 42 … … 58 59 59 60 60 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); // " N__=" suffix61 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); // "__CFA_FLAG__=" suffix 61 62 62 63 static void checkEnv1( const char * args[], int & nargs ) { // stage 1 … … 73 74 if ( prefix( val, "-compiler=" ) ) { 74 75 compiler_path = val.substr( 10 ); 76 } else if ( prefix( val, "-x=" ) ) { 77 lang = val.substr( 3 ); 75 78 } // if 76 79 } // if … … 94 97 } else if ( val == "-CFA" ) { 95 98 CFA_flag = true; 96 } else if ( val == "-save-temps" ) {99 } else if ( val == "-save-temps" || val == "--save-temps" ) { 97 100 save_temps = true; 98 101 } else if ( prefix( val, "-o=" ) ) { // output file for -CFA … … 100 103 } else if ( prefix( val, "-B=" ) ) { // location of cfa-cpp 101 104 bprefix = val.substr( 3 ); 105 } else if ( prefix( val, "-x=" ) ) { // ignore 102 106 } else { // normal flag for cfa-cpp 103 107 args[nargs++] = ( *new string( arg.substr( arg.find_first_of( "=" ) + 1 ) ) ).c_str(); … … 107 111 } // checkEnv2 108 112 109 110 static char tmpname[] = P_tmpdir "/CFAXXXXXX.ifa"; 113 #define CFA_SUFFIX ".ifa" 114 115 static char tmpname[] = P_tmpdir "/CFAXXXXXX" CFA_SUFFIX; 111 116 static int tmpfilefd = -1; 112 117 static bool startrm = false; … … 166 171 if ( arg == "-quiet" ) { 167 172 } else if ( arg == "-imultilib" || arg == "-imultiarch" ) { 168 i += 1; // and theargument173 i += 1; // and argument 169 174 } else if ( prefix( arg, "-A" ) ) { 170 175 } else if ( prefix( arg, "-D__GNU" ) ) { … … 173 178 //******** 174 179 } else if ( arg == "-D" && prefix( argv[i + 1], "__GNU" ) ) { 175 i += 1; // and theargument180 i += 1; // and argument 176 181 177 182 // strip flags controlling cpp step … … 180 185 cpp_flag = true; 181 186 } else if ( arg == "-D" && string( argv[i + 1] ) == "__CPP__" ) { 182 i += 1; // and theargument187 i += 1; // and argument 183 188 cpp_flag = true; 184 189 … … 190 195 cpp_out = argv[i]; 191 196 } else { 192 args[nargs++] = argv[i]; // pass theflag along197 args[nargs++] = argv[i]; // pass flag along 193 198 // CPP flags with an argument 194 199 if ( arg == "-D" || arg == "-U" || arg == "-I" || arg == "-MF" || arg == "-MT" || arg == "-MQ" || … … 196 201 arg == "-iwithprefix" || arg == "-iwithprefixbefore" || arg == "-isystem" || arg == "-isysroot" ) { 197 202 i += 1; 198 args[nargs++] = argv[i]; // pass theargument along203 args[nargs++] = argv[i]; // pass argument along 199 204 #ifdef __DEBUG_H__ 200 205 cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl; 201 206 #endif // __DEBUG_H__ 202 207 } else if ( arg == "-MD" || arg == "-MMD" ) { 208 // gcc frontend generates the dependency file-name after the -MD/-MMD flag, but it is necessary to 209 // prefix that file name with -MF. 203 210 args[nargs++] = "-MF"; // insert before file 204 211 i += 1; 205 args[nargs++] = argv[i]; // pass theargument along212 args[nargs++] = argv[i]; // pass argument along 206 213 #ifdef __DEBUG_H__ 207 214 cerr << "argv[" << i << "]:\"" << argv[i] << "\"" << endl; … … 247 254 248 255 args[0] = compiler_path.c_str(); 249 suffix( cpp_in, args, nargs ); // check suffix 256 if ( lang.size() == 0 ) { 257 suffix( cpp_in, args, nargs ); // check suffix 258 } else { 259 args[nargs++] = "-x"; 260 args[nargs++] = ( *new string( lang.c_str() ) ).c_str(); 261 } // if 250 262 args[nargs++] = cpp_in; 251 263 if ( o_flag ) { // location for output … … 270 282 // Run the C preprocessor and save the output in the given file. 271 283 272 if ( fork() == 0 ) { // child process ?284 if ( fork() == 0 ) { // child process ? 273 285 // -o xxx.ii cannot be used to write the output file from cpp because no output file is created if cpp detects 274 286 // an error (e.g., cannot find include file). Whereas, output is always generated, even when there is an error, … … 280 292 281 293 args[0] = compiler_path.c_str(); 282 suffix( cpp_in, args, nargs ); // check suffix 294 if ( lang.size() == 0 ) { 295 suffix( cpp_in, args, nargs ); // check suffix 296 } else { 297 args[nargs++] = "-x"; 298 args[nargs++] = ( *new string( lang.c_str() ) ).c_str(); 299 } // if 283 300 args[nargs++] = cpp_in; // input to cpp 284 301 args[nargs] = nullptr; // terminate argument list … … 305 322 306 323 if ( WIFSIGNALED(code) ) { // child failed ? 324 rmtmpfile(); // remove tmpname 307 325 cerr << "CC1 Translator error: stage 1, child failed " << WTERMSIG(code) << endl; 308 326 exit( EXIT_FAILURE ); 309 327 } // if 310 328 311 exit( WEXITSTATUS( code) );// bad cpp result stops top-level gcc329 exit( WEXITSTATUS( code ) ); // bad cpp result stops top-level gcc 312 330 } // Stage1 313 331 … … 357 375 } else if ( arg == "-fno-diagnostics-color" ) { 358 376 color_arg = Color_Auto; 359 } 377 } // if 360 378 361 379 if ( arg == "-quiet" || arg == "-version" || arg == "-fpreprocessed" || 362 // Currently CFA does not suppose precompiled .h files.363 prefix( arg, "--output-pch" ) ) {380 // Currently CFA does not suppose precompiled .h files. 381 prefix( arg, "--output-pch" ) ) { 364 382 365 383 // strip inappropriate flags with an argument … … 374 392 375 393 } else { 376 args[nargs++] = argv[i]; // pass theflag along394 args[nargs++] = argv[i]; // pass flag along 377 395 if ( arg == "-o" ) { 378 396 i += 1; 379 397 cpp_out = argv[i]; 380 args[nargs++] = argv[i]; // pass theargument along398 args[nargs++] = argv[i]; // pass argument along 381 399 #ifdef __DEBUG_H__ 382 400 cerr << "arg:\"" << argv[i] << "\"" << endl; … … 425 443 } // if 426 444 427 cfa_cpp_out = cfa_cpp_out.substr( 0, dot ) + ".ifa";445 cfa_cpp_out = cfa_cpp_out.substr( 0, dot ) + CFA_SUFFIX; 428 446 if ( creat( cfa_cpp_out.c_str(), 0666 ) == -1 ) { 429 447 perror( "CC1 Translator error: stage 2, creat" ); … … 446 464 // output. Otherwise, run the cfa-cpp preprocessor on the temporary file and save the result into the output file. 447 465 448 if ( fork() == 0 ) { // child runs CFA 466 if ( fork() == 0 ) { // child runs CFA preprocessor 449 467 cargs[0] = ( *new string( bprefix + "cfa-cpp" ) ).c_str(); 450 468 cargs[ncargs++] = cpp_in; … … 504 522 #endif // __DEBUG_H__ 505 523 506 if ( fork() == 0 ) { // child runs CFA524 if ( fork() == 0 ) { // child runs gcc 507 525 args[0] = compiler_path.c_str(); 508 526 args[nargs++] = "-S"; // only compile and put assembler output in specified file -
driver/cfa.cc
r3c64c668 r58fe85a 10 10 // Created On : Tue Aug 20 13:44:49 2002 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jan 31 16:48:03202013 // Update Count : 4 2112 // Last Modified On : Tue Nov 17 14:27:28 2020 13 // Update Count : 440 14 14 // 15 15 16 16 #include <iostream> 17 #include <cstdio> // perror 18 #include <cstdlib> // putenv, exit 19 #include <climits> // PATH_MAX 20 #include <unistd.h> // execvp 21 #include <string> // STL version 22 #include <string.h> // strcmp 23 #include <algorithm> // find 24 17 #include <cstdio> // perror 18 #include <cstdlib> // putenv, exit 19 #include <climits> // PATH_MAX 20 #include <string> // STL version 21 #include <algorithm> // find 22 23 #include <unistd.h> // execvp 25 24 #include <sys/types.h> 26 25 #include <sys/stat.h> … … 34 33 using std::to_string; 35 34 36 // #define __DEBUG_H__ 37 38 // "N__=" suffix 39 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); 40 41 void Putenv( char * argv[], string arg ) { 35 //#define __DEBUG_H__ 36 37 #define xstr(s) str(s) 38 #define str(s) #s 39 40 static string __CFA_FLAGPREFIX__( "__CFA_FLAG" ); // "__CFA_FLAG__=" suffix 41 42 static void Putenv( char * argv[], string arg ) { 42 43 // environment variables must have unique names 43 44 static int flags = 0; … … 49 50 } // Putenv 50 51 51 // check if string has prefix 52 bool prefix( const string & arg, const string & pre ) { 52 static bool prefix( const string & arg, const string & pre ) { // check if string has prefix 53 53 return arg.substr( 0, pre.size() ) == pre; 54 54 } // prefix 55 55 56 inline bool ends_with(const string & str, const string & sfix) {56 static 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 bool suffix( const string & arg ) {62 static 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 73 72 static inline bool dirExists( const string & path ) { // check if directory exists 74 73 struct stat info; … … 79 78 static inline string dir(const string & path) { 80 79 return path.substr(0, path.find_last_of('/')); 81 } 80 } // dir 82 81 83 82 // Different path modes … … 118 117 } 119 118 120 121 #define xstr(s) str(s)122 #define str(s) #s123 119 124 120 int main( int argc, char * argv[] ) { … … 158 154 PathMode path = FromProc(); 159 155 160 const char * args[argc + 100]; // cfa command line values, plus some space for additional flags156 const char * args[argc + 100]; // cfa command line values, plus some space for additional flags 161 157 int sargs = 1; // starting location for arguments in args list 162 158 int nargs = sargs; // number of arguments in args list; 0 => command name 163 159 164 const char * libs[argc + 20]; // non-user libraries must come separately, plus some added libraries and flags160 const char * libs[argc + 20]; // non-user libraries must come separately, plus some added libraries and flags 165 161 int nlibs = 0; 166 162 … … 180 176 181 177 if ( arg == "-Xlinker" || arg == "-o" ) { 182 args[nargs++] = argv[i]; // pass argumentalong178 args[nargs++] = argv[i]; // pass flag along 183 179 i += 1; 184 180 if ( i == argc ) continue; // next argument available ? 185 181 args[nargs++] = argv[i]; // pass argument along 186 182 if ( arg == "-o" ) o_file = i; // remember file 187 } else if ( arg == "-XCFA" ) { // CFA pass through188 i += 1;189 if ( i == argc ) continue; // next argument available ?190 Putenv( argv, argv[i] );191 183 192 184 // CFA specific arguments 193 185 186 } else if ( strncmp(arg.c_str(), "-XCFA", 5) == 0 ) { // CFA pass through 187 if ( arg.size() == 5 ) { 188 i += 1; 189 if ( i == argc ) continue; // next argument available ? 190 Putenv( argv, argv[i] ); 191 } else if ( arg[5] == ',' ) { // CFA specific arguments 192 Putenv( argv, argv[i] + 6 ); 193 } else { // CFA specific arguments 194 args[nargs++] = argv[i]; 195 } // if 194 196 } else if ( arg == "-CFA" ) { 195 197 CFA_flag = true; // strip the -CFA flag … … 200 202 } else if ( arg == "-nodebug" ) { 201 203 debug = false; // strip the nodebug flag 202 } else if ( arg == "-nolib" ) {203 nolib = true; // strip the nodebug flag204 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 flag 210 } else if ( arg == "-nolib" ) { 211 nolib = true; // strip the nolib flag 208 212 } else if ( arg == "-help" ) { 209 213 help = true; // strip the help flag 210 214 } else if ( arg == "-nohelp" ) { 211 215 help = false; // strip the nohelp flag 212 } else if ( arg == "-no-include-stdhdr" ) {213 noincstd_flag = true; // strip the no-include-stdhdr flag214 216 } else if ( arg == "-cfalib") { 215 217 compiling_libs = true; … … 225 227 } else if ( arg == "-v" ) { 226 228 verbose = true; // verbosity required 227 args[nargs++] = argv[i]; // pass argumentalong229 args[nargs++] = argv[i]; // pass flag along 228 230 } else if ( arg == "-g" ) { 229 231 debugging = true; // symbolic debugging required 230 args[nargs++] = argv[i]; // pass argumentalong231 } else if ( arg == "-save-temps" ) {232 args[nargs++] = argv[i]; // pass argumentalong232 args[nargs++] = argv[i]; // pass flag along 233 } else if ( arg == "-save-temps" || arg == "--save-temps" ) { 234 args[nargs++] = argv[i]; // pass flag along 233 235 Putenv( argv, arg ); // save cfa-cpp output 234 236 } else if ( prefix( arg, "-x" ) ) { // file suffix ? 235 237 string lang; 236 args[nargs++] = argv[i]; // pass argumentalong238 args[nargs++] = argv[i]; // pass flag along 237 239 if ( arg.length() == 2 ) { // separate argument ? 238 240 i += 1; … … 243 245 lang = arg.substr( 2 ); 244 246 } // if 245 x_flag = lang != "none"; 247 if ( x_flag ) { 248 cerr << argv[0] << " warning, only one -x flag per compile, ignoring subsequent flag." << endl; 249 } else { 250 x_flag = true; 251 Putenv( argv, string( "-x=" ) + lang ); 252 } // if 246 253 } else if ( prefix( arg, "-std=" ) || prefix( arg, "--std=" ) ) { 247 254 std_flag = true; // -std=XX provided 248 args[nargs++] = argv[i]; // pass argumentalong255 args[nargs++] = argv[i]; // pass flag along 249 256 } else if ( arg == "-w" ) { 250 args[nargs++] = argv[i]; // pass argumentalong257 args[nargs++] = argv[i]; // pass flag along 251 258 Putenv( argv, arg ); 252 259 } else if ( prefix( arg, "-W" ) ) { // check before next tests 253 260 if ( arg == "-Werror" || arg == "-Wall" ) { 254 args[nargs++] = argv[i]; // pass argumentalong261 args[nargs++] = argv[i]; // pass flag along 255 262 Putenv( argv, argv[i] ); 256 263 } else { … … 266 273 bprefix = arg.substr(2); // strip the -B flag 267 274 } else if ( arg == "-c" || arg == "-S" || arg == "-E" || arg == "-M" || arg == "-MM" ) { 268 args[nargs++] = argv[i]; // pass argumentalong275 args[nargs++] = argv[i]; // pass flag along 269 276 if ( arg == "-E" || arg == "-M" || arg == "-MM" ) { 270 277 cpp_flag = true; // cpp only 271 278 } // if 272 279 link = false; // no linkage required 280 } else if ( arg == "-D" || arg == "-U" || arg == "-I" || arg == "-MF" || arg == "-MT" || arg == "-MQ" || 281 arg == "-include" || arg == "-imacros" || arg == "-idirafter" || arg == "-iprefix" || 282 arg == "-iwithprefix" || arg == "-iwithprefixbefore" || arg == "-isystem" || arg == "-isysroot" ) { 283 args[nargs++] = argv[i]; // pass flag along 284 i += 1; 285 args[nargs++] = argv[i]; // pass argument along 273 286 } else if ( arg[1] == 'l' ) { 274 287 // if the user specifies a library, load it after user code … … 302 315 303 316 #ifdef __x86_64__ 304 args[nargs++] = "-mcx16"; // allow double-wide CA A317 args[nargs++] = "-mcx16"; // allow double-wide CAS 305 318 #endif // __x86_64__ 306 319 … … 322 335 string libbase; 323 336 switch(path) { 324 case Installed:337 case Installed: 325 338 args[nargs++] = "-I" CFA_INCDIR; 326 339 // do not use during build … … 332 345 libbase = CFA_LIBDIR; 333 346 break; 334 case BuildTree:335 case Distributed:347 case BuildTree: 348 case Distributed: 336 349 args[nargs++] = "-I" TOP_SRCDIR "libcfa/src"; 337 350 // do not use during build … … 367 380 string libdir = libbase + arch + "-" + config; 368 381 369 if ( path != Distributed) {382 if ( path != Distributed ) { 370 383 if ( ! nolib && ! dirExists( libdir ) ) { 371 384 cerr << argv[0] << " internal error, configuration " << config << " not installed." << endl; … … 385 398 } // if 386 399 400 string preludedir; 387 401 switch(path) { 388 case Installed : Putenv( argv, "--prelude-dir=" + libdir ); break; 389 case BuildTree : Putenv( argv, "--prelude-dir=" + libdir + "/prelude" ); break; 390 case Distributed : Putenv( argv, "--prelude-dir=" + dir(argv[0]) ); break; 391 } 402 case Installed : preludedir = libdir; break; 403 case BuildTree : preludedir = libdir + "/prelude"; break; 404 case Distributed : preludedir = dir(argv[0]); break; 405 } // switch 406 407 Putenv( argv, "--prelude-dir=" + preludedir ); 408 args[nargs++] = "-include"; 409 args[nargs++] = (*new string(preludedir + "/defines.hfa")).c_str(); 392 410 393 411 for ( int i = 0; i < nlibs; i += 1 ) { // copy non-user libraries after all user libraries … … 415 433 args[nargs++] = "-Wl,--pop-state"; 416 434 args[nargs++] = "-pthread"; 435 #if defined( __x86_64__ ) || defined( __ARM_ARCH ) 436 args[nargs++] = "-latomic"; // allow double-wide CAS 437 #endif // __x86_64__ 417 438 args[nargs++] = "-ldl"; 418 args[nargs++] = "-lrt";419 439 args[nargs++] = "-lm"; 420 440 } // if … … 454 474 if ( bprefix.length() == 0 ) { 455 475 switch(path) { 456 case Installed : bprefix = installlibdir; break;457 case BuildTree : bprefix = srcdriverdir ; break;458 case Distributed : bprefix = dir(argv[0]) ; break;459 } 460 if ( bprefix[bprefix.length() - 1] != '/' ) bprefix += '/';461 Putenv( argv, string("-B=") + bprefix );462 } // if476 case Installed : bprefix = installlibdir; break; 477 case BuildTree : bprefix = srcdriverdir ; break; 478 case Distributed : bprefix = dir(argv[0]) ; break; 479 } // switch 480 } // if 481 if ( bprefix[bprefix.length() - 1] != '/' ) bprefix += '/'; 482 Putenv( argv, string("-B=") + bprefix ); 463 483 464 484 args[nargs++] = "-Xlinker"; // used by backtrace … … 482 502 args[nargs++] = "-Wno-cast-function-type"; 483 503 #endif // HAVE_CAST_FUNCTION_TYPE 484 if ( ! std_flag ) { // default c11, if none specified485 args[nargs++] = "-std=gnu11"; 504 if ( ! std_flag && ! x_flag ) { 505 args[nargs++] = "-std=gnu11"; // default c11, if none specified 486 506 } // if 487 507 args[nargs++] = "-fgnu89-inline"; … … 533 553 // execute the command and return the result 534 554 535 execvp( args[0], (char * const *)args );// should not return555 execvp( args[0], (char * const *)args ); // should not return 536 556 perror( "CFA Translator error: execvp" ); 537 557 exit( EXIT_FAILURE ); -
libcfa/configure.ac
r3c64c668 r58fe85a 3 3 4 4 AC_PREREQ([2.68]) 5 AC_INIT([cfa-cc],[1.0.0 .0],[cforall@plg.uwaterloo.ca])5 AC_INIT([cfa-cc],[1.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([../ automake/cfa.m4])10 m4_include([../tools/build/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) 32 36 33 37 echo -n "checking for distributated build... " … … 55 59 AC_SUBST(CFADIR_HASH) 56 60 AC_SUBST(CFA_VERSION) 61 AC_SUBST(DIST_BWLIMIT) 57 62 58 63 #============================================================================== … … 100 105 AM_CONDITIONAL([BUILDLIB], [test "x${CONFIG_BUILDLIB}" = "xyes"]) 101 106 107 AM_T='$(T)' 108 AC_SUBST(AM_T) 109 102 110 #============================================================================== 103 111 #Trasforming cc1 will break compilation … … 109 117 110 118 # Checks for programs. 111 LT_INIT 119 LT_INIT([disable-static]) 112 120 113 121 AC_PROG_CXX … … 118 126 AC_PROG_MAKE_SET 119 127 128 129 130 #io_uring 5.4 and earlier uses defines 131 #io_uring 5.5 uses enum values 132 #io_uring 5.6 and later uses probes 133 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_decls 218 ]) 219 ], [ 220 ioring_from_decls 221 ]) 222 223 # check support for various io_uring flags 224 m4_foreach([op], [ioring_flags], [ 225 AC_CHECK_DECL(op, [AC_DEFINE([CFA_HAVE_]op)], [], [[#include <linux/io_uring.h>]]) 226 ]) 227 ]) 228 AC_CHECK_FUNC([preadv2], [AC_DEFINE([CFA_HAVE_PREADV2])]) 229 AC_CHECK_FUNC([pwritev2], [AC_DEFINE([CFA_HAVE_PWRITEV2])]) 230 120 231 AC_CONFIG_FILES([ 121 232 Makefile … … 123 234 prelude/Makefile 124 235 ]) 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) 125 239 126 240 AC_OUTPUT() -
libcfa/prelude/Makefile.am
r3c64c668 r58fe85a 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 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 24 26 25 27 CC = @LOCAL_CFACC@ … … 68 70 69 71 MOSTLYCLEANFILES = bootloader.c builtins.cf extras.cf gcc-builtins.c gcc-builtins.cf prelude.cfa 72 DISTCLEANFILES = $(DEPDIR)/builtins.Po 70 73 MAINTAINERCLEANFILES = ${addprefix ${libdir}/,${cfalib_DATA}} ${addprefix ${libdir}/,${lib_LIBRARIES}} 71 74 72 75 if ENABLE_DISTCC 73 76 distribution: @LOCAL_CFACC@ @LOCAL_CC1@ @CFACPP@ gcc-builtins.cf builtins.cf extras.cf prelude.cfa bootloader.c $(srcdir)/../../tools/build/push2dist.sh 74 ${AM_V_GEN}$(srcdir)/../../tools/build/push2dist.sh @CFADIR_HASH@ 77 ${AM_V_GEN}$(srcdir)/../../tools/build/push2dist.sh @CFADIR_HASH@ @DIST_BWLIMIT@ 75 78 @echo "Dummy file to track distribution to remote hosts" > ${@} 76 79 -
libcfa/prelude/bootloader.cf
r3c64c668 r58fe85a 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; 2 5 3 6 int main(int argc, char* argv[], char* envp[]) { 7 cfa_args_argc = argc; 8 cfa_args_argv = argv; 9 cfa_args_envp = envp; 4 10 return invoke_main(argc, argv, envp); 5 11 } -
libcfa/prelude/builtins.c
r3c64c668 r58fe85a 9 9 // Author : Peter A. Buhr 10 10 // Created On : Fri Jul 21 16:21:03 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : T hu Nov 21 16:31:39 201913 // Update Count : 1 0111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 27 14:42:00 2020 13 // Update Count : 111 14 14 // 15 16 #define __cforall_builtins__ 15 17 16 18 // type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions … … 49 51 void abort( const char fmt[], ... ) __attribute__ (( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ )); 50 52 53 forall(dtype T) 54 static inline T & identity(T & i) { 55 return i; 56 } 57 58 // generator support 59 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 51 77 // implicit increment, decrement if += defined, and implicit not if != defined 52 78 … … 70 96 // universal typed pointer constant 71 97 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__ 72 106 73 107 // exponentiation operator implementation -
libcfa/src/Makefile.am
r3c64c668 r58fe85a 11 11 ## Created On : Sun May 31 08:54:01 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Mon Jul 15 22:43:27 201914 ## Update Count : 2 4113 ## Last Modified On : Wed Dec 9 22:46:14 2020 14 ## Update Count : 250 15 15 ############################################################################### 16 16 … … 19 19 ACLOCAL_AMFLAGS = -I automake 20 20 21 include $( srcdir)/../../src/cfa.make21 include $(top_srcdir)/../tools/build/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 headers34 AM_CFAFLAGS = -quiet -cfalib -I$(srcdir)/stdhdr $(if $(findstring ${gdbwaittarget}, ${@}), -XCFA --gdb) @CONFIG_CFAFLAGS@35 AM_CFLAGS = -g -Wall -Wno-unused-function -fPIC - pthread @ARCH_FLAGS@ @CONFIG_CFLAGS@33 # The built sources must not depend on the installed inst_headers_src 34 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@ 36 36 AM_CCASFLAGS = -g -Wall -Wno-unused-function @ARCH_FLAGS@ @CONFIG_CFLAGS@ 37 37 CFACC = @CFACC@ … … 39 39 #---------------------------------------------------------------------------------------------------------------- 40 40 if BUILDLIB 41 headers_nosrc = math.hfa gmp.hfa time_t.hfa bits/align.hfa bits/containers.hfa bits/defs.hfa bits/debug.hfa bits/locks.hfa 42 headers = fstream.hfa iostream.hfa iterator.hfa limits.hfa rational.hfa time.hfa stdlib.hfa common.hfa \ 43 containers/maybe.hfa containers/pair.hfa containers/result.hfa containers/vector.hfa 44 45 libsrc = startup.cfa interpose.cfa bits/debug.cfa assert.cfa exception.c virtual.c heap.cfa ${headers:.hfa=.cfa} 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 65 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 94 47 95 # not all platforms support concurrency, add option do disable it 48 thread_headers_nosrc = concurrency/invoke.h 49 thread_headers = concurrency/coroutine.hfa concurrency/thread.hfa concurrency/kernel.hfa concurrency/monitor.hfa concurrency/mutex.hfa 50 thread_libsrc = concurrency/CtxSwitch-@ARCHITECTURE@.S concurrency/alarm.cfa concurrency/invoke.c concurrency/preemption.cfa ${thread_headers:.hfa=.cfa} 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 51 135 else 52 headers=53 thread_headers=54 headers_nosrc =55 thread_headers_nosrc =136 inst_headers_src = 137 inst_thread_headers_src = 138 inst_headers_nosrc = 139 inst_thread_headers_nosrc = 56 140 libsrc = 57 141 endif … … 96 180 97 181 prelude.o : prelude.cfa extras.cf gcc-builtins.cf builtins.cf @LOCAL_CFACC@ @CFACPP@ 98 ${AM_V_GEN}$(CFACOMPILE) -quiet -XCFA -l ${<} -c -o ${@}182 ${AM_V_GEN}$(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@} 99 183 100 184 prelude.lo: prelude.cfa extras.cf gcc-builtins.cf builtins.cf @LOCAL_CFACC@ @CFACPP@ 101 185 ${AM_V_GEN}$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile \ 102 $(CFACOMPILE) -quiet -XCFA -l ${<} -c -o ${@} 103 104 #---------------------------------------------------------------------------------------------------------------- 105 libcfa_la_SOURCES = prelude.cfa ${libsrc} 186 $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@} 187 188 #---------------------------------------------------------------------------------------------------------------- 189 libcfa_la_SOURCES = ${libsrc} 190 nodist_libcfa_la_SOURCES = prelude.cfa 106 191 libcfa_la_LDFLAGS = -version-info @CFA_VERSION@ 107 192 … … 112 197 113 198 cfa_includedir = $(CFA_INCDIR) 114 nobase_cfa_include_HEADERS = ${stdhdr} ${headers} ${headers_nosrc} ${thread_headers} ${thread_headers_nosrc} 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 201 116 202 #---------------------------------------------------------------------------------------------------------------- 117 203 maintainer-clean-local: 118 204 -rm -rf ${CFA_INCDIR} ${CFA_LIBDIR} 205 206 distclean-local: 207 find ${builddir} -path '*.Plo' -delete 119 208 120 209 -
libcfa/src/bits/containers.hfa
r3c64c668 r58fe85a 17 17 #include "bits/align.hfa" 18 18 #include "bits/defs.hfa" 19 19 #include <stdio.h> 20 20 //----------------------------------------------------------------------------- 21 21 // Array … … 36 36 #define __small_array_t(T) __small_array(T) 37 37 #else 38 #define __small_array_t(T) struct__small_array38 #define __small_array_t(T) __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 head{ 1p };149 tail{ &head };150 verify(*t ail == 1p);148 (this.head){ 1p }; 149 (this.tail){ &this.head }; 150 verify(*this.tail == 1p); 151 151 } 152 152 153 153 void append( __queue(T) & this, T * val ) with( this ) { 154 verify(tail != 0p); 155 verify(*tail == 1p); 156 *tail = val; 157 tail = &get_next( *val ); 158 *tail = 1p; 154 verify(this.tail != 0p); 155 verify(*this.tail == 1p); 156 *this.tail = val; 157 this.tail = &get_next( *val ); 158 *this.tail = 1p; 159 } 160 161 T * peek( __queue(T) & this ) { 162 verify(*this.tail == 1p); 163 T * front = this.head; 164 if( front != 1p ) { 165 verify(*this.tail == 1p); 166 return front; 167 } 168 verify(*this.tail == 1p); 169 return 0p; 159 170 } 160 171 161 172 T * pop_head( __queue(T) & this ) { 162 173 verify(*this.tail == 1p); 163 T * head = this.head;164 if( head != 1p ) {165 this.head = get_next( * head );166 if( get_next( * head ) == 1p ) {174 T * _head = this.head; 175 if( _head != 1p ) { 176 this.head = get_next( *_head ); 177 if( get_next( *_head ) == 1p ) { 167 178 this.tail = &this.head; 168 179 } 169 get_next( * head ) = 0p;180 get_next( *_head ) = 0p; 170 181 verify(*this.tail == 1p); 171 return head; 182 verify( get_next(*_head) == 0p ); 183 return _head; 172 184 } 173 185 verify(*this.tail == 1p); … … 181 193 (*it) = get_next( *val ); 182 194 183 if( t ail == &get_next( *val ) ) {184 t ail = it;195 if( this.tail == &get_next( *val ) ) { 196 this.tail = it; 185 197 } 186 198 187 199 get_next( *val ) = 0p; 188 200 189 verify( ( head == 1p) == (&head ==tail) );190 verify( *t ail == 1p );201 verify( (this.head == 1p) == (&this.head == this.tail) ); 202 verify( *this.tail == 1p ); 191 203 return val; 192 204 } 193 205 194 206 int ?!=?( const __queue(T) & this, __attribute__((unused)) zero_t zero ) { 195 return this.head != 0;207 return this.head != 1p; 196 208 } 197 209 } … … 227 239 forall(dtype T ) 228 240 static inline [void] ?{}( __dllist(T) & this, * [T * & next, T * & prev] ( T & ) __get ) { 229 this.head{ 0p };241 (this.head){ 0p }; 230 242 this.__get = __get; 231 243 } … … 236 248 void push_front( __dllist(T) & this, T & node ) with( this ) { 237 249 verify(__get); 238 if ( head ) {239 __get( node ).next = head;240 __get( node ).prev = __get( * head ).prev;250 if ( this.head ) { 251 __get( node ).next = this.head; 252 __get( node ).prev = __get( *this.head ).prev; 241 253 // inserted node must be consistent before it is seen 242 254 // prevent code movement across barrier 243 255 asm( "" : : : "memory" ); 244 __get( * head ).prev = &node;256 __get( *this.head ).prev = &node; 245 257 T & _prev = *__get( node ).prev; 246 258 __get( _prev ).next = &node; … … 252 264 // prevent code movement across barrier 253 265 asm( "" : : : "memory" ); 254 head = &node;266 this.head = &node; 255 267 } 256 268 257 269 void remove( __dllist(T) & this, T & node ) with( this ) { 258 270 verify(__get); 259 if ( &node == head ) {260 if ( __get( * head ).next ==head ) {261 head = 0p;271 if ( &node == this.head ) { 272 if ( __get( *this.head ).next == this.head ) { 273 this.head = 0p; 262 274 } else { 263 head = __get( *head ).next;275 this.head = __get( *this.head ).next; 264 276 } 265 277 } … … 273 285 return this.head != 0; 274 286 } 287 288 void move_to_front( __dllist(T) & src, __dllist(T) & dst, T & node ) { 289 remove (src, node); 290 push_front(dst, node); 291 } 275 292 } 276 293 #undef next -
libcfa/src/bits/debug.cfa
r3c64c668 r58fe85a 10 10 // Created On : Thu Mar 30 12:30:01 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 13:03:16202013 // Update Count : 1 112 // Last Modified On : Wed Jun 17 11:07:13 2020 13 // Update Count : 12 14 14 // 15 15 16 extern "C" {17 16 #include <stdio.h> 18 17 #include <stdlib.h> … … 21 20 #include <stdarg.h> 22 21 #include <unistd.h> 23 }24 22 25 23 enum { buffer_size = 4096 }; -
libcfa/src/bits/debug.hfa
r3c64c668 r58fe85a 9 9 // Author : Thierry Delisle 10 10 // Created On : Mon Nov 28 12:27:26 2016 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Tue Feb 4 12:29:21202013 // Update Count : 911 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Apr 27 10:15:00 2020 13 // Update Count : 10 14 14 // 15 15 16 16 #pragma once 17 18 #include <assert.h> 17 19 18 20 #ifdef __CFA_DEBUG__ … … 23 25 #define __cfaabi_dbg_ctx_param const char caller[] 24 26 #define __cfaabi_dbg_ctx_param2 , const char caller[] 27 #define __cfaabi_dbg_ctx_fwd caller 28 #define __cfaabi_dbg_ctx_fwd2 , caller 25 29 #else 26 30 #define __cfaabi_dbg_debug_do(...) … … 30 34 #define __cfaabi_dbg_ctx_param 31 35 #define __cfaabi_dbg_ctx_param2 36 #define __cfaabi_dbg_ctx_fwd 37 #define __cfaabi_dbg_ctx_fwd2 32 38 #endif 33 39 … … 36 42 #endif 37 43 #include <stdarg.h> 38 #include <stdio.h>39 44 40 45 extern void __cfaabi_bits_write( int fd, const char buffer[], int len ); … … 45 50 extern void __cfaabi_bits_print_vararg( int fd, const char fmt[], va_list arg ); 46 51 extern void __cfaabi_bits_print_buffer( int fd, char buffer[], int buffer_size, const char fmt[], ... ) __attribute__(( format(printf, 4, 5) )); 52 53 #if defined(__CFA_DEBUG_PRINT__) \ 54 || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__) \ 55 || defined(__CFA_DEBUG_PRINT_MONITOR__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__) \ 56 || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__) \ 57 || defined(__CFA_DEBUG_PRINT_READY_QUEUE__) 58 #include <stdio.h> 59 #include <unistd.h> 60 #endif 47 61 #ifdef __cforall 48 62 } 49 63 #endif 50 64 65 // Deprecated: Use the versions with the new module names. 51 66 #ifdef __CFA_DEBUG_PRINT__ 52 67 #define __cfaabi_dbg_write( buffer, len ) __cfaabi_bits_write( STDERR_FILENO, buffer, len ) 53 68 #define __cfaabi_dbg_acquire() __cfaabi_bits_acquire() 54 69 #define __cfaabi_dbg_release() __cfaabi_bits_release() 55 #define __cfaabi_dbg_print_safe(...) __cfaabi_bits_print_safe ( __VA_ARGS__)56 #define __cfaabi_dbg_print_nolock(...) __cfaabi_bits_print_nolock ( __VA_ARGS__)57 #define __cfaabi_dbg_print_buffer(...) __cfaabi_bits_print_buffer ( __VA_ARGS__)58 #define __cfaabi_dbg_print_buffer_decl(...) char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len );59 #define __cfaabi_dbg_print_buffer_local(...) __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_dbg_write( __dbg_text, __dbg_len );70 #define __cfaabi_dbg_print_safe(...) __cfaabi_bits_print_safe ( STDERR_FILENO, __VA_ARGS__ ) 71 #define __cfaabi_dbg_print_nolock(...) __cfaabi_bits_print_nolock ( STDERR_FILENO, __VA_ARGS__ ) 72 #define __cfaabi_dbg_print_buffer(...) __cfaabi_bits_print_buffer ( STDERR_FILENO, __VA_ARGS__ ) 73 #define __cfaabi_dbg_print_buffer_decl(...) char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( STDERR_FILENO, __dbg_text, __dbg_len ); 74 #define __cfaabi_dbg_print_buffer_local(...) __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_dbg_write( STDERR_FILENO, __dbg_text, __dbg_len ); 60 75 #else 61 76 #define __cfaabi_dbg_write(...) ((void)0) … … 69 84 #endif 70 85 86 // Debug print functions and statements: 87 // Most are wrappers around the bits printing function but are not always used. 88 // If they are used depends if the group (first argument) is active or not. The group must be one 89 // defined belowe. The other arguments depend on the wrapped function. 90 #define __cfadbg_write(group, buffer, len) \ 91 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_write(STDERR_FILENO, buffer, len)) 92 #define __cfadbg_acquire(group) \ 93 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_acquire()) 94 #define __cfadbg_release(group) \ 95 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_release()) 96 #define __cfadbg_print_safe(group, ...) \ 97 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_safe(STDERR_FILENO, __VA_ARGS__)) 98 #define __cfadbg_print_nolock(group, ...) \ 99 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_nolock(STDERR_FILENO, __VA_ARGS__)) 100 #define __cfadbg_print_buffer(group, ...) \ 101 __CFADBG_PRINT_GROUP_##group(__cfaabi_bits_print_buffer(STDERR_FILENO, __VA_ARGS__)) 102 #define __cfadbg_print_buffer_decl(group, ...) \ 103 __CFADBG_PRINT_GROUP_##group(char __dbg_text[256]; int __dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write( __dbg_text, __dbg_len )) 104 #define __cfadbg_print_buffer_local(group, ...) \ 105 __CFADBG_PRINT_GROUP_##group(__dbg_len = snprintf( __dbg_text, 256, __VA_ARGS__ ); __cfaabi_bits_write(STDERR_FILENO, __dbg_text, __dbg_len)) 106 107 // The debug print groups: 108 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_IO__) 109 # define __CFADBG_PRINT_GROUP_io(...) __VA_ARGS__ 110 #else 111 # define __CFADBG_PRINT_GROUP_io(...) ((void)0) 112 #endif 113 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_IO__) || defined(__CFA_DEBUG_PRINT_IO_CORE__) 114 # define __CFADBG_PRINT_GROUP_io_core(...) __VA_ARGS__ 115 #else 116 # define __CFADBG_PRINT_GROUP_io_core(...) ((void)0) 117 #endif 118 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_MONITOR__) 119 # define __CFADBG_PRINT_GROUP_monitor(...) __VA_ARGS__ 120 #else 121 # define __CFADBG_PRINT_GROUP_monitor(...) ((void)0) 122 #endif 123 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_PREEMPTION__) 124 # define __CFADBG_PRINT_GROUP_preemption(...) __VA_ARGS__ 125 #else 126 # define __CFADBG_PRINT_GROUP_preemption(...) ((void)0) 127 #endif 128 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_RUNTIME_CORE__) 129 # define __CFADBG_PRINT_GROUP_runtime_core(...) __VA_ARGS__ 130 #else 131 # define __CFADBG_PRINT_GROUP_runtime_core(...) ((void)0) 132 #endif 133 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__) 134 # define __CFADBG_PRINT_GROUP_ready_queue(...) __VA_ARGS__ 135 #else 136 # define __CFADBG_PRINT_GROUP_ready_queue(...) ((void)0) 137 #endif 138 #if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_EXCEPTION__) 139 # define __CFADBG_PRINT_GROUP_exception(...) __VA_ARGS__ 140 #else 141 # define __CFADBG_PRINT_GROUP_exception(...) ((void)0) 142 #endif 143 71 144 // Local Variables: // 72 145 // mode: c // -
libcfa/src/bits/defs.hfa
r3c64c668 r58fe85a 10 10 // Created On : Thu Nov 9 13:24:10 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Jan 28 22:38:27202013 // Update Count : 912 // Last Modified On : Sat Oct 24 10:53:15 2020 13 // Update Count : 21 14 14 // 15 15 16 16 #pragma once 17 17 18 #include <stdbool.h>19 #include <stddef.h>20 18 #include <stdint.h> 19 #include <assert.h> 21 20 22 21 #define likely(x) __builtin_expect(!!(x), 1) … … 30 29 #define __cfa_anonymous_object(x) inline struct x 31 30 #else 32 #define __cfa_anonymous_object(x) x __cfa_anonymous_object31 #define __cfa_anonymous_object(x) struct x __cfa_anonymous_object 33 32 #endif 34 33 … … 49 48 #endif 50 49 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 ); 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 55 63 } -
libcfa/src/bits/locks.hfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Oct 31 15:14:38 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 13:03:19202013 // Update Count : 1 112 // Last Modified On : Wed Aug 12 14:18:07 2020 13 // Update Count : 13 14 14 // 15 15 … … 27 27 28 28 // pause to prevent excess processor bus usage 29 #if defined( __sparc ) 30 #define Pause() __asm__ __volatile__ ( "rd %ccr,%g0" ) 31 #elif defined( __i386 ) || defined( __x86_64 ) 29 #if defined( __i386 ) || defined( __x86_64 ) 32 30 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 33 31 #elif defined( __ARM_ARCH ) 34 #define Pause() __asm__ __volatile__ ( " nop" : : : )32 #define Pause() __asm__ __volatile__ ( "YIELD" : : : ) 35 33 #else 36 34 #error unsupported architecture … … 54 52 55 53 #ifdef __CFA_DEBUG__ 56 void __cfaabi_dbg_record (__spinlock_t & this, const char prev_name[]);54 void __cfaabi_dbg_record_lock(__spinlock_t & this, const char prev_name[]); 57 55 #else 58 #define __cfaabi_dbg_record (x, y)56 #define __cfaabi_dbg_record_lock(x, y) 59 57 #endif 60 58 } … … 69 67 bool result = (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0); 70 68 if( result ) { 71 __cfaabi_dbg_record ( this, caller );69 __cfaabi_dbg_record_lock( this, caller ); 72 70 } else { 73 71 enable_interrupts_noPoll(); … … 99 97 #endif 100 98 } 101 __cfaabi_dbg_record ( this, caller );99 __cfaabi_dbg_record_lock( this, caller ); 102 100 } 103 101 … … 112 110 #endif 113 111 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 114 117 struct __bin_sem_t { 115 bool signaled;116 118 pthread_mutex_t lock; 117 119 pthread_cond_t cond; 120 int val; 118 121 }; 119 122 120 123 static inline void ?{}(__bin_sem_t & this) with( this ) { 121 signaled = false; 122 pthread_mutex_init(&lock, NULL); 123 pthread_cond_init (&cond, NULL); 124 // Create the mutex with error checking 125 pthread_mutexattr_t mattr; 126 pthread_mutexattr_init( &mattr ); 127 pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP); 128 pthread_mutex_init(&lock, &mattr); 129 130 pthread_cond_init (&cond, (const pthread_condattr_t *)0p); // workaround trac#208: cast should not be required 131 val = 0; 124 132 } 125 133 126 134 static inline void ^?{}(__bin_sem_t & this) with( this ) { 127 pthread_mutex_destroy(&lock);128 pthread_cond_destroy (&cond);135 CHECKED( pthread_mutex_destroy(&lock) ); 136 CHECKED( pthread_cond_destroy (&cond) ); 129 137 } 130 138 131 139 static inline void wait(__bin_sem_t & this) with( this ) { 132 140 verify(__cfaabi_dbg_in_kernel()); 133 pthread_mutex_lock(&lock);134 if(!signaled) { // this must be a loop, not if!141 CHECKED( pthread_mutex_lock(&lock) ); 142 while(val < 1) { 135 143 pthread_cond_wait(&cond, &lock); 136 144 } 137 signaled = false; 138 pthread_mutex_unlock(&lock); 139 } 140 141 static inline void post(__bin_sem_t & this) with( this ) { 142 verify(__cfaabi_dbg_in_kernel()); 143 144 pthread_mutex_lock(&lock); 145 bool needs_signal = !signaled; 146 signaled = true; 147 pthread_mutex_unlock(&lock); 148 149 if (needs_signal) 150 pthread_cond_signal(&cond); 145 val -= 1; 146 CHECKED( pthread_mutex_unlock(&lock) ); 147 } 148 149 static inline bool post(__bin_sem_t & this) with( this ) { 150 bool needs_signal = false; 151 152 CHECKED( pthread_mutex_lock(&lock) ); 153 if(val < 1) { 154 val += 1; 155 pthread_cond_signal(&cond); 156 needs_signal = true; 157 } 158 CHECKED( pthread_mutex_unlock(&lock) ); 159 160 return needs_signal; 161 } 162 163 #undef CHECKED 164 165 struct $thread; 166 extern void park( void ); 167 extern void unpark( struct $thread * this ); 168 static inline struct $thread * active_thread (); 169 170 // Semaphore which only supports a single thread 171 struct single_sem { 172 struct $thread * volatile ptr; 173 }; 174 175 static inline { 176 void ?{}(single_sem & this) { 177 this.ptr = 0p; 178 } 179 180 void ^?{}(single_sem &) {} 181 182 bool wait(single_sem & this) { 183 for() { 184 struct $thread * expected = this.ptr; 185 if(expected == 1p) { 186 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 187 return false; 188 } 189 } 190 else { 191 /* paranoid */ verify( expected == 0p ); 192 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 193 park(); 194 return true; 195 } 196 } 197 198 } 199 } 200 201 bool post(single_sem & this) { 202 for() { 203 struct $thread * expected = this.ptr; 204 if(expected == 1p) return false; 205 if(expected == 0p) { 206 if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 207 return false; 208 } 209 } 210 else { 211 if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 212 unpark( expected ); 213 return true; 214 } 215 } 216 } 217 } 218 } 219 220 // Synchronozation primitive which only supports a single thread and one post 221 // Similar to a binary semaphore with a 'one shot' semantic 222 // is expected to be discarded after each party call their side 223 struct oneshot { 224 // Internal state : 225 // 0p : is initial state (wait will block) 226 // 1p : fulfilled (wait won't block) 227 // any thread : a thread is currently waiting 228 struct $thread * volatile ptr; 229 }; 230 231 static inline { 232 void ?{}(oneshot & this) { 233 this.ptr = 0p; 234 } 235 236 void ^?{}(oneshot &) {} 237 238 // Wait for the post, return immidiately if it already happened. 239 // return true if the thread was parked 240 bool wait(oneshot & this) { 241 for() { 242 struct $thread * expected = this.ptr; 243 if(expected == 1p) return false; 244 /* paranoid */ verify( expected == 0p ); 245 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 246 park(); 247 /* paranoid */ verify( this.ptr == 1p ); 248 return true; 249 } 250 } 251 } 252 253 // Mark as fulfilled, wake thread if needed 254 // return true if a thread was unparked 255 bool post(oneshot & this) { 256 struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 257 if( got == 0p ) return false; 258 unpark( got ); 259 return true; 260 } 261 } 262 263 // base types for future to build upon 264 // It is based on the 'oneshot' type to allow multiple futures 265 // to block on the same instance, permitting users to block a single 266 // thread on "any of" [a given set of] futures. 267 // does not support multiple threads waiting on the same future 268 struct future_t { 269 // Internal state : 270 // 0p : is initial state (wait will block) 271 // 1p : fulfilled (wait won't block) 272 // 2p : in progress () 273 // 3p : abandoned, server should delete 274 // any oneshot : a context has been setup to wait, a thread could wait on it 275 struct oneshot * volatile ptr; 276 }; 277 278 static inline { 279 void ?{}(future_t & this) { 280 this.ptr = 0p; 281 } 282 283 void ^?{}(future_t &) {} 284 285 void reset(future_t & this) { 286 // needs to be in 0p or 1p 287 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 288 } 289 290 // check if the future is available 291 bool available( future_t & this ) { 292 return this.ptr == 1p; 293 } 294 295 // Prepare the future to be waited on 296 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 297 bool setup( future_t & this, oneshot & wait_ctx ) { 298 /* paranoid */ verify( wait_ctx.ptr == 0p ); 299 // The future needs to set the wait context 300 for() { 301 struct oneshot * expected = this.ptr; 302 // Is the future already fulfilled? 303 if(expected == 1p) return false; // Yes, just return false (didn't block) 304 305 // The future is not fulfilled, try to setup the wait context 306 /* paranoid */ verify( expected == 0p ); 307 if(__atomic_compare_exchange_n(&this.ptr, &expected, &wait_ctx, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 308 return true; 309 } 310 } 311 } 312 313 // Stop waiting on a future 314 // When multiple futures are waited for together in "any of" pattern 315 // futures that weren't fulfilled before the thread woke up 316 // should retract the wait ctx 317 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 318 void retract( future_t & this, oneshot & wait_ctx ) { 319 // Remove the wait context 320 struct oneshot * got = __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST); 321 322 // got == 0p: future was never actually setup, just return 323 if( got == 0p ) return; 324 325 // got == wait_ctx: since fulfil does an atomic_swap, 326 // if we got back the original then no one else saw context 327 // It is safe to delete (which could happen after the return) 328 if( got == &wait_ctx ) return; 329 330 // got == 1p: the future is ready and the context was fully consumed 331 // the server won't use the pointer again 332 // It is safe to delete (which could happen after the return) 333 if( got == 1p ) return; 334 335 // got == 2p: the future is ready but the context hasn't fully been consumed 336 // spin until it is safe to move on 337 if( got == 2p ) { 338 while( this.ptr != 1p ) Pause(); 339 return; 340 } 341 342 // got == any thing else, something wen't wrong here, abort 343 abort("Future in unexpected state"); 344 } 345 346 // Mark the future as abandoned, meaning it will be deleted by the server 347 bool abandon( future_t & this ) { 348 /* paranoid */ verify( this.ptr != 3p ); 349 350 // Mark the future as abandonned 351 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST); 352 353 // If the future isn't already fulfilled, let the server delete it 354 if( got == 0p ) return false; 355 356 // got == 2p: the future is ready but the context hasn't fully been consumed 357 // spin until it is safe to move on 358 if( got == 2p ) { 359 while( this.ptr != 1p ) Pause(); 360 got = 1p; 361 } 362 363 // The future is completed delete it now 364 /* paranoid */ verify( this.ptr != 1p ); 365 free( &this ); 366 return true; 367 } 368 369 // from the server side, mark the future as fulfilled 370 // delete it if needed 371 bool fulfil( future_t & this ) { 372 for() { 373 struct oneshot * expected = this.ptr; 374 // was this abandoned? 375 #if defined(__GNUC__) && __GNUC__ >= 7 376 #pragma GCC diagnostic push 377 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 378 #endif 379 if( expected == 3p ) { free( &this ); return false; } 380 #if defined(__GNUC__) && __GNUC__ >= 7 381 #pragma GCC diagnostic pop 382 #endif 383 384 /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen 385 /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 386 387 // If there is a wait context, we need to consume it and mark it as consumed after 388 // If there is no context then we can skip the in progress phase 389 struct oneshot * want = expected == 0p ? 1p : 2p; 390 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 391 if( expected == 0p ) { /* paranoid */ verify( this.ptr == 1p); return false; } 392 bool ret = post( *expected ); 393 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST); 394 return ret; 395 } 396 } 397 398 } 399 400 // Wait for the future to be fulfilled 401 bool wait( future_t & this ) { 402 oneshot temp; 403 if( !setup(this, temp) ) return false; 404 405 // Wait context is setup, just wait on it 406 bool ret = wait( temp ); 407 408 // Wait for the future to tru 409 while( this.ptr == 2p ) Pause(); 410 // Make sure the state makes sense 411 // Should be fulfilled, could be in progress but it's out of date if so 412 // since if that is the case, the oneshot was fulfilled (unparking this thread) 413 // and the oneshot should not be needed any more 414 __attribute__((unused)) struct oneshot * was = this.ptr; 415 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was ); 416 417 // Mark the future as fulfilled, to be consistent 418 // with potential calls to avail 419 // this.ptr = 1p; 420 return ret; 421 } 151 422 } 152 423 #endif -
libcfa/src/bits/signal.hfa
r3c64c668 r58fe85a 19 19 #include "bits/defs.hfa" 20 20 21 extern "C" {22 21 #include <errno.h> 23 22 #define __USE_GNU … … 26 25 #include <stdlib.h> 27 26 #include <string.h> 28 }29 27 30 28 // Short hands for signal context information … … 54 52 sig, handler, flags, errno, strerror( errno ) 55 53 ); 56 _ exit( EXIT_FAILURE );54 _Exit( EXIT_FAILURE ); 57 55 } // if 58 56 } -
libcfa/src/common.hfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Jul 11 17:54:36 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jul 12 08:02:18 201813 // Update Count : 512 // Last Modified On : Sat Aug 15 08:51:29 2020 13 // Update Count : 14 14 14 // 15 15 … … 67 67 68 68 static inline { 69 char min( char t1, char t2 ) { return t1 < t2 ? t1 : t2; } // optimization 70 intptr_t min( intptr_t t1, intptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization 71 uintptr_t min( uintptr_t t1, uintptr_t t2 ) { return t1 < t2 ? t1 : t2; } // optimization 69 72 forall( otype T | { int ?<?( T, T ); } ) 70 73 T min( T t1, T t2 ) { return t1 < t2 ? t1 : t2; } 71 74 75 char max( char t1, char t2 ) { return t1 > t2 ? t1 : t2; } // optimization 76 intptr_t max( intptr_t t1, intptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization 77 uintptr_t max( uintptr_t t1, uintptr_t t2 ) { return t1 > t2 ? t1 : t2; } // optimization 72 78 forall( otype T | { int ?>?( T, T ); } ) 73 79 T max( T t1, T t2 ) { return t1 > t2 ? t1 : t2; } -
libcfa/src/concurrency/CtxSwitch-i386.S
r3c64c668 r58fe85a 10 10 // Created On : Tue Dec 6 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jul 21 22:29:25 2017 13 // Update Count : 1 14 // 15 // This library is free software; you can redistribute it and/or modify it 16 // under the terms of the GNU Lesser General Public License as published by the 17 // Free Software Foundation; either version 2.1 of the License, or (at your 18 // option) any later version. 19 // 20 // This library is distributed in the hope that it will be useful, but WITHOUT 21 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 22 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 23 // for more details. 24 // 25 // You should have received a copy of the GNU Lesser General Public License 26 // along with this library. 12 // Last Modified On : Sun Sep 6 18:23:37 2020 13 // Update Count : 5 27 14 // 28 15 29 // This context switch routine depends on the fact that the stack of a new 30 // thread has been set up to look like the thread has saved its context in 31 // the normal manner. 32 // 33 // void CtxSwitch( machine_context *from, machine_context *to ); 16 // The context switch routine requires the initial the stack of a thread to 17 // look like the thread has saved its context in the normal manner. 34 18 35 // Offsets in the context structure. This needs to be synchronized with the 36 // high level code a little better. 19 // Offsets must synchronized with the __stack_context_t in invoke.h. 37 20 38 21 #define PTR_BYTE 4 39 22 #define SP_OFFSET ( 0 * PTR_BYTE ) 40 23 #define FP_OFFSET ( 1 * PTR_BYTE ) 41 #define PC_OFFSET ( 2 * PTR_BYTE )42 24 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" 43 30 .text 44 31 .align 2 45 .glob l __cfactx_switch46 .type __cfactx_switch, @function32 .global __cfactx_switch 33 .type __cfactx_switch, @function 47 34 __cfactx_switch: 48 35 49 36 // Copy the "from" context argument from the stack to register eax 50 // Return address is at 0(%esp), with parameters following 37 // Return address is at 0(%esp), with parameters following. 51 38 52 39 movl 4(%esp),%eax … … 63 50 movl %ebp,FP_OFFSET(%eax) 64 51 65 // Copy the "to" context argument from the stack to register eax 66 // Having pushed three words (= 12 bytes) on the stack, the67 // argument is now at 8 + 12 = 20(%esp)52 // Copy the "to" context argument from the stack to register eax. Having 53 // pushed 3 words (= 12 bytes) on the stack, the argument is now at 54 // 8 + 12 = 20(%esp). 68 55 69 56 movl 20(%esp),%eax … … 83 70 84 71 ret 85 .size __cfactx_switch, .-__cfactx_switch72 .size __cfactx_switch, .-__cfactx_switch 86 73 87 74 // Local Variables: // -
libcfa/src/concurrency/CtxSwitch-x86_64.S
r3c64c668 r58fe85a 7 7 // CtxSwitch-x86_64.S -- 8 8 // 9 // Author : Thierry Delisle10 // Created On : Mon Nov 28 12:27:26 20169 // Author : Peter A. Buhr 10 // Created On : Mon Aug 10 08:10:26 2020 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jul 21 22:28:11 2017 13 // Update Count : 1 14 // 15 // This library is free software; you can redistribute it and/or modify it 16 // under the terms of the GNU Lesser General Public License as published by the 17 // Free Software Foundation; either version 2.1 of the License, or (at your 18 // option) any later version. 19 // 20 // This library is distributed in the hope that it will be useful, but WITHOUT 21 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 22 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 23 // for more details. 24 // 25 // You should have received a copy of the GNU Lesser General Public License 26 // along with this library. 12 // Last Modified On : Sat Oct 24 14:36:25 2020 13 // Update Count : 10 27 14 // 28 15 29 // This context switch routine depends on the fact that the stack of a new 30 // thread has been set up to look like the thread has saved its context in 31 // the normal manner. 32 // 33 // void CtxSwitch( machine_context *from, machine_context *to ); 16 // The context switch routine requires the initial the stack of a thread to 17 // look like the thread has saved its context in the normal manner. 34 18 35 // Offsets in the context structure. This needs to be synchronized with the 36 // high level code a little better. 19 // Offsets must synchronized with the __stack_context_t in invoke.h. 37 20 38 21 #define PTR_BYTE 8 … … 40 23 #define FP_OFFSET ( 1 * PTR_BYTE ) 41 24 42 //----------------------------------------------------------------------------- 43 // Regular context switch routine which enables switching from one context to anouther 25 // Context switch between coroutines/tasks. 26 // void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) ; 27 // Arguments "from" in register rdi, "to" in register rsi. 28 29 .file "CtxSwitch-x86_64.S" 44 30 .text 45 31 .align 2 46 .glob l __cfactx_switch47 .type __cfactx_switch, @function32 .global __cfactx_switch 33 .type __cfactx_switch, @function 48 34 __cfactx_switch: 49 35 … … 77 63 78 64 ret 79 .size __cfactx_switch, .-__cfactx_switch65 .size __cfactx_switch, .-__cfactx_switch 80 66 81 //----------------------------------------------------------------------------- 82 // Stub used to create new stacks which are ready to be context switched to 67 // Stub to create new stacks which can be context switched to 68 // void __cfactx_invoke_stub( void ); 69 83 70 .text 84 71 .align 2 85 .glob l __cfactx_invoke_stub86 .type __cfactx_invoke_stub, @function72 .global __cfactx_invoke_stub 73 .type __cfactx_invoke_stub, @function 87 74 __cfactx_invoke_stub: 88 movq %rbx, %rdi 75 movq %rbx, %rdi // move main and this to first two arguments 89 76 movq %r12, %rsi 90 jmp *%r13 91 .size __cfactx_invoke_stub, .-__cfactx_invoke_stub77 jmp *%r13 // jmp to invoke 78 .size __cfactx_invoke_stub, .-__cfactx_invoke_stub 92 79 93 80 // Local Variables: // 94 // mode: c//81 // mode: asm // 95 82 // tab-width: 4 // 96 83 // End: // -
libcfa/src/concurrency/alarm.cfa
r3c64c668 r58fe85a 10 10 // Created On : Fri Jun 2 11:31:25 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 5 08:41:36202013 // Update Count : 6912 // Last Modified On : Wed Jun 17 16:11:35 2020 13 // Update Count : 75 14 14 // 15 15 16 16 #define __cforall_thread__ 17 17 18 extern "C" {19 18 #include <errno.h> 20 19 #include <stdio.h> 20 #include <unistd.h> 21 21 #include <string.h> 22 #include <unistd.h>23 22 #include <sys/time.h> 24 }25 23 26 24 #include "alarm.hfa" 27 #include "kernel _private.hfa"25 #include "kernel/fwd.hfa" 28 26 #include "preemption.hfa" 29 27 … … 47 45 //============================================================================================= 48 46 49 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period ) with( this ) {47 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period) with( this ) { 50 48 this.thrd = thrd; 51 49 this.alarm = alarm; 52 50 this.period = period; 53 next = 0;54 51 set = false; 55 kernel_alarm = false;52 type = User; 56 53 } 57 54 58 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ) with( this ) {55 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ) with( this ) { 59 56 this.proc = proc; 60 57 this.alarm = alarm; 61 58 this.period = period; 62 next = 0;63 59 set = false; 64 kernel_alarm = true; 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; 65 68 } 66 69 … … 71 74 } 72 75 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; 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); 78 85 } 79 86 80 return it == this->tail; 81 } 82 #endif 83 84 static inline void insert_at( alarm_list_t * this, alarm_node_t * n, __alarm_it_t p ) { 85 verify( !n->next ); 86 if( p == this->tail ) { 87 this->tail = &n->next; 88 } 89 else { 90 n->next = *p; 91 } 92 *p = n; 93 94 verify( validate( this ) ); 95 } 96 97 void insert( alarm_list_t * this, alarm_node_t * n ) { 98 alarm_node_t ** it = &this->head; 99 while( (*it) && (n->alarm > (*it)->alarm) ) { 100 it = &(*it)->next; 101 } 102 103 insert_at( this, n, it ); 104 105 verify( validate( this ) ); 87 verify( validate( *this ) ); 106 88 } 107 89 108 90 alarm_node_t * pop( alarm_list_t * this ) { 109 alarm_node_t * head = this->head; 91 verify( validate( *this ) ); 92 alarm_node_t * head = & (*this)`first; 110 93 if( head ) { 111 this->head = head->next; 112 if( !head->next ) { 113 this->tail = &this->head; 114 } 115 head->next = 0p; 94 remove(*head); 116 95 } 117 verify( validate( this ) );96 verify( validate( *this ) ); 118 97 return head; 119 98 } 120 99 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 147 100 void register_self( alarm_node_t * this ) { 148 alarm_list_t * alarms = &event_kernel->alarms;101 alarm_list_t & alarms = event_kernel->alarms; 149 102 150 103 disable_interrupts(); … … 152 105 { 153 106 verify( validate( alarms ) ); 154 bool first = ! alarms->head;107 bool first = ! & alarms`first; 155 108 156 insert( alarms, this );109 insert( &alarms, this ); 157 110 if( first ) { 158 __kernel_set_timer( alarms ->head->alarm - __kernel_get_time() );111 __kernel_set_timer( alarms`first.alarm - __kernel_get_time() ); 159 112 } 160 113 } … … 168 121 lock( event_kernel->lock __cfaabi_dbg_ctx2 ); 169 122 { 170 verify( validate( &event_kernel->alarms ) );171 remove( &event_kernel->alarms,this );123 verify( validate( event_kernel->alarms ) ); 124 remove( *this ); 172 125 } 173 126 unlock( event_kernel->lock ); … … 176 129 } 177 130 131 //============================================================================================= 132 // Utilities 133 //============================================================================================= 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 178 146 // Local Variables: // 179 147 // mode: c // -
libcfa/src/concurrency/alarm.hfa
r3c64c668 r58fe85a 23 23 #include "time.hfa" 24 24 25 #include "containers/list.hfa" 26 25 27 struct $thread; 26 28 struct processor; … … 37 39 //============================================================================================= 38 40 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 39 47 struct alarm_node_t { 40 48 Time alarm; // time when alarm goes off 41 49 Duration period; // if > 0 => period of alarm 42 alarm_node_t * next; // intrusive link list field 50 51 DLISTED_MGD_IMPL_IN(alarm_node_t) 43 52 44 53 union { 45 $thread * thrd; // thrd who created event 46 processor * proc; // proc who created event 54 $thread * thrd; // thrd who created event 55 processor * proc; // proc who created event 56 Alarm_Callback callback; // callback to handle event 47 57 }; 48 58 49 59 bool set :1; // whether or not the alarm has be registered 50 bool kernel_alarm :1; // true if this is not a user defined alarm60 enum alarm_type type; // true if this is not a user defined alarm 51 61 }; 52 53 typedef alarm_node_t ** __alarm_it_t; 62 DLISTED_MGD_IMPL_OUT(alarm_node_t) 54 63 55 64 void ?{}( alarm_node_t & this, $thread * thrd, Time alarm, Duration period ); 56 65 void ?{}( alarm_node_t & this, processor * proc, Time alarm, Duration period ); 66 void ?{}( alarm_node_t & this, Alarm_Callback callback, Time alarm, Duration period ); 57 67 void ^?{}( alarm_node_t & this ); 58 68 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 } 69 typedef dlist(alarm_node_t, alarm_node_t) alarm_list_t; 68 70 69 71 void insert( alarm_list_t * this, alarm_node_t * n ); -
libcfa/src/concurrency/coroutine.cfa
r3c64c668 r58fe85a 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:25202013 // Update Count : 1612 // Last Modified On : Tue Dec 15 12:06:04 2020 13 // Update Count : 23 14 14 // 15 15 … … 18 18 #include "coroutine.hfa" 19 19 20 extern "C" {21 20 #include <stddef.h> 22 21 #include <malloc.h> … … 24 23 #include <string.h> 25 24 #include <unistd.h> 26 // use this define to make unwind.h play nice, definetely a hack 27 #define HIDE_EXPORTS 25 #include <sys/mman.h> // mprotect 28 26 #include <unwind.h> 29 #undef HIDE_EXPORTS30 #include <sys/mman.h>31 }32 27 33 28 #include "kernel_private.hfa" 29 #include "exception.hfa" 30 #include "math.hfa" 31 32 #define CFA_COROUTINE_USE_MMAP 0 34 33 35 34 #define __CFA_INVOKE_PRIVATE__ … … 47 46 48 47 //----------------------------------------------------------------------------- 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 //----------------------------------------------------------------------------- 49 85 // Global state variables 50 86 51 87 // minimum feasible stack size in bytes 52 #define MinStackSize 1000 88 static const size_t MinStackSize = 1000; 53 89 extern size_t __page_size; // architecture pagesize HACK, should go in proper runtime singleton 90 extern int __map_prot; 54 91 55 92 void __stack_prepare( __stack_info_t * this, size_t create_size ); 93 void __stack_clean ( __stack_info_t * this ); 56 94 57 95 //----------------------------------------------------------------------------- … … 74 112 bool userStack = ((intptr_t)this.storage & 0x1) != 0; 75 113 if ( ! userStack && this.storage ) { 76 __attribute__((may_alias)) intptr_t * istorage = (intptr_t *)&this.storage; 77 *istorage &= (intptr_t)-1; 78 79 void * storage = this.storage->limit; 80 __cfaabi_dbg_debug_do( 81 storage = (char*)(storage) - __page_size; 82 if ( mprotect( storage, __page_size, PROT_READ | PROT_WRITE ) == -1 ) { 83 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) ); 84 } 85 ); 86 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage); 87 free( storage ); 114 __stack_clean( &this ); 88 115 } 89 116 } … … 101 128 void ^?{}($coroutine& this) { 102 129 if(this.state != Halted && this.state != Start && this.state != Primed) { 103 $coroutine * src = TL_GET( this_thread )->curr_cor;130 $coroutine * src = active_coroutine(); 104 131 $coroutine * dst = &this; 105 132 … … 134 161 assert(__page_size != 0l); 135 162 size_t size = libCeiling( storageSize, 16 ) + stack_data_size; 163 size = ceiling(size, __page_size); 136 164 137 165 // If we are running debug, we also need to allocate a guardpage to catch stack overflows. 138 166 void * storage; 139 __cfaabi_dbg_debug_do( 140 storage = memalign( __page_size, size + __page_size ); 141 ); 142 __cfaabi_dbg_no_debug_do( 143 storage = (void*)malloc(size); 144 ); 145 167 #if CFA_COROUTINE_USE_MMAP 168 storage = mmap(0p, size + __page_size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 169 if(storage == ((void*)-1)) { 170 abort( "coroutine stack creation : internal error, mmap failure, error(%d) %s.", errno, strerror( errno ) ); 171 } 172 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) { 173 abort( "coroutine stack creation : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) ); 174 } // if 175 storage = (void *)(((intptr_t)storage) + __page_size); 176 #else 177 __cfaabi_dbg_debug_do( 178 storage = memalign( __page_size, size + __page_size ); 179 ); 180 __cfaabi_dbg_no_debug_do( 181 storage = (void*)malloc(size); 182 ); 183 184 __cfaabi_dbg_debug_do( 185 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) { 186 abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) ); 187 } 188 storage = (void *)(((intptr_t)storage) + __page_size); 189 ); 190 #endif 146 191 __cfaabi_dbg_print_safe("Kernel : Created stack %p of size %zu\n", storage, size); 147 __cfaabi_dbg_debug_do(148 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {149 abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) );150 }151 storage = (void *)(((intptr_t)storage) + __page_size);152 );153 192 154 193 verify( ((intptr_t)storage & (libAlign() - 1)) == 0ul ); 155 194 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_MMAP 202 storage = (void *)(((intptr_t)storage) - __page_size); 203 if(munmap(storage, size + __page_size) == -1) { 204 abort( "coroutine stack destruction : internal error, munmap failure, error(%d) %s.", errno, strerror( errno ) ); 205 } 206 #else 207 __cfaabi_dbg_debug_do( 208 storage = (char*)(storage) - __page_size; 209 if ( mprotect( storage, __page_size, __map_prot ) == -1 ) { 210 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) ); 211 } 212 ); 213 214 free( storage ); 215 #endif 216 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage); 156 217 } 157 218 … … 175 236 size = libFloor(create_size - stack_data_size - diff, libAlign()); 176 237 } // if 177 assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of % d bytes for a stack.", size, MinStackSize );178 179 this->storage = (__stack_t *)((intptr_t)storage + size );238 assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %zd bytes for a stack.", size, MinStackSize ); 239 240 this->storage = (__stack_t *)((intptr_t)storage + size - sizeof(__stack_t)); 180 241 this->storage->limit = storage; 181 this->storage->base = (void*)((intptr_t)storage + size); 242 this->storage->base = (void*)((intptr_t)storage + size - sizeof(__stack_t)); 243 this->storage->exception_context.top_resume = 0p; 244 this->storage->exception_context.current_exception = 0p; 182 245 __attribute__((may_alias)) intptr_t * istorage = (intptr_t*)&this->storage; 183 246 *istorage |= userStack ? 0x1 : 0x0; … … 205 268 206 269 struct $coroutine * __cfactx_cor_finish(void) { 207 struct $coroutine * cor = kernelTLS.this_thread->curr_cor;270 struct $coroutine * cor = active_coroutine(); 208 271 209 272 if(cor->state == Primed) { 210 suspend();273 __cfactx_suspend(); 211 274 } 212 275 -
libcfa/src/concurrency/coroutine.hfa
r3c64c668 r58fe85a 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) *); 20 34 21 35 //----------------------------------------------------------------------------- … … 23 37 // Anything that implements this trait can be resumed. 24 38 // Anything that is resumed is a coroutine. 25 trait is_coroutine(dtype T ) {26 void main(T & this);27 $coroutine * get_coroutine(T & this);39 trait is_coroutine(dtype T | IS_RESUMPTION_EXCEPTION(CoroutineCancelled, (T))) { 40 void main(T & this); 41 $coroutine * get_coroutine(T & this); 28 42 }; 29 43 … … 46 60 //----------------------------------------------------------------------------- 47 61 // 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 53 62 forall(dtype T | is_coroutine(T)) 54 63 void prime(T & cor); 55 64 56 static inline struct $coroutine * active_coroutine() { return TL_GET( this_thread)->curr_cor; }65 static inline struct $coroutine * active_coroutine() { return active_thread()->curr_cor; } 57 66 58 67 //----------------------------------------------------------------------------- … … 75 84 static inline void $ctx_switch( $coroutine * src, $coroutine * dst ) __attribute__((nonnull (1, 2))) { 76 85 // set state of current coroutine to inactive 77 src->state = src->state == Halted ? Halted : Inactive;86 src->state = src->state == Halted ? Halted : Blocked; 78 87 79 88 // set new coroutine that task is executing 80 TL_GET( this_thread)->curr_cor = dst;89 active_thread()->curr_cor = dst; 81 90 82 91 // context switch to specified coroutine … … 93 102 } 94 103 95 extern void __stack_prepare ( __stack_info_t * this, size_t size /* ignored if storage already allocated */); 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 96 107 97 108 // Suspend implementation inlined for performance 98 static inline void suspend(void) { 99 // optimization : read TLS once and reuse it 100 // Safety note: this is preemption safe since if 101 // preemption occurs after this line, the pointer 102 // will also migrate which means this value will 103 // stay in syn with the TLS 104 $coroutine * src = TL_GET( this_thread )->curr_cor; 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(); 105 117 106 assertf( src->last != 0,107 "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n"108 "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",109 src->name, src );110 assertf( src->last->state != Halted,111 "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n"112 "Possible cause is terminated coroutine's main routine has already returned.",113 src->name, src, src->last->name, src->last );118 assertf( src->last != 0, 119 "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n" 120 "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.", 121 src->name, src ); 122 assertf( src->last->state != Halted, 123 "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n" 124 "Possible cause is terminated coroutine's main routine has already returned.", 125 src->name, src, src->last->name, src->last ); 114 126 115 $ctx_switch( src, src->last ); 127 $ctx_switch( src, src->last ); 128 } 116 129 } 130 131 forall(dtype T | is_coroutine(T)) 132 void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc ); 117 133 118 134 // Resume implementation inlined for performance … … 124 140 // will also migrate which means this value will 125 141 // stay in syn with the TLS 126 $coroutine * src = TL_GET( this_thread )->curr_cor;142 $coroutine * src = active_coroutine(); 127 143 $coroutine * dst = get_coroutine(cor); 128 144 129 145 if( unlikely(dst->context.SP == 0p) ) { 130 TL_GET( this_thread )->curr_cor = dst;131 146 __stack_prepare(&dst->stack, 65000); 132 147 __cfactx_start(main, dst, cor, __cfactx_invoke_coroutine); 133 TL_GET( this_thread )->curr_cor = src;134 148 } 135 149 … … 148 162 // always done for performance testing 149 163 $ctx_switch( src, dst ); 164 if ( unlikely(dst->cancellation) ) { 165 __cfaehm_cancelled_coroutine( cor, dst ); 166 } 150 167 151 168 return cor; … … 158 175 // will also migrate which means this value will 159 176 // stay in syn with the TLS 160 $coroutine * src = TL_GET( this_thread )->curr_cor;177 $coroutine * src = active_coroutine(); 161 178 162 179 // not resuming self ? -
libcfa/src/concurrency/invoke.c
r3c64c668 r58fe85a 10 10 // Created On : Tue Jan 17 12:27:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 9 16:37:42 201813 // Update Count : 512 // Last Modified On : Sat Oct 24 14:35:28 2020 13 // Update Count : 32 14 14 // 15 15 … … 109 109 110 110 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 aligned111 void *fixedRegisters[3]; // fixed registers ebx, edi, esi (popped on 1st uSwitch, values unimportant) 112 void *rturn; // where to go on return from uSwitch 113 void *dummyReturn; // fake return compiler would have pushed on call to uInvoke 114 void *argument[3]; // for 16-byte ABI, 16-byte alignment starts here 115 void *padding; // padding to force 16-byte alignment, as "base" is 16-byte aligned 116 116 }; 117 117 … … 122 122 123 123 fs->dummyReturn = NULL; 124 fs->argument[0] = main; // argument to invoke125 fs->argument[1] = this; // argument to invoke124 fs->argument[0] = main; // argument to invoke 125 fs->argument[1] = this; // argument to invoke 126 126 fs->rturn = invoke; 127 127 … … 129 129 130 130 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 alignment131 void *fixedRegisters[5]; // fixed registers rbx, r12, r13, r14, r15 132 void *rturn; // where to go on return from uSwitch 133 void *dummyReturn; // NULL return address to provide proper alignment 134 134 }; 135 135 136 136 cor->context.SP = (char *)stack->base - sizeof( struct FakeStack ); 137 cor->context.FP = NULL; // terminate stack with NULL fp137 cor->context.FP = NULL; // terminate stack with NULL fp 138 138 139 139 struct FakeStack *fs = (struct FakeStack *)cor->context.SP; … … 141 141 fs->dummyReturn = NULL; 142 142 fs->rturn = __cfactx_invoke_stub; 143 fs->fixedRegisters[0] = main; 144 fs->fixedRegisters[1] = this; 143 fs->fixedRegisters[0] = main; // argument to invoke 144 fs->fixedRegisters[1] = this; // argument to invoke 145 145 fs->fixedRegisters[2] = invoke; 146 146 147 #elif defined( __ARM_ARCH ) 148 #error ARM needs to be upgrade to use to parameters like X86/X64 (A.K.A. : I broke this and do not know how to fix it) 147 #elif defined( __ARM_ARCH_32 ) 148 #error ARM needs to be upgrade to use two parameters like X86/X64 (A.K.A. : I broke this and do not know how to fix it) 149 // More details about the error: 150 // To avoid the thunk problem, I changed the invoke routine to pass the main explicitly 151 // instead of relying on an assertion. This effectively hoists any required thunk one level 152 // which was enough to get to global scope in most cases. 153 // This means that __cfactx_invoke_... now takes two parameters and the FakeStack needs 154 // to be adjusted as a consequence of that. 155 // I don't know how to do that for ARM, hence the #error 156 149 157 struct FakeStack { 150 float fpRegs[16]; // floating point registers151 void * intRegs[9];// integer/pointer registers152 void * arg[2];// placeholder for this pointer158 float fpRegs[16]; // floating point registers 159 void * intRegs[9]; // integer/pointer registers 160 void * arg[2]; // placeholder for this pointer 153 161 }; 154 162 … … 162 170 fs->arg[1] = invoke; 163 171 172 #elif defined( __ARM_ARCH ) 173 struct FakeStack { 174 void * intRegs[12]; // x19-x30 integer registers 175 double fpRegs[8]; // v8-v15 floating point 176 }; 177 178 cor->context.SP = (char *)stack->base - sizeof( struct FakeStack ); 179 cor->context.FP = NULL; 180 181 struct FakeStack *fs = (struct FakeStack *)cor->context.SP; 182 183 fs->intRegs[0] = main; // argument to invoke x19 => x0 184 fs->intRegs[1] = this; // argument to invoke x20 => x1 185 fs->intRegs[2] = invoke; 186 fs->intRegs[11] = __cfactx_invoke_stub; // link register x30 => ret moves to pc 164 187 #else 165 188 #error uknown hardware architecture -
libcfa/src/concurrency/invoke.h
r3c64c668 r58fe85a 17 17 #include "bits/defs.hfa" 18 18 #include "bits/locks.hfa" 19 #include "kernel/fwd.hfa" 19 20 20 21 #ifdef __cforall … … 26 27 #define _INVOKE_H_ 27 28 28 #ifdef __ARM_ARCH 29 // function prototypes are only really used by these macros on ARM 30 void disable_global_interrupts(); 31 void enable_global_interrupts(); 32 33 #define TL_GET( member ) ( { __typeof__( kernelTLS.member ) target; \ 34 disable_global_interrupts(); \ 35 target = kernelTLS.member; \ 36 enable_global_interrupts(); \ 37 target; } ) 38 #define TL_SET( member, value ) disable_global_interrupts(); \ 39 kernelTLS.member = value; \ 40 enable_global_interrupts(); 41 #else 42 #define TL_GET( member ) kernelTLS.member 43 #define TL_SET( member, value ) kernelTLS.member = value; 44 #endif 45 46 #ifdef __cforall 47 extern "Cforall" { 48 extern __attribute__((aligned(128))) thread_local struct KernelThreadData { 49 struct $thread * volatile this_thread; 50 struct processor * volatile this_processor; 51 52 struct { 53 volatile unsigned short disable_count; 54 volatile bool enabled; 55 volatile bool in_progress; 56 } preemption_state; 57 58 uint32_t rand_seed; 59 } kernelTLS __attribute__ ((tls_model ( "initial-exec" ))); 60 } 61 #endif 29 struct __cfaehm_try_resume_node; 30 struct __cfaehm_base_exception_t; 31 struct exception_context_t { 32 struct __cfaehm_try_resume_node * top_resume; 33 struct __cfaehm_base_exception_t * current_exception; 34 }; 62 35 63 36 struct __stack_context_t { … … 85 58 // base of stack 86 59 void * base; 60 61 // Information for exception handling. 62 struct exception_context_t exception_context; 87 63 }; 88 64 … … 92 68 }; 93 69 94 enum coroutine_state { Halted, Start, Primed, Inactive, Active, Rerun }; 95 enum __Preemption_Reason { __NO_PREEMPTION, __ALARM_PREEMPTION, __POLL_PREEMPTION, __MANUAL_PREEMPTION }; 70 enum __Coroutine_State { Halted, Start, Primed, Blocked, Ready, Active, Cancelled, Halting }; 96 71 97 72 struct $coroutine { … … 106 81 107 82 // current execution status for coroutine 108 enum coroutine_state state;83 enum __Coroutine_State state; 109 84 110 85 // first coroutine to resume this one … … 118 93 119 94 }; 95 // Wrapper for gdb 96 struct cfathread_coroutine_t { struct $coroutine debug; }; 97 98 static inline struct __stack_t * __get_stack( struct $coroutine * cor ) { 99 return (struct __stack_t*)(((uintptr_t)cor->stack.storage) & ((uintptr_t)-2)); 100 } 120 101 121 102 // struct which calls the monitor is accepting … … 150 131 struct __condition_node_t * dtor_node; 151 132 }; 133 // Wrapper for gdb 134 struct cfathread_monitor_t { struct $monitor debug; }; 152 135 153 136 struct __monitor_group_t { … … 157 140 // last function that acquired monitors 158 141 fptr_t func; 142 }; 143 144 // Link lists fields 145 // instrusive link field for threads 146 struct __thread_desc_link { 147 struct $thread * next; 148 struct $thread * prev; 149 volatile unsigned long long ts; 150 int preferred; 159 151 }; 160 152 … … 165 157 166 158 // current execution status for coroutine 167 volatile int state; 168 enum __Preemption_Reason preempted; 159 // Possible values are: 160 // - TICKET_BLOCKED (-1) thread is blocked 161 // - TICKET_RUNNING ( 0) thread is running 162 // - TICKET_UNBLOCK ( 1) thread should ignore next block 163 volatile int ticket; 164 enum __Coroutine_State state:8; 165 enum __Preemption_Reason preempted:8; 169 166 170 167 //SKULLDUGGERY errno is not save in the thread data structure because returnToKernel appears to be the only function to require saving and restoring it 168 169 // pointer to the cluster on which the thread is running 170 struct cluster * curr_cluster; 171 172 // Link lists fields 173 // instrusive link field for threads 174 struct __thread_desc_link link; 171 175 172 176 // coroutine body used to store context … … 182 186 struct $monitor * self_mon_p; 183 187 184 // pointer to the cluster on which the thread is running185 struct cluster * curr_cluster;186 187 188 // monitors currently held by this thread 188 189 struct __monitor_group_t monitors; 189 190 190 // Link lists fields 191 // instrusive link field for threads 192 struct $thread * next; 191 // used to put threads on user data structures 192 struct { 193 struct $thread * next; 194 struct $thread * back; 195 } seqable; 193 196 194 197 struct { … … 196 199 struct $thread * prev; 197 200 } node; 198 }; 201 202 #if defined( __CFA_WITH_VERIFY__ ) 203 void * canary; 204 #endif 205 }; 206 // Wrapper for gdb 207 struct cfathread_thread_t { struct $thread debug; }; 208 209 #ifdef __CFA_DEBUG__ 210 void __cfaabi_dbg_record_thrd($thread & this, bool park, const char prev_name[]); 211 #else 212 #define __cfaabi_dbg_record_thrd(x, y, z) 213 #endif 199 214 200 215 #ifdef __cforall 201 216 extern "Cforall" { 217 202 218 static inline $thread *& get_next( $thread & this ) __attribute__((const)) { 203 return this. next;219 return this.link.next; 204 220 } 205 221 206 222 static inline [$thread *&, $thread *& ] __get( $thread & this ) __attribute__((const)) { 207 223 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; 208 236 } 209 237 -
libcfa/src/concurrency/kernel.cfa
r3c64c668 r58fe85a 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 13:03:15202013 // Update Count : 5812 // Last Modified On : Mon Aug 31 07:08:20 2020 13 // Update Count : 71 14 14 // 15 15 16 16 #define __cforall_thread__ 17 // #define __CFA_DEBUG_PRINT_RUNTIME_CORE__ 17 18 18 19 //C Includes 19 #include <stddef.h>20 20 #include <errno.h> 21 #include <string.h>22 extern "C" {23 21 #include <stdio.h> 24 #include <fenv.h>25 #include <sys/resource.h>26 22 #include <signal.h> 27 23 #include <unistd.h> 28 #include <limits.h> // PTHREAD_STACK_MIN29 #include <sys/mman.h> // mprotect30 }31 24 32 25 //CFA Includes 33 #include "time.hfa"34 26 #include "kernel_private.hfa" 35 27 #include "preemption.hfa" 36 #include "startup.hfa"37 28 38 29 //Private includes … … 40 31 #include "invoke.h" 41 32 33 42 34 //----------------------------------------------------------------------------- 43 35 // Some assembly required 44 36 #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 53 37 // mxcr : SSE Status and Control bits (control bits are preserved across function calls) 54 38 // fcw : X87 FPU control word (preserved across function calls) … … 72 56 73 57 #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 82 58 #define __x87_store \ 83 59 uint32_t __mxcr; \ … … 98 74 ) 99 75 100 101 #elif defined( __ARM_ARCH ) 102 #define CtxGet( ctx ) __asm__ ( \ 103 "mov %0,%%sp\n" \ 104 "mov %1,%%r11\n" \ 105 : "=rm" (ctx.SP), "=rm" (ctx.FP) ) 76 #elif defined( __arm__ ) 77 #define __x87_store 78 #define __x87_load 79 80 #elif defined( __aarch64__ ) 81 #define __x87_store \ 82 uint32_t __fpcntl[2]; \ 83 __asm__ volatile ( \ 84 "mrs x9, FPCR\n" \ 85 "mrs x10, FPSR\n" \ 86 "stp x9, x10, %0\n" \ 87 : "=m" (__fpcntl) : : "x9", "x10" \ 88 ) 89 90 #define __x87_load \ 91 __asm__ volatile ( \ 92 "ldp x9, x10, %0\n" \ 93 "msr FPSR, x10\n" \ 94 "msr FPCR, x9\n" \ 95 : "=m" (__fpcntl) : : "x9", "x10" \ 96 ) 97 106 98 #else 107 #error un knownhardware architecture99 #error unsupported hardware architecture 108 100 #endif 109 101 102 extern $thread * mainThread; 103 extern processor * mainProcessor; 104 110 105 //----------------------------------------------------------------------------- 111 //Start and stop routine for the kernel, declared first to make sure they run first 112 static void __kernel_startup (void) __attribute__(( constructor( STARTUP_PRIORITY_KERNEL ) )); 113 static void __kernel_shutdown(void) __attribute__(( destructor ( STARTUP_PRIORITY_KERNEL ) )); 114 115 //----------------------------------------------------------------------------- 116 // Kernel storage 117 KERNEL_STORAGE(cluster, mainCluster); 118 KERNEL_STORAGE(processor, mainProcessor); 119 KERNEL_STORAGE($thread, mainThread); 120 KERNEL_STORAGE(__stack_t, mainThreadCtx); 121 122 cluster * mainCluster; 123 processor * mainProcessor; 124 $thread * mainThread; 125 126 extern "C" { 127 struct { __dllist_t(cluster) list; __spinlock_t lock; } __cfa_dbg_global_clusters; 128 } 129 130 size_t __page_size = 0; 131 132 //----------------------------------------------------------------------------- 133 // Global state 134 thread_local struct KernelThreadData kernelTLS __attribute__ ((tls_model ( "initial-exec" ))) = { 135 NULL, // cannot use 0p 136 NULL, 137 { 1, false, false }, 138 6u //this should be seeded better but due to a bug calling rdtsc doesn't work 139 }; 140 141 //----------------------------------------------------------------------------- 142 // Struct to steal stack 143 struct current_stack_info_t { 144 __stack_t * storage; // pointer to stack object 145 void * base; // base of stack 146 void * limit; // stack grows towards stack limit 147 void * context; // address of cfa_context_t 148 }; 149 150 void ?{}( current_stack_info_t & this ) { 151 __stack_context_t ctx; 152 CtxGet( ctx ); 153 this.base = ctx.FP; 154 155 rlimit r; 156 getrlimit( RLIMIT_STACK, &r); 157 size_t size = r.rlim_cur; 158 159 this.limit = (void *)(((intptr_t)this.base) - size); 160 this.context = &storage_mainThreadCtx; 161 } 162 163 //----------------------------------------------------------------------------- 164 // Main thread construction 165 166 void ?{}( $coroutine & this, current_stack_info_t * info) with( this ) { 167 stack.storage = info->storage; 168 with(*stack.storage) { 169 limit = info->limit; 170 base = info->base; 171 } 172 __attribute__((may_alias)) intptr_t * istorage = (intptr_t*) &stack.storage; 173 *istorage |= 0x1; 174 name = "Main Thread"; 175 state = Start; 176 starter = 0p; 177 last = 0p; 178 cancellation = 0p; 179 } 180 181 void ?{}( $thread & this, current_stack_info_t * info) with( this ) { 182 state = Start; 183 self_cor{ info }; 184 curr_cor = &self_cor; 185 curr_cluster = mainCluster; 186 self_mon.owner = &this; 187 self_mon.recursion = 1; 188 self_mon_p = &self_mon; 189 next = 0p; 190 191 node.next = 0p; 192 node.prev = 0p; 193 doregister(curr_cluster, this); 194 195 monitors{ &self_mon_p, 1, (fptr_t)0 }; 196 } 197 198 //----------------------------------------------------------------------------- 199 // Processor coroutine 200 void ?{}(processorCtx_t & this) { 201 202 } 203 204 // Construct the processor context of non-main processors 205 static void ?{}(processorCtx_t & this, processor * proc, current_stack_info_t * info) { 206 (this.__cor){ info }; 207 this.proc = proc; 208 } 209 210 static void * __invoke_processor(void * arg); 211 212 void ?{}(processor & this, const char name[], cluster & cltr) with( this ) { 213 this.name = name; 214 this.cltr = &cltr; 215 terminated{ 0 }; 216 destroyer = 0p; 217 do_terminate = false; 218 preemption_alarm = 0p; 219 pending_preemption = false; 220 runner.proc = &this; 221 222 idleLock{}; 223 224 __cfaabi_dbg_print_safe("Kernel : Starting core %p\n", &this); 225 226 this.stack = __create_pthread( &this.kernel_thread, __invoke_processor, (void *)&this ); 227 228 __cfaabi_dbg_print_safe("Kernel : core %p started\n", &this); 229 } 230 231 void ^?{}(processor & this) with( this ){ 232 if( ! __atomic_load_n(&do_terminate, __ATOMIC_ACQUIRE) ) { 233 __cfaabi_dbg_print_safe("Kernel : core %p signaling termination\n", &this); 234 235 __atomic_store_n(&do_terminate, true, __ATOMIC_RELAXED); 236 wake( &this ); 237 238 P( terminated ); 239 verify( kernelTLS.this_processor != &this); 240 } 241 242 pthread_join( kernel_thread, 0p ); 243 free( this.stack ); 244 } 245 246 void ?{}(cluster & this, const char name[], Duration preemption_rate) with( this ) { 247 this.name = name; 248 this.preemption_rate = preemption_rate; 249 ready_queue{}; 250 ready_queue_lock{}; 251 252 procs{ __get }; 253 idles{ __get }; 254 threads{ __get }; 255 256 doregister(this); 257 } 258 259 void ^?{}(cluster & this) { 260 unregister(this); 261 } 106 // Kernel Scheduling logic 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 262 116 263 117 //============================================================================================= 264 118 // Kernel Scheduling logic 265 119 //============================================================================================= 266 static $thread * __next_thread(cluster * this);267 static void __run_thread(processor * this, $thread * dst);268 static void __halt(processor * this);269 270 120 //Main of the processor contexts 271 121 void main(processorCtx_t & runner) { 272 122 // Because of a bug, we couldn't initialized the seed on construction 273 123 // Do it here 274 kernelTLS.rand_seed ^= rdtscl(); 124 __cfaabi_tls.rand_seed ^= rdtscl(); 125 __cfaabi_tls.ready_rng.fwd_seed = 25214903917_l64u * (rdtscl() ^ (uintptr_t)&runner); 126 __tls_rand_advance_bck(); 275 127 276 128 processor * this = runner.proc; 277 129 verify(this); 278 130 279 __cfaabi_dbg_print_safe("Kernel : core %p starting\n", this); 280 281 doregister(this->cltr, this); 131 __cfadbg_print_safe(runtime_core, "Kernel : core %p starting\n", this); 132 #if !defined(__CFA_NO_STATISTICS__) 133 if( this->print_halts ) { 134 __cfaabi_bits_print_safe( STDOUT_FILENO, "Processor : %d - %s (%p)\n", this->id, this->name, (void*)this); 135 } 136 #endif 282 137 283 138 { … … 285 140 preemption_scope scope = { this }; 286 141 287 __cfa abi_dbg_print_safe("Kernel : core %p started\n", this);142 __cfadbg_print_safe(runtime_core, "Kernel : core %p started\n", this); 288 143 289 144 $thread * readyThread = 0p; 290 for( unsigned int spin_count = 0; ! __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST); spin_count++ ) { 145 MAIN_LOOP: 146 for() { 147 // Try to get the next thread 291 148 readyThread = __next_thread( this->cltr ); 292 149 293 if(readyThread) { 294 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 295 /* paranoid */ verifyf( readyThread->state == Inactive || readyThread->state == Start || readyThread->preempted != __NO_PREEMPTION, "state : %d, preempted %d\n", readyThread->state, readyThread->preempted); 296 /* paranoid */ verifyf( readyThread->next == 0p, "Expected null got %p", readyThread->next ); 297 298 __run_thread(this, readyThread); 299 300 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 301 302 spin_count = 0; 303 } else { 304 // spin(this, &spin_count); 305 __halt(this); 150 if( !readyThread ) { 151 readyThread = __next_thread_slow( this->cltr ); 306 152 } 307 } 308 309 __cfaabi_dbg_print_safe("Kernel : core %p stopping\n", this); 310 } 311 312 unregister(this->cltr, this); 153 154 HALT: 155 if( !readyThread ) { 156 // Don't block if we are done 157 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 158 159 #if !defined(__CFA_NO_STATISTICS__) 160 __tls_stats()->ready.sleep.halts++; 161 #endif 162 163 // Push self to idle stack 164 push(this->cltr->idles, * this); 165 166 // Confirm the ready-queue is empty 167 readyThread = __next_thread_slow( this->cltr ); 168 if( readyThread ) { 169 // A thread was found, cancel the halt 170 remove(this->cltr->idles, * this); 171 172 #if !defined(__CFA_NO_STATISTICS__) 173 __tls_stats()->ready.sleep.cancels++; 174 #endif 175 176 // continue the mai loop 177 break HALT; 178 } 179 180 #if !defined(__CFA_NO_STATISTICS__) 181 if(this->print_halts) { 182 __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->id, rdtscl()); 183 } 184 #endif 185 186 wait( this->idle ); 187 188 #if !defined(__CFA_NO_STATISTICS__) 189 if(this->print_halts) { 190 __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->id, rdtscl()); 191 } 192 #endif 193 194 // We were woken up, remove self from idle 195 remove(this->cltr->idles, * this); 196 197 // DON'T just proceed, start looking again 198 continue MAIN_LOOP; 199 } 200 201 /* paranoid */ verify( readyThread ); 202 203 // We found a thread run it 204 __run_thread(this, readyThread); 205 206 // Are we done? 207 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 208 } 209 210 __cfadbg_print_safe(runtime_core, "Kernel : core %p stopping\n", this); 211 } 313 212 314 213 V( this->terminated ); 315 214 316 __cfaabi_dbg_print_safe("Kernel : core %p terminated\n", this); 215 if(this == mainProcessor) { 216 // HACK : the coroutine context switch expects this_thread to be set 217 // and it make sense for it to be set in all other cases except here 218 // fake it 219 __cfaabi_tls.this_thread = mainThread; 220 } 221 222 __cfadbg_print_safe(runtime_core, "Kernel : core %p terminated\n", this); 317 223 } 318 224 … … 324 230 // from the processor coroutine to the target thread 325 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 326 237 $coroutine * proc_cor = get_coroutine(this->runner); 327 328 // Update global state329 kernelTLS.this_thread = thrd_dst;330 238 331 239 // set state of processor coroutine to inactive 332 240 verify(proc_cor->state == Active); 333 proc_cor->state = Inactive;241 proc_cor->state = Blocked; 334 242 335 243 // Actually run the thread 336 244 RUNNING: while(true) { 337 if(unlikely(thrd_dst->preempted)) { 338 thrd_dst->preempted = __NO_PREEMPTION; 339 verify(thrd_dst->state == Active || thrd_dst->state == Rerun); 340 } else { 341 verify(thrd_dst->state == Start || thrd_dst->state == Primed || thrd_dst->state == Inactive); 342 thrd_dst->state = Active; 343 } 344 345 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 245 thrd_dst->preempted = __NO_PREEMPTION; 246 thrd_dst->state = Active; 247 248 // Update global state 249 kernelTLS().this_thread = thrd_dst; 250 251 /* paranoid */ verify( ! __preemption_enabled() ); 252 /* paranoid */ verify( kernelTLS().this_thread == thrd_dst ); 253 /* paranoid */ verify( thrd_dst->curr_cluster == this->cltr ); 254 /* paranoid */ verify( thrd_dst->context.SP ); 255 /* paranoid */ verify( thrd_dst->state != Halted ); 256 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst ); // add escape condition if we are setting up the processor 257 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst ); // add escape condition if we are setting up the processor 258 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_dst->canary ); 259 260 346 261 347 262 // set context switch to the thread that the processor is executing 348 verify( thrd_dst->context.SP );349 263 __cfactx_switch( &proc_cor->context, &thrd_dst->context ); 350 264 // when __cfactx_switch returns we are back in the processor coroutine 351 265 352 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 353 266 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_dst->canary ); 267 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit), "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst ); 268 /* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ), "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst ); 269 /* paranoid */ verify( thrd_dst->context.SP ); 270 /* paranoid */ verify( thrd_dst->curr_cluster == this->cltr ); 271 /* paranoid */ verify( kernelTLS().this_thread == thrd_dst ); 272 /* paranoid */ verify( ! __preemption_enabled() ); 273 274 // Reset global state 275 kernelTLS().this_thread = 0p; 354 276 355 277 // We just finished running a thread, there are a few things that could have happened. 356 278 // 1 - Regular case : the thread has blocked and now one has scheduled it yet. 357 279 // 2 - Racy case : the thread has blocked but someone has already tried to schedule it. 358 // 3 - Polite Racy case : the thread has blocked, someone has already tried to schedule it, but the thread is nice and wants to go through the ready-queue any way359 280 // 4 - Preempted 360 281 // In case 1, we may have won a race so we can't write to the state again. 361 282 // In case 2, we lost the race so we now own the thread. 362 // In case 3, we lost the race but can just reschedule the thread.363 283 364 284 if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) { … … 368 288 } 369 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; 295 } 296 297 /* paranoid */ verify( thrd_dst->state == Active ); 298 thrd_dst->state = Blocked; 299 370 300 // set state of processor coroutine to active and the thread to inactive 371 static_assert(sizeof(thrd_dst->state) == sizeof(int)); 372 enum coroutine_state old_state = __atomic_exchange_n(&thrd_dst->state, Inactive, __ATOMIC_SEQ_CST); 373 switch(old_state) { 374 case Halted: 375 // The thread has halted, it should never be scheduled/run again, leave it back to Halted and move on 376 thrd_dst->state = Halted; 377 378 // We may need to wake someone up here since 379 unpark( this->destroyer ); 380 this->destroyer = 0p; 381 break RUNNING; 382 case Active: 301 int old_ticket = __atomic_fetch_sub(&thrd_dst->ticket, 1, __ATOMIC_SEQ_CST); 302 switch(old_ticket) { 303 case TICKET_RUNNING: 383 304 // This is case 1, the regular case, nothing more is needed 384 305 break RUNNING; 385 case Rerun:306 case TICKET_UNBLOCK: 386 307 // This is case 2, the racy case, someone tried to run this thread before it finished blocking 387 308 // In this case, just run it again. … … 389 310 default: 390 311 // This makes no sense, something is wrong abort 391 abort( "Finished running a thread that was Inactive/Start/Primed %d\n", old_state);312 abort(); 392 313 } 393 314 } … … 395 316 // Just before returning to the processor, set the processor coroutine to active 396 317 proc_cor->state = Active; 318 319 /* paranoid */ verify( ! __preemption_enabled() ); 397 320 } 398 321 399 322 // KERNEL_ONLY 400 323 void returnToKernel() { 401 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 402 $coroutine * proc_cor = get_coroutine(kernelTLS.this_processor->runner); 403 $thread * thrd_src = kernelTLS.this_thread; 324 /* paranoid */ verify( ! __preemption_enabled() ); 325 $coroutine * proc_cor = get_coroutine(kernelTLS().this_processor->runner); 326 $thread * thrd_src = kernelTLS().this_thread; 327 328 #if !defined(__CFA_NO_STATISTICS__) 329 struct processor * last_proc = kernelTLS().this_processor; 330 #endif 404 331 405 332 // Run the thread on this processor … … 409 336 __x87_store; 410 337 #endif 411 verify( proc_cor->context.SP ); 338 /* paranoid */ verify( proc_cor->context.SP ); 339 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_src->canary ); 412 340 __cfactx_switch( &thrd_src->context, &proc_cor->context ); 341 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd_src->canary ); 413 342 #if defined( __i386 ) || defined( __x86_64 ) 414 343 __x87_load; … … 417 346 } 418 347 419 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 420 } 421 422 // KERNEL_ONLY 423 // Context invoker for processors 424 // This is the entry point for processors (kernel threads) 425 // It effectively constructs a coroutine by stealing the pthread stack 426 static void * __invoke_processor(void * arg) { 427 processor * proc = (processor *) arg; 428 kernelTLS.this_processor = proc; 429 kernelTLS.this_thread = 0p; 430 kernelTLS.preemption_state.[enabled, disable_count] = [false, 1]; 431 // SKULLDUGGERY: We want to create a context for the processor coroutine 432 // which is needed for the 2-step context switch. However, there is no reason 433 // to waste the perfectly valid stack create by pthread. 434 current_stack_info_t info; 435 __stack_t ctx; 436 info.storage = &ctx; 437 (proc->runner){ proc, &info }; 438 439 __cfaabi_dbg_print_safe("Coroutine : created stack %p\n", get_coroutine(proc->runner)->stack.storage); 440 441 //Set global state 442 kernelTLS.this_thread = 0p; 443 444 //We now have a proper context from which to schedule threads 445 __cfaabi_dbg_print_safe("Kernel : core %p created (%p, %p)\n", proc, &proc->runner, &ctx); 446 447 // SKULLDUGGERY: Since the coroutine doesn't have its own stack, we can't 448 // resume it to start it like it normally would, it will just context switch 449 // back to here. Instead directly call the main since we already are on the 450 // appropriate stack. 451 get_coroutine(proc->runner)->state = Active; 452 main( proc->runner ); 453 get_coroutine(proc->runner)->state = Halted; 454 455 // Main routine of the core returned, the core is now fully terminated 456 __cfaabi_dbg_print_safe("Kernel : core %p main ended (%p)\n", proc, &proc->runner); 457 458 return 0p; 459 } 460 461 static void Abort( int ret, const char func[] ) { 462 if ( ret ) { // pthread routines return errno values 463 abort( "%s : internal error, error(%d) %s.", func, ret, strerror( ret ) ); 464 } // if 465 } // Abort 466 467 void * __create_pthread( pthread_t * pthread, void * (*start)(void *), void * arg ) { 468 pthread_attr_t attr; 469 470 Abort( pthread_attr_init( &attr ), "pthread_attr_init" ); // initialize attribute 471 472 size_t stacksize; 473 // default stack size, normally defined by shell limit 474 Abort( pthread_attr_getstacksize( &attr, &stacksize ), "pthread_attr_getstacksize" ); 475 assert( stacksize >= PTHREAD_STACK_MIN ); 476 477 void * stack; 478 __cfaabi_dbg_debug_do( 479 stack = memalign( __page_size, stacksize + __page_size ); 480 // pthread has no mechanism to create the guard page in user supplied stack. 481 if ( mprotect( stack, __page_size, PROT_NONE ) == -1 ) { 482 abort( "mprotect : internal error, mprotect failure, error(%d) %s.", errno, strerror( errno ) ); 483 } // if 484 ); 485 __cfaabi_dbg_no_debug_do( 486 stack = malloc( stacksize ); 487 ); 488 489 Abort( pthread_attr_setstack( &attr, stack, stacksize ), "pthread_attr_setstack" ); 490 491 Abort( pthread_create( pthread, &attr, start, arg ), "pthread_create" ); 492 return stack; 493 } 494 495 // KERNEL_ONLY 496 static void __kernel_first_resume( processor * this ) { 497 $thread * src = mainThread; 498 $coroutine * dst = get_coroutine(this->runner); 499 500 verify( ! kernelTLS.preemption_state.enabled ); 501 502 kernelTLS.this_thread->curr_cor = dst; 503 __stack_prepare( &dst->stack, 65000 ); 504 __cfactx_start(main, dst, this->runner, __cfactx_invoke_coroutine); 505 506 verify( ! kernelTLS.preemption_state.enabled ); 507 508 dst->last = &src->self_cor; 509 dst->starter = dst->starter ? dst->starter : &src->self_cor; 510 511 // set state of current coroutine to inactive 512 src->state = src->state == Halted ? Halted : Inactive; 513 514 // context switch to specified coroutine 515 verify( dst->context.SP ); 516 __cfactx_switch( &src->context, &dst->context ); 517 // when __cfactx_switch returns we are back in the src coroutine 518 519 mainThread->curr_cor = &mainThread->self_cor; 520 521 // set state of new coroutine to active 522 src->state = Active; 523 524 verify( ! kernelTLS.preemption_state.enabled ); 525 } 526 527 // KERNEL_ONLY 528 static void __kernel_last_resume( processor * this ) { 529 $coroutine * src = &mainThread->self_cor; 530 $coroutine * dst = get_coroutine(this->runner); 531 532 verify( ! kernelTLS.preemption_state.enabled ); 533 verify( dst->starter == src ); 534 verify( dst->context.SP ); 535 536 // context switch to the processor 537 __cfactx_switch( &src->context, &dst->context ); 348 #if !defined(__CFA_NO_STATISTICS__) 349 if(last_proc != kernelTLS().this_processor) { 350 __tls_stats()->ready.threads.migration++; 351 } 352 #endif 353 354 /* paranoid */ verify( ! __preemption_enabled() ); 355 /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) < ((uintptr_t)__get_stack(thrd_src->curr_cor)->base ), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too small.\n", thrd_src ); 356 /* paranoid */ verifyf( ((uintptr_t)thrd_src->context.SP) > ((uintptr_t)__get_stack(thrd_src->curr_cor)->limit), "ERROR : Returning $thread %p has been corrupted.\n StackPointer too large.\n", thrd_src ); 538 357 } 539 358 … … 541 360 // Scheduler routines 542 361 // KERNEL ONLY 543 void __schedule_thread( $thread * thrd ) with( *thrd->curr_cluster ) { 544 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 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 ); 545 368 /* paranoid */ #if defined( __CFA_WITH_VERIFY__ ) 546 /* paranoid */ if( thrd->state == Inactive|| thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION,547 "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted );548 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active || thrd->state == Rerun,549 "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted );369 /* paranoid */ if( thrd->state == Blocked || thrd->state == Start ) assertf( thrd->preempted == __NO_PREEMPTION, 370 "Error inactive thread marked as preempted, state %d, preemption %d\n", thrd->state, thrd->preempted ); 371 /* paranoid */ if( thrd->preempted != __NO_PREEMPTION ) assertf(thrd->state == Active, 372 "Error preempted thread marked as not currently running, state %d, preemption %d\n", thrd->state, thrd->preempted ); 550 373 /* paranoid */ #endif 551 /* paranoid */ verifyf( thrd->next == 0p, "Expected null got %p", thrd->next ); 552 553 lock ( ready_queue_lock __cfaabi_dbg_ctx2 ); 554 bool was_empty = !(ready_queue != 0); 555 append( ready_queue, thrd ); 556 unlock( ready_queue_lock ); 557 558 if(was_empty) { 559 lock (proc_list_lock __cfaabi_dbg_ctx2); 560 if(idles) { 561 wake_fast(idles.head); 562 } 563 unlock (proc_list_lock); 564 } 565 else if( struct processor * idle = idles.head ) { 566 wake_fast(idle); 567 } 568 569 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 374 /* paranoid */ verifyf( thrd->link.next == 0p, "Expected null got %p", thrd->link.next ); 375 /* paranoid */ verify( 0x0D15EA5E0D15EA5Ep == thrd->canary ); 376 377 378 if (thrd->preempted == __NO_PREEMPTION) thrd->state = Ready; 379 380 ready_schedule_lock(); 381 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 382 struct cluster * cl = thrd->curr_cluster; 383 384 // push the thread to the cluster ready-queue 385 push( cl, thrd ); 386 387 // variable thrd is no longer safe to use 388 389 // wake the cluster using the save variable. 390 __wake_one( cl ); 391 ready_schedule_unlock(); 392 393 /* paranoid */ verify( ! __preemption_enabled() ); 570 394 } 571 395 572 396 // KERNEL ONLY 573 static $thread * __next_thread(cluster * this) with( *this ) { 574 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 575 576 lock( ready_queue_lock __cfaabi_dbg_ctx2 ); 577 $thread * head = pop_head( ready_queue ); 578 unlock( ready_queue_lock ); 579 580 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 581 return head; 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; 582 422 } 583 423 … … 585 425 if( !thrd ) return; 586 426 587 disable_interrupts(); 588 static_assert(sizeof(thrd->state) == sizeof(int)); 589 enum coroutine_state old_state = __atomic_exchange_n(&thrd->state, Rerun, __ATOMIC_SEQ_CST); 590 switch(old_state) { 591 case Active: 427 int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST); 428 switch(old_ticket) { 429 case TICKET_RUNNING: 592 430 // Wake won the race, the thread will reschedule/rerun itself 593 431 break; 594 case Inactive:432 case TICKET_BLOCKED: 595 433 /* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION ); 596 597 // Wake lost the race, 598 thrd->state = Inactive; 599 __schedule_thread( thrd ); 434 /* paranoid */ verify( thrd->state == Blocked ); 435 436 { 437 /* paranoid */ verify( publicTLS_get(this_proc_id) ); 438 bool full = publicTLS_get(this_proc_id)->full_proc; 439 if(full) disable_interrupts(); 440 441 /* paranoid */ verify( ! __preemption_enabled() ); 442 443 // Wake lost the race, 444 __schedule_thread( thrd ); 445 446 /* paranoid */ verify( ! __preemption_enabled() ); 447 448 if(full) enable_interrupts( __cfaabi_dbg_ctx ); 449 /* paranoid */ verify( publicTLS_get(this_proc_id) ); 450 } 451 600 452 break; 601 case Rerun:602 abort("More than one thread attempted to schedule thread %p\n", thrd);603 break;604 case Halted:605 case Start:606 case Primed:607 453 default: 608 454 // This makes no sense, something is wrong abort 609 abort(); 610 } 455 abort("Thread %p (%s) has mismatch park/unpark\n", thrd, thrd->self_cor.name); 456 } 457 } 458 459 void park( void ) { 460 /* paranoid */ verify( __preemption_enabled() ); 461 disable_interrupts(); 462 /* paranoid */ verify( ! __preemption_enabled() ); 463 /* paranoid */ verify( kernelTLS().this_thread->preempted == __NO_PREEMPTION ); 464 465 returnToKernel(); 466 467 /* paranoid */ verify( ! __preemption_enabled() ); 611 468 enable_interrupts( __cfaabi_dbg_ctx ); 612 } 613 614 void park( void ) { 615 /* paranoid */ verify( kernelTLS.preemption_state.enabled ); 616 disable_interrupts(); 617 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 618 /* paranoid */ verify( kernelTLS.this_thread->preempted == __NO_PREEMPTION ); 619 620 returnToKernel(); 621 622 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 623 enable_interrupts( __cfaabi_dbg_ctx ); 624 /* paranoid */ verify( kernelTLS.preemption_state.enabled ); 625 626 } 627 628 // KERNEL ONLY 629 void __leave_thread() { 630 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 631 returnToKernel(); 632 abort(); 469 /* paranoid */ verify( __preemption_enabled() ); 470 471 } 472 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 } 633 505 } 634 506 635 507 // KERNEL ONLY 636 508 bool force_yield( __Preemption_Reason reason ) { 637 /* paranoid */ verify( kernelTLS.preemption_state.enabled);509 /* paranoid */ verify( __preemption_enabled() ); 638 510 disable_interrupts(); 639 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled);640 641 $thread * thrd = kernelTLS .this_thread;642 /* paranoid */ verify(thrd->state == Active || thrd->state == Rerun);511 /* paranoid */ verify( ! __preemption_enabled() ); 512 513 $thread * thrd = kernelTLS().this_thread; 514 /* paranoid */ verify(thrd->state == Active); 643 515 644 516 // SKULLDUGGERY: It is possible that we are preempting this thread just before … … 647 519 // If that is the case, abandon the preemption. 648 520 bool preempted = false; 649 if(thrd-> next == 0p) {521 if(thrd->link.next == 0p) { 650 522 preempted = true; 651 523 thrd->preempted = reason; … … 653 525 } 654 526 655 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled);527 /* paranoid */ verify( ! __preemption_enabled() ); 656 528 enable_interrupts_noPoll(); 657 /* paranoid */ verify( kernelTLS.preemption_state.enabled);529 /* paranoid */ verify( __preemption_enabled() ); 658 530 659 531 return preempted; … … 661 533 662 534 //============================================================================================= 663 // Kernel Setup logic535 // Kernel Idle Sleep 664 536 //============================================================================================= 665 //----------------------------------------------------------------------------- 666 // Kernel boot procedures 667 static void __kernel_startup(void) { 668 verify( ! kernelTLS.preemption_state.enabled ); 669 __cfaabi_dbg_print_safe("Kernel : Starting\n"); 670 671 __page_size = sysconf( _SC_PAGESIZE ); 672 673 __cfa_dbg_global_clusters.list{ __get }; 674 __cfa_dbg_global_clusters.lock{}; 675 676 // Initialize the main cluster 677 mainCluster = (cluster *)&storage_mainCluster; 678 (*mainCluster){"Main Cluster"}; 679 680 __cfaabi_dbg_print_safe("Kernel : Main cluster ready\n"); 681 682 // Start by initializing the main thread 683 // SKULLDUGGERY: the mainThread steals the process main thread 684 // which will then be scheduled by the mainProcessor normally 685 mainThread = ($thread *)&storage_mainThread; 686 current_stack_info_t info; 687 info.storage = (__stack_t*)&storage_mainThreadCtx; 688 (*mainThread){ &info }; 689 690 __cfaabi_dbg_print_safe("Kernel : Main thread ready\n"); 691 692 693 694 // Construct the processor context of the main processor 695 void ?{}(processorCtx_t & this, processor * proc) { 696 (this.__cor){ "Processor" }; 697 this.__cor.starter = 0p; 698 this.proc = proc; 699 } 700 701 void ?{}(processor & this) with( this ) { 702 name = "Main Processor"; 703 cltr = mainCluster; 704 terminated{ 0 }; 705 do_terminate = false; 706 preemption_alarm = 0p; 707 pending_preemption = false; 708 kernel_thread = pthread_self(); 709 710 runner{ &this }; 711 __cfaabi_dbg_print_safe("Kernel : constructed main processor context %p\n", &runner); 712 } 713 714 // Initialize the main processor and the main processor ctx 715 // (the coroutine that contains the processing control flow) 716 mainProcessor = (processor *)&storage_mainProcessor; 717 (*mainProcessor){}; 718 719 //initialize the global state variables 720 kernelTLS.this_processor = mainProcessor; 721 kernelTLS.this_thread = mainThread; 722 723 // Enable preemption 724 kernel_start_preemption(); 725 726 // Add the main thread to the ready queue 727 // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread 728 __schedule_thread(mainThread); 729 730 // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX 731 // context. Hence, the main thread does not begin through __cfactx_invoke_thread, like all other threads. The trick here is that 732 // mainThread is on the ready queue when this call is made. 733 __kernel_first_resume( kernelTLS.this_processor ); 734 735 736 737 // THE SYSTEM IS NOW COMPLETELY RUNNING 738 __cfaabi_dbg_print_safe("Kernel : Started\n--------------------------------------------------\n\n"); 739 740 verify( ! kernelTLS.preemption_state.enabled ); 537 // Wake a thread from the front if there are any 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 ); 741 571 enable_interrupts( __cfaabi_dbg_ctx ); 742 verify( TL_GET( preemption_state.enabled ) ); 743 } 744 745 static void __kernel_shutdown(void) { 746 __cfaabi_dbg_print_safe("\n--------------------------------------------------\nKernel : Shutting down\n"); 747 748 verify( TL_GET( preemption_state.enabled ) ); 749 disable_interrupts(); 750 verify( ! kernelTLS.preemption_state.enabled ); 751 752 // SKULLDUGGERY: Notify the mainProcessor it needs to terminates. 753 // When its coroutine terminates, it return control to the mainThread 754 // which is currently here 755 __atomic_store_n(&mainProcessor->do_terminate, true, __ATOMIC_RELEASE); 756 __kernel_last_resume( kernelTLS.this_processor ); 757 mainThread->self_cor.state = Halted; 758 759 // THE SYSTEM IS NOW COMPLETELY STOPPED 760 761 // Disable preemption 762 kernel_stop_preemption(); 763 764 // Destroy the main processor and its context in reverse order of construction 765 // These were manually constructed so we need manually destroy them 766 ^(mainProcessor->runner){}; 767 ^(mainProcessor){}; 768 769 // Final step, destroy the main thread since it is no longer needed 770 // Since we provided a stack to this taxk it will not destroy anything 771 ^(mainThread){}; 772 773 ^(__cfa_dbg_global_clusters.list){}; 774 ^(__cfa_dbg_global_clusters.lock){}; 775 776 __cfaabi_dbg_print_safe("Kernel : Shutdown complete\n"); 777 } 778 779 //============================================================================================= 780 // Kernel Quiescing 781 //============================================================================================= 782 static void __halt(processor * this) with( *this ) { 783 // verify( ! __atomic_load_n(&do_terminate, __ATOMIC_SEQ_CST) ); 784 785 with( *cltr ) { 786 lock (proc_list_lock __cfaabi_dbg_ctx2); 787 remove (procs, *this); 788 push_front(idles, *this); 789 unlock (proc_list_lock); 790 } 791 792 __cfaabi_dbg_print_safe("Kernel : Processor %p ready to sleep\n", this); 793 794 wait( idleLock ); 795 796 __cfaabi_dbg_print_safe("Kernel : Processor %p woke up and ready to run\n", this); 797 798 with( *cltr ) { 799 lock (proc_list_lock __cfaabi_dbg_ctx2); 800 remove (idles, *this); 801 push_front(procs, *this); 802 unlock (proc_list_lock); 572 } 573 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]; 803 607 } 804 608 } … … 814 618 // the globalAbort flag is true. 815 619 lock( kernel_abort_lock __cfaabi_dbg_ctx2 ); 620 621 // disable interrupts, it no longer makes sense to try to interrupt this processor 622 disable_interrupts(); 816 623 817 624 // first task to abort ? … … 831 638 } 832 639 833 return kernelTLS.this_thread;640 return __cfaabi_tls.this_thread; 834 641 } 835 642 836 643 void kernel_abort_msg( void * kernel_data, char * abort_text, int abort_text_size ) { 837 $thread * thrd = kernel_data;644 $thread * thrd = ( $thread * ) kernel_data; 838 645 839 646 if(thrd) { … … 856 663 857 664 int kernel_abort_lastframe( void ) __attribute__ ((__nothrow__)) { 858 return get_coroutine(kernelTLS .this_thread) == get_coroutine(mainThread) ? 4 : 2;665 return get_coroutine(kernelTLS().this_thread) == get_coroutine(mainThread) ? 4 : 2; 859 666 } 860 667 … … 883 690 void ^?{}(semaphore & this) {} 884 691 885 voidP(semaphore & this) with( this ){692 bool P(semaphore & this) with( this ){ 886 693 lock( lock __cfaabi_dbg_ctx2 ); 887 694 count -= 1; 888 695 if ( count < 0 ) { 889 696 // queue current task 890 append( waiting, kernelTLS.this_thread);697 append( waiting, active_thread() ); 891 698 892 699 // atomically release spin lock and block 893 700 unlock( lock ); 894 701 park(); 702 return true; 895 703 } 896 704 else { 897 705 unlock( lock ); 898 } 899 } 900 901 void V(semaphore & this) with( this ) { 706 return false; 707 } 708 } 709 710 bool V(semaphore & this) with( this ) { 902 711 $thread * thrd = 0p; 903 712 lock( lock __cfaabi_dbg_ctx2 ); … … 912 721 // make new owner 913 722 unpark( thrd ); 914 } 915 916 //----------------------------------------------------------------------------- 917 // Global Queues 918 void doregister( cluster & cltr ) { 919 lock ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2); 920 push_front( __cfa_dbg_global_clusters.list, cltr ); 921 unlock ( __cfa_dbg_global_clusters.lock ); 922 } 923 924 void unregister( cluster & cltr ) { 925 lock ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2); 926 remove( __cfa_dbg_global_clusters.list, cltr ); 927 unlock( __cfa_dbg_global_clusters.lock ); 928 } 929 930 void doregister( cluster * cltr, $thread & thrd ) { 931 lock (cltr->thread_list_lock __cfaabi_dbg_ctx2); 932 cltr->nthreads += 1; 933 push_front(cltr->threads, thrd); 934 unlock (cltr->thread_list_lock); 935 } 936 937 void unregister( cluster * cltr, $thread & thrd ) { 938 lock (cltr->thread_list_lock __cfaabi_dbg_ctx2); 939 remove(cltr->threads, thrd ); 940 cltr->nthreads -= 1; 941 unlock(cltr->thread_list_lock); 942 } 943 944 void doregister( cluster * cltr, processor * proc ) { 945 lock (cltr->proc_list_lock __cfaabi_dbg_ctx2); 946 cltr->nprocessors += 1; 947 push_front(cltr->procs, *proc); 948 unlock (cltr->proc_list_lock); 949 } 950 951 void unregister( cluster * cltr, processor * proc ) { 952 lock (cltr->proc_list_lock __cfaabi_dbg_ctx2); 953 remove(cltr->procs, *proc ); 954 cltr->nprocessors -= 1; 955 unlock(cltr->proc_list_lock); 723 724 return thrd != 0p; 725 } 726 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; 956 739 } 957 740 … … 960 743 __cfaabi_dbg_debug_do( 961 744 extern "C" { 962 void __cfaabi_dbg_record (__spinlock_t & this, const char prev_name[]) {745 void __cfaabi_dbg_record_lock(__spinlock_t & this, const char prev_name[]) { 963 746 this.prev_name = prev_name; 964 this.prev_thrd = kernelTLS .this_thread;747 this.prev_thrd = kernelTLS().this_thread; 965 748 } 966 749 } … … 972 755 return true; 973 756 } 757 758 //----------------------------------------------------------------------------- 759 // Statistics 760 #if !defined(__CFA_NO_STATISTICS__) 761 void print_halts( processor & this ) { 762 this.print_halts = true; 763 } 764 765 void print_stats_now( cluster & this, int flags ) { 766 __print_stats( this.stats, this.print_stats, "Cluster", this.name, (void*)&this ); 767 } 768 769 extern int __print_alarm_stats; 770 void print_alarm_stats() { 771 __print_alarm_stats = -1; 772 } 773 #endif 974 774 // Local Variables: // 975 775 // mode: c // -
libcfa/src/concurrency/kernel.hfa
r3c64c668 r58fe85a 16 16 #pragma once 17 17 18 #include <stdbool.h>19 20 18 #include "invoke.h" 21 19 #include "time_t.hfa" 22 20 #include "coroutine.hfa" 23 21 22 #include "containers/list.hfa" 23 24 24 extern "C" { 25 #include <pthread.h>26 #include <semaphore.h>25 #include <bits/pthreadtypes.h> 26 #include <linux/types.h> 27 27 } 28 28 … … 37 37 void ?{}(semaphore & this, int count = 1); 38 38 void ^?{}(semaphore & this); 39 void P (semaphore & this); 40 void V (semaphore & this); 39 bool P (semaphore & this); 40 bool V (semaphore & this); 41 bool V (semaphore & this, unsigned count); 41 42 42 43 … … 45 46 extern struct cluster * mainCluster; 46 47 47 // Processor 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 48 58 coroutine processorCtx_t { 49 59 struct processor * proc; … … 51 61 52 62 // Wrapper around kernel threads 53 struct processor {63 struct __attribute__((aligned(128))) processor { 54 64 // Main state 65 inline __processor_id_t; 66 67 // Cluster from which to get threads 68 struct cluster * cltr; 69 70 // Set to true to notify the processor should terminate 71 volatile bool do_terminate; 72 55 73 // Coroutine ctx who does keeps the state of the processor 56 74 struct processorCtx_t runner; 57 75 58 // Cluster from which to get threads59 struct cluster * cltr;60 61 76 // Name of the processor 62 77 const char * name; … … 64 79 // Handle to pthreads 65 80 pthread_t kernel_thread; 66 67 // RunThread data68 // Action to do after a thread is ran69 $thread * destroyer;70 81 71 82 // Preemption data … … 76 87 bool pending_preemption; 77 88 78 // Idle lock 79 __bin_sem_t idleLock; 80 81 // Termination 82 // Set to true to notify the processor should terminate 83 volatile bool do_terminate; 84 85 // Termination synchronisation 89 // Idle lock (kernel semaphore) 90 __bin_sem_t idle; 91 92 // Termination synchronisation (user semaphore) 86 93 semaphore terminated; 87 94 … … 90 97 91 98 // Link lists fields 92 struct __dbg_node_proc { 93 struct processor * next; 94 struct processor * prev; 95 } node; 99 DLISTED_MGD_IMPL_IN(processor) 100 101 #if !defined(__CFA_NO_STATISTICS__) 102 int print_stats; 103 bool print_halts; 104 #endif 96 105 97 106 #ifdef __CFA_DEBUG__ … … 108 117 static inline void ?{}(processor & this, const char name[]) { this{name, *mainCluster }; } 109 118 110 static inline [processor *&, processor *& ] __get( processor & this ) __attribute__((const)) { return this.node.[next, prev]; } 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 }; 111 220 112 221 //----------------------------------------------------------------------------- 113 222 // Cluster 114 struct cluster { 115 // Ready queue locks 116 __spinlock_t ready_queue_lock; 117 223 struct __attribute__((aligned(128))) cluster { 118 224 // Ready queue for threads 119 __ queue_t($thread)ready_queue;225 __ready_queue_t ready_queue; 120 226 121 227 // Name of the cluster … … 125 231 Duration preemption_rate; 126 232 127 // List of processors 128 __spinlock_t proc_list_lock; 129 __dllist_t(struct processor) procs; 130 __dllist_t(struct processor) idles; 131 unsigned int nprocessors; 233 // List of idle processors 234 __cluster_idles idles; 132 235 133 236 // List of threads … … 141 244 cluster * prev; 142 245 } node; 246 247 struct { 248 io_context * ctxs; 249 unsigned cnt; 250 } io; 251 252 #if !defined(__CFA_NO_STATISTICS__) 253 struct __stats_t * stats; 254 int print_stats; 255 #endif 143 256 }; 144 257 extern Duration default_preemption(); 145 258 146 void ?{} (cluster & this, const char name[], Duration preemption_rate );259 void ?{} (cluster & this, const char name[], Duration preemption_rate, unsigned num_io, const io_context_params & io_params); 147 260 void ^?{}(cluster & this); 148 261 149 static inline void ?{} (cluster & this) { this{"Anonymous Cluster", default_preemption()}; } 150 static inline void ?{} (cluster & this, Duration preemption_rate) { this{"Anonymous Cluster", preemption_rate}; } 151 static inline void ?{} (cluster & this, const char name[]) { this{name, default_preemption()}; } 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}; } 152 274 153 275 static inline [cluster *&, cluster *& ] __get( cluster & this ) __attribute__((const)) { return this.node.[next, prev]; } 154 276 155 static inline struct processor * active_processor() { return TL_GET( this_processor ); } // UNSAFE 156 static inline struct cluster * active_cluster () { return TL_GET( this_processor )->cltr; } 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 157 293 158 294 // Local Variables: // -
libcfa/src/concurrency/kernel_private.hfa
r3c64c668 r58fe85a 10 10 // Created On : Mon Feb 13 12:27:26 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Nov 30 19:25:02 201913 // Update Count : 812 // Last Modified On : Wed Aug 12 08:21:33 2020 13 // Update Count : 9 14 14 // 15 15 … … 20 20 21 21 #include "alarm.hfa" 22 22 #include "stats.hfa" 23 23 24 24 //----------------------------------------------------------------------------- 25 25 // Scheduler 26 27 struct __attribute__((aligned(128))) __scheduler_lock_id_t; 26 28 27 29 extern "C" { … … 31 33 } 32 34 33 void __schedule_thread( $thread * ) __attribute__((nonnull (1))); 34 35 //Block current thread and release/wake-up the following resources 36 void __leave_thread() __attribute__((noreturn)); 35 void __schedule_thread( $thread * ) 36 #if defined(NDEBUG) || (!defined(__CFA_DEBUG__) && !defined(__CFA_VERIFY__)) 37 __attribute__((nonnull (1))) 38 #endif 39 ; 40 41 extern bool __preemption_enabled(); 42 43 //release/wake-up the following resources 44 void __thread_finish( $thread * thrd ); 37 45 38 46 //----------------------------------------------------------------------------- … … 41 49 42 50 void * __create_pthread( pthread_t *, void * (*)(void *), void * ); 43 44 static inline void wake_fast(processor * this) { 45 __cfaabi_dbg_print_safe("Kernel : Waking up processor %p\n", this); 46 post( this->idleLock ); 47 } 48 49 static inline void wake(processor * this) { 50 disable_interrupts(); 51 wake_fast(this); 52 enable_interrupts( __cfaabi_dbg_ctx ); 53 } 54 55 struct event_kernel_t { 56 alarm_list_t alarms; 57 __spinlock_t lock; 58 }; 59 60 extern event_kernel_t * event_kernel; 61 62 struct __cfa_kernel_preemption_state_t { 63 bool enabled; 64 bool in_progress; 65 unsigned short disable_count; 66 }; 67 68 extern volatile thread_local __cfa_kernel_preemption_state_t preemption_state __attribute__ ((tls_model ( "initial-exec" ))); 51 void __destroy_pthread( pthread_t pthread, void * stack, void ** retval ); 52 53 54 55 extern cluster * mainCluster; 69 56 70 57 //----------------------------------------------------------------------------- … … 79 66 ) 80 67 68 #define TICKET_BLOCKED (-1) // thread is blocked 69 #define TICKET_RUNNING ( 0) // thread is running 70 #define TICKET_UNBLOCK ( 1) // thread should ignore next block 71 81 72 //----------------------------------------------------------------------------- 82 73 // Utils 83 #define KERNEL_STORAGE(T,X) static char storage_##X[sizeof(T)]84 85 static inline uint32_t __tls_rand() {86 kernelTLS.rand_seed ^= kernelTLS.rand_seed << 6;87 kernelTLS.rand_seed ^= kernelTLS.rand_seed >> 21;88 kernelTLS.rand_seed ^= kernelTLS.rand_seed << 7;89 return kernelTLS.rand_seed;90 }91 92 93 void doregister( struct cluster & cltr );94 void unregister( struct cluster & cltr );95 96 74 void doregister( struct cluster * cltr, struct $thread & thrd ); 97 75 void unregister( struct cluster * cltr, struct $thread & thrd ); 98 76 99 void doregister( struct cluster * cltr, struct processor * proc ); 100 void unregister( struct cluster * cltr, struct processor * proc ); 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 } 120 } 121 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 127 //======================================================================= 128 // Reader-writer lock implementation 129 // Concurrent with doregister/unregister, 130 // i.e., threads can be added at any point during or between the entry/exit 131 132 //----------------------------------------------------------------------- 133 // simple spinlock underlying the RWLock 134 // Blocking acquire 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 } 142 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 101 285 102 286 // Local Variables: // -
libcfa/src/concurrency/monitor.cfa
r3c64c668 r58fe85a 82 82 // Enter single monitor 83 83 static void __enter( $monitor * this, const __monitor_group_t & group ) { 84 $thread * thrd = active_thread(); 85 84 86 // Lock the monitor spinlock 85 87 lock( this->lock __cfaabi_dbg_ctx2 ); 86 // Interrupts disable inside critical section87 $thread * thrd = kernelTLS.this_thread;88 88 89 89 __cfaabi_dbg_print_safe( "Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner); 90 90 91 if( !this->owner ) { 91 if( unlikely(0 != (0x1 & (uintptr_t)this->owner)) ) { 92 abort( "Attempt by thread \"%.256s\" (%p) to access joined monitor %p.", thrd->self_cor.name, thrd, this ); 93 } 94 else if( !this->owner ) { 92 95 // No one has the monitor, just take it 93 96 __set_owner( this, thrd ); … … 114 117 115 118 // Some one else has the monitor, wait in line for it 116 /* paranoid */ verify( thrd-> next == 0p );119 /* paranoid */ verify( thrd->link.next == 0p ); 117 120 append( this->entry_queue, thrd ); 118 /* paranoid */ verify( thrd-> next == 1p );121 /* paranoid */ verify( thrd->link.next == 1p ); 119 122 120 123 unlock( this->lock ); … … 123 126 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 124 127 125 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );128 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 126 129 return; 127 130 } … … 129 132 __cfaabi_dbg_print_safe( "Kernel : %10p Entered mon %p\n", thrd, this); 130 133 131 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );134 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 132 135 /* paranoid */ verify( this->lock.lock ); 133 136 … … 137 140 } 138 141 139 static void __dtor_enter( $monitor * this, fptr_t func ) { 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 140 148 // Lock the monitor spinlock 141 149 lock( this->lock __cfaabi_dbg_ctx2 ); 142 // Interrupts disable inside critical section143 $thread * thrd = kernelTLS.this_thread;144 150 145 151 __cfaabi_dbg_print_safe( "Kernel : %10p Entering dtor for mon %p (%p)\n", thrd, this, this->owner); … … 152 158 __set_owner( this, thrd ); 153 159 154 verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this ); 160 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 161 /* paranoid */ verify( !is_thrd || thrd->state == Halted || thrd->state == Cancelled ); 155 162 156 163 unlock( this->lock ); 157 164 return; 158 165 } 159 else if( this->owner == thrd ) {166 else if( this->owner == thrd && !join) { 160 167 // We already have the monitor... but where about to destroy it so the nesting will fail 161 168 // Abort! 162 169 abort( "Attempt to destroy monitor %p by thread \"%.256s\" (%p) in nested mutex.", this, thrd->self_cor.name, thrd ); 163 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 184 unlock( this->lock ); 185 return; 186 } 187 188 // The monitor is busy, if this is a thread and the thread owns itself, it better be active 189 /* paranoid */ verify( !is_thrd || this->owner != thrd || (thrd->state != Halted && thrd->state != Cancelled) ); 164 190 165 191 __lock_size_t count = 1; … … 183 209 184 210 // Release the next thread 185 /* paranoid */ verifyf( urgent->owner->waiting_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );211 /* paranoid */ verifyf( urgent->owner->waiting_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 186 212 unpark( urgent->owner->waiting_thread ); 187 213 … … 190 216 191 217 // Some one was waiting for us, enter 192 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this ); 218 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 219 220 __cfaabi_dbg_print_safe( "Kernel : Destroying %p\n", this); 221 return; 193 222 } 194 223 else { … … 199 228 200 229 // Some one else has the monitor, wait in line for it 201 /* paranoid */ verify( thrd-> next == 0p );230 /* paranoid */ verify( thrd->link.next == 0p ); 202 231 append( this->entry_queue, thrd ); 203 /* paranoid */ verify( thrd-> next == 1p );232 /* paranoid */ verify( thrd->link.next == 1p ); 204 233 unlock( this->lock ); 205 234 … … 207 236 park(); 208 237 209 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );238 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 210 239 return; 211 240 } 212 213 __cfaabi_dbg_print_safe( "Kernel : Destroying %p\n", this);214 215 241 } 216 242 … … 220 246 lock( this->lock __cfaabi_dbg_ctx2 ); 221 247 222 __cfaabi_dbg_print_safe( "Kernel : %10p Leaving mon %p (%p)\n", kernelTLS.this_thread, this, this->owner);223 224 /* paranoid */ verifyf( kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );248 __cfaabi_dbg_print_safe( "Kernel : %10p Leaving mon %p (%p)\n", active_thread(), this, this->owner); 249 250 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 225 251 226 252 // Leaving a recursion level, decrement the counter … … 251 277 252 278 // Leave single monitor for the last time 253 void __dtor_leave( $monitor * this ) {279 void __dtor_leave( $monitor * this, bool join ) { 254 280 __cfaabi_dbg_debug_do( 255 if( TL_GET( this_thread) != this->owner ) {256 abort( "Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, TL_GET( this_thread), this->owner);281 if( active_thread() != this->owner ) { 282 abort( "Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, active_thread(), this->owner); 257 283 } 258 if( this->recursion != 1 ) {284 if( this->recursion != 1 && !join ) { 259 285 abort( "Destroyed monitor %p has %d outstanding nested calls.\n", this, this->recursion - 1); 260 286 } 261 287 ) 262 } 263 264 extern "C" { 265 // Leave the thread monitor 266 // last routine called by a thread. 267 // Should never return 268 void __cfactx_thrd_leave() { 269 $thread * thrd = TL_GET( this_thread ); 270 $monitor * this = &thrd->self_mon; 271 272 // Lock the monitor now 273 lock( this->lock __cfaabi_dbg_ctx2 ); 274 275 disable_interrupts(); 276 277 thrd->state = Halted; 278 279 /* paranoid */ verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this ); 280 281 // Leaving a recursion level, decrement the counter 282 this->recursion -= 1; 283 284 // If we haven't left the last level of recursion 285 // it must mean there is an error 286 if( this->recursion != 0) { abort( "Thread internal monitor has unbalanced recursion" ); } 287 288 // Fetch the next thread, can be null 289 $thread * new_owner = next_thread( this ); 290 291 // Release the monitor lock 292 unlock( this->lock ); 293 294 // Unpark the next owner if needed 295 /* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this ); 296 /* paranoid */ verify( ! kernelTLS.preemption_state.enabled ); 297 /* paranoid */ verify( ! kernelTLS.this_processor->destroyer ); 298 /* paranoid */ verify( thrd->state == Halted ); 299 300 kernelTLS.this_processor->destroyer = new_owner; 301 302 // Leave the thread 303 __leave_thread(); 304 305 // Control flow should never reach here! 306 } 288 289 this->owner = ($thread*)(1 | (uintptr_t)this->owner); 290 } 291 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 ); 307 325 } 308 326 … … 326 344 // Sorts monitors before entering 327 345 void ?{}( monitor_guard_t & this, $monitor * m [], __lock_size_t count, fptr_t func ) { 328 $thread * thrd = TL_GET( this_thread);346 $thread * thrd = active_thread(); 329 347 330 348 // Store current array … … 361 379 362 380 // Restore thread context 363 TL_GET( this_thread)->monitors = this.prev;381 active_thread()->monitors = this.prev; 364 382 } 365 383 366 384 // Ctor for monitor guard 367 385 // Sorts monitors before entering 368 void ?{}( monitor_dtor_guard_t & this, $monitor * m [], fptr_t func ) {386 void ?{}( monitor_dtor_guard_t & this, $monitor * m [], fptr_t func, bool join ) { 369 387 // optimization 370 $thread * thrd = TL_GET( this_thread);388 $thread * thrd = active_thread(); 371 389 372 390 // Store current array … … 376 394 this.prev = thrd->monitors; 377 395 396 // Save whether we are in a join or not 397 this.join = join; 398 378 399 // Update thread context (needed for conditions) 379 400 (thrd->monitors){m, 1, func}; 380 401 381 __dtor_enter( this.m, func );402 __dtor_enter( this.m, func, join ); 382 403 } 383 404 … … 385 406 void ^?{}( monitor_dtor_guard_t & this ) { 386 407 // Leave the monitors in order 387 __dtor_leave( this.m );408 __dtor_leave( this.m, this.join ); 388 409 389 410 // Restore thread context 390 TL_GET( this_thread)->monitors = this.prev;411 active_thread()->monitors = this.prev; 391 412 } 392 413 … … 428 449 429 450 // Create the node specific to this wait operation 430 wait_ctx( TL_GET( this_thread), user_info );451 wait_ctx( active_thread(), user_info ); 431 452 432 453 // Append the current wait operation to the ones already queued on the condition … … 479 500 //Some more checking in debug 480 501 __cfaabi_dbg_debug_do( 481 $thread * this_thrd = TL_GET( this_thread);502 $thread * this_thrd = active_thread(); 482 503 if ( this.monitor_count != this_thrd->monitors.size ) { 483 504 abort( "Signal on condition %p made with different number of monitor(s), expected %zi got %zi", &this, this.monitor_count, this_thrd->monitors.size ); … … 527 548 528 549 // Create the node specific to this wait operation 529 wait_ctx_primed( kernelTLS.this_thread, 0 )550 wait_ctx_primed( active_thread(), 0 ) 530 551 531 552 //save contexts … … 534 555 //Find the thread to run 535 556 $thread * signallee = pop_head( this.blocked )->waiting_thread; 536 /* paranoid */ verify( signallee->next == 0p );537 557 __set_owner( monitors, count, signallee ); 538 558 … … 627 647 628 648 // Create the node specific to this wait operation 629 wait_ctx_primed( kernelTLS.this_thread, 0 );649 wait_ctx_primed( active_thread(), 0 ); 630 650 631 651 // Save monitor states … … 679 699 680 700 // Create the node specific to this wait operation 681 wait_ctx_primed( kernelTLS.this_thread, 0 );701 wait_ctx_primed( active_thread(), 0 ); 682 702 683 703 monitor_save; … … 685 705 686 706 for( __lock_size_t i = 0; i < count; i++) { 687 verify( monitors[i]->owner == kernelTLS.this_thread);707 verify( monitors[i]->owner == active_thread() ); 688 708 } 689 709 … … 721 741 static inline void __set_owner( $monitor * monitors [], __lock_size_t count, $thread * owner ) { 722 742 /* paranoid */ verify ( monitors[0]->lock.lock ); 723 /* paranoid */ verifyf( monitors[0]->owner == kernelTLS.this_thread, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, monitors[0]->owner, monitors[0]->recursion, monitors[0] );743 /* paranoid */ verifyf( monitors[0]->owner == active_thread(), "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), monitors[0]->owner, monitors[0]->recursion, monitors[0] ); 724 744 monitors[0]->owner = owner; 725 745 monitors[0]->recursion = 1; 726 746 for( __lock_size_t i = 1; i < count; i++ ) { 727 747 /* paranoid */ verify ( monitors[i]->lock.lock ); 728 /* paranoid */ verifyf( monitors[i]->owner == kernelTLS.this_thread, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, monitors[i]->owner, monitors[i]->recursion, monitors[i] );748 /* paranoid */ verifyf( monitors[i]->owner == active_thread(), "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), monitors[i]->owner, monitors[i]->recursion, monitors[i] ); 729 749 monitors[i]->owner = owner; 730 750 monitors[i]->recursion = 0; … … 752 772 //regardless of if we are ready to baton pass, 753 773 //we need to set the monitor as in use 754 /* paranoid */ verifyf( !this->owner || kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );774 /* paranoid */ verifyf( !this->owner || active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 755 775 __set_owner( this, urgent->owner->waiting_thread ); 756 776 … … 761 781 // Get the next thread in the entry_queue 762 782 $thread * new_owner = pop_head( this->entry_queue ); 763 /* paranoid */ verifyf( !this->owner || kernelTLS.this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", kernelTLS.this_thread, this->owner, this->recursion, this );764 /* paranoid */ verify( !new_owner || new_owner-> next == 0p );783 /* paranoid */ verifyf( !this->owner || active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this ); 784 /* paranoid */ verify( !new_owner || new_owner->link.next == 0p ); 765 785 __set_owner( this, new_owner ); 766 786 … … 884 904 } 885 905 886 __cfaabi_dbg_print_safe( "Kernel : Runing %i (%p)\n", ready2run, ready2run ? node->waiting_thread :0p );906 __cfaabi_dbg_print_safe( "Kernel : Runing %i (%p)\n", ready2run, ready2run ? (thread*)node->waiting_thread : (thread*)0p ); 887 907 return ready2run ? node->waiting_thread : 0p; 888 908 } 889 909 890 910 static inline void brand_condition( condition & this ) { 891 $thread * thrd = TL_GET( this_thread);911 $thread * thrd = active_thread(); 892 912 if( !this.monitors ) { 893 913 // __cfaabi_dbg_print_safe( "Branding\n" ); … … 908 928 // For each thread in the entry-queue 909 929 for( $thread ** thrd_it = &entry_queue.head; 910 *thrd_it!= 1p;911 thrd_it = &(*thrd_it)-> next930 (*thrd_it) != 1p; 931 thrd_it = &(*thrd_it)->link.next 912 932 ) { 913 933 // For each acceptable check if it matches -
libcfa/src/concurrency/monitor.hfa
r3c64c668 r58fe85a 53 53 $monitor * m; 54 54 __monitor_group_t prev; 55 bool join; 55 56 }; 56 57 57 void ?{}( monitor_dtor_guard_t & this, $monitor ** m, void (*func)() );58 void ?{}( monitor_dtor_guard_t & this, $monitor ** m, void (*func)(), bool join ); 58 59 void ^?{}( monitor_dtor_guard_t & this ); 59 60 … … 131 132 132 133 void wait ( condition & this, uintptr_t user_info = 0 ); 134 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; } 133 135 bool signal ( condition & this ); 134 136 bool signal_block( condition & this ); 135 static inline bool is_empty ( condition & this ) { return this.blocked.head == 1p; }137 static inline bool signal_all ( condition & this ) { bool ret = false; while(!is_empty(this)) { ret = signal(this) || ret; } return ret; } 136 138 uintptr_t front ( condition & this ); 137 139 -
libcfa/src/concurrency/mutex.cfa
r3c64c668 r58fe85a 30 30 this.lock{}; 31 31 this.blocked_threads{}; 32 this.is_locked = false; 32 33 } 33 34 … … 39 40 lock( lock __cfaabi_dbg_ctx2 ); 40 41 if( is_locked ) { 41 append( blocked_threads, kernelTLS.this_thread);42 append( blocked_threads, active_thread() ); 42 43 unlock( lock ); 43 44 park(); … … 85 86 lock( lock __cfaabi_dbg_ctx2 ); 86 87 if( owner == 0p ) { 87 owner = kernelTLS.this_thread;88 owner = active_thread(); 88 89 recursion_count = 1; 89 90 unlock( lock ); 90 91 } 91 else if( owner == kernelTLS.this_thread) {92 else if( owner == active_thread() ) { 92 93 recursion_count++; 93 94 unlock( lock ); 94 95 } 95 96 else { 96 append( blocked_threads, kernelTLS.this_thread);97 append( blocked_threads, active_thread() ); 97 98 unlock( lock ); 98 99 park(); … … 104 105 lock( lock __cfaabi_dbg_ctx2 ); 105 106 if( owner == 0p ) { 106 owner = kernelTLS.this_thread;107 owner = active_thread(); 107 108 recursion_count = 1; 108 109 ret = true; 109 110 } 110 else if( owner == kernelTLS.this_thread) {111 else if( owner == active_thread() ) { 111 112 recursion_count++; 112 113 ret = true; … … 158 159 void wait(condition_variable & this) { 159 160 lock( this.lock __cfaabi_dbg_ctx2 ); 160 append( this.blocked_threads, kernelTLS.this_thread);161 append( this.blocked_threads, active_thread() ); 161 162 unlock( this.lock ); 162 163 park(); … … 166 167 void wait(condition_variable & this, L & l) { 167 168 lock( this.lock __cfaabi_dbg_ctx2 ); 168 append( this.blocked_threads, kernelTLS.this_thread);169 append( this.blocked_threads, active_thread() ); 169 170 unlock(l); 170 171 unlock(this.lock); -
libcfa/src/concurrency/preemption.cfa
r3c64c668 r58fe85a 10 10 // Created On : Mon Jun 5 14:20:42 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Dec 5 16:34:05 201913 // Update Count : 4312 // Last Modified On : Fri Nov 6 07:42:13 2020 13 // Update Count : 54 14 14 // 15 15 … … 19 19 #include <assert.h> 20 20 21 extern "C" {22 21 #include <errno.h> 23 22 #include <stdio.h> … … 25 24 #include <unistd.h> 26 25 #include <limits.h> // PTHREAD_STACK_MIN 27 }28 26 29 27 #include "bits/signal.hfa" 28 #include "kernel_private.hfa" 30 29 31 30 #if !defined(__CFA_DEFAULT_PREEMPTION__) … … 43 42 // FwdDeclarations : Signal handlers 44 43 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_ARCH)58 #elif defined( __arm__ ) 59 59 #define CFA_REG_IP arm_pc 60 #elif defined( __aarch64__ ) 61 #define CFA_REG_IP pc 60 62 #else 61 #error un knownhardware architecture63 #error unsupported hardware architecture 62 64 #endif 63 65 … … 83 85 // Get next expired node 84 86 static inline alarm_node_t * get_expired( alarm_list_t * alarms, Time currtime ) { 85 if( ! alarms->head) return 0p; // If no alarms return null86 if( alarms->head->alarm >= currtime ) return 0p; // If alarms head not expired return null87 if( ! & (*alarms)`first ) return 0p; // If no alarms return null 88 if( (*alarms)`first.alarm >= currtime ) return 0p; // If alarms head not expired return null 87 89 return pop(alarms); // Otherwise just pop head 88 90 } 89 91 90 92 // Tick one frame of the Discrete Event Simulation for alarms 91 static void tick_preemption( ) {93 static void tick_preemption(void) { 92 94 alarm_node_t * node = 0p; // Used in the while loop but cannot be declared in the while condition 93 95 alarm_list_t * alarms = &event_kernel->alarms; // Local copy for ease of reading … … 97 99 while( node = get_expired( alarms, currtime ) ) { 98 100 // __cfaabi_dbg_print_buffer_decl( " KERNEL: preemption tick.\n" ); 101 Duration period = node->period; 102 if( period == 0) { 103 node->set = false; // Node is one-shot, just mark it as not pending 104 } 99 105 100 106 // Check if this is a kernel 101 if( node-> kernel_alarm) {107 if( node->type == Kernel ) { 102 108 preempt( node->proc ); 103 109 } 110 else if( node->type == User ) { 111 timeout( node->thrd ); 112 } 104 113 else { 105 timeout( node->thrd);114 node->callback(*node); 106 115 } 107 116 108 117 // Check if this is a periodic alarm 109 Duration period = node->period;110 118 if( period > 0 ) { 111 119 // __cfaabi_dbg_print_buffer_local( " KERNEL: alarm period is %lu.\n", period.tv ); … … 113 121 insert( alarms, node ); // Reinsert the node for the next time it triggers 114 122 } 115 else {116 node->set = false; // Node is one-shot, just mark it as not pending117 }118 123 } 119 124 120 125 // If there are still alarms pending, reset the timer 121 if( alarms->head) {122 __cfa abi_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 cap ed = max(delta, 50`us);126 if( & (*alarms)`first ) { 127 __cfadbg_print_buffer_decl(preemption, " KERNEL: @%ju(%ju) resetting alarm to %ju.\n", currtime.tv, __kernel_get_time().tv, (alarms->head->alarm - currtime).tv); 128 Duration delta = (*alarms)`first.alarm - currtime; 129 Duration capped = max(delta, 50`us); 125 130 // itimerval tim = { caped }; 126 131 // __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); 127 132 128 __kernel_set_timer( cap ed );133 __kernel_set_timer( capped ); 129 134 } 130 135 } … … 158 163 // Kernel Signal Tools 159 164 //============================================================================================= 160 161 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; ) 165 // In a user-level threading system, there are handful of thread-local variables where this problem occurs on the ARM. 166 // 167 // For each kernel thread running user-level threads, there is a flag variable to indicate if interrupts are 168 // enabled/disabled for that kernel thread. Therefore, this variable is made thread local. 169 // 170 // For example, this code fragment sets the state of the "interrupt" variable in thread-local memory. 171 // 172 // _Thread_local volatile int interrupts; 173 // int main() { 174 // interrupts = 0; // disable interrupts } 175 // 176 // which generates the following code on the ARM 177 // 178 // (gdb) disassemble main 179 // Dump of assembler code for function main: 180 // 0x0000000000000610 <+0>: mrs x1, tpidr_el0 181 // 0x0000000000000614 <+4>: mov w0, #0x0 // #0 182 // 0x0000000000000618 <+8>: add x1, x1, #0x0, lsl #12 183 // 0x000000000000061c <+12>: add x1, x1, #0x10 184 // 0x0000000000000620 <+16>: str wzr, [x1] 185 // 0x0000000000000624 <+20>: ret 186 // 187 // The mrs moves a pointer from coprocessor register tpidr_el0 into register x1. Register w0 is set to 0. The two adds 188 // increase the TLS pointer with the displacement (offset) 0x10, which is the location in the TSL of variable 189 // "interrupts". Finally, 0 is stored into "interrupts" through the pointer in register x1 that points into the 190 // TSL. Now once x1 has the pointer to the location of the TSL for kernel thread N, it can be be preempted at a 191 // user-level and the user thread is put on the user-level ready-queue. When the preempted thread gets to the front of 192 // the user-level ready-queue it is run on kernel thread M. It now stores 0 into "interrupts" back on kernel thread N, 193 // turning off interrupt on the wrong kernel thread. 194 // 195 // On the x86, the following code is generated for the same code fragment. 196 // 197 // (gdb) disassemble main 198 // Dump of assembler code for function main: 199 // 0x0000000000400420 <+0>: movl $0x0,%fs:0xfffffffffffffffc 200 // 0x000000000040042c <+12>: xor %eax,%eax 201 // 0x000000000040042e <+14>: retq 202 // 203 // and there is base-displacement addressing used to atomically reset variable "interrupts" off of the TSL pointer in 204 // register "fs". 205 // 206 // Hence, the ARM has base-displacement address for the general purpose registers, BUT not to the coprocessor 207 // registers. As a result, generating the address for the write into variable "interrupts" is no longer atomic. 208 // 209 // Note this problem does NOT occur when just using multiple kernel threads because the preemption ALWAYS restarts the 210 // thread on the same kernel thread. 211 // 212 // The obvious question is why does ARM use a coprocessor register to store the TSL pointer given that coprocessor 213 // registers are second-class registers with respect to the instruction set. One possible answer is that they did not 214 // want to dedicate one of the general registers to hold the TLS pointer and there was a free coprocessor register 215 // available. 216 217 //----------------------------------------------------------------------------- 218 // Some assembly required 219 #define __cfaasm_label(label, when) when: asm volatile goto(".global __cfaasm_" #label "_" #when "\n" "__cfaasm_" #label "_" #when ":":::"memory":when) 220 221 //---------- 222 // special case for preemption since used often 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 } 162 264 163 265 extern "C" { 164 266 // Disable interrupts by incrementing the counter 165 267 void disable_interrupts() { 166 with( kernelTLS.preemption_state ) { 268 // create a assembler label before 269 // marked as clobber all to avoid movement 270 __cfaasm_label(dsable, before); 271 272 with( __cfaabi_tls.preemption_state ) { 167 273 #if GCC_VERSION > 50000 168 274 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free"); … … 181 287 verify( new_val < 65_000u ); // If this triggers someone is disabling interrupts without enabling them 182 288 } 289 290 // create a assembler label after 291 // marked as clobber all to avoid movement 292 __cfaasm_label(dsable, after); 293 183 294 } 184 295 … … 186 297 // If counter reaches 0, execute any pending __cfactx_switch 187 298 void enable_interrupts( __cfaabi_dbg_ctx_param ) { 188 processor * proc = kernelTLS.this_processor; // Cache the processor now since interrupts can start happening after the atomic store 189 190 with( kernelTLS.preemption_state ){ 299 // Cache the processor now since interrupts can start happening after the atomic store 300 processor * proc = __cfaabi_tls.this_processor; 301 /* paranoid */ verify( proc ); 302 303 with( __cfaabi_tls.preemption_state ){ 191 304 unsigned short prev = disable_count; 192 305 disable_count -= 1; 193 verify( prev != 0u ); // If this triggers someone is enabled already enabled interruptsverify( prev != 0u ); 306 307 // If this triggers someone is enabled already enabled interruptsverify( prev != 0u ); 308 /* paranoid */ verify( prev != 0u ); 194 309 195 310 // Check if we need to prempt the thread because an interrupt was missed 196 311 if( prev == 1 ) { 197 312 #if GCC_VERSION > 50000 198 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free");313 static_assert(__atomic_always_lock_free(sizeof(enabled), &enabled), "Must be lock-free"); 199 314 #endif 200 315 … … 220 335 // Don't execute any pending __cfactx_switch even if counter reaches 0 221 336 void enable_interrupts_noPoll() { 222 unsigned short prev = kernelTLS.preemption_state.disable_count; 223 kernelTLS.preemption_state.disable_count -= 1; 224 verifyf( prev != 0u, "Incremented from %u\n", prev ); // If this triggers someone is enabled already enabled interrupts 337 unsigned short prev = __cfaabi_tls.preemption_state.disable_count; 338 __cfaabi_tls.preemption_state.disable_count -= 1; 339 // If this triggers someone is enabled already enabled interrupts 340 /* paranoid */ verifyf( prev != 0u, "Incremented from %u\n", prev ); 225 341 if( prev == 1 ) { 226 342 #if GCC_VERSION > 50000 227 static_assert(__atomic_always_lock_free(sizeof(kernelTLS.preemption_state.enabled), &kernelTLS.preemption_state.enabled), "Must be lock-free");343 static_assert(__atomic_always_lock_free(sizeof(__cfaabi_tls.preemption_state.enabled), &__cfaabi_tls.preemption_state.enabled), "Must be lock-free"); 228 344 #endif 229 345 // Set enabled flag to true 230 346 // should be atomic to avoid preemption in the middle of the operation. 231 347 // use memory order RELAXED since there is no inter-thread on this variable requirements 232 __atomic_store_n(& kernelTLS.preemption_state.enabled, true, __ATOMIC_RELAXED);348 __atomic_store_n(&__cfaabi_tls.preemption_state.enabled, true, __ATOMIC_RELAXED); 233 349 234 350 // Signal the compiler that a fence is needed but only for signal handlers … … 237 353 } 238 354 } 355 356 //----------------------------------------------------------------------------- 357 // Kernel Signal Debug 358 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 unnecessary 367 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); } 368 369 ret = sigismember(&oldset, SIGUSR1); 370 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 371 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); } 372 373 ret = sigismember(&oldset, SIGALRM); 374 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 375 if(ret == 0) { abort("ERROR SIGALRM is enabled"); } 376 377 ret = sigismember(&oldset, SIGTERM); 378 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 379 if(ret == 1) { abort("ERROR SIGTERM is disabled"); } 380 381 __cfaasm_label(debug, after); 382 } 383 384 #ifdef __CFA_WITH_VERIFY__ 385 bool __cfaabi_dbg_in_kernel() { 386 return !__preemption_enabled(); 387 } 388 #endif 389 390 #undef __cfaasm_label 391 392 //----------------------------------------------------------------------------- 393 // Signal handling 239 394 240 395 // sigprocmask wrapper : unblock a single signal … … 256 411 257 412 if ( pthread_sigmask( SIG_BLOCK, &mask, 0p ) == -1 ) { 258 abort( "internal error, pthread_sigmask" );413 abort( "internal error, pthread_sigmask" ); 259 414 } 260 415 } … … 268 423 // reserved for future use 269 424 static void timeout( $thread * this ) { 270 //TODO : implement waking threads 271 } 425 unpark( this ); 426 } 427 428 //----------------------------------------------------------------------------- 429 // Some assembly required 430 #if defined( __i386 ) 431 #ifdef __PIC__ 432 #define RELOC_PRELUDE( label ) \ 433 "calll .Lcfaasm_prelude_" #label "$pb\n\t" \ 434 ".Lcfaasm_prelude_" #label "$pb:\n\t" \ 435 "popl %%eax\n\t" \ 436 ".Lcfaasm_prelude_" #label "_end:\n\t" \ 437 "addl $_GLOBAL_OFFSET_TABLE_+(.Lcfaasm_prelude_" #label "_end-.Lcfaasm_prelude_" #label "$pb), %%eax\n\t" 438 #define RELOC_PREFIX "" 439 #define RELOC_SUFFIX "@GOT(%%eax)" 440 #else 441 #define RELOC_PREFIX "$" 442 #define RELOC_SUFFIX "" 443 #endif 444 #define __cfaasm_label( label ) struct asm_region label = \ 445 ({ \ 446 struct asm_region region; \ 447 asm( \ 448 RELOC_PRELUDE( label ) \ 449 "movl " RELOC_PREFIX "__cfaasm_" #label "_before" RELOC_SUFFIX ", %[vb]\n\t" \ 450 "movl " RELOC_PREFIX "__cfaasm_" #label "_after" RELOC_SUFFIX ", %[va]\n\t" \ 451 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 452 ); \ 453 region; \ 454 }); 455 #elif defined( __x86_64 ) 456 #ifdef __PIC__ 457 #define RELOC_PREFIX "" 458 #define RELOC_SUFFIX "@GOTPCREL(%%rip)" 459 #else 460 #define RELOC_PREFIX "$" 461 #define RELOC_SUFFIX "" 462 #endif 463 #define __cfaasm_label( label ) struct asm_region label = \ 464 ({ \ 465 struct asm_region region; \ 466 asm( \ 467 "movq " RELOC_PREFIX "__cfaasm_" #label "_before" RELOC_SUFFIX ", %[vb]\n\t" \ 468 "movq " RELOC_PREFIX "__cfaasm_" #label "_after" RELOC_SUFFIX ", %[va]\n\t" \ 469 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 470 ); \ 471 region; \ 472 }); 473 #elif defined( __aarch64__ ) 474 #ifdef __PIC__ 475 // Note that this works only for gcc 476 #define __cfaasm_label( label ) struct asm_region label = \ 477 ({ \ 478 struct asm_region region; \ 479 asm( \ 480 "adrp %[vb], _GLOBAL_OFFSET_TABLE_" "\n\t" \ 481 "ldr %[vb], [%[vb], #:gotpage_lo15:__cfaasm_" #label "_before]" "\n\t" \ 482 "adrp %[va], _GLOBAL_OFFSET_TABLE_" "\n\t" \ 483 "ldr %[va], [%[va], #:gotpage_lo15:__cfaasm_" #label "_after]" "\n\t" \ 484 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 485 ); \ 486 region; \ 487 }); 488 #else 489 #error this is not the right thing to do 490 /* 491 #define __cfaasm_label( label ) struct asm_region label = \ 492 ({ \ 493 struct asm_region region; \ 494 asm( \ 495 "adrp %[vb], __cfaasm_" #label "_before" "\n\t" \ 496 "add %[vb], %[vb], :lo12:__cfaasm_" #label "_before" "\n\t" \ 497 "adrp %[va], :got:__cfaasm_" #label "_after" "\n\t" \ 498 "add %[va], %[va], :lo12:__cfaasm_" #label "_after" "\n\t" \ 499 : [vb]"=r"(region.before), [va]"=r"(region.after) \ 500 ); \ 501 region; \ 502 }); 503 */ 504 #endif 505 #else 506 #error unknown hardware architecture 507 #endif 272 508 273 509 // KERNEL ONLY … … 275 511 // If true : preemption is safe 276 512 // If false : preemption is unsafe and marked as pending 277 static inline bool preemption_ready() { 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 520 // Check if preemption is safe 279 bool ready = kernelTLS.preemption_state.enabled && ! kernelTLS.preemption_state.in_progress; 280 521 bool ready = true; 522 if( __cfaasm_in( ip, get ) ) { ready = false; goto EXIT; }; 523 if( __cfaasm_in( ip, check ) ) { ready = false; goto EXIT; }; 524 if( __cfaasm_in( ip, dsable ) ) { ready = false; goto EXIT; }; 525 if( __cfaasm_in( ip, debug ) ) { ready = false; goto EXIT; }; 526 if( !__cfaabi_tls.preemption_state.enabled) { ready = false; goto EXIT; }; 527 if( __cfaabi_tls.preemption_state.in_progress ) { ready = false; goto EXIT; }; 528 529 EXIT: 281 530 // Adjust the pending flag accordingly 282 kernelTLS.this_processor->pending_preemption = !ready;531 __cfaabi_tls.this_processor->pending_preemption = !ready; 283 532 return ready; 284 533 } … … 290 539 // Startup routine to activate preemption 291 540 // Called from kernel_startup 292 void kernel_start_preemption() {541 void __kernel_alarm_startup() { 293 542 __cfaabi_dbg_print_safe( "Kernel : Starting preemption\n" ); 294 543 295 544 // Start with preemption disabled until ready 296 kernelTLS.preemption_state.enabled = false;297 kernelTLS.preemption_state.disable_count = 1;545 __cfaabi_tls.preemption_state.enabled = false; 546 __cfaabi_tls.preemption_state.disable_count = 1; 298 547 299 548 // Initialize the event kernel … … 303 552 // Setup proper signal handlers 304 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 555 306 556 signal_block( SIGALRM ); … … 311 561 // Shutdown routine to deactivate preemption 312 562 // Called from kernel_shutdown 313 void kernel_stop_preemption() {563 void __kernel_alarm_shutdown() { 314 564 __cfaabi_dbg_print_safe( "Kernel : Preemption stopping\n" ); 315 565 … … 325 575 // Wait for the preemption thread to finish 326 576 327 pthread_join( alarm_thread, 0p ); 328 free( alarm_stack ); 577 __destroy_pthread( alarm_thread, alarm_stack, 0p ); 329 578 330 579 // Preemption is now fully stopped … … 352 601 // Kernel Signal Handlers 353 602 //============================================================================================= 603 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; ) 354 604 355 605 // Context switch signal handler 356 606 // Receives SIGUSR1 signal and causes the current thread to yield 357 607 static void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) { 358 __cfaabi_dbg_debug_do( last_interrupt = (void *)(cxt->uc_mcontext.CFA_REG_IP); ) 608 void * ip = (void *)(cxt->uc_mcontext.CFA_REG_IP); 609 __cfaabi_dbg_debug_do( last_interrupt = ip; ) 359 610 360 611 // SKULLDUGGERY: if a thread creates a processor and the immediately deletes it, 361 612 // the interrupt that is supposed to force the kernel thread to preempt might arrive 362 // before the kernel thread has even started running. When that happens an iterrupt363 // w ea null 'this_processor' will be caught, just ignore it.364 if(! kernelTLS.this_processor ) return;613 // before the kernel thread has even started running. When that happens, an interrupt 614 // with a null 'this_processor' will be caught, just ignore it. 615 if(! __cfaabi_tls.this_processor ) return; 365 616 366 617 choose(sfp->si_value.sival_int) { 367 618 case PREEMPT_NORMAL : ;// Normal case, nothing to do here 368 case PREEMPT_TERMINATE: verify( __atomic_load_n( & kernelTLS.this_processor->do_terminate, __ATOMIC_SEQ_CST ) );619 case PREEMPT_TERMINATE: verify( __atomic_load_n( &__cfaabi_tls.this_processor->do_terminate, __ATOMIC_SEQ_CST ) ); 369 620 default: 370 621 abort( "internal error, signal value is %d", sfp->si_value.sival_int ); … … 372 623 373 624 // Check if it is safe to preempt here 374 if( !preemption_ready( ) ) { return; }375 376 __cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", kernelTLS.this_processor, kernelTLS.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) );625 if( !preemption_ready( ip ) ) { return; } 626 627 __cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", __cfaabi_tls.this_processor, __cfaabi_tls.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) ); 377 628 378 629 // Sync flag : prevent recursive calls to the signal handler 379 kernelTLS.preemption_state.in_progress = true;630 __cfaabi_tls.preemption_state.in_progress = true; 380 631 381 632 // Clear sighandler mask before context switching. … … 387 638 } 388 639 389 // TODO: this should go in finish action390 640 // Clear the in progress flag 391 kernelTLS.preemption_state.in_progress = false;641 __cfaabi_tls.preemption_state.in_progress = false; 392 642 393 643 // Preemption can occur here … … 395 645 force_yield( __ALARM_PREEMPTION ); // Do the actual __cfactx_switch 396 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 397 655 398 656 // Main of the alarm thread 399 657 // Waits on SIGALRM and send SIGUSR1 to whom ever needs it 400 658 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 #endif 669 401 670 // Block sigalrms to control when they arrive 402 671 sigset_t mask; … … 456 725 EXIT: 457 726 __cfaabi_dbg_print_safe( "Kernel : Preemption thread stopping\n" ); 727 unregister(&id); 728 729 #if !defined(__CFA_NO_STATISTICS__) 730 if( 0 != __print_alarm_stats ) { 731 __print_stats( &local_stats, __print_alarm_stats, "Alarm", "Thread", 0p ); 732 } 733 #endif 458 734 return 0p; 459 735 } 460 461 //=============================================================================================462 // Kernel Signal Debug463 //=============================================================================================464 465 void __cfaabi_check_preemption() {466 bool ready = kernelTLS.preemption_state.enabled;467 if(!ready) { abort("Preemption should be ready"); }468 469 sigset_t oldset;470 int ret;471 ret = pthread_sigmask(0, 0p, &oldset);472 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); }473 474 ret = sigismember(&oldset, SIGUSR1);475 if(ret < 0) { abort("ERROR sigismember returned %d", ret); }476 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); }477 478 ret = sigismember(&oldset, SIGALRM);479 if(ret < 0) { abort("ERROR sigismember returned %d", ret); }480 if(ret == 0) { abort("ERROR SIGALRM is enabled"); }481 482 ret = sigismember(&oldset, SIGTERM);483 if(ret < 0) { abort("ERROR sigismember returned %d", ret); }484 if(ret == 1) { abort("ERROR SIGTERM is disabled"); }485 }486 487 #ifdef __CFA_WITH_VERIFY__488 bool __cfaabi_dbg_in_kernel() {489 return !kernelTLS.preemption_state.enabled;490 }491 #endif492 736 493 737 // Local Variables: // -
libcfa/src/concurrency/preemption.hfa
r3c64c668 r58fe85a 16 16 #pragma once 17 17 18 #include "bits/locks.hfa" 18 19 #include "alarm.hfa" 19 #include "kernel_private.hfa"20 20 21 void kernel_start_preemption(); 22 void kernel_stop_preemption(); 21 struct event_kernel_t { 22 alarm_list_t alarms; 23 __spinlock_t lock; 24 }; 25 26 extern event_kernel_t * event_kernel; 27 23 28 void update_preemption( processor * this, Duration duration ); 24 29 -
libcfa/src/concurrency/thread.cfa
r3c64c668 r58fe85a 19 19 20 20 #include "kernel_private.hfa" 21 #include "exception.hfa" 21 22 22 23 #define __CFA_INVOKE_PRIVATE__ … … 28 29 context{ 0p, 0p }; 29 30 self_cor{ name, storage, storageSize }; 31 ticket = TICKET_RUNNING; 30 32 state = Start; 31 33 preempted = __NO_PREEMPTION; … … 35 37 self_mon_p = &self_mon; 36 38 curr_cluster = &cl; 37 next = 0p; 39 link.next = 0p; 40 link.prev = 0p; 41 link.preferred = -1; 42 #if defined( __CFA_WITH_VERIFY__ ) 43 canary = 0x0D15EA5E0D15EA5Ep; 44 #endif 45 46 seqable.next = 0p; 47 seqable.back = 0p; 38 48 39 49 node.next = 0p; … … 45 55 46 56 void ^?{}($thread& this) with( this ) { 57 #if defined( __CFA_WITH_VERIFY__ ) 58 canary = 0xDEADDEADDEADDEADp; 59 #endif 47 60 unregister(curr_cluster, this); 48 61 ^self_cor{}; 62 } 63 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 guard 90 void (*dtor)(T& mutex this) = ^?{}; 91 bool join = defaultResumptionHandler != (void(*)(ThreadCancelled(T)&))0; 92 (this.mg){&m, (void(*)())dtor, join}; 93 94 95 /* paranoid */ verifyf( Halted == desc->state || Cancelled == desc->state, "Expected thread to be Halted or Cancelled, was %d\n", (int)desc->state ); 96 97 // After the guard set-up and any wait, check for cancellation. 98 struct _Unwind_Exception * cancellation = desc->self_cor.cancellation; 99 if ( likely( 0p == cancellation ) ) { 100 return; 101 } else if ( Cancelled == desc->state ) { 102 return; 103 } 104 desc->state = Cancelled; 105 if (!join) { 106 defaultResumptionHandler = default_thread_cancel_handler; 107 } 108 109 ThreadCancelled(T) except; 110 // TODO: Remove explitate vtable set once trac#186 is fixed. 111 except.virtual_table = &get_exception_vtable(&except); 112 except.the_thread = &thrd; 113 except.the_exception = __cfaehm_cancellation_exception( cancellation ); 114 throwResume except; 115 116 except.the_exception->virtual_table->free( except.the_exception ); 117 free( cancellation ); 118 desc->self_cor.cancellation = 0p; 119 } 120 121 void ^?{}( thread_dtor_guard_t & this ) { 122 ^(this.mg){}; 49 123 } 50 124 … … 59 133 60 134 this_thrd->context.[SP, FP] = this_thrd->self_cor.context.[SP, FP]; 61 verify( this_thrd->context.SP );135 /* paranoid */ verify( this_thrd->context.SP ); 62 136 63 __schedule_thread( this_thrd);137 __schedule_thread( this_thrd ); 64 138 enable_interrupts( __cfaabi_dbg_ctx ); 65 139 } … … 84 158 } 85 159 160 //----------------------------------------------------------------------------- 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; 165 } 166 167 uint64_t thread_rand() { 168 disable_interrupts(); 169 uint64_t ret = __tls_rand(); 170 enable_interrupts( __cfaabi_dbg_ctx ); 171 return ret; 172 } 173 86 174 // Local Variables: // 87 175 // mode: c // -
libcfa/src/concurrency/thread.hfa
r3c64c668 r58fe85a 22 22 #include "kernel.hfa" 23 23 #include "monitor.hfa" 24 #include "exception.hfa" 24 25 25 26 //----------------------------------------------------------------------------- 26 27 // thread trait 27 28 trait is_thread(dtype T) { 28 void ^?{}(T& mutex this);29 void main(T& this);30 $thread* get_thread(T& this);29 void ^?{}(T& mutex this); 30 void main(T& this); 31 $thread* get_thread(T& this); 31 32 }; 33 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) *); 32 44 33 45 // define that satisfies the trait without using the thread keyword … … 66 78 static inline void ?{}($thread & this, const char * const name, struct cluster & cl, size_t stackSize ) { this{ name, cl, 0p, stackSize }; } 67 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 ); 87 68 88 //----------------------------------------------------------------------------- 69 89 // thread runner … … 82 102 forall( dtype T | sized(T) | is_thread(T) ) 83 103 void ^?{}( scoped(T)& this ); 84 85 //-----------------------------------------------------------------------------86 // Thread getters87 static inline struct $thread * active_thread () { return TL_GET( this_thread ); }88 104 89 105 //----------------------------------------------------------------------------- … … 106 122 bool force_yield( enum __Preemption_Reason ); 107 123 108 static inline void yield() { 109 force_yield(__MANUAL_PREEMPTION); 110 } 124 //---------- 125 // sleep: force thread to block and be rescheduled after Duration duration 126 void sleep( Duration duration ); 111 127 112 // Yield: yield N times 113 static inline void yield( unsigned times ) { 114 for( times ) { 115 yield(); 116 } 117 } 128 //---------- 129 // join 130 forall( dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) ) 131 T & join( T & this ); 118 132 119 133 // Local Variables: // -
libcfa/src/containers/vector.hfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Jul 5 18:00:07 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Jul 22 10:01:18 201713 // Update Count : 312 // Last Modified On : Wed Jun 17 11:02:46 2020 13 // Update Count : 4 14 14 // 15 15 16 16 #pragma once 17 17 18 extern "C" {19 18 #include <stdbool.h> 20 }21 19 22 20 //------------------------------------------------------------------------------ -
libcfa/src/exception.c
r3c64c668 r58fe85a 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : T hu Feb 22 18:17:34 201813 // Update Count : 1111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 27 16:27:00 2020 13 // Update Count : 35 14 14 // 15 15 16 // Normally we would get this from the CFA prelude. 16 17 #include <stddef.h> // for size_t 17 18 19 #include <unwind.h> // for struct _Unwind_Exception {...}; 20 18 21 #include "exception.h" 19 20 // Implementation of the secret header.21 22 22 23 #include <stdlib.h> 23 24 #include <stdio.h> 24 #include <unwind.h>25 25 #include <bits/debug.hfa> 26 27 // FIX ME: temporary hack to keep ARM build working 26 #include "concurrency/invoke.h" 27 #include "stdhdr/assert.h" 28 29 #if defined( __ARM_ARCH ) 30 #warning FIX ME: temporary hack to keep ARM build working 28 31 #ifndef _URC_FATAL_PHASE1_ERROR 29 #define _URC_FATAL_PHASE1_ERROR 232 #define _URC_FATAL_PHASE1_ERROR 3 30 33 #endif // ! _URC_FATAL_PHASE1_ERROR 31 34 #ifndef _URC_FATAL_PHASE2_ERROR 32 35 #define _URC_FATAL_PHASE2_ERROR 2 33 36 #endif // ! _URC_FATAL_PHASE2_ERROR 37 #endif // __ARM_ARCH 34 38 35 39 #include "lsda.h" 36 40 41 /* The exception class for our exceptions. Because of the vendor component 42 * its value would not be standard. 43 * Vendor: UWPL 44 * Language: CFA\0 45 */ 46 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643; 37 47 38 48 // Base exception vtable is abstract, you should not have base exceptions. 39 struct __cfa abi_ehm__base_exception_t_vtable40 ___cfa abi_ehm__base_exception_t_vtable_instance = {49 struct __cfaehm_base_exception_t_vtable 50 ___cfaehm_base_exception_t_vtable_instance = { 41 51 .parent = NULL, 42 52 .size = 0, … … 47 57 48 58 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 58 59 // Get the current exception context. 59 60 // There can be a single global until multithreading occurs, then each stack 60 // needs its own. It will have to be updated to handle that. 61 struct exception_context_t * this_exception_context() { 61 // needs its own. We get this from libcfathreads (no weak attribute). 62 __attribute__((weak)) struct exception_context_t * this_exception_context() { 63 static struct exception_context_t shared_stack = {NULL, NULL}; 62 64 return &shared_stack; 63 65 } 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))76 66 77 67 78 68 // RESUMPTION ================================================================ 79 69 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; 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 } 93 89 } 94 } 95 96 __cfaabi_dbg_print_safe("Unhandled exception\n"); 97 shared_stack.current_resume = original_head; 98 99 // Fall back to termination: 100 __cfaabi_ehm__throw_terminate(except); 101 // TODO: Default handler for resumption. 90 } // End the search and return to the top of the stack. 91 92 // No handler found, fall back to the default operation. 93 __cfadbg_print_safe(exception, "Unhandled exception\n"); 94 defaultHandler(except); 102 95 } 103 96 … … 106 99 // be added after the node is built but before it is made the top node. 107 100 108 void __cfa abi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node,101 void __cfaehm_try_resume_setup(struct __cfaehm_try_resume_node * node, 109 102 _Bool (*handler)(exception_t * except)) { 110 node->next = shared_stack.top_resume; 103 struct exception_context_t * context = this_exception_context(); 104 node->next = context->top_resume; 111 105 node->handler = handler; 112 shared_stack.top_resume = node; 113 } 114 115 void __cfaabi_ehm__try_resume_cleanup(struct __cfaabi_ehm__try_resume_node * node) { 116 shared_stack.top_resume = node->next; 117 } 118 119 120 // TERMINATION =============================================================== 121 122 // MEMORY MANAGEMENT (still for integers) 123 // May have to move to cfa for constructors and destructors (references). 124 125 struct __cfaabi_ehm__node { 126 struct __cfaabi_ehm__node * next; 127 }; 106 context->top_resume = node; 107 } 108 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 ========================================================= 128 116 129 117 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node))) 130 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1) 118 #define EXCEPT_TO_NODE(except) ((struct __cfaehm_node *)(except) - 1) 119 #define UNWIND_TO_NODE(unwind) ((struct __cfaehm_node *)(unwind)) 120 #define NULL_MAP(map, ptr) ((ptr) ? (map(ptr)) : NULL) 121 122 // How to clean up an exception in various situations. 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 } 131 135 132 136 // Creates a copy of the indicated exception and sets current_exception to it. 133 static void __cfa abi_ehm__allocate_exception( exception_t * except ) {137 static void __cfaehm_allocate_exception( exception_t * except ) { 134 138 struct exception_context_t * context = this_exception_context(); 135 139 136 140 // Allocate memory for the exception. 137 struct __cfa abi_ehm__node * store = malloc(138 sizeof( struct __cfa abi_ehm__node ) + except->virtual_table->size );141 struct __cfaehm_node * store = malloc( 142 sizeof( struct __cfaehm_node ) + except->virtual_table->size ); 139 143 140 144 if ( ! store ) { … … 143 147 } 144 148 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 145 156 // Add the node to the list: 146 store->next = EXCEPT_TO_NODE(context->current_exception); 147 context->current_exception = NODE_TO_EXCEPT(store); 148 149 // Copy the exception to storage. 150 except->virtual_table->copy( context->current_exception, except ); 157 store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception); 158 context->current_exception = except_store; 151 159 } 152 160 153 161 // Delete the provided exception, unsetting current_exception if relivant. 154 static void __cfa abi_ehm__delete_exception( exception_t * except ) {155 struct exception_context_t * context = this_exception_context(); 156 157 __cfa abi_dbg_print_safe("Deleting Exception\n");162 static void __cfaehm_delete_exception( exception_t * except ) { 163 struct exception_context_t * context = this_exception_context(); 164 165 __cfadbg_print_safe(exception, "Deleting Exception\n"); 158 166 159 167 // Remove the exception from the list. 160 struct __cfa abi_ehm__node * to_free = EXCEPT_TO_NODE(except);161 struct __cfa abi_ehm__node * node;168 struct __cfaehm_node * to_free = EXCEPT_TO_NODE(except); 169 struct __cfaehm_node * node; 162 170 163 171 if ( context->current_exception == except ) { 164 172 node = to_free->next; 165 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0;173 context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node); 166 174 } else { 167 175 node = EXCEPT_TO_NODE(context->current_exception); 168 176 // It may always be in the first or second position. 169 while ( to_free != node->next ) {177 while ( to_free != node->next ) { 170 178 node = node->next; 171 179 } … … 178 186 } 179 187 180 // If this isn't a rethrow (*except==0), delete the provided exception. 181 void __cfaabi_ehm__cleanup_terminate( void * except ) { 182 if ( *(void**)except ) __cfaabi_ehm__delete_exception( *(exception_t **)except ); 183 } 184 185 186 // We need a piece of storage to raise the exception 187 struct _Unwind_Exception this_exception_storage; 188 // CANCELLATION ============================================================== 188 189 189 190 // Function needed by force unwind … … 192 193 int version, 193 194 _Unwind_Action actions, 194 _Unwind_Exception_Class exception Class,195 _Unwind_Exception_Class exception_class, 195 196 struct _Unwind_Exception * unwind_exception, 196 struct _Unwind_Context * context, 197 void * some_param) { 198 if( actions & _UA_END_OF_STACK ) exit(1); 199 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON; 200 201 return _URC_FATAL_PHASE2_ERROR; 197 struct _Unwind_Context * unwind_context, 198 void * stop_param) { 199 // Verify actions follow the rules we expect. 200 verify(actions & _UA_CLEANUP_PHASE); 201 verify(actions & _UA_FORCE_UNWIND); 202 verify(!(actions & _UA_SEARCH_PHASE)); 203 verify(!(actions & _UA_HANDLER_FRAME)); 204 205 if ( actions & _UA_END_OF_STACK ) { 206 abort(); 207 } else { 208 return _URC_NO_REASON; 209 } 210 } 211 212 __attribute__((weak)) _Unwind_Reason_Code 213 __cfaehm_cancellation_unwind( struct _Unwind_Exception * exception ) { 214 return _Unwind_ForcedUnwind( exception, _Stop_Fn, (void*)0x22 ); 215 } 216 217 // Cancel the current stack, prefroming approprate clean-up and messaging. 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; 202 250 } 203 251 204 252 // The exception that is being thrown must already be stored. 205 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) { 206 if ( ! this_exception_context()->current_exception ) { 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 ) { 207 256 printf("UNWIND ERROR missing exception in begin unwind\n"); 208 257 abort(); 209 258 } 210 259 struct _Unwind_Exception * storage = 260 &EXCEPT_TO_NODE(context->current_exception)->unwind_exception; 211 261 212 262 // Call stdlibc to raise the exception 213 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage ); 263 __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context); 264 _Unwind_Reason_Code ret = _Unwind_RaiseException( storage ); 214 265 215 266 // If we reach here it means something happened. For resumption to work we need to find a way … … 220 271 // the whole stack. 221 272 222 if( ret == _URC_END_OF_STACK ) { 223 // No proper handler was found. This can be handled in many ways, C++ calls std::terminate. 224 // Here we force unwind the stack, basically raising a cancellation. 225 printf("Uncaught exception %p\n", &this_exception_storage); 226 227 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 ); 228 printf("UNWIND ERROR %d after force unwind\n", ret); 273 // We did not simply reach the end of the stack without finding a handler. This is an error. 274 if ( ret != _URC_END_OF_STACK ) { 275 printf("UNWIND ERROR %d after raise exception\n", ret); 229 276 abort(); 230 277 } 231 278 232 // We did not simply reach the end of the stack without finding a handler. This is an error. 233 printf("UNWIND ERROR %d after raise exception\n", ret); 279 // No handler found, go to the default operation. 280 __cfadbg_print_safe(exception, "Uncaught exception %p\n", storage); 281 282 __attribute__((cleanup(__cfaehm_cleanup_default))) 283 exception_t * exception = context->current_exception; 284 defaultHandler( exception ); 285 } 286 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; 234 297 abort(); 235 298 } 236 299 237 void __cfaabi_ehm__throw_terminate( exception_t * val ) { 238 __cfaabi_dbg_print_safe("Throwing termination exception\n"); 239 240 __cfaabi_ehm__allocate_exception( val ); 241 __cfaabi_ehm__begin_unwind(); 242 } 243 244 void __cfaabi_ehm__rethrow_terminate(void) { 245 __cfaabi_dbg_print_safe("Rethrowing termination exception\n"); 246 247 __cfaabi_ehm__begin_unwind(); 248 } 249 250 #pragma GCC push_options 251 #pragma GCC optimize("O0") 252 300 void __cfaehm_rethrow_terminate(void) { 301 __cfadbg_print_safe(exception, "Rethrowing termination exception\n"); 302 303 __cfaehm_begin_unwind( __cfaehm_rethrow_adapter ); 304 abort(); 305 } 306 307 #if defined( __x86_64 ) || defined( __i386 ) 253 308 // This is our personality routine. For every stack frame annotated with 254 309 // ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding. 255 310 // Once in the search phase and once in the cleanup phase. 256 _Unwind_Reason_Code __gcfa_personality_v0 ( 257 int version, _Unwind_Action actions, unsigned long long exceptionClass, 258 struct _Unwind_Exception* unwind_exception, 259 struct _Unwind_Context* context) 311 _Unwind_Reason_Code __gcfa_personality_v0( 312 int version, 313 _Unwind_Action actions, 314 unsigned long long exception_class, 315 struct _Unwind_Exception * unwind_exception, 316 struct _Unwind_Context * unwind_context) 260 317 { 261 318 262 //__cfaabi_dbg_print_safe("CFA: 0x%lx\n", _Unwind_GetCFA(context)); 263 __cfaabi_dbg_print_safe("Personality function (%d, %x, %llu, %p, %p):", 264 version, actions, exceptionClass, unwind_exception, context); 265 266 // If we've reached the end of the stack then there is nothing much we can do... 267 if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK; 268 319 //__cfadbg_print_safe(exception, "CFA: 0x%lx\n", _Unwind_GetCFA(context)); 320 __cfadbg_print_safe(exception, "Personality function (%d, %x, %llu, %p, %p):", 321 version, actions, exception_class, unwind_exception, unwind_context); 322 323 // Verify that actions follow the rules we expect. 324 // This function should never be called at the end of the stack. 325 verify(!(actions & _UA_END_OF_STACK)); 326 // Either only the search phase flag is set or... 269 327 if (actions & _UA_SEARCH_PHASE) { 270 __cfaabi_dbg_print_safe(" lookup phase"); 271 } 272 else if (actions & _UA_CLEANUP_PHASE) { 273 __cfaabi_dbg_print_safe(" cleanup phase"); 274 } 275 // Just in case, probably can't actually happen 276 else { 277 printf(" error\n"); 278 return _URC_FATAL_PHASE1_ERROR; 328 verify(actions == _UA_SEARCH_PHASE); 329 __cfadbg_print_safe(exception, " lookup phase"); 330 // ... we are in clean-up phase. 331 } else { 332 verify(actions & _UA_CLEANUP_PHASE); 333 __cfadbg_print_safe(exception, " cleanup phase"); 334 // We shouldn't be the handler frame during forced unwind. 335 if (actions & _UA_HANDLER_FRAME) { 336 verify(!(actions & _UA_FORCE_UNWIND)); 337 __cfadbg_print_safe(exception, " (handler frame)"); 338 } else if (actions & _UA_FORCE_UNWIND) { 339 __cfadbg_print_safe(exception, " (force unwind)"); 340 } 279 341 } 280 342 281 343 // Get a pointer to the language specific data from which we will read what we need 282 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData(context );283 284 if ( !lsd ) { //Nothing to do, keep unwinding344 const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context ); 345 346 if ( !lsd ) { //Nothing to do, keep unwinding 285 347 printf(" no LSD"); 286 348 goto UNWIND; … … 289 351 // Get the instuction pointer and a reading pointer into the exception table 290 352 lsda_header_info lsd_info; 291 const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info); 292 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context ); 353 const unsigned char * cur_ptr = parse_lsda_header(unwind_context, lsd, &lsd_info); 354 _Unwind_Ptr instruction_ptr = _Unwind_GetIP(unwind_context); 355 356 struct exception_context_t * context = this_exception_context(); 293 357 294 358 // Linearly search the table for stuff to do 295 while ( cur_ptr < lsd_info.action_table ) {359 while ( cur_ptr < lsd_info.action_table ) { 296 360 _Unwind_Ptr callsite_start; 297 361 _Unwind_Ptr callsite_len; … … 306 370 307 371 // Have we reach the correct frame info yet? 308 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {372 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) { 309 373 #ifdef __CFA_DEBUG_PRINT__ 310 374 void * ls = (void*)lsd_info.Start; … … 314 378 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len; 315 379 void * ip = (void*)instruction_ptr; 316 __cfa abi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n",380 __cfadbg_print_safe(exception, "\nfound %p - %p (%p, %p, %p), looking for %p\n", 317 381 bp, ep, ls, cs, cl, ip); 318 382 #endif // __CFA_DEBUG_PRINT__ … … 321 385 322 386 // Have we gone too far? 323 if ( lsd_info.Start + callsite_start > instruction_ptr ) {387 if ( lsd_info.Start + callsite_start > instruction_ptr ) { 324 388 printf(" gone too far"); 325 389 break; 326 390 } 327 391 328 // Something to do? 329 if( callsite_landing_pad ) { 330 // Which phase are we in 331 if (actions & _UA_SEARCH_PHASE) { 332 // In search phase, these means we found a potential handler we must check. 333 334 // We have arbitrarily decided that 0 means nothing to do and 1 means there is 335 // a potential handler. This doesn't seem to conflict the gcc default behavior. 336 if (callsite_action != 0) { 337 // Now we want to run some code to see if the handler matches 338 // This is the tricky part where we want to the power to run arbitrary code 339 // However, generating a new exception table entry and try routine every time 340 // is way more expansive than we might like 341 // The information we have is : 342 // - The GR (Series of registers) 343 // GR1=GP Global Pointer of frame ref by context 344 // - The instruction pointer 345 // - The instruction pointer info (???) 346 // - The CFA (Canonical Frame Address) 347 // - The BSP (Probably the base stack pointer) 348 349 350 // The current apprach uses one exception table entry per try block 351 _uleb128_t imatcher; 352 // Get the relative offset to the {...}? 353 cur_ptr = read_uleb128(cur_ptr, &imatcher); 354 355 _Unwind_Reason_Code (*matcher)(exception_t *) = 356 MATCHER_FROM_CONTEXT(context); 357 int index = matcher(shared_stack.current_exception); 358 _Unwind_Reason_Code ret = (0 == index) 359 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 360 shared_stack.current_handler_index = index; 361 362 // Based on the return value, check if we matched the exception 363 if( ret == _URC_HANDLER_FOUND) { 364 __cfaabi_dbg_print_safe(" handler found\n"); 365 } else { 366 __cfaabi_dbg_print_safe(" no handler\n"); 367 } 368 return ret; 392 // Check for what we must do: 393 if ( 0 == callsite_landing_pad ) { 394 // Nothing to do, move along 395 __cfadbg_print_safe(exception, " no landing pad"); 396 } else if (actions & _UA_SEARCH_PHASE) { 397 // In search phase, these means we found a potential handler we must check. 398 399 // We have arbitrarily decided that 0 means nothing to do and 1 means there is 400 // a potential handler. This doesn't seem to conflict the gcc default behavior. 401 if (callsite_action != 0) { 402 // Now we want to run some code to see if the handler matches 403 // This is the tricky part where we want to the power to run arbitrary code 404 // However, generating a new exception table entry and try routine every time 405 // is way more expansive than we might like 406 // The information we have is : 407 // - The GR (Series of registers) 408 // GR1=GP Global Pointer of frame ref by context 409 // - The instruction pointer 410 // - The instruction pointer info (???) 411 // - The CFA (Canonical Frame Address) 412 // - The BSP (Probably the base stack pointer) 413 414 // The current apprach uses one exception table entry per try block 415 _uleb128_t imatcher; 416 // Get the relative offset to the {...}? 417 cur_ptr = read_uleb128(cur_ptr, &imatcher); 418 419 _Unwind_Word match_pos = 420 # if defined( __x86_64 ) 421 _Unwind_GetCFA(unwind_context) + 8; 422 # elif defined( __i386 ) 423 _Unwind_GetCFA(unwind_context) + 24; 424 # elif defined( __ARM_ARCH ) 425 # warning FIX ME: check if anything needed for ARM 426 42; 427 # endif 428 int (*matcher)(exception_t *) = *(int(**)(exception_t *))match_pos; 429 430 int index = matcher(context->current_exception); 431 _Unwind_Reason_Code ret = (0 == index) 432 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 433 UNWIND_TO_NODE(unwind_exception)->handler_index = index; 434 435 // Based on the return value, check if we matched the exception 436 if (ret == _URC_HANDLER_FOUND) { 437 __cfadbg_print_safe(exception, " handler found\n"); 438 } else { 439 // TODO: Continue the search if there is more in the table. 440 __cfadbg_print_safe(exception, " no handler\n"); 369 441 } 370 371 // This is only a cleanup handler, ignore it 372 __cfaabi_dbg_print_safe(" no action"); 442 return ret; 373 443 } 374 else if (actions & _UA_CLEANUP_PHASE) { 375 376 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 377 // If this is a potential exception handler 378 // but not the one that matched the exception in the seach phase, 379 // just ignore it 380 goto UNWIND; 381 } 382 383 // We need to run some clean-up or a handler 384 // These statment do the right thing but I don't know any specifics at all 385 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception ); 386 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 ); 387 388 // I assume this sets the instruction pointer to the adress of the landing pad 389 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT 390 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) ); 391 392 __cfaabi_dbg_print_safe(" action\n"); 393 394 // Return have some action to run 395 return _URC_INSTALL_CONTEXT; 444 445 // This is only a cleanup handler, ignore it 446 __cfadbg_print_safe(exception, " no action"); 447 } else { 448 // In clean-up phase, no destructors here but this could be the handler. 449 450 if ( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 451 // If this is a potential exception handler 452 // but not the one that matched the exception in the seach phase, 453 // just ignore it 454 goto UNWIND; 396 455 } 456 457 // We need to run some clean-up or a handler 458 // These statment do the right thing but I don't know any specifics at all 459 _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(0), 460 (_Unwind_Ptr)unwind_exception ); 461 _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(1), 0 ); 462 463 // I assume this sets the instruction pointer to the adress of the landing pad 464 // It doesn't actually set it, it only state the value that needs to be set once we 465 // return _URC_INSTALL_CONTEXT 466 _Unwind_SetIP( unwind_context, ((lsd_info.LPStart) + (callsite_landing_pad)) ); 467 468 __cfadbg_print_safe(exception, " action\n"); 469 470 // Return have some action to run 471 return _URC_INSTALL_CONTEXT; 397 472 } 398 399 // Nothing to do, move along400 __cfaabi_dbg_print_safe(" no landing pad");401 473 } 402 474 // No handling found 403 __cfa abi_dbg_print_safe(" table end reached\n");475 __cfadbg_print_safe(exception, " table end reached"); 404 476 405 477 UNWIND: 406 __cfa abi_dbg_print_safe(" unwind\n");478 __cfadbg_print_safe(exception, " unwind\n"); 407 479 408 480 // Keep unwinding the stack 409 481 return _URC_CONTINUE_UNWIND; 410 482 } 483 484 #pragma GCC push_options 485 #pragma GCC optimize(0) 411 486 412 487 // Try statements are hoisted out see comments for details. While this could probably be unique 413 488 // and simply linked from libcfa but there is one problem left, see the exception table for details 414 489 __attribute__((noinline)) 415 void __cfa abi_ehm__try_terminate(void (*try_block)(),490 void __cfaehm_try_terminate(void (*try_block)(), 416 491 void (*catch_block)(int index, exception_t * except), 417 492 __attribute__((unused)) int (*match_block)(exception_t * except)) { … … 419 494 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy); 420 495 421 // Setup statments: These 2 statments won't actually result in any code, they only setup global tables.422 // However, they clobber gcc cancellation support from gcc. We can replace the personality routine but423 // replacing the exception table gcc generates is not really doable, it generates labels based on how the424 // assembly works.425 426 496 // Setup the personality routine and exception table. 497 // Unforturnately these clobber gcc cancellation support which means we can't get access to 498 // the attribute cleanup tables at the same time. We would have to inspect the assembly to 499 // create a new set ourselves. 427 500 #ifdef __PIC__ 428 501 asm volatile (".cfi_personality 0x9b,CFA.ref.__gcfa_personality_v0"); … … 449 522 // Label which defines the end of the area for which the handler is setup. 450 523 asm volatile (".TRYEND:"); 451 // Label which defines the start of the exception landing pad. Basically what is called when the exception is452 // caught. Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the453 // exception runtime.524 // Label which defines the start of the exception landing pad. Basically what is called when 525 // the exception is caught. Note, if multiple handlers are given, the multiplexing should be 526 // done by the generated code, not the exception runtime. 454 527 asm volatile (".CATCH:"); 455 528 456 529 // Exception handler 457 catch_block( shared_stack.current_handler_index, 458 shared_stack.current_exception ); 530 // Note: Saving the exception context on the stack breaks termination exceptions. 531 catch_block( EXCEPT_TO_NODE( this_exception_context()->current_exception )->handler_index, 532 this_exception_context()->current_exception ); 459 533 } 460 534 … … 464 538 465 539 #ifdef __PIC__ 466 #if defined( __i386 ) || defined( __x86_64 )467 540 asm ( 468 541 // HEADER … … 481 554 // handler landing pad offset and 1 (action code, gcc seems to use 0). 482 555 ".LLSDACSBCFA2:\n" 483 " .uleb128 .TRYSTART-__cfa abi_ehm__try_terminate\n"556 " .uleb128 .TRYSTART-__cfaehm_try_terminate\n" 484 557 " .uleb128 .TRYEND-.TRYSTART\n" 485 " .uleb128 .CATCH-__cfa abi_ehm__try_terminate\n"558 " .uleb128 .CATCH-__cfaehm_try_terminate\n" 486 559 " .uleb128 1\n" 487 560 ".LLSDACSECFA2:\n" 488 561 // TABLE FOOTER 489 562 " .text\n" 490 " .size __cfa abi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"563 " .size __cfaehm_try_terminate, .-__cfaehm_try_terminate\n" 491 564 ); 492 565 … … 507 580 " .quad __gcfa_personality_v0\n" 508 581 #else // then __i386 509 " .long __gcfa_personality_v0\n"582 " .long __gcfa_personality_v0\n" 510 583 #endif 511 584 ); 512 #else513 #error Exception Handling: unknown architecture for position independent code.514 #endif // __i386 || __x86_64515 585 #else // __PIC__ 516 #if defined( __i386 ) || defined( __x86_64 )517 586 asm ( 518 587 // HEADER … … 529 598 ".LLSDACSBCFA2:\n" 530 599 // Handled area start (relative to start of function) 531 " .uleb128 .TRYSTART-__cfa abi_ehm__try_terminate\n"600 " .uleb128 .TRYSTART-__cfaehm_try_terminate\n" 532 601 // Handled area length 533 602 " .uleb128 .TRYEND-.TRYSTART\n" 534 603 // Handler landing pad address (relative to start of function) 535 " .uleb128 .CATCH-__cfa abi_ehm__try_terminate\n"604 " .uleb128 .CATCH-__cfaehm_try_terminate\n" 536 605 // Action code, gcc seems to always use 0. 537 606 " .uleb128 1\n" … … 539 608 ".LLSDACSECFA2:\n" 540 609 " .text\n" 541 " .size __cfa abi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"610 " .size __cfaehm_try_terminate, .-__cfaehm_try_terminate\n" 542 611 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n" 543 612 " .section .note.GNU-stack,\"x\",@progbits\n" 544 613 ); 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 } 545 633 #else 546 #error Exception Handling: unknown architecture for position dependent code. 547 #endif // __i386 || __x86_64 548 #endif // __PIC__ 549 550 #pragma GCC pop_options 634 #error unsupported hardware architecture 635 #endif // __x86_64 || __i386 -
libcfa/src/exception.h
r3c64c668 r58fe85a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // exception.h -- Builtins for exception handling.7 // exception.h -- Internal exception handling definitions. 8 8 // 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Jun 26 15:11:00 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : T hu Feb 22 18:11:15 201813 // Update Count : 811 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 27 14:45:00 2020 13 // Update Count : 11 14 14 // 15 15 16 16 #pragma once 17 17 18 // This could be considered several headers. All are internal to the exception 19 // system but needed to depending on whether they are C/Cforall code and 20 // whether or not they are part of the builtins. 18 21 19 22 #ifdef __cforall … … 21 24 #endif 22 25 23 struct __cfaabi_ehm__base_exception_t; 24 typedef struct __cfaabi_ehm__base_exception_t exception_t; 25 struct __cfaabi_ehm__base_exception_t_vtable { 26 const struct __cfaabi_ehm__base_exception_t_vtable * parent; 26 // Included in C code or the built-ins. 27 #if !defined(__cforall) || defined(__cforall_builtins__) 28 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; 27 33 size_t size; 28 void (*copy)(struct __cfa abi_ehm__base_exception_t *this,29 struct __cfa abi_ehm__base_exception_t * other);30 void (*free)(struct __cfa abi_ehm__base_exception_t *this);31 const char * (*msg)(struct __cfa abi_ehm__base_exception_t *this);34 void (*copy)(struct __cfaehm_base_exception_t *this, 35 struct __cfaehm_base_exception_t * other); 36 void (*free)(struct __cfaehm_base_exception_t *this); 37 const char * (*msg)(struct __cfaehm_base_exception_t *this); 32 38 }; 33 struct __cfa abi_ehm__base_exception_t {34 struct __cfa abi_ehm__base_exception_t_vtable const * virtual_table;39 struct __cfaehm_base_exception_t { 40 struct __cfaehm_base_exception_t_vtable const * virtual_table; 35 41 }; 36 extern struct __cfa abi_ehm__base_exception_t_vtable37 ___cfa abi_ehm__base_exception_t_vtable_instance;42 extern struct __cfaehm_base_exception_t_vtable 43 ___cfaehm_base_exception_t_vtable_instance; 38 44 39 45 46 void __cfaehm_cancel_stack(exception_t * except) __attribute__((noreturn)); 47 40 48 // Used in throw statement translation. 41 void __cfa abi_ehm__throw_terminate(exception_t * except) __attribute__((noreturn));42 void __cfa abi_ehm__rethrow_terminate() __attribute__((noreturn));43 void __cfa abi_ehm__throw_resume(exception_t * except);49 void __cfaehm_throw_terminate(exception_t * except, void (*)(exception_t *)); 50 void __cfaehm_rethrow_terminate() __attribute__((noreturn)); 51 void __cfaehm_throw_resume(exception_t * except, void (*)(exception_t *)); 44 52 45 53 // Function catches termination exceptions. 46 void __cfa abi_ehm__try_terminate(47 void (*try_block)(),48 void (*catch_block)(int index, exception_t * except),49 int (*match_block)(exception_t * except));54 void __cfaehm_try_terminate( 55 void (*try_block)(), 56 void (*catch_block)(int index, exception_t * except), 57 int (*match_block)(exception_t * except)); 50 58 51 59 // Clean-up the exception in catch blocks. 52 void __cfa abi_ehm__cleanup_terminate(void * except);60 void __cfaehm_cleanup_terminate(void * except); 53 61 54 62 // Data structure creates a list of resume handlers. 55 struct __cfa abi_ehm__try_resume_node {56 struct __cfaabi_ehm__try_resume_node * next;57 _Bool (*handler)(exception_t * except);63 struct __cfaehm_try_resume_node { 64 struct __cfaehm_try_resume_node * next; 65 _Bool (*handler)(exception_t * except); 58 66 }; 59 67 60 68 // These act as constructor and destructor for the resume node. 61 void __cfa abi_ehm__try_resume_setup(62 struct __cfaabi_ehm__try_resume_node * node,63 _Bool (*handler)(exception_t * except));64 void __cfa abi_ehm__try_resume_cleanup(65 struct __cfaabi_ehm__try_resume_node * node);69 void __cfaehm_try_resume_setup( 70 struct __cfaehm_try_resume_node * node, 71 _Bool (*handler)(exception_t * except)); 72 void __cfaehm_try_resume_cleanup( 73 struct __cfaehm_try_resume_node * node); 66 74 67 75 // Check for a standard way to call fake deconstructors. 68 struct __cfaabi_ehm__cleanup_hook {}; 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 69 93 70 94 #ifdef __cforall 71 95 } 96 97 // Built-ins not visible in C. 98 #if defined(__cforall_builtins__) 99 100 // Not all the built-ins can be expressed in C. These can't be 101 // implemented in the .c file either so they all have to be inline. 102 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 *))defaultTerminationHandler 124 ); 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 *))defaultResumptionHandler 132 ); 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 72 150 #endif 151 152 #endif -
libcfa/src/executor.cfa
r3c64c668 r58fe85a 4 4 // buffer. 5 5 6 #include <bits/containers.hfa>7 6 #include <thread.hfa> 8 #include < stdio.h>7 #include <containers/list.hfa> 9 8 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 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 20 14 21 T * remove( Buffer( T ) & mutex buf) with(buf) {22 if ( queue.head != 0 ) wait( delay ); // no request to process ? => wait23 // return pop_head( queue ); 24 } // remove 25 } // distribution 15 void insert( Buffer(T) & mutex buf, T * elem ) with(buf) { 16 dlist( T, T ) * qptr = &queue; // workaround https://cforall.uwaterloo.ca/trac/ticket/166 17 insert_last( *qptr, *elem ); // insert element into buffer 18 signal( delay ); // restart 19 } // insert 26 20 27 struct WRequest { // client request, no return 28 void (* action)( void ); 29 WRequest * next; // intrusive queue field 21 T * remove( Buffer(T) & mutex buf ) with(buf) { 22 dlist( T, T ) * qptr = &queue; // workaround https://cforall.uwaterloo.ca/trac/ticket/166 23 // if ( (*qptr)`is_empty ) wait( delay ); // no request to process ? => wait 24 if ( (*qptr)`is_empty ) return 0p; // no request to process ? => wait 25 return &pop_first( *qptr ); 26 } // remove 27 } // forall 28 29 struct WRequest { // client request, no return 30 void (* action)( void ); 31 DLISTED_MGD_IMPL_IN(WRequest) 30 32 }; // WRequest 33 DLISTED_MGD_IMPL_OUT(WRequest) 31 34 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; } 35 void ?{}( WRequest & req ) with(req) { action = 0; } 36 void ?{}( WRequest & req, void (* action)( void ) ) with(req) { req.action = action; } 35 37 bool stop( WRequest & req ) { return req.action == 0; } 36 38 void doit( WRequest & req ) { req.action(); } 37 39 38 // Each worker has its own work buffer to reduce contention between client and server. Hence, work requests arrive and39 // a re distributed into buffers in a roughly round-robin order.40 // Each worker has its own set (when requests buffers > workers) of work buffers to reduce contention between client 41 // and server, where work requests arrive and are distributed into buffers in a roughly round-robin order. 40 42 41 43 thread Worker { 42 Buffer( WRequest ) * requests; 43 unsigned int start, range; 44 Buffer(WRequest) * requests; 45 WRequest * request; 46 unsigned int start, range; 44 47 }; // Worker 45 48 46 49 void main( Worker & w ) with(w) { 47 for ( int i = 0;; i = (i + 1) % range ) {48 WRequest *request = remove( requests[i + start] );49 if ( ! request ) { yield(); continue; }50 if ( stop( *request ) ) break;51 doit( *request );52 delete( request );53 } // for50 for ( int i = 0;; i = (i + 1) % range ) { 51 request = remove( requests[i + start] ); 52 if ( ! request ) { yield(); continue; } 53 if ( stop( *request ) ) break; 54 doit( *request ); 55 delete( request ); 56 } // for 54 57 } // Worker::main 55 58 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];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]; 59 62 } // ?{} 60 63 64 WRequest * current_request( Worker & worker ) { return worker.request; } 65 61 66 struct Executor { 62 cluster * cluster; // if workers execute on separate cluster 63 processor ** processors; // array of virtual processors adding parallelism for workers 64 Buffer( WRequest ) * requests; // list of work requests 65 Worker ** workers; // array of workers executing work requests 66 unsigned int nprocessors, nworkers, nmailboxes; // number of mailboxes/workers/processor tasks 67 bool sepClus; // use same or separate cluster for executor 67 cluster * cluster; // if workers execute on separate cluster 68 processor ** processors; // array of virtual processors adding parallelism for workers 69 Buffer(WRequest) * requests; // list of work requests 70 Worker ** workers; // array of workers executing work requests 71 unsigned int nprocessors, nworkers, nrqueues; // number of processors/threads/request queues 72 bool sepClus; // use same or separate cluster for executor 73 unsigned int next; // demultiplexed across worker buffers 68 74 }; // Executor 69 75 70 static thread_local unsigned int next; // demultiplexed across worker buffers71 76 unsigned int tickets( Executor & ex ) with(ex) { 72 //return uFetchAdd( next, 1 ) % nmailboxes;73 return next++ % nmailboxes;// no locking, interference randomizes77 //return uFetchAdd( next, 1 ) % nrqueues; 78 return next++ % nrqueues; // no locking, interference randomizes 74 79 } // tickets 75 80 76 void ?{}( Executor & ex, unsigned int np, unsigned int nw, unsigned int n m, 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 );81 void ?{}( Executor & ex, unsigned int np, unsigned int nw, unsigned int nr, bool sc = false ) with(ex) { 82 [nprocessors, nworkers, nrqueues, sepClus] = [np, nw, nr, sc]; 83 assert( nrqueues >= nworkers ); 84 cluster = sepClus ? new( "Executor" ) : active_cluster(); 85 processors = aalloc( nprocessors ); 86 requests = anew( nrqueues ); 87 workers = aalloc( nworkers ); 83 88 84 for ( i; nprocessors ) {85 processors[ i] = new( *cluster );86 } // for89 for ( i; nprocessors ) { 90 processors[i] = new( *cluster ); 91 } // for 87 92 88 unsigned int reqPerWorker = nmailboxes / nworkers, extras = nmailboxes % nworkers; 89 for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker + ( i < extras ? 1 : 0 ) ) { 90 workers[ i ] = new( cluster, requests, step, reqPerWorker + ( i < extras ? 1 : 0 ) ); 91 } // for 93 unsigned int reqPerWorker = nrqueues / nworkers, extras = nrqueues % nworkers; 94 // for ( unsigned int i = 0, start = 0, range; i < nworkers; i += 1, start += range ) { 95 for ( i; nworkers : start; 0u ~ @ ~ range : range; ) { 96 range = reqPerWorker + ( i < extras ? 1 : 0 ); 97 workers[i] = new( cluster, requests, start, range ); 98 } // for 92 99 } // ?{} 93 100 94 101 void ?{}( Executor & ex, unsigned int nprocessors, unsigned int nworkers, bool sepClus = false ) { 95 ex{ nprocessors, nworkers, nworkers, sepClus };102 ex{ nprocessors, nworkers, nworkers, sepClus }; 96 103 } 97 104 void ?{}( Executor & ex, unsigned int nprocessors, bool sepClus = false ) { 98 ex{ nprocessors, nprocessors, nprocessors, sepClus };105 ex{ nprocessors, nprocessors, nprocessors, sepClus }; 99 106 } 100 void ?{}( Executor & ex ) { // special for current cluster101 ex{ 0, active_cluster()->nprocessors, false };107 void ?{}( Executor & ex ) { // special for current cluster, no processors added 108 ex{ 0, active_cluster()->nprocessors, false }; 102 109 } 103 110 void ^?{}( Executor & ex ) with(ex) { 104 // Add one sentinel per worker to stop them. Since in destructor, no new work should be queued. Cannot combine next105 // two loops and only have a single sentinel because workers arrive in arbitrary order, so worker1 may take the 106 //single sentinel while waiting for worker 0 to end.111 // Add one sentinel per worker to stop them. Since in destructor, no new external work should be queued. Cannot 112 // combine next two loops and only have a single sentinel because workers arrive in arbitrary order, so worker1 may 113 // take the single sentinel while waiting for worker 0 to end. 107 114 108 WRequest sentinel[nworkers];109 unsigned int reqPerWorker = nmailboxes / nworkers;110 for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker ) {111 insert( requests[step], &sentinel[i] );// force eventually termination112 } // for113 for ( i; nworkers ) {114 delete( workers[ i] );115 } // for116 for ( i; nprocessors ) {117 delete( processors[ i] );118 } // for115 WRequest sentinel[nworkers]; 116 unsigned int reqPerWorker = nrqueues / nworkers; 117 for ( unsigned int i = 0, step = 0; i < nworkers; i += 1, step += reqPerWorker ) { 118 insert( requests[step], &sentinel[i] ); // force eventually termination 119 } // for 120 for ( i; nworkers ) { 121 delete( workers[i] ); 122 } // for 123 for ( i; nprocessors ) { 124 delete( processors[i] ); 125 } // for 119 126 120 delete( workers ); 121 delete( requests ); 122 delete( processors ); 123 if ( sepClus ) { delete( cluster ); } 127 free( workers ); 128 // adelete( nrqueues, requests ); 129 for ( i; nrqueues ) ^?{}( requests[i] ); // FIX ME: problem with resolver 130 free( requests ); 131 free( processors ); 132 if ( sepClus ) { delete( cluster ); } 124 133 } // ^?{} 125 134 126 135 void send( Executor & ex, void (* action)( void ) ) { // asynchronous call, no return value 127 WRequest * node = new( action );128 insert( ex.requests[tickets( ex )], node );136 WRequest * node = new( action ); 137 insert( ex.requests[tickets( ex )], node ); 129 138 } // send 139 130 140 131 141 int counter = 0; 132 142 133 void work ie( void ) {134 __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST );135 //fprintf( stderr, "workie\n" );143 void work( void ) { 144 __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST ); 145 // fprintf( stderr, "workie\n" ); 136 146 } 137 147 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 ); 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 ); 147 160 } 148 161 149 162 // Local Variables: // 163 // tab-width: 4" // 150 164 // compile-command: "cfa executor.cfa" // 151 165 // End: // -
libcfa/src/fstream.cfa
r3c64c668 r58fe85a 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:01:01202013 // Update Count : 3 6312 // Last Modified On : Fri Jun 19 16:24:54 2020 13 // Update Count : 384 14 14 // 15 15 … … 26 26 27 27 28 // *********************************** ofstream ***********************************28 // *********************************** ofstream *********************************** 29 29 30 30 … … 123 123 #ifdef __CFA_DEBUG__ 124 124 if ( file == 0p ) { 125 abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 125 throw (Open_Failure){ os }; 126 // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 126 127 } // if 127 128 #endif // __CFA_DEBUG__ … … 134 135 135 136 void close( ofstream & os ) { 136 if ( (FILE *)(os.$file) == stdout || (FILE *)(os.$file) == stderr ) return; 137 if ( (FILE *)(os.$file) == 0p ) return; 138 if ( (FILE *)(os.$file) == (FILE *)stdout || (FILE *)(os.$file) == (FILE *)stderr ) return; 137 139 138 140 if ( fclose( (FILE *)(os.$file) ) == EOF ) { 139 141 abort | IO_MSG "close output" | nl | strerror( errno ); 140 142 } // if 143 os.$file = 0p; 141 144 } // close 142 145 … … 179 182 180 183 181 // *********************************** ifstream ***********************************184 // *********************************** ifstream *********************************** 182 185 183 186 … … 219 222 #ifdef __CFA_DEBUG__ 220 223 if ( file == 0p ) { 221 abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 224 throw (Open_Failure){ is }; 225 // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 222 226 } // if 223 227 #endif // __CFA_DEBUG__ … … 230 234 231 235 void close( ifstream & is ) { 232 if ( (FILE *)(is.$file) == stdin ) return; 236 if ( (FILE *)(is.$file) == 0p ) return; 237 if ( (FILE *)(is.$file) == (FILE *)stdin ) return; 233 238 234 239 if ( fclose( (FILE *)(is.$file) ) == EOF ) { 235 240 abort | IO_MSG "close input" | nl | strerror( errno ); 236 241 } // if 242 is.$file = 0p; 237 243 } // close 238 244 … … 276 282 ifstream & sin = sinFile, & stdin = sinFile; 277 283 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 278 309 // Local Variables: // 279 310 // tab-width: 4 // -
libcfa/src/fstream.hfa
r3c64c668 r58fe85a 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Feb 17 08:29:23202013 // Update Count : 1 7512 // Last Modified On : Fri Jun 19 16:29:17 2020 13 // Update Count : 189 14 14 // 15 15 … … 17 17 18 18 #include "iostream.hfa" 19 #include <exception.hfa> 19 20 20 21 21 // *********************************** ofstream ***********************************22 // *********************************** ofstream *********************************** 22 23 23 24 … … 78 79 79 80 80 // *********************************** ifstream ***********************************81 // *********************************** ifstream *********************************** 81 82 82 83 … … 106 107 extern ifstream & sin, & stdin; // aliases 107 108 109 110 // *********************************** exceptions *********************************** 111 112 113 DATA_EXCEPTION(Open_Failure)( 114 union { 115 ofstream * ostream; 116 ifstream * istream; 117 }; 118 // TEMPORARY: need polymorphic exceptions 119 int tag; // 1 => ostream; 0 => istream 120 ); 121 122 void ?{}( Open_Failure & this, ofstream & ostream ); 123 void ?{}( Open_Failure & this, ifstream & istream ); 124 108 125 // Local Variables: // 109 126 // mode: c // -
libcfa/src/heap.cfa
r3c64c668 r58fe85a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // heap.c --7 // heap.cfa -- 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 : Tue Feb 4 10:04:51202013 // Update Count : 64812 // Last Modified On : Wed Dec 16 12:28:25 2020 13 // Update Count : 1023 14 14 // 15 15 16 16 #include <unistd.h> // sbrk, sysconf 17 #include <stdlib.h> // EXIT_FAILURE 17 18 #include <stdbool.h> // true, false 18 19 #include <stdio.h> // snprintf, fileno 19 20 #include <errno.h> // errno 20 21 #include <string.h> // memset, memcpy 21 extern "C" { 22 #include <limits.h> // ULONG_MAX 23 #include <malloc.h> // memalign, malloc_usable_size 22 24 #include <sys/mman.h> // mmap, munmap 23 } // extern "C" 24 25 // #comment TD : Many of these should be merged into math I believe 26 #include "bits/align.hfa" // libPow2 25 26 #include "bits/align.hfa" // libAlign 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 "stdlib.hfa" // bsearchl 31 #include "malloc.h" 32 33 #define MIN(x, y) (y > x ? x : y) 30 #include "math.hfa" // ceiling 31 #include "bitmanip.hfa" // is_pow2, ceiling2 34 32 35 33 static bool traceHeap = false; … … 74 72 // Define the default extension heap amount in units of bytes. When the uC++ supplied heap reaches the brk address, 75 73 // the brk address is extended by the extension amount. 76 __CFA_DEFAULT_HEAP_EXPANSION__ = (1 * 1024 * 1024),74 __CFA_DEFAULT_HEAP_EXPANSION__ = (10 * 1024 * 1024), 77 75 78 76 // Define the mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; … … 91 89 92 90 #ifdef __CFA_DEBUG__ 93 static unsigned int allocFree;// running total of allocations minus frees91 static size_t allocUnfreed; // running total of allocations minus frees 94 92 95 93 static void prtUnfreed() { 96 if ( alloc Free!= 0 ) {94 if ( allocUnfreed != 0 ) { 97 95 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT. 98 96 char helpText[512]; 99 int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with % u(0x%x) bytes of storage allocated but not freed.\n"97 int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with %zu(0x%zx) bytes of storage allocated but not freed.\n" 100 98 "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n", 101 (long int)getpid(), alloc Free, allocFree); // always print the UNIX pid99 (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid 102 100 __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug 103 101 } // if … … 106 104 extern "C" { 107 105 void heapAppStart() { // called by __cfaabi_appready_startup 108 alloc Free= 0;106 allocUnfreed = 0; 109 107 } // heapAppStart 110 108 … … 118 116 119 117 // statically allocated variables => zero filled. 120 static size_t pageSize; // architecture pagesize 118 size_t __page_size; // architecture pagesize 119 int __map_prot; // common mmap/mprotect protection 121 120 static size_t heapExpand; // sbrk advance 122 121 static size_t mmapStart; // cross over point for mmap … … 127 126 #define LOCKFREE 1 128 127 #define BUCKETLOCK SPINLOCK 129 #if BUCKETLOCK == LOCKFREE 130 #include <uStackLF.h> 128 #if BUCKETLOCK == SPINLOCK 129 #elif BUCKETLOCK == LOCKFREE 130 #include <stackLockFree.hfa> 131 #else 132 #error undefined lock type for bucket lock 131 133 #endif // LOCKFREE 132 134 … … 136 138 137 139 struct HeapManager { 138 // struct FreeHeader; // forward declaration139 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 32_t padding; // unused, force home/blocksize to overlay alignment in fake header147 uint64_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) 151 // FreeHeader * home; // allocated block points back to home locations (must overlay alignment) 152 // 2nd low-order bit => zero filled 152 153 void * home; // allocated block points back to home locations (must overlay alignment) 153 154 size_t blockSize; // size for munmap (must overlay alignment) 154 #if BUCK LOCK == SPINLOCK155 #if BUCKETLOCK == SPINLOCK 155 156 Storage * next; // freed block points next freed block of same size 156 157 #endif // SPINLOCK 157 158 }; 159 size_t size; // allocation size in bytes 158 160 159 161 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_POINTER__ == 4 160 uint 32_t padding; // unused, force home/blocksize to overlay alignment in fake header162 uint64_t padding; // unused, force home/blocksize to overlay alignment in fake header 161 163 #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_POINTER__ == 4 162 164 }; 163 // future code 164 #if BUCKLOCK == LOCKFREE 165 Stack<Storage>::Link next; // freed block points next freed block of same size (double-wide) 165 #if BUCKETLOCK == LOCKFREE 166 Link(Storage) next; // freed block points next freed block of same size (double-wide) 166 167 #endif // LOCKFREE 167 168 }; 168 169 } real; // RealHeader 170 169 171 struct FakeHeader { 170 172 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 171 uint32_t alignment; // low-order bits of home/blockSize used for tricks173 uint32_t alignment; // 1st low-order bit => fake header & alignment 172 174 #endif // __ORDER_LITTLE_ENDIAN__ 173 175 … … 187 189 188 190 struct FreeHeader { 189 #if BUCK LOCK == SPINLOCK191 #if BUCKETLOCK == SPINLOCK 190 192 __spinlock_t lock; // must be first field for alignment 191 193 Storage * freeList; 192 #elif BUCKLOCK == LOCKFREE193 // future code194 StackLF<Storage> freeList;195 194 #else 196 #error undefined lock type for bucket lock197 #endif // SPINLOCK195 StackLF(Storage) freeList; 196 #endif // BUCKETLOCK 198 197 size_t blockSize; // size of allocations on this list 199 198 }; // FreeHeader … … 208 207 }; // HeapManager 209 208 209 #if BUCKETLOCK == LOCKFREE 210 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 } // distribution 215 #endif // LOCKFREE 216 210 217 static inline size_t getKey( const HeapManager.FreeHeader & freeheader ) { return freeheader.blockSize; } 211 218 … … 214 221 #define __STATISTICS__ 215 222 216 // Bucket size must be multiple of 16. 217 // Powers of 2 are common allocation sizes, so make powers of 2 generate the minimum required size. 223 // Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16. 224 // Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size. 225 // malloc(0) returns 0p, so no bucket is necessary for 0 bytes returning an address that can be freed. 218 226 static const unsigned int bucketSizes[] @= { // different bucket sizes 219 16 , 32, 48, 64 + sizeof(HeapManager.Storage), // 4220 96 , 112, 128 + sizeof(HeapManager.Storage), // 3227 16 + sizeof(HeapManager.Storage), 32 + sizeof(HeapManager.Storage), 48 + sizeof(HeapManager.Storage), 64 + sizeof(HeapManager.Storage), // 4 228 96 + sizeof(HeapManager.Storage), 112 + sizeof(HeapManager.Storage), 128 + sizeof(HeapManager.Storage), // 3 221 229 160, 192, 224, 256 + sizeof(HeapManager.Storage), // 4 222 230 320, 384, 448, 512 + sizeof(HeapManager.Storage), // 4 … … 236 244 }; 237 245 238 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );246 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" ); 239 247 240 248 #ifdef FASTLOOKUP … … 243 251 #endif // FASTLOOKUP 244 252 245 static int mmapFd = -1;// fake or actual fd for anonymous file253 static const off_t mmapFd = -1; // fake or actual fd for anonymous file 246 254 #ifdef __CFA_DEBUG__ 247 255 static bool heapBoot = 0; // detect recursion during boot 248 256 #endif // __CFA_DEBUG__ 257 258 // The constructor for heapManager is called explicitly in memory_startup. 249 259 static HeapManager heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing 250 260 … … 252 262 #ifdef __STATISTICS__ 253 263 // Heap statistics counters. 264 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; 268 static unsigned int calloc_calls; 269 static unsigned long long int calloc_storage; 270 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; 274 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; 278 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; 254 283 static unsigned long long int mmap_storage; 255 static unsigned int m map_calls;284 static unsigned int munmap_calls; 256 285 static unsigned long long int munmap_storage; 257 static unsigned int munmap_calls;286 static unsigned int sbrk_calls; 258 287 static unsigned long long int sbrk_storage; 259 static unsigned int sbrk_calls;260 static unsigned long long int malloc_storage;261 static unsigned int malloc_calls;262 static unsigned long long int free_storage;263 static unsigned int free_calls;264 static unsigned long long int calloc_storage;265 static unsigned int calloc_calls;266 static unsigned long long int memalign_storage;267 static unsigned int memalign_calls;268 static unsigned long long int cmemalign_storage;269 static unsigned int cmemalign_calls;270 static unsigned long long int realloc_storage;271 static unsigned int realloc_calls;272 288 // Statistics file descriptor (changed by malloc_stats_fd). 273 static int stat fd = STDERR_FILENO; // default stderr289 static int stat_fd = STDERR_FILENO; // default stderr 274 290 275 291 // Use "write" because streams may be shutdown when calls are made. 276 292 static void printStats() { 277 char helpText[ 512];293 char helpText[1024]; 278 294 __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText), 279 295 "\nHeap statistics:\n" 280 296 " malloc: calls %u / storage %llu\n" 297 " aalloc: calls %u / storage %llu\n" 281 298 " calloc: calls %u / storage %llu\n" 282 299 " memalign: calls %u / storage %llu\n" 300 " amemalign: calls %u / storage %llu\n" 283 301 " cmemalign: calls %u / storage %llu\n" 302 " resize: calls %u / storage %llu\n" 284 303 " realloc: calls %u / storage %llu\n" 285 304 " free: calls %u / storage %llu\n" … … 288 307 " sbrk: calls %u / storage %llu\n", 289 308 malloc_calls, malloc_storage, 309 aalloc_calls, aalloc_storage, 290 310 calloc_calls, calloc_storage, 291 311 memalign_calls, memalign_storage, 312 amemalign_calls, amemalign_storage, 292 313 cmemalign_calls, cmemalign_storage, 314 resize_calls, resize_storage, 293 315 realloc_calls, realloc_storage, 294 316 free_calls, free_storage, … … 300 322 301 323 static int printStatsXML( FILE * stream ) { // see malloc_info 302 char helpText[ 512];324 char helpText[1024]; 303 325 int len = snprintf( helpText, sizeof(helpText), 304 326 "<malloc version=\"1\">\n" … … 307 329 "</sizes>\n" 308 330 "<total type=\"malloc\" count=\"%u\" size=\"%llu\"/>\n" 331 "<total type=\"aalloc\" count=\"%u\" size=\"%llu\"/>\n" 309 332 "<total type=\"calloc\" count=\"%u\" size=\"%llu\"/>\n" 310 333 "<total type=\"memalign\" count=\"%u\" size=\"%llu\"/>\n" 334 "<total type=\"amemalign\" count=\"%u\" size=\"%llu\"/>\n" 311 335 "<total type=\"cmemalign\" count=\"%u\" size=\"%llu\"/>\n" 336 "<total type=\"resize\" count=\"%u\" size=\"%llu\"/>\n" 312 337 "<total type=\"realloc\" count=\"%u\" size=\"%llu\"/>\n" 313 338 "<total type=\"free\" count=\"%u\" size=\"%llu\"/>\n" … … 317 342 "</malloc>", 318 343 malloc_calls, malloc_storage, 344 aalloc_calls, aalloc_storage, 319 345 calloc_calls, calloc_storage, 320 346 memalign_calls, memalign_storage, 347 amemalign_calls, amemalign_storage, 321 348 cmemalign_calls, cmemalign_storage, 349 resize_calls, resize_storage, 322 350 realloc_calls, realloc_storage, 323 351 free_calls, free_storage, … … 332 360 333 361 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 // } // noMemory339 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 } // if345 } // checkAlign346 347 348 static inline bool setHeapExpand( size_t value ) {349 if ( heapExpand < pageSize ) return true;350 heapExpand = value;351 return false;352 } // setHeapExpand353 354 355 362 // thunk problem 356 363 size_t Bsearchl( unsigned int key, const unsigned int * vals, size_t dim ) { … … 369 376 370 377 static inline bool setMmapStart( size_t value ) { // true => mmapped, false => sbrk 371 if ( value < pageSize || bucketSizes[NoBucketSizes - 1] < value ) return true;378 if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false; 372 379 mmapStart = value; // set global 373 380 … … 376 383 assert( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 377 384 assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? 378 return false;385 return true; 379 386 } // setMmapStart 387 388 389 // <-------+----------------------------------------------------> bsize (bucket size) 390 // |header |addr 391 //================================================================================== 392 // align/offset | 393 // <-----------------<------------+-----------------------------> bsize (bucket size) 394 // |fake-header | addr 395 #define headerAddr( addr ) ((HeapManager.Storage.Header *)( (char *)addr - sizeof(HeapManager.Storage) )) 396 #define realHeader( header ) ((HeapManager.Storage.Header *)((char *)header - header->kind.fake.offset)) 397 398 // <-------<<--------------------- dsize ---------------------->> bsize (bucket size) 399 // |header |addr 400 //================================================================================== 401 // align/offset | 402 // <------------------------------<<---------- dsize --------->>> bsize (bucket size) 403 // |fake-header |addr 404 #define dataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header )) 405 406 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 380 412 381 413 … … 391 423 static inline void fakeHeader( HeapManager.Storage.Header *& header, size_t & alignment ) { 392 424 if ( unlikely( (header->kind.fake.alignment & 1) == 1 ) ) { // fake header ? 393 size_t offset = header->kind.fake.offset;394 425 alignment = header->kind.fake.alignment & -2; // remove flag from value 395 426 #ifdef __CFA_DEBUG__ 396 427 checkAlign( alignment ); // check alignment 397 428 #endif // __CFA_DEBUG__ 398 header = (HeapManager.Storage.Header *)((char *)header - offset); 429 header = realHeader( header ); // backup from fake to real header 430 } else { 431 alignment = libAlign(); // => no fake header 399 432 } // if 400 433 } // fakeHeader 401 434 402 435 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 ) { 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 ) { 421 438 header = headerAddr( addr ); 422 439 423 if ( unlikely( heapEnd < addr ) ) {// mmapped ?440 if ( unlikely( addr < heapBegin || heapEnd < addr ) ) { // mmapped ? 424 441 fakeHeader( header, alignment ); 425 442 size = header->kind.real.blockSize & -3; // mmap size … … 428 445 429 446 #ifdef __CFA_DEBUG__ 430 checkHeader( addr < heapBegin ||header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ?447 checkHeader( header < (HeapManager.Storage.Header *)heapBegin, name, addr ); // bad low address ? 431 448 #endif // __CFA_DEBUG__ 432 449 … … 449 466 } // headers 450 467 451 452 static inline void * extend( size_t size ) with ( heapManager ) { 468 #ifdef __CFA_DEBUG__ 469 #if __SIZEOF_POINTER__ == 4 470 #define MASK 0xdeadbeef 471 #else 472 #define MASK 0xdeadbeefdeadbeef 473 #endif 474 #define STRIDE size_t 475 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 ) { 453 490 lock( extlock __cfaabi_dbg_ctx2 ); 454 491 ptrdiff_t rem = heapRemaining - size; … … 456 493 // If the size requested is bigger than the current remaining storage, increase the size of the heap. 457 494 458 size_t increase = libCeiling( size > heapExpand ? size : heapExpand, libAlign() ); 459 if ( sbrk( increase ) == (void *)-1 ) { 495 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size ); 496 // Do not call abort or strerror( errno ) as they may call malloc. 497 if ( sbrk( increase ) == (void *)-1 ) { // failed, no memory ? 460 498 unlock( extlock ); 461 errno = ENOMEM; 462 return 0p; 499 __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size ); 500 _exit( EXIT_FAILURE ); 501 } // if 502 if ( mprotect( (char *)heapEnd + heapRemaining, increase, __map_prot ) ) { 503 unlock( extlock ); 504 __cfaabi_bits_print_nolock( STDERR_FILENO, "extend() : internal error, mprotect failure, heapEnd:%p size:%zd, errno:%d.\n", heapEnd, increase, errno ); 505 _exit( EXIT_FAILURE ); 463 506 } // if 464 507 #ifdef __STATISTICS__ … … 468 511 #ifdef __CFA_DEBUG__ 469 512 // Set new memory to garbage so subsequent uninitialized usages might fail. 470 memset( (char *)heapEnd + heapRemaining, '\377', increase ); 513 memset( (char *)heapEnd + heapRemaining, '\xde', increase ); 514 //Memset( (char *)heapEnd + heapRemaining, increase ); 471 515 #endif // __CFA_DEBUG__ 472 516 rem = heapRemaining + increase - size; … … 481 525 482 526 483 static inline void * doMalloc( size_t size ) with ( heapManager ) {527 static inline void * doMalloc( size_t size ) with( heapManager ) { 484 528 HeapManager.Storage * block; // pointer to new block of storage 485 529 … … 487 531 // along with the block and is a multiple of the alignment size. 488 532 489 if ( unlikely( size > ~0ul- sizeof(HeapManager.Storage) ) ) return 0p;533 if ( unlikely( size > ULONG_MAX - sizeof(HeapManager.Storage) ) ) return 0p; 490 534 size_t tsize = size + sizeof(HeapManager.Storage); 491 535 if ( likely( tsize < mmapStart ) ) { // small size => sbrk … … 497 541 posn = Bsearchl( (unsigned int)tsize, bucketSizes, (size_t)maxBucketsUsed ); 498 542 HeapManager.FreeHeader * freeElem = &freeLists[posn]; 499 // #ifdef FASTLOOKUP 500 // if ( tsize < LookupSizes ) 501 // freeElem = &freeLists[lookup[tsize]]; 502 // else 503 // #endif // FASTLOOKUP 504 // freeElem = bsearchl( tsize, freeLists, (size_t)maxBucketsUsed ); // binary search 505 // HeapManager.FreeHeader * freeElem = 506 // #ifdef FASTLOOKUP 507 // tsize < LookupSizes ? &freeLists[lookup[tsize]] : 508 // #endif // FASTLOOKUP 509 // bsearchl( tsize, freeLists, (size_t)maxBucketsUsed ); // binary search 510 assert( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ? 511 assert( tsize <= freeElem->blockSize ); // search failure ? 543 verify( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ? 544 verify( tsize <= freeElem->blockSize ); // search failure ? 512 545 tsize = freeElem->blockSize; // total space needed for request 513 546 514 547 // Spin until the lock is acquired for this particular size of block. 515 548 516 #if defined( SPINLOCK )549 #if BUCKETLOCK == SPINLOCK 517 550 lock( freeElem->lock __cfaabi_dbg_ctx2 ); 518 551 block = freeElem->freeList; // remove node from stack 519 552 #else 520 block = freeElem->freeList.pop();521 #endif // SPINLOCK553 block = pop( freeElem->freeList ); 554 #endif // BUCKETLOCK 522 555 if ( unlikely( block == 0p ) ) { // no free block ? 523 #if defined( SPINLOCK )556 #if BUCKETLOCK == SPINLOCK 524 557 unlock( freeElem->lock ); 525 #endif // SPINLOCK558 #endif // BUCKETLOCK 526 559 527 560 // Freelist for that size was empty, so carve it out of the heap if there's enough left, or get some more … … 529 562 530 563 block = (HeapManager.Storage *)extend( tsize ); // mutual exclusion on call 531 if ( unlikely( block == 0p ) ) return 0p; 532 #if defined( SPINLOCK ) 564 #if BUCKETLOCK == SPINLOCK 533 565 } else { 534 566 freeElem->freeList = block->header.kind.real.next; 535 567 unlock( freeElem->lock ); 536 #endif // SPINLOCK568 #endif // BUCKETLOCK 537 569 } // if 538 570 539 571 block->header.kind.real.home = freeElem; // pointer back to free list of apropriate size 540 572 } else { // large size => mmap 541 if ( unlikely( size > ~0ul - pageSize ) ) return 0p;542 tsize = libCeiling( tsize, pageSize ); // must be multiple of page size573 if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p; 574 tsize = ceiling2( tsize, __page_size ); // must be multiple of page size 543 575 #ifdef __STATISTICS__ 544 576 __atomic_add_fetch( &mmap_calls, 1, __ATOMIC_SEQ_CST ); 545 577 __atomic_add_fetch( &mmap_storage, tsize, __ATOMIC_SEQ_CST ); 546 578 #endif // __STATISTICS__ 547 block = (HeapManager.Storage *)mmap( 0, tsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 ); 548 if ( block == (HeapManager.Storage *)MAP_FAILED ) { 579 580 block = (HeapManager.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 ); 581 if ( block == (HeapManager.Storage *)MAP_FAILED ) { // failed ? 582 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory 549 583 // Do not call strerror( errno ) as it may call malloc. 550 abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu err or:%d.", &heapManager, tsize, errno );551 } // if584 abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu errno:%d.", &heapManager, tsize, errno ); 585 } //if 552 586 #ifdef __CFA_DEBUG__ 553 587 // Set new memory to garbage so subsequent uninitialized usages might fail. 554 memset( block, '\377', tsize ); 588 memset( block, '\xde', tsize ); 589 //Memset( block, tsize ); 555 590 #endif // __CFA_DEBUG__ 556 591 block->header.kind.real.blockSize = tsize; // storage size for munmap 557 592 } // if 558 593 594 block->header.kind.real.size = size; // store allocation size 559 595 void * addr = &(block->data); // adjust off header to user bytes 596 verify( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ? 560 597 561 598 #ifdef __CFA_DEBUG__ 562 assert( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ? 563 __atomic_add_fetch( &allocFree, tsize, __ATOMIC_SEQ_CST ); 599 __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST ); 564 600 if ( traceHeap() ) { 565 601 enum { BufferSize = 64 }; 566 602 char helpText[BufferSize]; 567 603 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 );569 604 __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug 570 605 } // if … … 575 610 576 611 577 static inline void doFree( void * addr ) with ( heapManager ) {612 static inline void doFree( void * addr ) with( heapManager ) { 578 613 #ifdef __CFA_DEBUG__ 579 614 if ( unlikely( heapManager.heapBegin == 0p ) ) { … … 592 627 #endif // __STATISTICS__ 593 628 if ( munmap( header, size ) == -1 ) { 594 #ifdef __CFA_DEBUG__595 629 abort( "Attempt to deallocate storage %p not allocated or with corrupt header.\n" 596 630 "Possible cause is invalid pointer.", 597 631 addr ); 598 #endif // __CFA_DEBUG__599 632 } // if 600 633 } else { 601 634 #ifdef __CFA_DEBUG__ 602 635 // Set free memory to garbage so subsequent usages might fail. 603 memset( ((HeapManager.Storage *)header)->data, '\377', freeElem->blockSize - sizeof( HeapManager.Storage ) ); 636 memset( ((HeapManager.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( HeapManager.Storage ) ); 637 //Memset( ((HeapManager.Storage *)header)->data, freeElem->blockSize - sizeof( HeapManager.Storage ) ); 604 638 #endif // __CFA_DEBUG__ 605 639 … … 607 641 free_storage += size; 608 642 #endif // __STATISTICS__ 609 #if defined( SPINLOCK )643 #if BUCKETLOCK == SPINLOCK 610 644 lock( freeElem->lock __cfaabi_dbg_ctx2 ); // acquire spin lock 611 645 header->kind.real.next = freeElem->freeList; // push on stack … … 613 647 unlock( freeElem->lock ); // release spin lock 614 648 #else 615 freeElem->freeList.push(*(HeapManager.Storage *)header );616 #endif // SPINLOCK649 push( freeElem->freeList, *(HeapManager.Storage *)header ); 650 #endif // BUCKETLOCK 617 651 } // if 618 652 619 653 #ifdef __CFA_DEBUG__ 620 __atomic_add_fetch( &alloc Free, -size, __ATOMIC_SEQ_CST );654 __atomic_add_fetch( &allocUnfreed, -size, __ATOMIC_SEQ_CST ); 621 655 if ( traceHeap() ) { 622 enum { BufferSize = 64 }; 623 char helpText[BufferSize]; 656 char helpText[64]; 624 657 int len = snprintf( helpText, sizeof(helpText), "Free( %p ) size:%zu\n", addr, size ); 625 658 __cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug … … 629 662 630 663 631 size_t prtFree( HeapManager & manager ) with ( manager ) {664 size_t prtFree( HeapManager & manager ) with( manager ) { 632 665 size_t total = 0; 633 666 #ifdef __STATISTICS__ … … 641 674 #endif // __STATISTICS__ 642 675 643 #if defined( SPINLOCK )676 #if BUCKETLOCK == SPINLOCK 644 677 for ( HeapManager.Storage * p = freeLists[i].freeList; p != 0p; p = p->header.kind.real.next ) { 645 678 #else 646 for ( HeapManager.Storage * p = freeLists[i].freeList.top(); p != 0p; p = p->header.kind.real.next.top ) { 647 #endif // SPINLOCK 679 for(;;) { 680 // for ( HeapManager.Storage * p = top( freeLists[i].freeList ); p != 0p; p = (p)`next->top ) { 681 // for ( HeapManager.Storage * p = top( freeLists[i].freeList ); p != 0p; /* p = getNext( p )->top */) { 682 // HeapManager.Storage * temp = p->header.kind.real.next.top; // FIX ME: direct assignent fails, initialization works` 683 // typeof(p) temp = (( p )`next)->top; // FIX ME: direct assignent fails, initialization works` 684 // p = temp; 685 #endif // BUCKETLOCK 648 686 total += size; 649 687 #ifdef __STATISTICS__ … … 665 703 666 704 667 static void ?{}( HeapManager & manager ) with ( manager ) { 668 pageSize = sysconf( _SC_PAGESIZE ); 705 static void ?{}( HeapManager & manager ) with( manager ) { 706 __page_size = sysconf( _SC_PAGESIZE ); 707 __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 669 708 670 709 for ( unsigned int i = 0; i < NoBucketSizes; i += 1 ) { // initialize the free lists … … 680 719 #endif // FASTLOOKUP 681 720 682 if ( setMmapStart( default_mmap_start() ) ) {721 if ( ! setMmapStart( default_mmap_start() ) ) { 683 722 abort( "HeapManager : internal error, mmap start initialization failure." ); 684 723 } // if … … 686 725 687 726 char * end = (char *)sbrk( 0 ); 688 sbrk( (char *)libCeiling( (long unsigned int)end, libAlign() ) - end ); // move start of heap to multiple of alignment 689 heapBegin = heapEnd = sbrk( 0 ); // get new start point 727 heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, __page_size ) - end ); // move start of heap to multiple of alignment 690 728 } // HeapManager 691 729 … … 695 733 if ( traceHeapTerm() ) { 696 734 printStats(); 697 // if ( prtfree() ) prtFree( heapManager, true );735 // prtUnfreed() called in heapAppStop() 698 736 } // if 699 737 #endif // __STATISTICS__ … … 704 742 void memory_startup( void ) { 705 743 #ifdef __CFA_DEBUG__ 706 if ( unlikely( heapBoot ) ) { // check for recursion during system boot 707 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT. 744 if ( heapBoot ) { // check for recursion during system boot 708 745 abort( "boot() : internal error, recursively invoked during system boot." ); 709 746 } // if … … 711 748 #endif // __CFA_DEBUG__ 712 749 713 // assert( heapManager.heapBegin != 0 );750 //verify( heapManager.heapBegin != 0 ); 714 751 //heapManager{}; 715 if ( heapManager.heapBegin == 0p ) heapManager{}; 752 if ( heapManager.heapBegin == 0p ) heapManager{}; // sanity check 716 753 } // memory_startup 717 754 … … 723 760 724 761 static inline void * mallocNoStats( size_t size ) { // necessary for malloc statistics 725 //assert( heapManager.heapBegin != 0 ); 726 if ( unlikely( heapManager.heapBegin == 0p ) ) heapManager{}; // called before memory_startup ? 727 void * addr = doMalloc( size ); 728 if ( unlikely( addr == 0p ) ) errno = ENOMEM; // POSIX 729 return addr; 762 verify( heapManager.heapBegin != 0p ); // called before memory_startup ? 763 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 764 765 #if __SIZEOF_POINTER__ == 8 766 verify( size < ((typeof(size_t))1 << 48) ); 767 #endif // __SIZEOF_POINTER__ == 8 768 return doMalloc( size ); 730 769 } // mallocNoStats 731 770 732 771 733 static inline void * callocNoStats( size_t noOfElems, size_t elemSize ) { 734 size_t size = noOfElems * elemSize; 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 735 775 char * addr = (char *)mallocNoStats( size ); 736 if ( unlikely( addr == 0p ) ) return 0p;737 776 738 777 HeapManager.Storage.Header * header; 739 778 HeapManager.FreeHeader * freeElem; 740 779 size_t bsize, alignment; 741 bool mapped __attribute__(( unused )) = headers( "calloc", addr, header, freeElem, bsize, alignment );742 780 #ifndef __CFA_DEBUG__ 781 bool mapped = 782 #endif // __CFA_DEBUG__ 783 headers( "calloc", addr, header, freeElem, bsize, alignment ); 784 #ifndef __CFA_DEBUG__ 785 743 786 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. 744 787 if ( ! mapped ) 745 788 #endif // __CFA_DEBUG__ 746 // Zero entire data space even when > than size => realloc without a new allocation and zero fill works. 747 // <-------00000000000000000000000000000000000000000000000000000> bsize (bucket size) 789 // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined 748 790 // `-header`-addr `-size 749 memset( addr, '\0', bsize - sizeof(HeapManager.Storage) );// set to zeros791 memset( addr, '\0', size ); // set to zeros 750 792 751 793 header->kind.real.blockSize |= 2; // mark as zero filled … … 754 796 755 797 756 static inline void * memalignNoStats( size_t alignment, size_t size ) { // necessary for malloc statistics 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 757 801 #ifdef __CFA_DEBUG__ 758 802 checkAlign( alignment ); // check alignment … … 772 816 // add sizeof(Storage) for fake header 773 817 char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(HeapManager.Storage) ); 774 if ( unlikely( addr == 0p ) ) return addr;775 818 776 819 // address in the block of the "next" alignment address 777 char * user = (char *) libCeiling( (uintptr_t)(addr + sizeof(HeapManager.Storage)), alignment );820 char * user = (char *)ceiling2( (uintptr_t)(addr + sizeof(HeapManager.Storage)), alignment ); 778 821 779 822 // address of header from malloc 780 823 HeapManager.Storage.Header * realHeader = headerAddr( addr ); 824 realHeader->kind.real.size = size; // correct size to eliminate above alignment offset 781 825 // address of fake header * before* the alignment location 782 826 HeapManager.Storage.Header * fakeHeader = headerAddr( user ); … … 790 834 791 835 792 static inline void * cmemalignNoStats( size_t alignment, size_t noOfElems, size_t elemSize ) { 793 size_t size = noOfElems * elemSize; 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 794 839 char * addr = (char *)memalignNoStats( alignment, size ); 795 if ( unlikely( addr == 0p ) ) return 0p; 840 796 841 HeapManager.Storage.Header * header; 797 842 HeapManager.FreeHeader * freeElem; 798 843 size_t bsize; 799 bool mapped __attribute__(( unused )) = headers( "cmemalign", addr, header, freeElem, bsize, alignment );800 844 #ifndef __CFA_DEBUG__ 845 bool mapped = 846 #endif // __CFA_DEBUG__ 847 headers( "cmemalign", addr, header, freeElem, bsize, alignment ); 848 801 849 // 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__ 802 851 if ( ! mapped ) 803 852 #endif // __CFA_DEBUG__ 804 memset( addr, '\0', dataStorage( bsize, addr, header ) ); // set to zeros 805 header->kind.real.blockSize |= 2; // mark as zero filled 806 853 // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined 854 // `-header`-addr `-size 855 memset( addr, '\0', size ); // set to zeros 856 857 header->kind.real.blockSize |= 2; // mark as zero filled 807 858 return addr; 808 859 } // cmemalignNoStats 809 860 810 861 811 // supported mallopt options812 #ifndef M_MMAP_THRESHOLD813 #define M_MMAP_THRESHOLD (-1)814 #endif // M_TOP_PAD815 #ifndef M_TOP_PAD816 #define M_TOP_PAD (-2)817 #endif // M_TOP_PAD818 819 820 862 extern "C" { 821 // The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not 822 // initialized. If size is 0, then malloc() returns either 0p, or a unique pointer value that can later be 823 // successfully passed to free(). 863 // Allocates size bytes and returns a pointer to the allocated memory. The contents are undefined. If size is 0, 864 // then malloc() returns a unique pointer value that can later be successfully passed to free(). 824 865 void * malloc( size_t size ) { 825 866 #ifdef __STATISTICS__ … … 831 872 } // malloc 832 873 833 // The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to 834 // the allocated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either 0p, or a 835 // unique pointer value that can later be successfully passed to free(). 836 void * calloc( size_t noOfElems, size_t elemSize ) { 874 875 // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes. 876 void * aalloc( size_t dim, size_t elemSize ) { 877 size_t size = dim * elemSize; 878 #ifdef __STATISTICS__ 879 __atomic_add_fetch( &aalloc_calls, 1, __ATOMIC_SEQ_CST ); 880 __atomic_add_fetch( &aalloc_storage, size, __ATOMIC_SEQ_CST ); 881 #endif // __STATISTICS__ 882 883 return mallocNoStats( size ); 884 } // aalloc 885 886 887 // Same as aalloc() with memory set to zero. 888 void * calloc( size_t dim, size_t elemSize ) { 837 889 #ifdef __STATISTICS__ 838 890 __atomic_add_fetch( &calloc_calls, 1, __ATOMIC_SEQ_CST ); 839 __atomic_add_fetch( &calloc_storage, noOfElems* elemSize, __ATOMIC_SEQ_CST );840 #endif // __STATISTICS__ 841 842 return callocNoStats( noOfElems, elemSize );891 __atomic_add_fetch( &calloc_storage, dim * elemSize, __ATOMIC_SEQ_CST ); 892 #endif // __STATISTICS__ 893 894 return callocNoStats( dim, elemSize ); 843 895 } // calloc 844 896 845 // The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be 846 // unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size 847 // is larger than the old size, the added memory will not be initialized. If ptr is 0p, then the call is 848 // equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not 0p, then the call 849 // is equivalent to free(ptr). Unless ptr is 0p, it must have been returned by an earlier call to malloc(), 850 // calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done. 851 void * realloc( void * oaddr, size_t size ) { 852 #ifdef __STATISTICS__ 853 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 897 898 // Change the size of the memory block pointed to by oaddr to size bytes. The contents are undefined. If oaddr is 899 // 0p, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and oaddr is 900 // not 0p, then the call is equivalent to free(oaddr). Unless oaddr is 0p, it must have been returned by an earlier 901 // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done. 902 void * resize( void * oaddr, size_t size ) { 903 #ifdef __STATISTICS__ 904 __atomic_add_fetch( &resize_calls, 1, __ATOMIC_SEQ_CST ); 854 905 #endif // __STATISTICS__ 855 906 856 907 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 857 if ( unlikely( size == 0 ) ) { free( oaddr ); return mallocNoStats( size ); } // special cases 858 if ( unlikely( oaddr == 0p ) ) return mallocNoStats( size ); 908 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 909 if ( unlikely( oaddr == 0p ) ) { 910 #ifdef __STATISTICS__ 911 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 912 #endif // __STATISTICS__ 913 return mallocNoStats( size ); 914 } // if 859 915 860 916 HeapManager.Storage.Header * header; 861 917 HeapManager.FreeHeader * freeElem; 862 size_t bsize, oalign = 0; 918 size_t bsize, oalign; 919 headers( "resize", oaddr, header, freeElem, bsize, oalign ); 920 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 921 922 // same size, DO NOT preserve STICKY PROPERTIES. 923 if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size 924 header->kind.real.blockSize &= -2; // no alignment and turn off 0 fill 925 header->kind.real.size = size; // reset allocation size 926 return oaddr; 927 } // if 928 929 #ifdef __STATISTICS__ 930 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 931 #endif // __STATISTICS__ 932 933 // change size, DO NOT preserve STICKY PROPERTIES. 934 free( oaddr ); 935 return mallocNoStats( size ); // create new area 936 } // resize 937 938 939 // Same as resize() but the contents are unchanged in the range from the start of the region up to the minimum of 940 // the old and new sizes. 941 void * realloc( void * oaddr, size_t size ) { 942 #ifdef __STATISTICS__ 943 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 944 #endif // __STATISTICS__ 945 946 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 947 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 948 if ( unlikely( oaddr == 0p ) ) { 949 #ifdef __STATISTICS__ 950 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 951 #endif // __STATISTICS__ 952 return mallocNoStats( size ); 953 } // if 954 955 HeapManager.Storage.Header * header; 956 HeapManager.FreeHeader * freeElem; 957 size_t bsize, oalign; 863 958 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 864 959 865 960 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 866 if ( size <= odsize && odsize <= size * 2 ) { // allow up to 50% wasted storage in smaller size 867 // Do not know size of original allocation => cannot do 0 fill for any additional space because do not know 868 // where to start filling, i.e., do not overwrite existing values in space. 869 // 870 // This case does not result in a new profiler entry because the previous one still exists and it must match with 871 // the free for this memory. Hence, this realloc does not appear in the profiler output. 961 size_t osize = header->kind.real.size; // old allocation size 962 bool ozfill = (header->kind.real.blockSize & 2); // old allocation zero filled 963 if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage 964 header->kind.real.size = size; // reset allocation size 965 if ( unlikely( ozfill ) && size > osize ) { // previous request zero fill and larger ? 966 memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage 967 } // if 872 968 return oaddr; 873 969 } // if 874 970 875 971 #ifdef __STATISTICS__ 876 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST );972 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 877 973 #endif // __STATISTICS__ 878 974 … … 880 976 881 977 void * naddr; 882 if ( unlikely( oalign != 0 ) ) { // previous request memalign? 883 if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill 884 naddr = cmemalignNoStats( oalign, 1, size ); // create new aligned area 885 } else { 886 naddr = memalignNoStats( oalign, size ); // create new aligned area 978 if ( likely( oalign == libAlign() ) ) { // previous request not aligned ? 979 naddr = mallocNoStats( size ); // create new area 980 } else { 981 naddr = memalignNoStats( oalign, size ); // create new aligned area 982 } // if 983 984 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 985 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 986 free( oaddr ); 987 988 if ( unlikely( ozfill ) ) { // previous request zero fill ? 989 header->kind.real.blockSize |= 2; // mark new request as zero filled 990 if ( size > osize ) { // previous request larger ? 991 memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage 887 992 } // if 888 } else { 889 if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill 890 naddr = callocNoStats( 1, size ); // create new area 891 } else { 892 naddr = mallocNoStats( size ); // create new area 893 } // if 894 } // if 895 if ( unlikely( naddr == 0p ) ) return 0p; 896 897 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 898 size_t ndsize = dataStorage( bsize, naddr, header ); // data storage avilable in bucket 899 // To preserve prior fill, the entire bucket must be copied versus the size. 900 memcpy( naddr, oaddr, MIN( odsize, ndsize ) ); // copy bytes 901 free( oaddr ); 993 } // if 902 994 return naddr; 903 995 } // realloc 904 996 905 // The obsolete function memalign() allocates size bytes and returns a pointer to the allocated memory. The memory 906 // address will be a multiple of alignment, which must be a power of two.997 998 // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete) 907 999 void * memalign( size_t alignment, size_t size ) { 908 1000 #ifdef __STATISTICS__ … … 915 1007 916 1008 917 // The cmemalign() function is the same as calloc() with memory alignment. 918 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize ) { 1009 // Same as aalloc() with memory alignment. 1010 void * amemalign( size_t alignment, size_t dim, size_t elemSize ) { 1011 size_t size = dim * elemSize; 919 1012 #ifdef __STATISTICS__ 920 1013 __atomic_add_fetch( &cmemalign_calls, 1, __ATOMIC_SEQ_CST ); 921 __atomic_add_fetch( &cmemalign_storage, noOfElems * elemSize, __ATOMIC_SEQ_CST ); 922 #endif // __STATISTICS__ 923 924 return cmemalignNoStats( alignment, noOfElems, elemSize ); 1014 __atomic_add_fetch( &cmemalign_storage, size, __ATOMIC_SEQ_CST ); 1015 #endif // __STATISTICS__ 1016 1017 return memalignNoStats( alignment, size ); 1018 } // amemalign 1019 1020 1021 // Same as calloc() with memory alignment. 1022 void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) { 1023 #ifdef __STATISTICS__ 1024 __atomic_add_fetch( &cmemalign_calls, 1, __ATOMIC_SEQ_CST ); 1025 __atomic_add_fetch( &cmemalign_storage, dim * elemSize, __ATOMIC_SEQ_CST ); 1026 #endif // __STATISTICS__ 1027 1028 return cmemalignNoStats( alignment, dim, elemSize ); 925 1029 } // cmemalign 926 1030 927 // The function aligned_alloc() is the same as memalign(), except for the added restriction that size should be a 928 // multiple of alignment. 1031 1032 // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple 1033 // of alignment. This requirement is universally ignored. 929 1034 void * aligned_alloc( size_t alignment, size_t size ) { 930 1035 return memalign( alignment, size ); … … 932 1037 933 1038 934 // The function posix_memalign() allocates size bytes and places the address of the allocated memory in *memptr. The935 // address of the allocated memory will be a multiple of alignment, which must be a power of two and a multiple of936 // sizeof(void *). If size is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later937 // be successfully passed tofree(3).1039 // Allocates size bytes and places the address of the allocated memory in *memptr. The address of the allocated 1040 // memory shall be a multiple of alignment, which must be a power of two and a multiple of sizeof(void *). If size 1041 // is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later be successfully passed to 1042 // free(3). 938 1043 int posix_memalign( void ** memptr, size_t alignment, size_t size ) { 939 if ( alignment < sizeof(void *) || ! libPow2( alignment ) ) return EINVAL; // check alignment1044 if ( alignment < libAlign() || ! is_pow2( alignment ) ) return EINVAL; // check alignment 940 1045 * memptr = memalign( alignment, size ); 941 if ( unlikely( * memptr == 0p ) ) return ENOMEM;942 1046 return 0; 943 1047 } // posix_memalign 944 1048 945 // The obsolete function valloc() allocates size bytes and returns a pointer to the allocated memory. The memory 946 // address will be a multiple of the page size. It is equivalent to memalign(sysconf(_SC_PAGESIZE),size). 1049 1050 // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the 1051 // page size. It is equivalent to memalign(sysconf(_SC_PAGESIZE),size). 947 1052 void * valloc( size_t size ) { 948 return memalign( pageSize, size );1053 return memalign( __page_size, size ); 949 1054 } // valloc 950 1055 951 1056 952 // The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to 953 // malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior 954 // occurs. If ptr is 0p, no operation is performed. 1057 // Same as valloc but rounds size to multiple of page size. 1058 void * pvalloc( size_t size ) { 1059 return memalign( __page_size, ceiling2( size, __page_size ) ); 1060 } // pvalloc 1061 1062 1063 // Frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() 1064 // or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is 1065 // 0p, no operation is performed. 955 1066 void free( void * addr ) { 956 1067 #ifdef __STATISTICS__ … … 973 1084 974 1085 975 // The malloc_alignment() function returns the alignment of theallocation.1086 // Returns the alignment of an allocation. 976 1087 size_t malloc_alignment( void * addr ) { 977 1088 if ( unlikely( addr == 0p ) ) return libAlign(); // minimum alignment … … 980 1091 return header->kind.fake.alignment & -2; // remove flag from value 981 1092 } else { 982 return libAlign (); // minimum alignment1093 return libAlign(); // minimum alignment 983 1094 } // if 984 1095 } // malloc_alignment 985 1096 986 1097 987 // The malloc_zero_fill() function returns true if the allocation is zero filled, i.e., initially allocated by calloc(). 1098 // Set the alignment for an the allocation and return previous alignment or 0 if no alignment. 1099 size_t $malloc_alignment_set( void * addr, size_t alignment ) { 1100 if ( unlikely( addr == 0p ) ) return libAlign(); // minimum alignment 1101 size_t ret; 1102 HeapManager.Storage.Header * header = headerAddr( addr ); 1103 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1104 ret = header->kind.fake.alignment & -2; // remove flag from old value 1105 header->kind.fake.alignment = alignment | 1; // add flag to new value 1106 } else { 1107 ret = 0; // => no alignment to change 1108 } // if 1109 return ret; 1110 } // $malloc_alignment_set 1111 1112 1113 // Returns true if the allocation is zero filled, e.g., allocated by calloc(). 988 1114 bool malloc_zero_fill( void * addr ) { 989 1115 if ( unlikely( addr == 0p ) ) return false; // null allocation is not zero fill 990 1116 HeapManager.Storage.Header * header = headerAddr( addr ); 991 1117 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 992 header = (HeapManager.Storage.Header *)((char *)header - header->kind.fake.offset);993 } // if 994 return (header->kind.real.blockSize & 2) != 0; // zero filled (calloc/cmemalign)?1118 header = realHeader( header ); // backup from fake to real header 1119 } // if 1120 return (header->kind.real.blockSize & 2) != 0; // zero filled ? 995 1121 } // malloc_zero_fill 996 1122 997 998 // The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to 999 // a block of memory allocated by malloc(3) or a related function. 1123 // Set allocation is zero filled and return previous zero filled. 1124 bool $malloc_zero_fill_set( void * addr ) { 1125 if ( unlikely( addr == 0p ) ) return false; // null allocation is not zero fill 1126 HeapManager.Storage.Header * header = headerAddr( addr ); 1127 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1128 header = realHeader( header ); // backup from fake to real header 1129 } // if 1130 bool ret = (header->kind.real.blockSize & 2) != 0; // zero filled ? 1131 header->kind.real.blockSize |= 2; // mark as zero filled 1132 return ret; 1133 } // $malloc_zero_fill_set 1134 1135 1136 // Returns original total allocation size (not bucket size) => array size is dimension * sizeif(T). 1137 size_t malloc_size( void * addr ) { 1138 if ( unlikely( addr == 0p ) ) return 0; // null allocation has zero size 1139 HeapManager.Storage.Header * header = headerAddr( addr ); 1140 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1141 header = realHeader( header ); // backup from fake to real header 1142 } // if 1143 return header->kind.real.size; 1144 } // malloc_size 1145 1146 // Set allocation size and return previous size. 1147 size_t $malloc_size_set( void * addr, size_t size ) { 1148 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size 1149 HeapManager.Storage.Header * header = headerAddr( addr ); 1150 if ( (header->kind.fake.alignment & 1) == 1 ) { // fake header ? 1151 header = realHeader( header ); // backup from fake to real header 1152 } // if 1153 size_t ret = header->kind.real.size; 1154 header->kind.real.size = size; 1155 return ret; 1156 } // $malloc_size_set 1157 1158 1159 // Returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by 1160 // malloc or a related function. 1000 1161 size_t malloc_usable_size( void * addr ) { 1001 1162 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size … … 1005 1166 1006 1167 headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment ); 1007 return dataStorage( bsize, addr, header ); // data storage in bucket1168 return dataStorage( bsize, addr, header ); // data storage in bucket 1008 1169 } // malloc_usable_size 1009 1170 1010 1171 1011 // The malloc_stats() function prints (on default standard error) statistics about memory allocated by malloc(3) and 1012 // related functions. 1172 // Prints (on default standard error) statistics about memory allocated by malloc and related functions. 1013 1173 void malloc_stats( void ) { 1014 1174 #ifdef __STATISTICS__ … … 1018 1178 } // malloc_stats 1019 1179 1020 // The malloc_stats_fd() function changes the file descripter where malloc_stats() writes the statistics. 1180 1181 // Changes the file descripter where malloc_stats() writes statistics. 1021 1182 int malloc_stats_fd( int fd __attribute__(( unused )) ) { 1022 1183 #ifdef __STATISTICS__ 1023 int temp = stat fd;1024 stat fd = fd;1184 int temp = stat_fd; 1185 stat_fd = fd; 1025 1186 return temp; 1026 1187 #else … … 1030 1191 1031 1192 1032 // The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see 1033 // malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that 1034 // parameter. 1193 // Adjusts parameters that control the behaviour of the memory-allocation functions (see malloc). The param argument 1194 // specifies the parameter to be modified, and value specifies the new value for that parameter. 1035 1195 int mallopt( int option, int value ) { 1036 1196 choose( option ) { 1037 1197 case M_TOP_PAD: 1038 if ( setHeapExpand( value ) )return 1;1198 heapExpand = ceiling2( value, __page_size ); return 1; 1039 1199 case M_MMAP_THRESHOLD: 1040 1200 if ( setMmapStart( value ) ) return 1; 1201 break; 1041 1202 } // switch 1042 1203 return 0; // error, unsupported 1043 1204 } // mallopt 1044 1205 1045 // The malloc_trim() function attempts to release free memory at the top of the heap (by calling sbrk(2) with a 1046 // suitable argument).1206 1207 // Attempt to release free memory at the top of the heap (by calling sbrk with a suitable argument). 1047 1208 int malloc_trim( size_t ) { 1048 1209 return 0; // => impossible to release memory … … 1050 1211 1051 1212 1052 // The malloc_info() function exports an XML string that describes the current state of the memory-allocation1053 // implementation in the caller. The string is printed on the file stream stream. The exported string includes1054 // information about all arenas (see malloc(3)).1213 // Exports an XML string that describes the current state of the memory-allocation implementation in the caller. 1214 // The string is printed on the file stream stream. The exported string includes information about all arenas (see 1215 // malloc). 1055 1216 int malloc_info( int options, FILE * stream ) { 1056 if ( options != 0 ) { errno = EINVAL; return -1; } 1217 if ( options != 0 ) { errno = EINVAL; return -1; } 1218 #ifdef __STATISTICS__ 1057 1219 return printStatsXML( stream ); 1220 #else 1221 return 0; // unsupported 1222 #endif // __STATISTICS__ 1058 1223 } // malloc_info 1059 1224 1060 1225 1061 // The malloc_get_state() function records the current state of all malloc(3) internal bookkeeping variables (but1062 // not the actual contents of the heap or the state of malloc_hook(3) functions pointers). The state is recorded in1063 // a system-dependent opaque data structure dynamically allocated via malloc(3), and a pointer to that data1064 // structure is returned as the function result. (It is the caller's responsibility to free(3)this memory.)1226 // Records the current state of all malloc internal bookkeeping variables (but not the actual contents of the heap 1227 // or the state of malloc_hook functions pointers). The state is recorded in a system-dependent opaque data 1228 // structure dynamically allocated via malloc, and a pointer to that data structure is returned as the function 1229 // result. (The caller must free this memory.) 1065 1230 void * malloc_get_state( void ) { 1066 1231 return 0p; // unsupported … … 1068 1233 1069 1234 1070 // The malloc_set_state() function restores the state of all malloc(3) internal bookkeeping variables to the values1071 // recorded in the opaque datastructure pointed to by state.1072 int malloc_set_state( void * ptr) {1235 // Restores the state of all malloc internal bookkeeping variables to the values recorded in the opaque data 1236 // structure pointed to by state. 1237 int malloc_set_state( void * ) { 1073 1238 return 0; // unsupported 1074 1239 } // malloc_set_state … … 1077 1242 1078 1243 // Must have CFA linkage to overload with C linkage realloc. 1079 void * re alloc( void * oaddr, size_t nalign, size_t size ) {1244 void * resize( void * oaddr, size_t nalign, size_t size ) { 1080 1245 #ifdef __STATISTICS__ 1081 __atomic_add_fetch( &re alloc_calls, 1, __ATOMIC_SEQ_CST );1246 __atomic_add_fetch( &resize_calls, 1, __ATOMIC_SEQ_CST ); 1082 1247 #endif // __STATISTICS__ 1083 1248 1084 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1085 if ( unlikely( size == 0 ) ) { free( oaddr ); return mallocNoStats( size ); } // special cases 1086 if ( unlikely( oaddr == 0p ) ) return mallocNoStats( size ); 1087 1088 if ( unlikely( nalign == 0 ) ) nalign = libAlign(); // reset alignment to minimum 1249 if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum 1089 1250 #ifdef __CFA_DEBUG__ 1090 1251 else … … 1092 1253 #endif // __CFA_DEBUG__ 1093 1254 1094 HeapManager.Storage.Header * header; 1095 HeapManager.FreeHeader * freeElem; 1096 size_t bsize, oalign = 0; 1097 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 1098 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 1099 1100 if ( oalign != 0 && (uintptr_t)oaddr % nalign == 0 ) { // has alignment and just happens to work out 1101 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1102 return realloc( oaddr, size ); 1255 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1256 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 1257 if ( unlikely( oaddr == 0p ) ) { 1258 #ifdef __STATISTICS__ 1259 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 1260 #endif // __STATISTICS__ 1261 return memalignNoStats( nalign, size ); 1262 } // if 1263 1264 // Attempt to reuse existing alignment. 1265 HeapManager.Storage.Header * header = headerAddr( oaddr ); 1266 bool isFakeHeader = header->kind.fake.alignment & 1; // old fake header ? 1267 size_t oalign; 1268 if ( isFakeHeader ) { 1269 oalign = header->kind.fake.alignment & -2; // old alignment 1270 if ( (uintptr_t)oaddr % nalign == 0 // lucky match ? 1271 && ( oalign <= nalign // going down 1272 || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ? 1273 ) { 1274 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1275 HeapManager.FreeHeader * freeElem; 1276 size_t bsize, oalign; 1277 headers( "resize", oaddr, header, freeElem, bsize, oalign ); 1278 size_t odsize = dataStorage( bsize, oaddr, header ); // data storage available in bucket 1279 1280 if ( size <= odsize && odsize <= size * 2 ) { // allow 50% wasted data storage 1281 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1282 1283 header->kind.real.blockSize &= -2; // turn off 0 fill 1284 header->kind.real.size = size; // reset allocation size 1285 return oaddr; 1286 } // if 1287 } // if 1288 } else if ( ! isFakeHeader // old real header (aligned on libAlign) ? 1289 && nalign == libAlign() ) { // new alignment also on libAlign => no fake header needed 1290 return resize( oaddr, size ); // duplicate special case checks 1103 1291 } // if 1104 1292 1105 1293 #ifdef __STATISTICS__ 1294 __atomic_add_fetch( &resize_storage, size, __ATOMIC_SEQ_CST ); 1295 #endif // __STATISTICS__ 1296 1297 // change size, DO NOT preserve STICKY PROPERTIES. 1298 free( oaddr ); 1299 return memalignNoStats( nalign, size ); // create new aligned area 1300 } // resize 1301 1302 1303 void * realloc( void * oaddr, size_t nalign, size_t size ) { 1304 if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum 1305 #ifdef __CFA_DEBUG__ 1306 else 1307 checkAlign( nalign ); // check alignment 1308 #endif // __CFA_DEBUG__ 1309 1310 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1311 if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases 1312 if ( unlikely( oaddr == 0p ) ) { 1313 #ifdef __STATISTICS__ 1314 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 1315 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 1316 #endif // __STATISTICS__ 1317 return memalignNoStats( nalign, size ); 1318 } // if 1319 1320 // Attempt to reuse existing alignment. 1321 HeapManager.Storage.Header * header = headerAddr( oaddr ); 1322 bool isFakeHeader = header->kind.fake.alignment & 1; // old fake header ? 1323 size_t oalign; 1324 if ( isFakeHeader ) { 1325 oalign = header->kind.fake.alignment & -2; // old alignment 1326 if ( (uintptr_t)oaddr % nalign == 0 // lucky match ? 1327 && ( oalign <= nalign // going down 1328 || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ? 1329 ) { 1330 headerAddr( oaddr )->kind.fake.alignment = nalign | 1; // update alignment (could be the same) 1331 return realloc( oaddr, size ); // duplicate alignment and special case checks 1332 } // if 1333 } else if ( ! isFakeHeader // old real header (aligned on libAlign) ? 1334 && nalign == libAlign() ) // new alignment also on libAlign => no fake header needed 1335 return realloc( oaddr, size ); // duplicate alignment and special case checks 1336 1337 #ifdef __STATISTICS__ 1338 __atomic_add_fetch( &realloc_calls, 1, __ATOMIC_SEQ_CST ); 1106 1339 __atomic_add_fetch( &realloc_storage, size, __ATOMIC_SEQ_CST ); 1107 1340 #endif // __STATISTICS__ 1108 1341 1342 HeapManager.FreeHeader * freeElem; 1343 size_t bsize; 1344 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 1345 1109 1346 // change size and copy old content to new storage 1110 1347 1111 void * naddr; 1112 if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill 1113 naddr = cmemalignNoStats( nalign, 1, size ); // create new aligned area 1114 } else { 1115 naddr = memalignNoStats( nalign, size ); // create new aligned area 1116 } // if 1348 size_t osize = header->kind.real.size; // old allocation size 1349 bool ozfill = (header->kind.real.blockSize & 2); // old allocation zero filled 1350 1351 void * naddr = memalignNoStats( nalign, size ); // create new aligned area 1117 1352 1118 1353 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 1119 size_t ndsize = dataStorage( bsize, naddr, header ); // data storage avilable in bucket 1120 // To preserve prior fill, the entire bucket must be copied versus the size. 1121 memcpy( naddr, oaddr, MIN( odsize, ndsize ) ); // copy bytes 1354 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 1122 1355 free( oaddr ); 1356 1357 if ( unlikely( ozfill ) ) { // previous request zero fill ? 1358 header->kind.real.blockSize |= 2; // mark new request as zero filled 1359 if ( size > osize ) { // previous request larger ? 1360 memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage 1361 } // if 1362 } // if 1123 1363 return naddr; 1124 1364 } // realloc -
libcfa/src/interpose.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Mar 29 16:10:31 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Feb 17 10:18:53202013 // Update Count : 1 6612 // Last Modified On : Fri Mar 13 17:35:37 2020 13 // Update Count : 178 14 14 // 15 15 16 16 #include <stdarg.h> // va_start, va_end 17 #include <stdio.h> 17 18 #include <string.h> // strlen 18 19 #include <unistd.h> // _exit, getpid … … 143 144 void abort( const char fmt[], ... ) __attribute__(( format(printf, 1, 2), __nothrow__, __leaf__, __noreturn__ )); 144 145 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__ )); 145 147 146 148 extern "C" { … … 152 154 va_list argp; 153 155 va_start( argp, fmt ); 154 abort( false, fmt, argp );156 __abort( false, fmt, argp ); 155 157 va_end( argp ); 156 158 } … … 218 220 } 219 221 220 void abort( bool signalAbort, const char fmt[], ... ) { 221 void * kernel_data = kernel_abort(); // must be done here to lock down kernel 222 int len; 223 224 signal( SIGABRT, SIG_DFL ); // prevent final "real" abort from recursing to handler 225 226 len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld) ", (long int)getpid() ); // use UNIX pid (versus getPid) 227 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 228 229 assert( fmt ); 230 va_list args; 231 va_start( args, fmt ); 232 233 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 234 va_end( args ); 235 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 236 237 if ( fmt[strlen( fmt ) - 1] != '\n' ) { // add optional newline if missing at the end of the format text 238 __cfaabi_dbg_write( "\n", 1 ); 239 } // if 240 kernel_abort_msg( kernel_data, abort_text, abort_text_size ); 241 242 __cfaabi_backtrace( signalAbort ? 4 : 2 ); 243 244 __cabi_libc.abort(); // print stack trace in handler 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) 240 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 241 242 assert( fmt ); 243 len = vsnprintf( abort_text, abort_text_size, fmt, args ); 244 __cfaabi_bits_write( STDERR_FILENO, abort_text, len ); 245 246 // add optional newline if missing at the end of the format text 247 if ( fmt[strlen( fmt ) - 1] != '\n' ) { 248 __cfaabi_bits_write( STDERR_FILENO, "\n", 1 ); 249 } // if 250 kernel_abort_msg( kernel_data, abort_text, abort_text_size ); 251 } 252 253 // Second stage: print the backtrace 254 if(stage == 2) { 255 // increment stage 256 stage = __atomic_add_fetch( &__abort_stage, 1, __ATOMIC_SEQ_CST ); 257 258 // print stack trace in handler 259 __cfaabi_backtrace( signalAbort ? 4 : 2 ); 260 } 261 262 do { 263 // Finally call abort 264 __cabi_libc.abort(); 265 266 // Loop so that we never return 267 } while(true); 245 268 } 246 269 … … 248 271 va_list args; 249 272 va_start( args, fmt ); 250 abort( false, fmt, args ); 273 __abort( false, fmt, args ); 274 // CONTROL NEVER REACHES HERE! 251 275 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 ); 252 284 } 253 285 -
libcfa/src/iostream.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 20 15:53:23202013 // Update Count : 82912 // Last Modified On : Mon Aug 24 08:31:35 2020 13 // Update Count : 1130 14 14 // 15 15 16 16 #include "iostream.hfa" 17 17 18 extern "C" {19 18 #include <stdio.h> 20 19 #include <stdbool.h> // true/false 21 20 #include <stdint.h> // UINT64_MAX 22 //#include <string.h> // strlen, strcmp 21 #include <float.h> // DBL_DIG, LDBL_DIG 22 #include <complex.h> // creal, cimag 23 //#include <string.h> // strlen, strcmp, memcpy 24 extern "C" { 23 25 extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))); 24 26 extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2))); 25 27 extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); 26 28 extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); 27 #include <float.h> // DBL_DIG, LDBL_DIG28 #include <math.h> // isfinite29 #include <complex.h> // creal, cimag30 29 } // extern "C" 31 30 32 33 //*********************************** ostream *********************************** 31 #include "math.hfa" // isfinite, floor, ceiling_div 32 #include "bitmanip.hfa" // high1 33 34 35 // *********************************** ostream *********************************** 34 36 35 37 36 38 forall( dtype ostype | ostream( ostype ) ) { 37 ostype & ?|?( ostype & os, zero_t ) {38 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );39 fmt( os, "%d", 0n );40 return os;41 } // ?|?42 void ?|?( ostype & os, zero_t z ) {43 (ostype &)(os | z); ends( os );44 } // ?|?45 46 ostype & ?|?( ostype & os, one_t ) {47 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) );48 fmt( os, "%d", 1n );49 return os;50 } // ?|?51 void ?|?( ostype & os, one_t o ) {52 (ostype &)(os | o); ends( os );53 } // ?|?54 55 39 ostype & ?|?( ostype & os, bool b ) { 56 40 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); … … 165 149 #define P10_UINT64 10_000_000_000_000_000_000_ULL // 19 zeroes 166 150 167 static void base10_128( ostype & os, unsigned int128 val ) { 168 if ( val > UINT64_MAX ) { 151 static inline void base10_128( ostype & os, unsigned int128 val ) { 152 #if defined(__GNUC__) && __GNUC_PREREQ(7,0) // gcc version >= 7 153 if ( val > P10_UINT64 ) { 154 #else 155 if ( (uint64_t)(val >> 64) != 0 || (uint64_t)val > P10_UINT64 ) { // patch gcc 5 & 6 -O3 bug 156 #endif // __GNUC_PREREQ(7,0) 169 157 base10_128( os, val / P10_UINT64 ); // recursive 170 158 fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) ); … … 174 162 } // base10_128 175 163 176 static void base10_128( ostype & os, int128 val ) {164 static inline void base10_128( ostype & os, int128 val ) { 177 165 if ( val < 0 ) { 178 166 fmt( os, "-" ); // leading negative sign … … 445 433 } // distribution 446 434 447 // *********************************** manipulators ***********************************448 449 // *********************************** integral ***********************************435 // *********************************** manipulators *********************************** 436 437 // *********************************** integral *********************************** 450 438 451 439 static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; … … 453 441 454 442 // Default prefix for non-decimal prints is 0b, 0, 0x. 455 #define IntegralFMTImpl( T, CODE,IFMTNP, IFMTP ) \443 #define IntegralFMTImpl( T, IFMTNP, IFMTP ) \ 456 444 forall( dtype ostype | ostream( ostype ) ) { \ 457 445 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ … … 459 447 \ 460 448 if ( f.base == 'b' || f.base == 'B' ) { /* bespoke binary format */ \ 461 int bits; \ 462 if ( f.val == (T){0} ) bits = 1; /* force at least one bit to print */ \ 463 else bits = sizeof(long long int) * 8 - __builtin_clzll( f.val ); /* position of most significant bit */ \ 464 bits = bits > sizeof(f.val) * 8 ? sizeof(f.val) * 8 : bits; \ 465 int spaces = f.wd - bits; /* can be negative */ \ 466 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 467 /* printf( "%d %d\n", bits, spaces ); */ \ 449 int bits = high1( f.val ); /* position of most significant bit */ \ 450 if ( bits == 0 ) bits = 1; /* 0 value => force one bit to print */ \ 451 int spaces; \ 468 452 if ( ! f.flags.left ) { /* right justified ? */ \ 469 453 /* Note, base prefix then zero padding or spacing then prefix. */ \ 470 if ( f.flags.pad0 || f.flags.pc ) { \ 454 if ( f.flags.pc ) { \ 455 spaces = f.wd - f.pc; \ 456 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 457 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \ 471 458 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \ 472 if ( f.flags.pc )spaces = f.pc - bits; \459 spaces = f.pc - bits; \ 473 460 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \ 474 461 } else { \ 475 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \ 476 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \ 462 spaces = f.wd - bits; \ 463 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 464 if ( f.flags.pad0 ) { \ 465 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \ 466 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \ 467 } else { \ 468 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \ 469 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \ 470 } /* if */ \ 477 471 } /* if */ \ 478 } else if ( ! f.flags.nobsdp ) { \ 479 fmt( os, "0%c", f.base ); \ 472 } else { \ 473 if ( ! f.flags.nobsdp ) fmt( os, "0%c", f.base ); \ 474 if ( f.flags.pc ) { \ 475 spaces = f.pc - bits; \ 476 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \ 477 spaces = f.wd - f.pc; \ 478 } else { /* pad0 flag ignored with left flag */ \ 479 spaces = f.wd - bits; \ 480 } /* if */ \ 481 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \ 480 482 } /* if */ \ 481 int shift = (bits - 1) / 4 * 4; /* floor( bits - 1, 4 ) */\483 int shift = floor( bits - 1, 4 ); \ 482 484 typeof( f.val ) temp = f.val; \ 483 485 fmt( os, "%s", shortbin[(temp >> shift) & 0xf] ); \ … … 490 492 if ( f.flags.left && spaces > 0 ) fmt( os, "%*s", spaces, " " ); \ 491 493 return os; \ 492 } /* if */ \494 } /* if */ \ 493 495 \ 494 496 char fmtstr[sizeof(IFMTP)]; /* sizeof includes '\0' */ \ … … 500 502 if ( ! f.flags.nobsdp ) { fmtstr[star] = '#'; star -= 1; } \ 501 503 if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \ 502 if ( f.flags.sign && f.base == CODE) { fmtstr[star] = '+'; star -= 1; } \504 if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \ 503 505 if ( f.flags.pad0 && ! f.flags.pc ) { fmtstr[star] = '0'; star -= 1; } \ 504 506 fmtstr[star] = '%'; \ … … 506 508 if ( ! f.flags.pc ) { /* no precision */ \ 507 509 fmtstr[sizeof(IFMTNP)-2] = f.base; /* sizeof includes '\0' */ \ 508 /* printf( "%s %c %c\n", &fmtstr[star], f.base, CODE); */ \510 /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \ 509 511 fmt( os, &fmtstr[star], f.wd, f.val ); \ 510 512 } else { /* precision */ \ 511 513 fmtstr[sizeof(IFMTP)-2] = f.base; /* sizeof includes '\0' */ \ 512 /* printf( "%s %c %c\n", &fmtstr[star], f.base, CODE); */ \514 /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \ 513 515 fmt( os, &fmtstr[star], f.wd, f.pc, f.val ); \ 514 516 } /* if */ \ … … 518 520 } // distribution 519 521 520 IntegralFMTImpl( signed char, 'd',"% *hh ", "% *.*hh " )521 IntegralFMTImpl( unsigned char, 'u',"% *hh ", "% *.*hh " )522 IntegralFMTImpl( signed short int, 'd',"% *h ", "% *.*h " )523 IntegralFMTImpl( unsigned short int, 'u',"% *h ", "% *.*h " )524 IntegralFMTImpl( signed int, 'd',"% * ", "% *.* " )525 IntegralFMTImpl( unsigned int, 'u',"% * ", "% *.* " )526 IntegralFMTImpl( signed long int, 'd',"% *l ", "% *.*l " )527 IntegralFMTImpl( unsigned long int, 'u',"% *l ", "% *.*l " )528 IntegralFMTImpl( signed long long int, 'd',"% *ll ", "% *.*ll " )529 IntegralFMTImpl( unsigned long long int, 'u',"% *ll ", "% *.*ll " )530 531 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 532 534 #if defined( __SIZEOF_INT128__ ) 533 535 // Default prefix for non-decimal prints is 0b, 0, 0x. 534 536 #define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \ 535 537 forall( dtype ostype | ostream( ostype ) ) \ 536 static void base10_128( ostype & os, _Ostream_Manip(T) fmt ) { \ 537 if ( fmt.val > UINT64_MAX ) { \ 538 fmt.val /= P10_UINT64; \ 539 base10_128( os, fmt ); /* recursive */ \ 540 _Ostream_Manip(unsigned long long int) fmt2 @= { (uint64_t)(fmt.val % P10_UINT64), 0, 19, 'u', { .all : 0 } }; \ 541 fmt2.flags.nobsdp = true; \ 542 printf( "fmt2 %c %lld %d\n", fmt2.base, fmt2.val, fmt2.all ); \ 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 ); */ \ 543 546 sepOff( os ); \ 544 (ostype &)(os | fmt 2); \547 (ostype &)(os | fmt); \ 545 548 } else { \ 546 printf( "fmt %c %lld %d\n", fmt.base, fmt.val, fmt.all ); \ 549 /* printf( "fmt2 %c %lld %d\n", f.base, (unsigned long long int)f.val, f.all ); */ \ 550 _Ostream_Manip(SIGNED long long int) fmt @= { (SIGNED long long int)f.val, f.wd, f.pc, f.base, { .all : f.all } }; \ 547 551 (ostype &)(os | fmt); \ 548 552 } /* if */ \ 549 } /* base10_128 */ \553 } /* base10_128 */ \ 550 554 forall( dtype ostype | ostream( ostype ) ) { \ 551 555 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \ 552 556 if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \ 553 557 \ 554 if ( f.base == 'b' | f.base == ' o' | f.base == 'x' | f.base == 'X' ) { \558 if ( f.base == 'b' | f.base == 'B' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \ 555 559 unsigned long long int msig = (unsigned long long int)(f.val >> 64); \ 556 560 unsigned long long int lsig = (unsigned long long int)(f.val); \ … … 562 566 } else { \ 563 567 fmt2.flags.pad0 = fmt2.flags.nobsdp = true; \ 564 if ( f.base == 'b' ) { \ 565 if ( f.wd > 64 ) fmt.wd = f.wd - 64; \ 566 fmt2.wd = 64; \ 568 if ( f.base == 'b' | f.base == 'B' ) { \ 569 if ( fmt.flags.pc && fmt.pc > 64 ) fmt.pc -= 64; else { fmt.flags.pc = false; fmt.pc = 0; } \ 570 if ( fmt.flags.left ) { \ 571 fmt.flags.left = false; \ 572 fmt.wd = 0; \ 573 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 574 fmt2.flags.left = true; \ 575 int msigd = high1( msig ); \ 576 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 577 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0b base specifier */ \ 578 if ( (int)fmt2.wd < 64 ) fmt2.wd = 64; /* cast deals with negative value */ \ 579 fmt2.flags.pc = true; fmt2.pc = 64; \ 580 } else { \ 581 if ( fmt.wd > 64 ) fmt.wd -= 64; \ 582 else fmt.wd = 1; \ 583 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 584 fmt2.wd = 64; \ 585 } /* if */ \ 586 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 567 587 (ostype &)(os | fmt | "" | fmt2); \ 568 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; } \ 569 590 fmt.val = (unsigned long long int)fmt.val >> 2; \ 570 if ( f.wd > 21 ) fmt.wd = f.wd - 21; \ 571 fmt2.wd = 1; \ 572 fmt2.val = ((msig & 0x3) << 1) + 1; \ 573 (ostype &)(os | fmt | "" | fmt2); \ 574 sepOff( os ); \ 575 fmt2.wd = 21; \ 576 fmt2.val = lsig & 0x7fffffffffffffff; \ 591 fmt2.val = ((msig & 0x3) << 1) + ((lsig & 0x8000000000000000U) != 0); \ 592 if ( fmt.flags.left ) { \ 593 fmt.flags.left = false; \ 594 fmt.wd = 0; \ 595 /* printf( "L %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 596 (ostype &)(os | fmt | "" | fmt2); \ 597 sepOff( os ); \ 598 fmt2.flags.left = true; \ 599 int msigd = ceiling_div( high1( fmt.val ), 3 ); \ 600 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 601 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 1; /* compensate for 0 base specifier */ \ 602 if ( (int)fmt2.wd < 21 ) fmt2.wd = 21; /* cast deals with negative value */ \ 603 fmt2.flags.pc = true; fmt2.pc = 21; \ 604 } else { \ 605 if ( fmt.wd > 22 ) fmt.wd -= 22; \ 606 else fmt.wd = 1; \ 607 /* printf( "R %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 608 (ostype &)(os | fmt | "" | fmt2); \ 609 sepOff( os ); \ 610 fmt2.wd = 21; \ 611 } /* if */ \ 612 fmt2.val = lsig & 0x7fffffffffffffffU; \ 613 /* printf( "\nC %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 577 614 (ostype &)(os | fmt2); \ 578 } else { \ 579 if ( f.flags.left ) { \ 580 if ( f.wd > 16 ) fmt2.wd = f.wd - 16; \ 581 fmt.wd = 16; \ 615 } else { /* f.base == 'x' | f.base == 'X' */ \ 616 if ( fmt.flags.pc && fmt.pc > 16 ) fmt.pc -= 16; else { fmt.flags.pc = false; fmt.pc = 0; } \ 617 if ( fmt.flags.left ) { \ 618 fmt.flags.left = false; \ 619 fmt.wd = 0; \ 620 /* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 621 fmt2.flags.left = true; \ 622 int msigd = high1( msig ); \ 623 fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \ 624 if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0x base specifier */ \ 625 if ( (int)fmt2.wd < 16 ) fmt2.wd = 16; /* cast deals with negative value */ \ 626 fmt2.flags.pc = true; fmt2.pc = 16; \ 582 627 } else { \ 583 if ( f.wd > 16 ) fmt.wd = f.wd - 16; \ 584 fmt2.wd = 16; \ 628 if ( fmt.wd > 16 ) fmt.wd -= 16; \ 629 else fmt.wd = 1; \ 630 /* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \ 631 fmt2.wd = 16; \ 585 632 } /* if */ \ 633 /* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \ 586 634 (ostype &)(os | fmt | "" | fmt2); \ 587 635 } /* if */ \ 588 636 } /* if */ \ 589 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 */ \ 590 641 base10_128( os, f ); \ 591 642 } /* if */ \ … … 598 649 IntegralFMTImpl128( unsigned int128, unsigned, 'u', "% *ll ", "% *.*ll " ) 599 650 #endif // __SIZEOF_INT128__ 600 601 //*********************************** floating point *********************************** 651 #endif // 0 652 653 #if 1 654 #if defined( __SIZEOF_INT128__ ) 655 // Default prefix for non-decimal prints is 0b, 0, 0x. 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 *********************************** 602 746 603 747 #define PrintWithDP2( os, format, val, ... ) \ … … 655 799 FloatingPointFMTImpl( long double, "% *L ", "% *.*L " ) 656 800 657 // *********************************** character ***********************************801 // *********************************** character *********************************** 658 802 659 803 forall( dtype ostype | ostream( ostype ) ) { … … 688 832 } // distribution 689 833 690 // *********************************** C string ***********************************834 // *********************************** C string *********************************** 691 835 692 836 forall( dtype ostype | ostream( ostype ) ) { … … 735 879 736 880 737 // *********************************** istream ***********************************881 // *********************************** istream *********************************** 738 882 739 883 … … 812 956 } // ?|? 813 957 958 #if defined( __SIZEOF_INT128__ ) 959 istype & ?|?( istype & is, int128 & i128 ) { 960 return (istype &)(is | (unsigned int128 &)i128); 961 } // ?|? 962 963 istype & ?|?( istype & is, unsigned int128 & ui128 ) { 964 char s[40]; 965 bool sign = false; 966 967 if ( fmt( is, " %[-]", s ) == 1 ) sign = true; // skip whitespace, negative sign ? 968 // If the input is too large, the value returned is undefined. If there is no input, no value is returned 969 if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) { // take first 39 characters, ignore remaining 970 ui128 = 0; 971 for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) { 972 ui128 = ui128 * 10 + s[i] - '0'; 973 } // for 974 if ( sign ) ui128 = -ui128; 975 } else if ( sign ) ungetc( is, '-' ); // return minus when no digits 976 return is; 977 } // ?|? 978 #endif // __SIZEOF_INT128__ 814 979 815 980 istype & ?|?( istype & is, float & f ) { … … 881 1046 } // distribution 882 1047 883 // *********************************** manipulators ***********************************1048 // *********************************** manipulators *********************************** 884 1049 885 1050 forall( dtype istype | istream( istype ) ) -
libcfa/src/iostream.hfa
r3c64c668 r58fe85a 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T hu Feb 20 15:30:56202013 // Update Count : 3 3712 // Last Modified On : Tue Aug 11 22:16:14 2020 13 // Update Count : 350 14 14 // 15 15 … … 19 19 20 20 21 // *********************************** ostream ***********************************21 // *********************************** ostream *********************************** 22 22 23 23 … … 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 74 69 ostype & ?|?( ostype &, bool ); 75 70 void ?|?( ostype &, bool ); … … 156 151 } // distribution 157 152 158 // *********************************** manipulators ***********************************153 // *********************************** manipulators *********************************** 159 154 160 155 forall( otype T ) … … 166 161 unsigned char all; 167 162 struct { 163 unsigned char neg:1; // val is negative 168 164 unsigned char pc:1; // precision specified 169 165 unsigned char left:1; // left justify … … 175 171 }; // _Ostream_Manip 176 172 177 // *********************************** integral ***********************************173 // *********************************** integral *********************************** 178 174 179 175 // See 6.7.9. 19) The initialization shall occur in initializer list order, each initializer provided for a particular … … 215 211 IntegralFMTDecl( int128, 'd' ) 216 212 IntegralFMTDecl( unsigned int128, 'u' ) 217 #endif 218 219 // *********************************** floating point ***********************************213 #endif // __SIZEOF_INT128__ 214 215 // *********************************** floating point *********************************** 220 216 221 217 // Default suffix for values with no fraction is "." … … 246 242 FloatingPointFMTDecl( long double ) 247 243 248 // *********************************** character ***********************************244 // *********************************** character *********************************** 249 245 250 246 static inline { … … 263 259 } // ?|? 264 260 265 // *********************************** C string ***********************************261 // *********************************** C string *********************************** 266 262 267 263 static inline { … … 282 278 283 279 284 // *********************************** istream ***********************************280 // *********************************** istream *********************************** 285 281 286 282 … … 314 310 istype & ?|?( istype &, unsigned int & ); 315 311 istype & ?|?( istype &, long int & ); 312 istype & ?|?( istype &, unsigned long int & ); 316 313 istype & ?|?( istype &, long long int & ); 317 istype & ?|?( istype &, unsigned long int & );318 314 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 319 320 320 istype & ?|?( istype &, float & ); … … 336 336 } // distribution 337 337 338 // *********************************** manipulators ***********************************338 // *********************************** manipulators *********************************** 339 339 340 340 struct _Istream_Cstr { … … 358 358 _Istream_Cstr excl( const char scanset[], char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : true } }; } 359 359 _Istream_Cstr & excl( const char scanset[], _Istream_Cstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; } 360 _Istream_Cstr ignore( c onst char s[] ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; }360 _Istream_Cstr ignore( char s[] ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; } 361 361 _Istream_Cstr & ignore( _Istream_Cstr & fmt ) { fmt.flags.ignore = true; return fmt; } 362 362 _Istream_Cstr wdi( unsigned int w, char s[] ) { return (_Istream_Cstr)@{ s, 0p, w, { .all : 0 } }; } … … 370 370 371 371 static inline { 372 _Istream_Char ignore( const char c) { return (_Istream_Char)@{ true }; }372 _Istream_Char ignore( const char ) { return (_Istream_Char)@{ true }; } 373 373 _Istream_Char & ignore( _Istream_Char & fmt ) { fmt.ignore = true; return fmt; } 374 374 } // distribution 375 375 forall( dtype istype | istream( istype ) ) istype & ?|?( istype & is, _Istream_Char f ); 376 376 377 forall( otype T)377 forall( dtype T | sized( T ) ) 378 378 struct _Istream_Manip { 379 379 T & val; // polymorphic base-type … … 413 413 414 414 415 // *********************************** time ***********************************415 // *********************************** time *********************************** 416 416 417 417 -
libcfa/src/math.hfa
r3c64c668 r58fe85a 10 10 // Created On : Mon Apr 18 23:37:04 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 10:27:11202013 // Update Count : 1 1712 // Last Modified On : Mon Aug 24 08:56:20 2020 13 // Update Count : 126 14 14 // 15 15 … … 19 19 #include <complex.h> 20 20 21 //--------------------------------------- 22 23 #include "common.hfa" 24 21 25 //---------------------- General ---------------------- 22 26 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 ); } 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 } // distribution 56 62 57 63 //---------------------- Exponential ---------------------- 58 64 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 ); } 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 } // distribution 83 91 84 92 //---------------------- Logarithm ---------------------- 85 93 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 ); } 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 } // distribution 133 143 134 144 //---------------------- Trigonometric ---------------------- 135 145 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 ); } 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 atan2 194 float atan( float x, float y ) { return atan2f( x, y ); } 195 double atan( double x, double y ) { return atan2( x, y ); } 196 long double atan( long double x, long double y ) { return atan2l( x, y ); } 197 } // distribution 186 198 187 199 //---------------------- Hyperbolic ---------------------- 188 200 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 ); } 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 } // distribution 230 244 231 245 //---------------------- Error / Gamma ---------------------- 232 246 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 ); } 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 } // distribution 257 273 258 274 //---------------------- Nearest Integer ---------------------- 259 275 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 ); } 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 } // distribution 309 370 310 371 //---------------------- Manipulation ---------------------- 311 372 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 ); } 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 } // distribution 349 412 350 413 //--------------------------------------- 351 414 352 #include "common.hfa" 353 354 //--------------------------------------- 355 356 forall( otype T | { void ?{}( T &, one_t ); T ?+?( T, T ); T ?-?( T, T );T ?*?( T, T ); } ) 357 T lerp( T x, T y, T a ) { return x * ((T){1} - a) + y * a; } 358 359 forall( otype T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); int ?<?( T, T ); } ) 360 T step( T edge, T x ) { return x < edge ? (T){0} : (T){1}; } 361 362 forall( otype T | { void ?{}( T &, int ); T clamp( T, T, T ); T ?-?( T, T ); T ?*?( T, T ); T ?/?( T, T ); } ) 363 T smoothstep( T edge0, T edge1, T x ) { T t = clamp( (x - edge0) / (edge1 - edge0), (T){0}, (T){1} ); return t * t * ((T){3} - (T){2} * t); } 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 364 425 365 426 // Local Variables: // -
libcfa/src/startup.cfa
r3c64c668 r58fe85a 14 14 // 15 15 16 #include <time.h> // tzset 16 #include <time.h> // tzset 17 #include <locale.h> // setlocale 17 18 #include "startup.hfa" 18 19 … … 21 22 void __cfaabi_appready_startup( void ) { 22 23 tzset(); // initialize time global variables 24 setlocale(LC_NUMERIC, ""); 23 25 #ifdef __CFA_DEBUG__ 24 26 extern void heapAppStart(); … … 41 43 struct __spinlock_t; 42 44 extern "C" { 43 void __cfaabi_dbg_record (struct __spinlock_t & this, const char prev_name[]) __attribute__(( weak )) {}45 void __cfaabi_dbg_record_lock(struct __spinlock_t & this, const char prev_name[]) __attribute__(( weak )) {} 44 46 } 45 47 -
libcfa/src/stdhdr/assert.h
r3c64c668 r58fe85a 33 33 #define verify(x) assert(x) 34 34 #define verifyf(x, ...) assertf(x, __VA_ARGS__) 35 #define verifyfail(...) 35 36 #define __CFA_WITH_VERIFY__ 36 37 #else 37 38 #define verify(x) 38 39 #define verifyf(x, ...) 40 #define verifyfail(...) 39 41 #endif 40 42 -
libcfa/src/stdhdr/malloc.h
r3c64c668 r58fe85a 10 10 // Created On : Thu Jul 20 15:58:16 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 11 09:06:31 201813 // Update Count : 1 012 // Last Modified On : Wed May 27 14:13:14 2020 13 // Update Count : 18 14 14 // 15 16 17 size_t default_mmap_start(); // CFA extras18 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"38 15 39 16 extern "C" { 40 17 #include_next <malloc.h> // has internal check for multiple expansion 41 18 } // extern "C" 19 20 #include <heap.hfa> 42 21 43 22 // Local Variables: // -
libcfa/src/stdlib.cfa
r3c64c668 r58fe85a 10 10 // Created On : Thu Jan 28 17:10:29 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T ue Feb 4 08:27:08202013 // Update Count : 48612 // Last Modified On : Thu Nov 12 07:46:09 2020 13 // Update Count : 503 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_size23 22 //#include <math.h> // fabsf, fabs, fabsl 24 23 #include <complex.h> // _Complex_I … … 27 26 //--------------------------------------- 28 27 29 forall( dtype T | sized(T) ) { 30 T * alloc_set( T ptr[], size_t dim, char fill ) { // realloc array with fill 31 size_t olen = malloc_usable_size( ptr ); // current allocation 32 void * nptr = (void *)realloc( (void *)ptr, dim * sizeof(T) ); // C realloc 33 size_t nlen = malloc_usable_size( nptr ); // new allocation 34 if ( nlen > olen ) { // larger ? 35 memset( (char *)nptr + olen, (int)fill, nlen - olen ); // initialize added storage 36 } // if 37 return (T *)nptr; 38 } // alloc_set 39 40 T * alloc_align_set( T ptr[], size_t align, char fill ) { // aligned realloc with fill 41 size_t olen = malloc_usable_size( ptr ); // current allocation 42 void * nptr = (void *)realloc( (void *)ptr, align, sizeof(T) ); // CFA realloc 43 // char * nptr = alloc_align( ptr, align ); 44 size_t nlen = malloc_usable_size( nptr ); // new allocation 45 if ( nlen > olen ) { // larger ? 46 memset( (char *)nptr + olen, (int)fill, nlen - olen ); // initialize added storage 47 } // if 48 return (T *)nptr; 49 } // alloc_align_set 50 } // distribution 51 52 // allocation/deallocation and constructor/destructor, non-array types 53 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) 54 T * new( Params p ) { 55 return &(*malloc()){ p }; // run constructor 56 } // new 57 58 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) 59 void delete( T * ptr ) { 60 if ( ptr ) { // ignore null 61 ^(*ptr){}; // run destructor 62 free( ptr ); 63 } // if 64 } // delete 65 66 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } ) 67 void delete( T * ptr, Params rest ) { 68 if ( ptr ) { // ignore null 69 ^(*ptr){}; // run destructor 70 free( ptr ); 71 } // if 72 delete( rest ); 73 } // delete 74 75 76 // allocation/deallocation and constructor/destructor, array types 77 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) 78 T * anew( size_t dim, Params p ) { 28 // Cforall allocation/deallocation and constructor/destructor, array types 29 30 forall( dtype T | sized(T), ttype TT | { void ?{}( T &, TT ); } ) 31 T * anew( size_t dim, TT p ) { 79 32 T * arr = alloc( dim ); 80 33 for ( unsigned int i = 0; i < dim; i += 1 ) { … … 85 38 86 39 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) 87 void adelete( size_t dim,T arr[] ) {40 void adelete( T arr[] ) { 88 41 if ( arr ) { // ignore null 42 size_t dim = malloc_size( arr ) / sizeof( T ); 89 43 for ( int i = dim - 1; i >= 0; i -= 1 ) { // reverse allocation order, must be unsigned 90 44 ^(arr[i]){}; // run destructor … … 94 48 } // adelete 95 49 96 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params); } )97 void adelete( size_t dim, T arr[], Paramsrest ) {50 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype TT | { void adelete( TT ); } ) 51 void adelete( T arr[], TT rest ) { 98 52 if ( arr ) { // ignore null 53 size_t dim = malloc_size( arr ) / sizeof( T ); 99 54 for ( int i = dim - 1; i >= 0; i -= 1 ) { // reverse allocation order, must be unsigned 100 55 ^(arr[i]){}; // run destructor … … 255 210 extern "C" { // override C version 256 211 void srandom( unsigned int seed ) { srand48( (long int)seed ); } 257 long int random( void ) { return mrand48(); } 212 long int random( void ) { return mrand48(); } // GENERATES POSITIVE AND NEGATIVE VALUES 258 213 } // extern "C" 259 214 -
libcfa/src/stdlib.hfa
r3c64c668 r58fe85a 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 08:27:01202013 // Update Count : 40112 // Last Modified On : Sat Dec 12 13:52:34 2020 13 // Update Count : 536 14 14 // 15 15 16 16 #pragma once 17 17 18 #include "bits/defs.hfa" 19 #include "bits/align.hfa" 18 #include "bits/defs.hfa" // OPTIONAL_THREAD 19 #include "bits/align.hfa" // libAlign 20 20 21 21 #include <stdlib.h> // *alloc, strto*, ato* 22 22 #include <heap.hfa> 23 24 // Reduce includes by explicitly defining these routines. 23 25 extern "C" { 24 void * memalign( size_t align, size_t size ); // malloc.h 26 void * memalign( size_t alignment, size_t size ); // malloc.h 27 void * pvalloc( size_t size ); // malloc.h 25 28 void * memset( void * dest, int fill, size_t size ); // string.h 26 29 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 heap28 30 } // extern "C" 29 30 void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap31 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 41 50 static inline forall( dtype T | sized(T) ) { 42 // C dynamic allocation51 // CFA safe equivalents, i.e., implicit size specification 43 52 44 53 T * malloc( void ) { 45 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( (size_t)sizeof(T) ); // C malloc54 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( (size_t)sizeof(T) ); // C allocation 46 55 else return (T *)memalign( _Alignof(T), sizeof(T) ); 47 56 } // malloc 48 57 58 T * aalloc( size_t dim ) { 59 $ARRAY_ALLOC( aalloc, amemalign, dim ); 60 } // aalloc 61 49 62 T * calloc( size_t dim ) { 50 if ( _Alignof(T) <= libAlign() )return (T *)(void *)calloc( dim, sizeof(T) ); // C calloc 51 else return (T *)cmemalign( _Alignof(T), dim, sizeof(T) ); 63 $ARRAY_ALLOC( calloc, cmemalign, dim ); 52 64 } // calloc 53 65 66 T * resize( T * ptr, size_t size ) { // CFA resize, eliminate return-type cast 67 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)resize( (void *)ptr, size ); // CFA resize 68 else return (T *)(void *)resize( (void *)ptr, _Alignof(T), size ); // CFA resize 69 } // resize 70 54 71 T * realloc( T * ptr, size_t size ) { // CFA realloc, eliminate return-type cast 55 return (T *)(void *)realloc( (void *)ptr, size ); // C realloc 72 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)realloc( (void *)ptr, size ); // C realloc 73 else return (T *)(void *)realloc( (void *)ptr, _Alignof(T), size ); // CFA realloc 56 74 } // realloc 57 75 … … 60 78 } // memalign 61 79 80 T * amemalign( size_t align, size_t dim ) { 81 return (T *)amemalign( align, dim, sizeof(T) ); // CFA amemalign 82 } // amemalign 83 62 84 T * cmemalign( size_t align, size_t dim ) { 63 85 return (T *)cmemalign( align, dim, sizeof(T) ); // CFA cmemalign … … 72 94 } // posix_memalign 73 95 74 // Cforall dynamic allocation 75 76 T * alloc( void ) { 77 return malloc(); 78 } // alloc 79 80 T * alloc( size_t dim ) { 81 if ( _Alignof(T) <= libAlign() ) return (T *)(void *)malloc( dim * (size_t)sizeof(T) ); 82 else return (T *)memalign( _Alignof(T), dim * sizeof(T) ); 83 } // alloc 84 85 T * alloc( T ptr[], size_t dim ) { // realloc 86 return (T *)(void *)realloc( (void *)ptr, dim * sizeof(T) ); // C realloc 87 } // alloc 88 89 T * alloc_set( char fill ) { 90 return (T *)memset( (T *)alloc(), (int)fill, sizeof(T) ); // initialize with fill value 91 } // alloc 92 93 T * alloc_set( T fill ) { 94 return (T *)memcpy( (T *)alloc(), &fill, sizeof(T) ); // initialize with fill value 95 } // alloc 96 97 T * alloc_set( size_t dim, char fill ) { 98 return (T *)memset( (T *)alloc( dim ), (int)fill, dim * sizeof(T) ); // initialize with fill value 99 } // alloc 100 101 T * alloc_set( size_t dim, T fill ) { 102 T * r = (T *)alloc( dim ); 103 for ( i; dim ) { memcpy( &r[i], &fill, sizeof(T) ); } // initialize with fill value 104 return r; 105 } // alloc 106 107 T * alloc_set( size_t dim, const T fill[] ) { 108 return (T *)memcpy( (T *)alloc( dim ), fill, dim * sizeof(T) ); // initialize with fill value 109 } // alloc 110 } // distribution 111 112 forall( dtype T | sized(T) ) { 113 T * alloc_set( T ptr[], size_t dim, char fill ); // realloc array with fill 114 } // distribution 96 T * valloc( void ) { 97 return (T *)valloc( sizeof(T) ); // C valloc 98 } // valloc 99 100 T * pvalloc( void ) { 101 return (T *)pvalloc( sizeof(T) ); // C pvalloc 102 } // pvalloc 103 } // distribution 104 105 /* 106 FIX ME : fix alloc interface after Ticker Number 214 is resolved, define and add union to S_fill. Then, modify postfix-fill functions to support T * with nmemb, char, and T object of any size. Finally, change alloc_internal. 107 Or, just follow the instructions below for that. 108 109 1. Replace the current forall-block that contains defintions of S_fill and S_realloc with following: 110 forall( dtype T | sized(T) ) { 111 union U_fill { char c; T * a; T t; }; 112 struct S_fill { char tag; U_fill(T) fill; }; 113 struct S_realloc { inline T *; }; 114 } 115 116 2. Replace all current postfix-fill functions with following for updated S_fill: 117 S_fill(T) ?`fill( char a ) { S_fill(T) ret = {'c'}; ret.fill.c = a; return ret; } 118 S_fill(T) ?`fill( T a ) { S_fill(T) ret = {'t'}; memcpy(&ret.fill.t, &a, sizeof(T)); return ret; } 119 S_fill(T) ?`fill( T a[], size_t nmemb ) { S_fill(T) ret = {'a', nmemb}; ret.fill.a = a; return ret; } 120 121 3. Replace the $alloc_internal function which is outside ttype forall-block with following function: 122 T * $alloc_internal( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill) { 123 T * ptr = NULL; 124 size_t size = sizeof(T); 125 size_t copy_end = 0; 126 127 if(Resize) { 128 ptr = (T*) (void *) resize( (int *)Resize, Align, Dim * size ); 129 } else if (Realloc) { 130 if (Fill.tag != '0') copy_end = min(malloc_size( Realloc ), Dim * size); 131 ptr = (T*) (void *) realloc( (int *)Realloc, Align, Dim * size ); 132 } else { 133 ptr = (T*) (void *) memalign( Align, Dim * size ); 134 } 135 136 if(Fill.tag == 'c') { 137 memset( (char *)ptr + copy_end, (int)Fill.fill.c, Dim * size - copy_end ); 138 } else if(Fill.tag == 't') { 139 for ( int i = copy_end; i <= Dim * size - size ; i += size ) { 140 memcpy( (char *)ptr + i, &Fill.fill.t, size ); 141 } 142 } else if(Fill.tag == 'a') { 143 memcpy( (char *)ptr + copy_end, Fill.fill.a, min(Dim * size - copy_end, size * Fill.nmemb) ); 144 } 145 146 return ptr; 147 } // $alloc_internal 148 */ 149 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}; } 115 160 116 161 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 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 159 234 160 235 static inline forall( dtype T | sized(T) ) { 161 // data, non-array types236 // CFA safe initialization/copy, i.e., implicit size specification, non-array types 162 237 T * memset( T * dest, char fill ) { 163 238 return (T *)memset( dest, fill, sizeof(T) ); … … 167 242 return (T *)memcpy( dest, src, sizeof(T) ); 168 243 } // memcpy 169 } // distribution 170 171 static inline forall( dtype T | sized(T) ) { 172 // data, array types 244 245 // CFA safe initialization/copy, i.e., implicit size specification, array types 173 246 T * amemset( T dest[], char fill, size_t dim ) { 174 247 return (T *)(void *)memset( dest, fill, dim * sizeof(T) ); // C memset … … 180 253 } // distribution 181 254 182 // allocation/deallocation and constructor/destructor, non-array types 183 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p ); 184 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr ); 185 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } ) void delete( T * ptr, Params rest ); 186 187 // allocation/deallocation and constructor/destructor, array types 188 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p ); 189 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] ); 190 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } ) void adelete( size_t dim, T arr[], Params rest ); 255 // CFA deallocation for multiple objects 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 ); 191 290 192 291 //--------------------------------------- … … 254 353 extern "C" { // override C version 255 354 void srandom( unsigned int seed ); 256 long int random( void ); 355 long int random( void ); // GENERATES POSITIVE AND NEGATIVE VALUES 356 // For positive values, use unsigned int, e.g., unsigned int r = random() % 100U; 257 357 } // extern "C" 258 358 … … 261 361 long int random( long int u ) { if ( u < 0 ) return random( u, 0 ); else return random( 0, u ); } // [0,u) 262 362 unsigned long int random( void ) { return lrand48(); } 363 unsigned long int random( unsigned long int u ) { return lrand48() % u; } // [0,u) 263 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) 264 unsigned long int random( unsigned long int u ) { return lrand48() % u; } // [0,u)265 365 266 366 char random( void ) { return (unsigned long int)random(); } … … 283 383 //--------------------------------------- 284 384 285 #include "common.hfa" 286 287 //--------------------------------------- 288 289 extern bool threading_enabled(void) OPTIONAL_THREAD; 385 extern bool threading_enabled( void ) OPTIONAL_THREAD; 290 386 291 387 // Local Variables: // -
libcfa/src/time.hfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Mar 14 23:18:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 4 08:24:32202013 // Update Count : 6 5412 // Last Modified On : Wed Jun 17 16:13:00 2020 13 // Update Count : 663 14 14 // 15 15 … … 20 20 21 21 #include <time.h> // timespec 22 extern "C" {23 22 #include <sys/time.h> // timeval 24 }25 23 #include <time_t.hfa> // Duration/Time types 26 24 … … 91 89 int64_t ?`w( Duration dur ) { return dur.tn / (7LL * 24LL * 60LL * 60LL * TIMEGRAN); } 92 90 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 93 100 Duration max( Duration lhs, Duration rhs ) { return (lhs.tn < rhs.tn) ? rhs : lhs;} 94 101 Duration min( Duration lhs, Duration rhs ) { return !(rhs.tn < lhs.tn) ? lhs : rhs;} -
longrun_tests/Makefile.am
r3c64c668 r58fe85a 18 18 ACLOCAL_AMFLAGS = -I automake 19 19 20 include $(top_srcdir)/ src/cfa.make20 include $(top_srcdir)/tools/build/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 processor stack wait yield46 TESTS = block coroutine create disjoint enter enter3 locks processor stack wait yield 47 47 48 48 # .INTERMEDIATE: $(TESTS) -
src/AST/Attribute.hpp
r3c64c668 r58fe85a 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); 53 55 }; 54 56 -
src/AST/CVQualifiers.hpp
r3c64c668 r58fe85a 27 27 Restrict = 1 << 1, 28 28 Volatile = 1 << 2, 29 Lvalue = 1 << 3, 30 Mutex = 1 << 4, 31 Atomic = 1 << 5, 32 NumQualifiers = 6 29 Mutex = 1 << 3, 30 Atomic = 1 << 4, 31 NumQualifiers = 5 33 32 }; 34 33 35 34 /// Mask for equivalence-preserving qualfiers 36 enum { EquivQualifiers = ~ (Restrict | Lvalue)};35 enum { EquivQualifiers = ~Restrict }; 37 36 38 37 /// Underlying data for qualifiers … … 44 43 bool is_restrict : 1; 45 44 bool is_volatile : 1; 46 bool is_lvalue : 1;47 45 bool is_mutex : 1; 48 46 bool is_atomic : 1; -
src/AST/Convert.cpp
r3c64c668 r58fe85a 9 9 // Author : Thierry Delisle 10 10 // Created On : Thu May 09 15::37::05 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Dec 11 21:39:32 201913 // Update Count : 3 311 // Last Modified By : Andrew Beach 12 // Last Modified On : Thr Nov 12 10:07:00 2020 13 // Update Count : 34 14 14 // 15 15 … … 20 20 21 21 #include "AST/Attribute.hpp" 22 #include "AST/Copy.hpp" 22 23 #include "AST/Decl.hpp" 23 24 #include "AST/Expr.hpp" 24 25 #include "AST/Init.hpp" 25 26 #include "AST/Stmt.hpp" 27 #include "AST/TranslationUnit.hpp" 26 28 #include "AST/TypeSubstitution.hpp" 27 29 … … 46 48 47 49 //================================================================================================ 48 namespace {50 namespace ast { 49 51 50 52 // This is to preserve the FindSpecialDecls hack. It does not (and perhaps should not) 51 53 // allow us to use the same stratagy in the new ast. 52 ast::Type * sizeType = nullptr; 53 ast::FunctionDecl * dereferenceOperator = nullptr; 54 ast::StructDecl * dtorStruct = nullptr; 55 ast::FunctionDecl * dtorStructDestroy = nullptr; 54 // xxx - since convert back pass works, this concern seems to be unnecessary. 55 56 // these need to be accessed in new FixInit now 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; 56 61 57 62 } … … 62 67 using Cache = std::unordered_map< const ast::Node *, BaseSyntaxNode * >; 63 68 Cache cache; 69 70 // Statements can no longer be shared. 71 // however, since StmtExprResult is now implemented, need to still maintain 72 // readonly references. 73 Cache readonlyCache; 64 74 65 75 template<typename T> … … 153 163 } 154 164 155 const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final { 156 auto&& bfwd = get<Expression>().accept1( node->bitfieldWidth ); 157 auto&& type = get<Type>().accept1( node->type ); 158 auto&& init = get<Initializer>().accept1( node->init ); 159 auto&& attr = get<Attribute>().acceptL( node->attributes ); 165 const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final { 160 166 if ( inCache( node ) ) { 161 167 return nullptr; 162 168 } 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 163 173 auto decl = new ObjectDecl( 164 174 node->name, … … 166 176 LinkageSpec::Spec( node->linkage.val ), 167 177 bfwd, 168 type ,169 init,178 type->clone(), 179 nullptr, // prevent infinite loop 170 180 attr, 171 181 Type::FuncSpecifiers( node->funcSpec.val ) 172 182 ); 173 return declWithTypePostamble( decl, node ); 183 184 // handles the case where node->init references itself 185 // xxx - does it really happen? 186 declWithTypePostamble(decl, node); 187 auto init = get<Initializer>().accept1( node->init ); 188 decl->init = init; 189 190 this->node = decl; 191 return nullptr; 174 192 } 175 193 176 194 const ast::DeclWithType * visit( const ast::FunctionDecl * node ) override final { 177 195 if ( inCache( node ) ) return nullptr; 196 197 // function decl contains real variables that the type must use. 198 // the structural change means function type in and out of decl 199 // must be handled **differently** on convert back to old. 200 auto ftype = new FunctionType( 201 cv(node->type), 202 (bool)node->type->isVarArgs 203 ); 204 ftype->returnVals = get<DeclarationWithType>().acceptL(node->returns); 205 ftype->parameters = get<DeclarationWithType>().acceptL(node->params); 206 207 ftype->forall = get<TypeDecl>().acceptL( node->type_params ); 208 if (!node->assertions.empty()) { 209 assert(!ftype->forall.empty()); 210 // find somewhere to place assertions back, for convenience it is the last slot 211 ftype->forall.back()->assertions = get<DeclarationWithType>().acceptL(node->assertions); 212 } 213 214 visitType(node->type, ftype); 215 178 216 auto decl = new FunctionDecl( 179 217 node->name, 180 218 Type::StorageClasses( node->storage.val ), 181 219 LinkageSpec::Spec( node->linkage.val ), 182 get<FunctionType>().accept1( node->type ), 220 ftype, 221 //get<FunctionType>().accept1( node->type ), 183 222 {}, 184 223 get<Attribute>().acceptL( node->attributes ), … … 188 227 decl->statements = get<CompoundStmt>().accept1( node->stmts ); 189 228 decl->withExprs = get<Expression>().acceptL( node->withExprs ); 190 if ( dereferenceOperator == node ) {229 if ( ast::dereferenceOperator == node ) { 191 230 Validate::dereferenceOperator = decl; 192 231 } 193 if ( dtorStructDestroy == node ) {232 if ( ast::dtorStructDestroy == node ) { 194 233 Validate::dtorStructDestroy = decl; 195 234 } … … 199 238 const ast::Decl * namedTypePostamble( NamedTypeDecl * decl, const ast::NamedTypeDecl * node ) { 200 239 // base comes from constructor 201 decl->parameters = get<TypeDecl>().acceptL( node->params );202 240 decl->assertions = get<DeclarationWithType>().acceptL( node->assertions ); 203 241 declPostamble( decl, node ); … … 250 288 ); 251 289 252 if ( dtorStruct == node ) {290 if ( ast::dtorStruct == node ) { 253 291 Validate::dtorStruct = decl; 254 292 } … … 303 341 304 342 const ast::Stmt * stmtPostamble( Statement * stmt, const ast::Stmt * node ) { 305 cache.emplace( node, stmt ); 343 // force statements in old tree to be unique. 344 // cache.emplace( node, stmt ); 345 readonlyCache.emplace( node, stmt ); 306 346 stmt->location = node->location; 307 347 stmt->labels = makeLabelL( stmt, node->labels ); … … 320 360 if ( inCache( node ) ) return nullptr; 321 361 auto stmt = new ExprStmt( nullptr ); 322 cache.emplace( node, stmt );323 362 stmt->expr = get<Expression>().accept1( node->expr ); 324 363 return stmtPostamble( stmt, node ); … … 493 532 } 494 533 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 495 546 const ast::Stmt * visit( const ast::WaitForStmt * node ) override final { 496 547 if ( inCache( node ) ) return nullptr; … … 556 607 557 608 for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) { 558 rslt->add( src_i->first ,609 rslt->add( src_i->first.typeString(), 559 610 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) );565 611 } 566 612 … … 575 621 assert( tgtResnSlots.empty() ); 576 622 577 if ( srcInferred. mode == ast::Expr::InferUnion::Params ) {623 if ( srcInferred.data.inferParams ) { 578 624 const ast::InferredParams &srcParams = srcInferred.inferParams(); 579 625 for (auto & srcParam : srcParams) { … … 581 627 srcParam.second.decl, 582 628 get<Declaration>().accept1(srcParam.second.declptr), 583 get<Type>().accept1(srcParam.second.actualType) ,584 get<Type>().accept1(srcParam.second.formalType) ,585 get<Expression>().accept1(srcParam.second.expr) 629 get<Type>().accept1(srcParam.second.actualType)->clone(), 630 get<Type>().accept1(srcParam.second.formalType)->clone(), 631 get<Expression>().accept1(srcParam.second.expr)->clone() 586 632 )); 587 633 assert(res.second); 588 634 } 589 } else if ( srcInferred.mode == ast::Expr::InferUnion::Slots ) { 635 } 636 if ( srcInferred.data.resnSlots ) { 590 637 const ast::ResnSlots &srcSlots = srcInferred.resnSlots(); 591 638 for (auto srcSlot : srcSlots) { … … 608 655 609 656 tgt->result = get<Type>().accept1(src->result); 657 // Unconditionally use a clone of the result type. 658 // We know this will leak some objects: much of the immediate conversion result. 659 // In some cases, using the conversion result directly gives unintended object sharing. 660 // A parameter (ObjectDecl, a child of a FunctionType) is shared by the weak-ref cache. 661 // But tgt->result must be fully owned privately by tgt. 662 // Applying these conservative copies here means 663 // - weak references point at the declaration's copy, not these expr.result copies (good) 664 // - we copy more objects than really needed (bad, tolerated) 665 if (tgt->result) { 666 tgt->result = tgt->result->clone(); 667 } 610 668 return visitBaseExpr_skipResultType(src, tgt); 611 669 } … … 680 738 new KeywordCastExpr( 681 739 get<Expression>().accept1(node->arg), 682 castTarget 740 castTarget, 741 {node->concrete_target.field, node->concrete_target.getter} 683 742 ) 684 743 ); … … 967 1026 968 1027 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 969 1032 auto rslt = new StmtExpr( 970 get<CompoundStmt>().accept1( node->stmts)1033 get<CompoundStmt>().accept1(stmts) 971 1034 ); 972 1035 973 1036 rslt->returnDecls = get<ObjectDecl>().acceptL(node->returnDecls); 974 1037 rslt->dtors = get<Expression>().acceptL(node->dtors); 1038 if (node->resultExpr) { 1039 // this MUST be found by children visit 1040 rslt->resultExpr = strict_dynamic_cast<ExprStmt *>(readonlyCache.at(node->resultExpr)); 1041 } 975 1042 976 1043 auto expr = visitBaseExpr( node, rslt ); … … 989 1056 990 1057 auto expr = visitBaseExpr( node, rslt ); 991 this->node = expr ;1058 this->node = expr->clone(); 992 1059 return nullptr; 993 1060 } … … 1079 1146 auto type = new BasicType{ cv( node ), (BasicType::Kind)(unsigned)node->kind }; 1080 1147 // I believe this should always be a BasicType. 1081 if ( sizeType == node ) {1148 if ( ast::sizeType == node ) { 1082 1149 Validate::SizeType = type; 1083 1150 } … … 1121 1188 1122 1189 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 1123 1193 auto ty = new FunctionType { 1124 1194 cv( node ), 1125 1195 (bool)node->isVarArgs 1126 1196 }; 1127 ty->returnVals = get<DeclarationWithType>().acceptL( node->returns ); 1128 ty->parameters = get<DeclarationWithType>().acceptL( node->params ); 1129 ty->forall = get<TypeDecl>().acceptL( node->forall ); 1197 auto returns = get<Type>().acceptL(node->returns); 1198 auto params = get<Type>().acceptL(node->params); 1199 1200 int ret_index = 0; 1201 for (auto t: returns) { 1202 // xxx - LinkageSpec shouldn't matter but needs to be something 1203 ObjectDecl * dummy = new ObjectDecl(dummy_returnvar_prefix + std::to_string(ret_index++), {}, LinkageSpec::C, nullptr, t, nullptr); 1204 ty->returnVals.push_back(dummy); 1205 } 1206 int param_index = 0; 1207 for (auto t: params) { 1208 ObjectDecl * dummy = new ObjectDecl(dummy_paramvar_prefix + std::to_string(param_index++), {}, LinkageSpec::C, nullptr, t, nullptr); 1209 ty->parameters.push_back(dummy); 1210 } 1211 1212 // ty->returnVals = get<DeclarationWithType>().acceptL( node->returns ); 1213 // ty->parameters = get<DeclarationWithType>().acceptL( node->params ); 1214 1215 auto types = get<TypeInstType>().acceptL( node->forall ); 1216 for (auto t : types) { 1217 auto newT = new TypeDecl(*t->baseType); 1218 newT->name = t->name; // converted by typeString() 1219 for (auto asst : newT->assertions) delete asst; 1220 newT->assertions.clear(); 1221 ty->forall.push_back(newT); 1222 } 1223 auto assts = get<VariableExpr>().acceptL( node->assertions ); 1224 if (!assts.empty()) { 1225 assert(!types.empty()); 1226 for (auto asst : assts) { 1227 auto newDecl = new ObjectDecl(*strict_dynamic_cast<ObjectDecl*>(asst->var)); 1228 delete newDecl->type; 1229 newDecl->type = asst->result->clone(); 1230 newDecl->storageClasses.is_extern = true; // hack 1231 ty->forall.back()->assertions.push_back(newDecl); 1232 } 1233 } 1234 1130 1235 return visitType( node, ty ); 1131 1236 } 1132 1237 1133 const ast::Type * postvisit( const ast::ReferenceToType * old, ReferenceToType * ty ) { 1134 ty->forall = get<TypeDecl>().acceptL( old->forall ); 1238 const ast::Type * postvisit( const ast::BaseInstType * old, ReferenceToType * ty ) { 1135 1239 ty->parameters = get<Expression>().acceptL( old->params ); 1136 1240 ty->hoistType = old->hoistType; … … 1215 1319 ty = new TypeInstType{ 1216 1320 cv( node ), 1217 node-> name,1321 node->typeString(), 1218 1322 get<TypeDecl>().accept1( node->base ), 1219 1323 get<Attribute>().acceptL( node->attributes ) … … 1222 1326 ty = new TypeInstType{ 1223 1327 cv( node ), 1224 node-> name,1328 node->typeString(), 1225 1329 node->kind == ast::TypeDecl::Ftype, 1226 1330 get<Attribute>().acceptL( node->attributes ) … … 1319 1423 }; 1320 1424 1321 std::list< Declaration * > convert( const std::list< ast::ptr< ast::Decl > >&& translationUnit ) {1425 std::list< Declaration * > convert( const ast::TranslationUnit && translationUnit ) { 1322 1426 ConverterNewToOld c; 1323 1427 std::list< Declaration * > decls; 1324 for(auto d : translationUnit ) {1428 for(auto d : translationUnit.decls) { 1325 1429 decls.emplace_back( c.decl( d ) ); 1326 1430 } … … 1343 1447 ast::Node * node = nullptr; 1344 1448 /// cache of nodes that might be referenced by readonly<> for de-duplication 1345 std::unordered_map< const BaseSyntaxNode *, ast::Node * > cache = {}; 1449 /// in case that some nodes are dropped by conversion (due to possible structural change) 1450 /// use smart pointers in cache value to prevent accidental invalidation. 1451 /// at conversion stage, all created nodes are guaranteed to be unique, therefore 1452 /// const_casting out of smart pointers is permitted. 1453 std::unordered_map< const BaseSyntaxNode *, ast::readonly<ast::Node> > cache = {}; 1346 1454 1347 1455 // Local Utilities: … … 1416 1524 auto it = cache.find( old ); 1417 1525 if ( it == cache.end() ) return false; 1418 node = it->second;1526 node = const_cast<ast::Node *>(it->second.get()); 1419 1527 return true; 1420 1528 } … … 1455 1563 virtual void visit( const FunctionDecl * old ) override final { 1456 1564 if ( inCache( old ) ) return; 1565 auto paramVars = GET_ACCEPT_V(type->parameters, DeclWithType); 1566 auto returnVars = GET_ACCEPT_V(type->returnVals, DeclWithType); 1567 auto forall = GET_ACCEPT_V(type->forall, TypeDecl); 1568 1569 // function type is now derived from parameter decls instead of storing them 1570 1571 /* 1572 auto ftype = new ast::FunctionType((ast::ArgumentFlag)old->type->isVarArgs, cv(old->type)); 1573 ftype->params.reserve(paramVars.size()); 1574 ftype->returns.reserve(returnVars.size()); 1575 1576 for (auto & v: paramVars) { 1577 ftype->params.emplace_back(v->get_type()); 1578 } 1579 for (auto & v: returnVars) { 1580 ftype->returns.emplace_back(v->get_type()); 1581 } 1582 ftype->forall = std::move(forall); 1583 */ 1584 1585 // can function type have attributes? seems not to be the case. 1586 // visitType(old->type, ftype); 1587 1588 // collect assertions and put directly in FunctionDecl 1589 std::vector<ast::ptr<ast::DeclWithType>> assertions; 1590 for (auto & param: forall) { 1591 for (auto & asst: param->assertions) { 1592 assertf(asst->unique(), "newly converted decl must be unique"); 1593 assertions.emplace_back(asst); 1594 } 1595 auto mut = param.get_and_mutate(); 1596 assertf(mut == param, "newly converted decl must be unique"); 1597 mut->assertions.clear(); 1598 } 1599 1457 1600 auto decl = new ast::FunctionDecl{ 1458 1601 old->location, 1459 1602 old->name, 1460 GET_ACCEPT_1(type, FunctionType), 1603 // GET_ACCEPT_1(type, FunctionType), 1604 std::move(forall), 1605 std::move(paramVars), 1606 std::move(returnVars), 1461 1607 {}, 1462 1608 { old->storageClasses.val }, 1463 1609 { old->linkage.val }, 1464 1610 GET_ACCEPT_V(attributes, Attribute), 1465 { old->get_funcSpec().val } 1611 { old->get_funcSpec().val }, 1612 old->type->isVarArgs 1466 1613 }; 1614 1615 // decl->type = ftype; 1467 1616 cache.emplace( old, decl ); 1617 1618 decl->assertions = std::move(assertions); 1468 1619 decl->withExprs = GET_ACCEPT_V(withExprs, Expr); 1469 1620 decl->stmts = GET_ACCEPT_1(statements, CompoundStmt); … … 1478 1629 1479 1630 if ( Validate::dereferenceOperator == old ) { 1480 dereferenceOperator = decl;1631 ast::dereferenceOperator = decl; 1481 1632 } 1482 1633 1483 1634 if ( Validate::dtorStructDestroy == old ) { 1484 dtorStructDestroy = decl;1635 ast::dtorStructDestroy = decl; 1485 1636 } 1486 1637 } … … 1507 1658 1508 1659 if ( Validate::dtorStruct == old ) { 1509 dtorStruct = decl;1660 ast::dtorStruct = decl; 1510 1661 } 1511 1662 } … … 1584 1735 cache.emplace( old, decl ); 1585 1736 decl->assertions = GET_ACCEPT_V(assertions, DeclWithType); 1586 decl->params = GET_ACCEPT_V(parameters, TypeDecl);1587 1737 decl->extension = old->extension; 1588 1738 decl->uniqueId = old->uniqueId; … … 1600 1750 ); 1601 1751 decl->assertions = GET_ACCEPT_V(assertions, DeclWithType); 1602 decl->params = GET_ACCEPT_V(parameters, TypeDecl);1603 1752 decl->extension = old->extension; 1604 1753 decl->uniqueId = old->uniqueId; … … 1859 2008 } 1860 2009 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 1861 2028 virtual void visit( const WaitForStmt * old ) override final { 1862 2029 if ( inCache( old ) ) return; … … 1932 2099 } 1933 2100 2101 // TypeSubstitution shouldn't exist yet in old. 1934 2102 ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) { 1935 2103 1936 2104 if (!old) return nullptr; 1937 2105 if (old->empty()) return nullptr; 2106 assert(false); 2107 2108 /* 1938 2109 ast::TypeSubstitution *rslt = new ast::TypeSubstitution(); 1939 2110 … … 1943 2114 } 1944 2115 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 1950 2116 return rslt; 2117 */ 1951 2118 } 1952 2119 … … 1956 2123 1957 2124 assert( oldInferParams.empty() || oldResnSlots.empty() ); 1958 assert( newInferred.mode == ast::Expr::InferUnion::Empty );2125 // assert( newInferred.mode == ast::Expr::InferUnion::Empty ); 1959 2126 1960 2127 if ( !oldInferParams.empty() ) { … … 2039 2206 old->location, 2040 2207 GET_ACCEPT_1(arg, Expr), 2041 castTarget 2208 castTarget, 2209 {old->concrete_target.field, old->concrete_target.getter} 2042 2210 ) 2043 2211 ); … … 2087 2255 old->location, 2088 2256 GET_ACCEPT_1(member, DeclWithType), 2089 GET_ACCEPT_1(aggregate, Expr) 2257 GET_ACCEPT_1(aggregate, Expr), 2258 ast::MemberExpr::NoOpConstructionChosen 2090 2259 ) 2091 2260 ); … … 2419 2588 // I believe this should always be a BasicType. 2420 2589 if ( Validate::SizeType == old ) { 2421 sizeType = type;2590 ast::sizeType = type; 2422 2591 } 2423 2592 visitType( old, type ); … … 2464 2633 cv( old ) 2465 2634 }; 2466 ty->returns = GET_ACCEPT_V( returnVals, DeclWithType ); 2467 ty->params = GET_ACCEPT_V( parameters, DeclWithType ); 2468 ty->forall = GET_ACCEPT_V( forall, TypeDecl ); 2635 auto returnVars = GET_ACCEPT_V(returnVals, DeclWithType); 2636 auto paramVars = GET_ACCEPT_V(parameters, DeclWithType); 2637 // ty->returns = GET_ACCEPT_V( returnVals, DeclWithType ); 2638 // ty->params = GET_ACCEPT_V( parameters, DeclWithType ); 2639 for (auto & v: returnVars) { 2640 ty->returns.emplace_back(v->get_type()); 2641 } 2642 for (auto & v: paramVars) { 2643 ty->params.emplace_back(v->get_type()); 2644 } 2645 // xxx - when will this be non-null? 2646 // will have to create dangling (no-owner) decls to be pointed to 2647 auto foralls = GET_ACCEPT_V( forall, TypeDecl ); 2648 2649 for (auto & param : foralls) { 2650 ty->forall.emplace_back(new ast::TypeInstType(param->name, param)); 2651 for (auto asst : param->assertions) { 2652 ty->assertions.emplace_back(new ast::VariableExpr({}, asst)); 2653 } 2654 } 2469 2655 visitType( old, ty ); 2470 2656 } 2471 2657 2472 void postvisit( const ReferenceToType * old, ast::ReferenceToType * ty ) { 2473 ty->forall = GET_ACCEPT_V( forall, TypeDecl ); 2658 void postvisit( const ReferenceToType * old, ast::BaseInstType * ty ) { 2474 2659 ty->params = GET_ACCEPT_V( parameters, Expr ); 2475 2660 ty->hoistType = old->hoistType; … … 2616 2801 old->location, 2617 2802 GET_ACCEPT_1(value, Expr), 2618 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast:: DoConstruct2803 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::NoConstruct 2619 2804 ); 2620 2805 } … … 2625 2810 GET_ACCEPT_V(initializers, Init), 2626 2811 GET_ACCEPT_V(designations, Designation), 2627 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast:: DoConstruct2812 (old->get_maybeConstructed()) ? ast::MaybeConstruct : ast::NoConstruct 2628 2813 ); 2629 2814 } … … 2656 2841 #undef GET_ACCEPT_1 2657 2842 2658 std::list< ast::ptr< ast::Decl > >convert( const std::list< Declaration * > && translationUnit ) {2843 ast::TranslationUnit convert( const std::list< Declaration * > && translationUnit ) { 2659 2844 ConverterOldToNew c; 2660 std::list< ast::ptr< ast::Decl > > decls; 2845 ast::TranslationUnit unit; 2846 if (Validate::SizeType) { 2847 // this should be a BasicType. 2848 auto old = strict_dynamic_cast<BasicType *>(Validate::SizeType); 2849 ast::sizeType = new ast::BasicType{ (ast::BasicType::Kind)(unsigned)old->kind }; 2850 } 2851 2661 2852 for(auto d : translationUnit) { 2662 2853 d->accept( c ); 2663 decls.emplace_back( c.decl() );2854 unit.decls.emplace_back( c.decl() ); 2664 2855 } 2665 2856 deleteAll(translationUnit); 2666 return decls; 2857 2858 // Load the local static varables into the global store. 2859 unit.global.sizeType = ast::sizeType; 2860 unit.global.dereference = ast::dereferenceOperator; 2861 unit.global.dtorStruct = ast::dtorStruct; 2862 unit.global.dtorDestroy = ast::dtorStructDestroy; 2863 2864 return unit; 2667 2865 } -
src/AST/Convert.hpp
r3c64c668 r58fe85a 18 18 #include <list> 19 19 20 #include "AST/Node.hpp"21 22 20 class Declaration; 23 21 namespace ast { 24 class Decl;22 struct TranslationUnit; 25 23 }; 26 24 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 );25 std::list< Declaration * > convert( const ast::TranslationUnit && translationUnit ); 26 ast::TranslationUnit convert( const std::list< Declaration * > && translationUnit ); -
src/AST/Decl.cpp
r3c64c668 r58fe85a 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 51 72 const Type * FunctionDecl::get_type() const { return type.get(); } 52 void FunctionDecl::set_type(Type * t) { type = strict_dynamic_cast< FunctionType* >( t ); } 73 void FunctionDecl::set_type( const Type * t ) { 74 type = strict_dynamic_cast< const FunctionType * >( t ); 75 } 53 76 54 77 // --- TypeDecl -
src/AST/Decl.hpp
r3c64c668 r58fe85a 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 template<typename node_t> friend node_t * mutate(const node_t * node); 35 #define MUTATE_FRIEND \ 36 template<typename node_t> friend node_t * mutate(const node_t * node); \ 37 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 36 38 37 39 namespace ast { … … 77 79 ptr<Expr> asmName; 78 80 bool isDeleted = false; 81 bool isTypeFixed = false; 79 82 80 83 DeclWithType( const CodeLocation& loc, const std::string& name, Storage::Classes storage, … … 88 91 virtual const Type * get_type() const = 0; 89 92 /// Set type of this declaration. May be verified by subclass 90 virtual void set_type( Type *) = 0;93 virtual void set_type( const Type * ) = 0; 91 94 92 95 const DeclWithType * accept( Visitor & v ) const override = 0; … … 111 114 112 115 const Type* get_type() const override { return type; } 113 void set_type( Type * ty ) override { type = ty; }116 void set_type( const Type * ty ) override { type = ty; } 114 117 115 118 const DeclWithType * accept( Visitor& v ) const override { return v.visit( this ); } … … 122 125 class FunctionDecl : public DeclWithType { 123 126 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 declarations 124 132 ptr<FunctionType> type; 125 133 ptr<CompoundStmt> stmts; 126 134 std::vector< ptr<Expr> > withExprs; 127 135 128 FunctionDecl( const CodeLocation & loc, const std::string & name, FunctionType * type, 136 137 FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall, 138 std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns, 129 139 CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::C, 130 std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {} )131 : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), type( type),132 stmts( stmts ) {}140 std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, bool isVarArgs = false); 141 // : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)), 142 // stmts( stmts ) {} 133 143 134 144 const Type * get_type() const override; 135 void set_type( Type * t) override;145 void set_type( const Type * t ) override; 136 146 137 147 bool has_body() const { return stmts; } … … 147 157 public: 148 158 ptr<Type> base; 149 std::vector<ptr<TypeDecl>> params;150 159 std::vector<ptr<DeclWithType>> assertions; 151 160 152 NamedTypeDecl( const CodeLocation& loc, const std::string& name, Storage::Classes storage, 153 Type* b, Linkage::Spec spec = Linkage::Cforall ) 154 : Decl( loc, name, storage, spec ), base( b ), params(), assertions() {} 161 NamedTypeDecl( 162 const CodeLocation & loc, const std::string & name, Storage::Classes storage, 163 const Type * b, Linkage::Spec spec = Linkage::Cforall ) 164 : Decl( loc, name, storage, spec ), base( b ), assertions() {} 155 165 156 166 /// Produces a name for the kind of alias … … 186 196 }; 187 197 188 TypeDecl( const CodeLocation & loc, const std::string & name, Storage::Classes storage, Type * b, 189 Kind k, bool s, Type * i = nullptr ) 190 : NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == Ttype || s ), 191 init( i ) {} 198 TypeDecl( 199 const CodeLocation & loc, const std::string & name, Storage::Classes storage, 200 const Type * b, TypeDecl::Kind k, bool s, const Type * i = nullptr ) 201 : NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == TypeDecl::Ttype || s ), 202 init( i ) {} 192 203 193 204 const char * typeString() const override; … … 259 270 260 271 bool is_coroutine() { return kind == Coroutine; } 261 bool is_monitor() { return kind == Monitor; } 262 bool is_thread() { return kind == Thread; } 272 bool is_generator() { return kind == Generator; } 273 bool is_monitor () { return kind == Monitor ; } 274 bool is_thread () { return kind == Thread ; } 263 275 264 276 const Decl * accept( Visitor & v ) const override { return v.visit( this ); } -
src/AST/DeclReplacer.cpp
r3c64c668 r58fe85a 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 }; 40 50 } 41 51 … … 54 64 DeclMap declMap; 55 65 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 ); 56 71 } 57 72 … … 88 103 return ninst; 89 104 } 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 90 112 } 91 113 } -
src/AST/DeclReplacer.hpp
r3c64c668 r58fe85a 23 23 class DeclWithType; 24 24 class TypeDecl; 25 class Expr; 25 26 26 27 namespace DeclReplacer { 27 28 using DeclMap = std::unordered_map< const DeclWithType *, const DeclWithType * >; 28 29 using TypeMap = std::unordered_map< const TypeDecl *, const TypeDecl * >; 30 using ExprMap = std::unordered_map< const DeclWithType *, const Expr * >; 29 31 30 32 const Node * replace( const Node * node, const DeclMap & declMap, bool debug = false ); 31 33 const Node * replace( const Node * node, const TypeMap & typeMap, bool debug = false ); 32 34 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); 33 36 } 34 37 } -
src/AST/Expr.cpp
r3c64c668 r58fe85a 20 20 #include <vector> 21 21 22 #include "Copy.hpp" // for shallowCopy 23 #include "Eval.hpp" // for call 22 24 #include "GenericSubstitution.hpp" 25 #include "LinkageSpec.hpp" 23 26 #include "Stmt.hpp" 24 27 #include "Type.hpp" … … 27 30 #include "Common/SemanticError.h" 28 31 #include "GenPoly/Lvalue.h" // for referencesPermissable 29 #include "InitTweak/InitTweak.h" // for get PointerBase32 #include "InitTweak/InitTweak.h" // for getFunction, getPointerBase 30 33 #include "ResolvExpr/typeops.h" // for extractResultType 31 34 #include "Tuples/Tuples.h" // for makeTupleType 32 35 33 36 namespace ast { 37 38 namespace { 39 std::set<std::string> const lvalueFunctionNames = {"*?", "?[?]"}; 40 } 41 42 // --- Expr 43 bool Expr::get_lvalue() const { 44 return false; 45 } 34 46 35 47 // --- ApplicationExpr … … 46 58 } 47 59 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 48 67 // --- UntypedExpr 49 68 50 UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, Expr * arg ) {69 UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, const Expr * arg ) { 51 70 assert( arg ); 52 71 53 UntypedExpr * ret = new UntypedExpr{ 54 loc, new NameExpr{loc, "*?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ arg } } 55 }; 72 UntypedExpr * ret = call( loc, "*?", arg ); 56 73 if ( const Type * ty = arg->result ) { 57 74 const Type * base = InitTweak::getPointerBase( ty ); … … 65 82 // base type 66 83 ret->result = base; 67 add_qualifiers( ret->result, CV::Lvalue );68 84 } 69 85 } … … 71 87 } 72 88 73 UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs ) { 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 ) { 74 95 assert( lhs && rhs ); 75 96 76 UntypedExpr * ret = new UntypedExpr{ 77 loc, new NameExpr{loc, "?=?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ lhs }, ptr<Expr>{ rhs } } 78 }; 97 UntypedExpr * ret = call( loc, "?=?", lhs, rhs ); 79 98 if ( lhs->result && rhs->result ) { 80 99 // if both expressions are typed, assumes that this assignment is a C bitwise assignment, … … 83 102 } 84 103 return ret; 104 } 105 106 // --- VariableExpr 107 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 pointer 126 VariableExpr * funcExpr = new VariableExpr{ loc, decl }; 127 funcExpr->result = new PointerType{ funcExpr->result }; 128 return funcExpr; 85 129 } 86 130 … … 108 152 AddressExpr::AddressExpr( const CodeLocation & loc, const Expr * a ) : Expr( loc ), arg( a ) { 109 153 if ( arg->result ) { 110 if ( arg-> result->is_lvalue() ) {154 if ( arg->get_lvalue() ) { 111 155 // lvalue, retains all levels of reference, and gains a pointer inside the references 112 156 Type * res = addrType( arg->result ); 113 res->set_lvalue( false ); // result of & is never an lvalue114 157 result = res; 115 158 } else { … … 118 161 dynamic_cast< const ReferenceType * >( arg->result.get() ) ) { 119 162 Type * res = addrType( refType->base ); 120 res->set_lvalue( false ); // result of & is never an lvalue121 163 result = res; 122 164 } else { … … 139 181 : Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ) {} 140 182 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 141 188 // --- KeywordCastExpr 142 189 143 190 const char * KeywordCastExpr::targetString() const { 144 191 return AggregateDecl::aggrString( target ); 192 } 193 194 // --- UntypedMemberExpr 195 196 bool UntypedMemberExpr::get_lvalue() const { 197 return aggregate->get_lvalue(); 145 198 } 146 199 … … 153 206 assert( aggregate->result ); 154 207 155 // take ownership of member type156 208 result = mem->get_type(); 209 157 210 // substitute aggregate generic parameters into member type 158 211 genericSubstitution( aggregate->result ).apply( result ); 159 // ensure lvalue and appropriate restrictions from aggregate type 160 add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue ); 161 } 162 163 // --- VariableExpr 164 165 VariableExpr::VariableExpr( const CodeLocation & loc ) 166 : Expr( loc ), var( nullptr ) {} 167 168 VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v ) 169 : Expr( loc ), var( v ) { 170 assert( var ); 171 assert( var->get_type() ); 172 result = var->get_type(); 173 add_qualifiers( result, CV::Lvalue ); 174 } 175 176 VariableExpr * VariableExpr::functionPointer( 177 const CodeLocation & loc, const FunctionDecl * decl ) { 178 // wrap usually-determined result type in a pointer 179 VariableExpr * funcExpr = new VariableExpr{ loc, decl }; 180 funcExpr->result = new PointerType{ funcExpr->result }; 181 return funcExpr; 212 // ensure appropriate restrictions from aggregate type 213 add_qualifiers( result, aggregate->result->qualifiers ); 214 } 215 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; 182 228 } 183 229 … … 258 304 : Expr( loc, new BasicType{ BasicType::SignedInt } ), arg1( a1 ), arg2( a2 ), isAnd( ia ) {} 259 305 306 // --- CommaExpr 307 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 260 313 // --- ConstructorExpr 261 314 … … 276 329 assert( t && i ); 277 330 result = t; 278 add_qualifiers( result, CV::Lvalue ); 331 } 332 333 bool CompoundLiteralExpr::get_lvalue() const { 334 return true; 279 335 } 280 336 … … 293 349 // like MemberExpr, TupleIndexExpr is always an lvalue 294 350 result = type->types[ index ]; 295 add_qualifiers( result, CV::Lvalue ); 351 } 352 353 bool TupleIndexExpr::get_lvalue() const { 354 return tuple->get_lvalue(); 296 355 } 297 356 -
src/AST/Expr.hpp
r3c64c668 r58fe85a 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 template<typename node_t> friend node_t * mutate(const node_t * node); 33 #define MUTATE_FRIEND \ 34 template<typename node_t> friend node_t * mutate(const node_t * node); \ 35 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 36 34 37 35 38 class ConverterOldToNew; … … 42 45 struct ParamEntry { 43 46 UniqueId decl; 44 ptr<Decl> declptr;47 readonly<Decl> declptr; 45 48 ptr<Type> actualType; 46 49 ptr<Type> formalType; … … 62 65 class Expr : public ParseNode { 63 66 public: 64 /// Saves space (~16 bytes) by combining ResnSlots and InferredParams 67 /* 68 * NOTE: the union approach is incorrect until the case of 69 * partial resolution in InferMatcher is eliminated. 70 * it is reverted to allow unresolved and resolved parameters 71 * to coexist in an expression node. 72 */ 65 73 struct InferUnion { 74 // mode is now unused 66 75 enum { Empty, Slots, Params } mode; 67 union data_t { 68 char def; 69 ResnSlots resnSlots; 70 InferredParams inferParams; 71 72 data_t() : def('\0') {} 73 ~data_t() {} 76 struct data_t { 77 // char def; 78 ResnSlots * resnSlots; 79 InferredParams * inferParams; 80 81 data_t(): resnSlots(nullptr), inferParams(nullptr) {} 82 data_t(const data_t &other) = delete; 83 ~data_t() { 84 delete resnSlots; 85 delete inferParams; 86 } 74 87 } data; 75 88 76 89 /// initializes from other InferUnion 77 90 void init_from( const InferUnion& o ) { 78 switch ( o.mode ) { 79 case Empty: return; 80 case Slots: new(&data.resnSlots) ResnSlots{ o.data.resnSlots }; return; 81 case Params: new(&data.inferParams) InferredParams{ o.data.inferParams }; return; 91 if (o.data.resnSlots) { 92 data.resnSlots = new ResnSlots(*o.data.resnSlots); 93 } 94 if (o.data.inferParams) { 95 data.inferParams = new InferredParams(*o.data.inferParams); 82 96 } 83 97 } … … 85 99 /// initializes from other InferUnion (move semantics) 86 100 void init_from( InferUnion&& o ) { 87 switch ( o.mode ) { 88 case Empty: return; 89 case Slots: new(&data.resnSlots) ResnSlots{ std::move(o.data.resnSlots) }; return; 90 case Params: 91 new(&data.inferParams) InferredParams{ std::move(o.data.inferParams) }; return; 92 } 93 } 94 95 /// clears variant fields 96 void reset() { 97 switch( mode ) { 98 case Empty: return; 99 case Slots: data.resnSlots.~ResnSlots(); return; 100 case Params: data.inferParams.~InferredParams(); return; 101 } 101 data.resnSlots = o.data.resnSlots; 102 data.inferParams = o.data.inferParams; 103 o.data.resnSlots = nullptr; 104 o.data.inferParams = nullptr; 102 105 } 103 106 … … 107 110 InferUnion& operator= ( const InferUnion& ) = delete; 108 111 InferUnion& operator= ( InferUnion&& ) = delete; 109 ~InferUnion() { reset(); } 112 113 bool hasSlots() const { return data.resnSlots; } 114 bool hasParams() const { return data.inferParams; } 110 115 111 116 ResnSlots& resnSlots() { 112 switch (mode) { 113 case Empty: new(&data.resnSlots) ResnSlots{}; mode = Slots; // fallthrough 114 case Slots: return data.resnSlots; 115 case Params: assertf(false, "Cannot return to resnSlots from Params"); abort(); 117 if (!data.resnSlots) { 118 data.resnSlots = new ResnSlots(); 116 119 } 117 assertf(false, "unreachable");120 return *data.resnSlots; 118 121 } 119 122 120 123 const ResnSlots& resnSlots() const { 121 if ( mode ==Slots) {122 return data.resnSlots;124 if (data.resnSlots) { 125 return *data.resnSlots; 123 126 } 124 127 assertf(false, "Mode was not already resnSlots"); … … 127 130 128 131 InferredParams& inferParams() { 129 switch (mode) { 130 case Slots: data.resnSlots.~ResnSlots(); // fallthrough 131 case Empty: new(&data.inferParams) InferredParams{}; mode = Params; // fallthrough 132 case Params: return data.inferParams; 132 if (!data.inferParams) { 133 data.inferParams = new InferredParams(); 133 134 } 134 assertf(false, "unreachable");135 return *data.inferParams; 135 136 } 136 137 137 138 const InferredParams& inferParams() const { 138 if ( mode ==Params) {139 return data.inferParams;139 if (data.inferParams) { 140 return *data.inferParams; 140 141 } 141 142 assertf(false, "Mode was not already Params"); … … 143 144 } 144 145 145 void set_inferParams( InferredParams && ps ) { 146 switch(mode) { 147 case Slots: 148 data.resnSlots.~ResnSlots(); 149 // fallthrough 150 case Empty: 151 new(&data.inferParams) InferredParams{ std::move( ps ) }; 152 mode = Params; 153 break; 154 case Params: 155 data.inferParams = std::move( ps ); 156 break; 157 } 146 void set_inferParams( InferredParams * ps ) { 147 delete data.resnSlots; 148 data.resnSlots = nullptr; 149 delete data.inferParams; 150 data.inferParams = ps; 158 151 } 159 152 … … 161 154 /// and the other is in `Params`. 162 155 void splice( InferUnion && o ) { 163 if ( o.mode == Empty ) return; 164 if ( mode == Empty ) { init_from( o ); return; } 165 assert( mode == o.mode && "attempt to splice incompatible InferUnion" ); 166 167 if ( mode == Slots ){ 168 data.resnSlots.insert( 169 data.resnSlots.end(), o.data.resnSlots.begin(), o.data.resnSlots.end() ); 170 } else if ( mode == Params ) { 171 for ( const auto & p : o.data.inferParams ) { 172 data.inferParams[p.first] = std::move(p.second); 156 if (o.data.resnSlots) { 157 if (data.resnSlots) { 158 data.resnSlots->insert( 159 data.resnSlots->end(), o.data.resnSlots->begin(), o.data.resnSlots->end() ); 160 delete o.data.resnSlots; 173 161 } 174 } else assertf(false, "invalid mode"); 162 else { 163 data.resnSlots = o.data.resnSlots; 164 } 165 o.data.resnSlots = nullptr; 166 } 167 168 if (o.data.inferParams) { 169 if (data.inferParams) { 170 for ( const auto & p : *o.data.inferParams ) { 171 (*data.inferParams)[p.first] = std::move(p.second); 172 } 173 delete o.data.inferParams; 174 } 175 else { 176 data.inferParams = o.data.inferParams; 177 } 178 o.data.inferParams = nullptr; 179 } 175 180 } 176 181 }; … … 185 190 186 191 Expr * set_extension( bool ex ) { extension = ex; return this; } 192 virtual bool get_lvalue() const; 187 193 188 194 virtual const Expr * accept( Visitor & v ) const override = 0; … … 201 207 ApplicationExpr( const CodeLocation & loc, const Expr * f, std::vector<ptr<Expr>> && as = {} ); 202 208 209 bool get_lvalue() const final; 210 203 211 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 204 212 private: … … 216 224 : Expr( loc ), func( f ), args( std::move(as) ) {} 217 225 226 bool get_lvalue() const final; 227 218 228 /// Creates a new dereference expression 219 static UntypedExpr * createDeref( const CodeLocation & loc, Expr * arg );229 static UntypedExpr * createDeref( const CodeLocation & loc, const Expr * arg ); 220 230 /// Creates a new assignment expression 221 static UntypedExpr * createAssign( const CodeLocation & loc, Expr * lhs,Expr * rhs );231 static UntypedExpr * createAssign( const CodeLocation & loc, const Expr * lhs, const Expr * rhs ); 222 232 223 233 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 241 251 }; 242 252 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 function 264 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_FRIEND 270 }; 271 243 272 /// Address-of expression `&e` 244 273 class AddressExpr final : public Expr { … … 271 300 }; 272 301 273 /// Whether a cast existed in the program source or not 302 /// Inidicates whether the cast is introduced by the CFA type system. 303 /// GeneratedCast for casts that the resolver introduces to force a return type 304 /// ExplicitCast for casts from user code 305 /// ExplicitCast for casts from desugaring advanced CFA features into simpler CFA 306 /// example 307 /// int * p; // declaration 308 /// (float *) p; // use, with subject cast 309 /// subject cast being GeneratedCast means we are considering an interpretation with a type mismatch 310 /// subject cast being ExplicitCast means someone in charge wants it that way 274 311 enum GeneratedFlag { ExplicitCast, GeneratedCast }; 275 312 … … 291 328 CastExpr( const Expr * a ) : CastExpr( a->location, a, GeneratedCast ) {} 292 329 330 bool get_lvalue() const final; 331 293 332 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 294 333 private: … … 301 340 public: 302 341 ptr<Expr> arg; 342 struct Concrete { 343 std::string field; 344 std::string getter; 345 346 Concrete() = default; 347 Concrete(const Concrete &) = default; 348 }; 303 349 ast::AggregateDecl::Aggregate target; 350 Concrete concrete_target; 351 304 352 305 353 KeywordCastExpr( const CodeLocation & loc, const Expr * a, ast::AggregateDecl::Aggregate t ) 306 354 : Expr( loc ), arg( a ), target( t ) {} 307 355 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 308 359 /// Get a name for the target type 309 360 const char * targetString() const; … … 338 389 : Expr( loc ), member( mem ), aggregate( agg ) { assert( aggregate ); } 339 390 391 bool get_lvalue() const final; 392 340 393 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 341 394 private: … … 352 405 MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg ); 353 406 407 bool get_lvalue() const final; 408 354 409 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 355 410 private: 356 411 MemberExpr * clone() const override { return new MemberExpr{ *this }; } 357 412 MUTATE_FRIEND 358 }; 359 360 /// A reference to a named variable. 361 class VariableExpr final : public Expr { 362 public: 363 readonly<DeclWithType> var; 364 365 VariableExpr( const CodeLocation & loc ); 366 VariableExpr( const CodeLocation & loc, const DeclWithType * v ); 367 368 /// generates a function pointer for a given function 369 static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl ); 370 371 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 372 private: 373 VariableExpr * clone() const override { return new VariableExpr{ *this }; } 374 MUTATE_FRIEND 413 414 // Custructor overload meant only for AST conversion 415 enum NoOpConstruction { NoOpConstructionChosen }; 416 MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg, 417 NoOpConstruction overloadSelector ); 418 friend class ::ConverterOldToNew; 419 friend class ::ConverterNewToOld; 375 420 }; 376 421 … … 386 431 const CodeLocation & loc, const Type * ty, const std::string & r, 387 432 std::optional<unsigned long long> i ) 388 : Expr( loc, ty ), rep( r ), ival( i ) {}433 : Expr( loc, ty ), rep( r ), ival( i ), underlyer(ty) {} 389 434 390 435 /// Gets the integer value of this constant, if one is appropriate to its type. … … 532 577 533 578 CommaExpr( const CodeLocation & loc, const Expr * a1, const Expr * a2 ) 534 : Expr( loc ), arg1( a1 ), arg2( a2 ) {} 579 : Expr( loc ), arg1( a1 ), arg2( a2 ) { 580 this->result = a2->result; 581 } 582 583 bool get_lvalue() const final; 535 584 536 585 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 577 626 578 627 ImplicitCopyCtorExpr( const CodeLocation& loc, const ApplicationExpr * call ) 579 : Expr( loc, call->result ) { assert( call); }628 : Expr( loc, call->result ), callExpr(call) { assert( call ); assert(call->result); } 580 629 581 630 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 605 654 CompoundLiteralExpr( const CodeLocation & loc, const Type * t, const Init * i ); 606 655 656 bool get_lvalue() const final; 657 607 658 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } 608 659 private: … … 660 711 661 712 TupleIndexExpr( const CodeLocation & loc, const Expr * t, unsigned i ); 713 714 bool get_lvalue() const final; 662 715 663 716 const Expr * accept( Visitor & v ) const override { return v.visit( this ); } … … 698 751 std::vector<ptr<Expr>> dtors; ///< destructor(s) for return variable(s) 699 752 753 readonly<ExprStmt> resultExpr; 754 700 755 StmtExpr( const CodeLocation & loc, const CompoundStmt * ss ); 701 756 -
src/AST/Fwd.hpp
r3c64c668 r58fe85a 10 10 // Created On : Wed May 8 16:05:00 2019 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Jun 24 09:48:00 201913 // Update Count : 112 // Last Modified On : Thr Jul 23 14:15:00 2020 13 // Update Count : 2 14 14 // 15 15 … … 53 53 class CatchStmt; 54 54 class FinallyStmt; 55 class SuspendStmt; 55 56 class WaitForStmt; 56 57 class WithStmt; … … 106 107 class QualifiedType; 107 108 class FunctionType; 108 class ReferenceToType; 109 class StructInstType; 110 class UnionInstType; 111 class EnumInstType; 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>; 112 114 class TraitInstType; 113 115 class TypeInstType; … … 135 137 typedef unsigned int UniqueId; 136 138 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 137 146 } -
src/AST/GenericSubstitution.cpp
r3c64c668 r58fe85a 42 42 private: 43 43 // make substitution for generic type 44 void makeSub( const ReferenceToType * ty ) {44 void makeSub( const BaseInstType * 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. pass.sub);64 return std::move(builder.core.sub); 65 65 } 66 66 -
src/AST/Init.hpp
r3c64c668 r58fe85a 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 template<typename node_t> friend node_t * mutate(const node_t * node); 27 #define MUTATE_FRIEND \ 28 template<typename node_t> friend node_t * mutate(const node_t * node); \ 29 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 28 30 29 31 namespace ast { … … 48 50 49 51 /// Flag for whether to construct from initialzier 50 enum ConstructFlag { DoConstruct, MaybeConstruct };52 enum ConstructFlag { NoConstruct, MaybeConstruct }; 51 53 52 54 /// Object initializer base class … … 69 71 ptr<Expr> value; 70 72 71 SingleInit( const CodeLocation & loc, const Expr * val, ConstructFlag mc = DoConstruct )73 SingleInit( const CodeLocation & loc, const Expr * val, ConstructFlag mc = NoConstruct ) 72 74 : Init( loc, mc ), value( val ) {} 73 75 … … 88 90 89 91 ListInit( const CodeLocation & loc, std::vector<ptr<Init>> && is, 90 std::vector<ptr<Designation>> && ds = {}, ConstructFlag mc = DoConstruct );92 std::vector<ptr<Designation>> && ds = {}, ConstructFlag mc = NoConstruct ); 91 93 92 94 using iterator = std::vector<ptr<Init>>::iterator; … … 116 118 ConstructorInit( 117 119 const CodeLocation & loc, const Stmt * ctor, const Stmt * dtor, const Init * init ) 118 : Init( loc, DoConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {}120 : Init( loc, MaybeConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {} 119 121 120 122 const Init * accept( Visitor & v ) const override { return v.visit( this ); } -
src/AST/Node.cpp
r3c64c668 r58fe85a 9 9 // Author : Thierry Delisle 10 10 // Created On : Thu May 16 14:16:00 2019 11 // Last Modified By : 12 // Last Modified On : 13 // Update Count : 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Jun 5 10:21:00 2020 13 // Update Count : 1 14 14 // 15 15 … … 17 17 #include "Fwd.hpp" 18 18 19 #include <csignal> // MEMORY DEBUG -- for raise 19 20 #include <iostream> 20 21 … … 29 30 #include "Print.hpp" 30 31 31 template< typename node_t, enum ast::Node::ref_type ref_t > 32 void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) { node->increment(ref_t); } 33 34 template< typename node_t, enum ast::Node::ref_type ref_t > 35 void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node ) { node->decrement(ref_t); } 36 37 template< typename node_t, enum ast::Node::ref_type ref_t > 38 void ast::ptr_base<node_t, ref_t>::_check() const { if(node) assert(node->was_ever_strong == false || node->strong_count > 0); } 32 /// MEMORY DEBUG -- allows breaking on ref-count changes of dynamically chosen object. 33 /// Process to use in GDB: 34 /// break ast::Node::_trap() 35 /// run 36 /// set variable MEM_TRAP_OBJ = <target> 37 /// disable <first breakpoint> 38 /// continue 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 } 39 79 40 80 template< typename node_t, enum ast::Node::ref_type ref_t > … … 226 266 template class ast::ptr_base< ast::FunctionType, ast::Node::ref_type::weak >; 227 267 template class ast::ptr_base< ast::FunctionType, ast::Node::ref_type::strong >; 228 template class ast::ptr_base< ast:: ReferenceToType, ast::Node::ref_type::weak >;229 template class ast::ptr_base< ast:: ReferenceToType, ast::Node::ref_type::strong >;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 >; 230 270 template class ast::ptr_base< ast::StructInstType, ast::Node::ref_type::weak >; 231 271 template class ast::ptr_base< ast::StructInstType, ast::Node::ref_type::strong >; -
src/AST/Node.hpp
r3c64c668 r58fe85a 10 10 // Created On : Wed May 8 10:27:04 2019 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Jun 3 13:26:00 201913 // Update Count : 512 // Last Modified On : Fri Jun 5 9:47:00 2020 13 // Update Count : 6 14 14 // 15 15 … … 38 38 Node& operator= (const Node&) = delete; 39 39 Node& operator= (Node&&) = delete; 40 virtual ~Node() = default;40 virtual ~Node() {} 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; } 51 52 52 53 private: … … 57 58 template<typename node_t> 58 59 friend node_t * mutate(const node_t * node); 60 template<typename node_t> 61 friend node_t * shallowCopy(const node_t * node); 59 62 60 63 mutable size_t strong_count = 0; … … 69 72 } 70 73 71 void decrement(ast::Node::ref_type ref ) const {74 void decrement(ast::Node::ref_type ref, bool do_delete = true) const { 72 75 switch (ref) { 73 76 case ref_type::strong: strong_count--; break; … … 75 78 } 76 79 77 if( !strong_count && !weak_count) {80 if( do_delete && !strong_count && !weak_count) { 78 81 delete this; 79 82 } … … 94 97 assertf( 95 98 node->weak_count == 0, 96 "Error: mutating node with weak references to it will invalid edsome references"99 "Error: mutating node with weak references to it will invalidate some references" 97 100 ); 98 101 return node->clone(); … … 104 107 // skip mutate if equivalent 105 108 if ( node->*field == val ) return node; 106 109 107 110 // mutate and return 108 111 node_t * ret = mutate( node ); … … 123 126 (ret->*field)[i] = std::forward< field_t >( val ); 124 127 return ret; 128 } 129 130 /// Mutate an entire indexed collection by cloning to accepted value 131 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; 125 137 } 126 138 … … 217 229 const node_t & operator* () const { _check(); return *node; } 218 230 explicit operator bool() const { _check(); return node; } 219 operator const node_t * () const { _check(); return node; } 231 operator const node_t * () const & { _check(); return node; } 232 operator const node_t * () && = delete; 233 234 const node_t * release() { 235 const node_t * ret = node; 236 if ( node ) { 237 _dec(node, false); 238 node = nullptr; 239 } 240 return ret; 241 } 220 242 221 243 /// wrapper for convenient access to dynamic_cast … … 223 245 const o_node_t * as() const { _check(); return dynamic_cast<const o_node_t *>(node); } 224 246 225 /// wrapper for convenient access to strict_dynamic_cast247 /// Wrapper that makes sure dynamic_cast returns non-null. 226 248 template<typename o_node_t> 227 const o_node_t * strict_as() const { _check(); return strict_dynamic_cast<const o_node_t *>(node); } 249 const o_node_t * strict_as() const { 250 if (const o_node_t * ret = as<o_node_t>()) return ret; 251 _strict_fail(); 252 } 253 254 /// Wrapper that makes sure dynamic_cast does not fail. 255 template<typename o_node_t, decltype(nullptr) null> 256 const o_node_t * strict_as() const { return node ? strict_as<o_node_t>() : nullptr; } 228 257 229 258 /// Returns a mutable version of the pointer in this node. … … 244 273 245 274 void _inc( const node_t * other ); 246 void _dec( const node_t * other );275 void _dec( const node_t * other, bool do_delete = true ); 247 276 void _check() const; 277 void _strict_fail() const __attribute__((noreturn)); 248 278 249 279 const node_t * node; -
src/AST/Pass.hpp
r3c64c668 r58fe85a 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 // | WithTypeSubstitution - provides polymorphic const TypeSubstitution * env for the 49 // current expression 50 // | WithStmtsToAdd - provides the ability to insert statements before or after the current 51 // statement by adding new statements into stmtsToAddBefore or 52 // stmtsToAddAfter respectively. 53 // | WithDeclsToAdd - provides the ability to insert declarations before or after the current 54 // declarations by adding new DeclStmt into declsToAddBefore or 55 // declsToAddAfter respectively. 56 // | WithShortCircuiting - provides the ability to skip visiting child nodes; set visit_children 57 // to false in pre{visit,visit} to skip visiting children 58 // | WithGuards - provides the ability to save/restore data like a LIFO stack; to save, 59 // call GuardValue with the variable to save, the variable will 60 // automatically be restored to its previous value after the corresponding 61 // postvisit/postmutate teminates. 62 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 63 // | WithSymbolTable - provides symbol table functionality 48 // | PureVisitor - makes the visitor pure, it never modifies nodes in place and always 49 // clones nodes it needs to make changes to 50 // | WithConstTypeSubstitution - provides polymorphic const TypeSubstitution * typeSubs for the 51 // current expression 52 // | WithStmtsToAdd - provides the ability to insert statements before or after the current 53 // statement by adding new statements into stmtsToAddBefore or 54 // stmtsToAddAfter respectively. 55 // | WithDeclsToAdd - provides the ability to insert declarations before or after the 56 // current declarations by adding new DeclStmt into declsToAddBefore or 57 // declsToAddAfter respectively. 58 // | WithShortCircuiting - provides the ability to skip visiting child nodes; set visit_children 59 // to false in pre{visit,visit} to skip visiting children 60 // | WithGuards - provides the ability to save/restore data like a LIFO stack; to save, 61 // call GuardValue with the variable to save, the variable will 62 // automatically be restored to its previous value after the 63 // corresponding postvisit/postmutate teminates. 64 // | WithVisitorRef - provides an pointer to the templated visitor wrapper 65 // | WithSymbolTable - provides symbol table functionality 66 // 67 // Other Special Members: 68 // | result - Either a method that takes no parameters or a field. If a method (or 69 // callable field) get_result calls it, otherwise the value is returned. 64 70 //------------------------------------------------------------------------------------------------- 65 template< typename pass_t >71 template< typename core_t > 66 72 class Pass final : public ast::Visitor { 67 73 public: 74 using core_type = core_t; 75 using type = Pass<core_t>; 76 68 77 /// Forward any arguments to the pass constructor 69 78 /// Propagate 'this' if necessary 70 79 template< typename... Args > 71 80 Pass( Args &&... args) 72 : pass( std::forward<Args>( args )... )81 : core( std::forward<Args>( args )... ) 73 82 { 74 83 // After the pass is constructed, check if it wants the have a pointer to the wrapping visitor 75 typedef Pass<pass_t> this_t; 76 this_t * const * visitor = __pass::visitor(pass, 0); 84 type * const * visitor = __pass::visitor(core, 0); 77 85 if(visitor) { 78 *const_cast<t his_t**>( visitor ) = this;86 *const_cast<type **>( visitor ) = this; 79 87 } 80 88 } … … 82 90 virtual ~Pass() = default; 83 91 84 /// Storage for the actual pass 85 pass_t pass; 92 /// Storage for the actual pass. 93 core_t core; 94 95 /// If the core defines a result, call it if possible, otherwise return it. 96 inline auto get_result() -> decltype( __pass::get_result( core, '0' ) ) { 97 return __pass::get_result( core, '0' ); 98 } 99 100 /// Construct and run a pass on a translation unit. 101 template< typename... Args > 102 static void run( TranslationUnit & decls, Args &&... args ) { 103 Pass<core_t> visitor( std::forward<Args>( args )... ); 104 accept_all( decls, visitor ); 105 } 106 107 /// Contruct and run a pass on a pointer to extract a value. 108 template< typename node_type, typename... Args > 109 static auto read( node_type const * node, Args&&... args ) { 110 Pass<core_t> visitor( std::forward<Args>( args )... ); 111 node_type const * temp = node->accept( visitor ); 112 assert( temp == node ); 113 return visitor.get_result(); 114 } 115 116 // Versions of the above for older compilers. 117 template< typename... Args > 118 static void run( TranslationUnit & decls ) { 119 Pass<core_t> visitor; 120 accept_all( decls, visitor ); 121 } 122 123 template< typename node_type, typename... Args > 124 static auto read( node_type const * node ) { 125 Pass<core_t> visitor; 126 node_type const * temp = node->accept( visitor ); 127 assert( temp == node ); 128 return visitor.get_result(); 129 } 86 130 87 131 /// Visit function declarations … … 111 155 const ast::Stmt * visit( const ast::CatchStmt * ) override final; 112 156 const ast::Stmt * visit( const ast::FinallyStmt * ) override final; 157 const ast::Stmt * visit( const ast::SuspendStmt * ) override final; 113 158 const ast::Stmt * visit( const ast::WaitForStmt * ) override final; 114 159 const ast::Decl * visit( const ast::WithStmt * ) override final; … … 178 223 const ast::TypeSubstitution * visit( const ast::TypeSubstitution * ) override final; 179 224 180 template<typename pass_type> 181 friend void accept_all( std::list< ptr<Decl> > & decls, Pass<pass_type>& visitor ); 225 template<typename core_type> 226 friend void accept_all( std::list< ptr<Decl> > & decls, Pass<core_type>& visitor ); 227 228 bool isInFunction() const { 229 return inFunction; 230 } 231 182 232 private: 183 233 184 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children( pass, 0); return ptr ? *ptr : true; }234 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(core, 0); return ptr ? *ptr : true; } 185 235 186 236 private: 187 237 const ast::Stmt * call_accept( const ast::Stmt * ); 188 238 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 *); 189 243 190 244 template< typename node_t > … … 206 260 void maybe_accept(const node_t * &, child_t parent_t::* child); 207 261 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 208 265 private: 209 266 /// Internal RAII guard for symbol table features 210 267 struct guard_symtab { 211 guard_symtab( Pass< pass_t> & pass ): pass( pass ) { __pass::symtab::enter(pass, 0); }212 ~guard_symtab() { __pass::symtab::leave(pass , 0); }213 Pass< pass_t> & pass;268 guard_symtab( Pass<core_t> & pass ): pass( pass ) { __pass::symtab::enter(pass.core, 0); } 269 ~guard_symtab() { __pass::symtab::leave(pass.core, 0); } 270 Pass<core_t> & pass; 214 271 }; 215 272 216 273 /// Internal RAII guard for scope features 217 274 struct guard_scope { 218 guard_scope( Pass<pass_t> & pass ): pass( pass ) { __pass::scope::enter(pass, 0); } 219 ~guard_scope() { __pass::scope::leave(pass, 0); } 220 Pass<pass_t> & pass; 275 guard_scope( Pass<core_t> & pass ): pass( pass ) { __pass::scope::enter(pass.core, 0); } 276 ~guard_scope() { __pass::scope::leave(pass.core, 0); } 277 Pass<core_t> & pass; 278 }; 279 280 /// Internal RAII guard for forall substitutions 281 struct guard_forall_subs { 282 guard_forall_subs( Pass<core_t> & pass, const FunctionType * type ) 283 : pass( pass ), type( type ) { __pass::forall::enter(pass.core, 0, type ); } 284 ~guard_forall_subs() { __pass::forall::leave(pass.core, 0, type ); } 285 Pass<core_t> & pass; 286 const FunctionType * type; 221 287 }; 222 288 223 289 private: 224 290 bool inFunction = false; 291 bool atFunctionTop = false; 225 292 }; 226 293 227 294 /// Apply a pass to an entire translation unit 228 template<typename pass_t> 229 void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<pass_t> & visitor ); 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 ); 230 300 231 301 //------------------------------------------------------------------------------------------------- … … 233 303 //------------------------------------------------------------------------------------------------- 234 304 235 /// Keep track of the polymorphic const TypeSubstitution * env for the current expression 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. 236 309 struct WithConstTypeSubstitution { 237 const TypeSubstitution * env= nullptr;310 const TypeSubstitution * typeSubs = nullptr; 238 311 }; 239 312 … … 267 340 }; 268 341 269 template< typename pass_t>270 friend auto __pass::at_cleanup( pass_t & pass, int ) -> decltype( &pass.at_cleanup );342 template< typename core_t> 343 friend auto __pass::at_cleanup( core_t & core, int ) -> decltype( &core.at_cleanup ); 271 344 public: 272 345 … … 304 377 305 378 /// Used to get a pointer to the pass with its wrapped type 306 template<typename pass_t>379 template<typename core_t> 307 380 struct WithVisitorRef { 308 Pass<pass_t> * const visitor = nullptr; 381 Pass<core_t> * const visitor = nullptr; 382 383 bool isInFunction() const { 384 return visitor->isInFunction(); 385 } 309 386 }; 310 387 … … 313 390 SymbolTable symtab; 314 391 }; 392 315 393 } 316 394 … … 320 398 extern struct PassVisitorStats { 321 399 size_t depth = 0; 322 Stats::Counters::MaxCounter<double> * max = nullptr;323 Stats::Counters::AverageCounter<double> * avg = nullptr;400 Stats::Counters::MaxCounter<double> * max; 401 Stats::Counters::AverageCounter<double> * avg; 324 402 } pass_visitor_stats; 325 403 } -
src/AST/Pass.impl.hpp
r3c64c668 r58fe85a 20 20 #include <unordered_map> 21 21 22 #include "AST/TranslationUnit.hpp" 22 23 #include "AST/TypeSubstitution.hpp" 23 24 … … 25 26 using namespace ast; \ 26 27 /* back-up the visit children */ \ 27 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children( pass, 0) ); \28 __attribute__((unused)) ast::__pass::visit_children_guard guard1( ast::__pass::visit_children(core, 0) ); \ 28 29 /* setup the scope for passes that want to run code at exit */ \ 29 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup (pass, 0) ); \ 30 __attribute__((unused)) ast::__pass::guard_value guard2( ast::__pass::at_cleanup (core, 0) ); \ 31 /* begin tracing memory allocation if requested by this pass */ \ 32 __pass::beginTrace( core, 0 ); \ 30 33 /* call the implementation of the previsit of this pass */ \ 31 __pass::previsit( pass, node, 0 );34 __pass::previsit( core, node, 0 ); 32 35 33 36 #define VISIT( code... ) \ … … 40 43 #define VISIT_END( type, node ) \ 41 44 /* call the implementation of the postvisit of this pass */ \ 42 auto __return = __pass::postvisit( pass, node, 0 ); \45 auto __return = __pass::postvisit( core, node, 0 ); \ 43 46 assertf(__return, "post visit should never return null"); \ 47 /* end tracing memory allocation if requested by this pass */ \ 48 __pass::endTrace( core, 0 ); \ 44 49 return __return; 45 50 … … 53 58 54 59 namespace ast { 60 template<typename node_t> 61 node_t * shallowCopy( const node_t * node ); 62 55 63 namespace __pass { 56 64 // Check if this is either a null pointer or a pointer to an empty container … … 60 68 } 61 69 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 62 75 //------------------------------ 63 76 template<typename it_t, template <class...> class container_t> … … 119 132 } 120 133 121 template< typename pass_t >134 template< typename core_t > 122 135 template< typename node_t > 123 auto ast::Pass< pass_t >::call_accept( const node_t * node )136 auto ast::Pass< core_t >::call_accept( const node_t * node ) 124 137 -> typename std::enable_if< 125 138 !std::is_base_of<ast::Expr, node_t>::value && … … 127 140 , decltype( node->accept(*this) ) 128 141 >::type 129 130 142 { 131 143 __pedantic_pass_assert( __visit_children() ); 132 __pedantic_pass_assert( expr);144 __pedantic_pass_assert( node ); 133 145 134 146 static_assert( !std::is_base_of<ast::Expr, node_t>::value, "ERROR"); … … 138 150 } 139 151 140 template< typename pass_t >141 const ast::Expr * ast::Pass< pass_t >::call_accept( const ast::Expr * expr ) {152 template< typename core_t > 153 const ast::Expr * ast::Pass< core_t >::call_accept( const ast::Expr * expr ) { 142 154 __pedantic_pass_assert( __visit_children() ); 143 155 __pedantic_pass_assert( expr ); 144 156 145 const ast::TypeSubstitution ** env_ptr = __pass::env( pass, 0);146 if ( env_ptr && expr->env ) {147 * env_ptr = expr->env;157 const ast::TypeSubstitution ** typeSubs_ptr = __pass::typeSubs( core, 0 ); 158 if ( typeSubs_ptr && expr->env ) { 159 *typeSubs_ptr = expr->env; 148 160 } 149 161 … … 151 163 } 152 164 153 template< typename pass_t >154 const ast::Stmt * ast::Pass< pass_t >::call_accept( const ast::Stmt * stmt ) {165 template< typename core_t > 166 const ast::Stmt * ast::Pass< core_t >::call_accept( const ast::Stmt * stmt ) { 155 167 __pedantic_pass_assert( __visit_children() ); 156 168 __pedantic_pass_assert( stmt ); 157 169 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 158 178 // add a few useful symbols to the scope 159 179 using __pass::empty; 160 180 161 181 // get the stmts/decls that will need to be spliced in 162 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);163 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0);164 auto decls_before = __pass::declsToAddBefore( pass, 0);165 auto decls_after = __pass::declsToAddAfter ( pass, 0);182 auto stmts_before = __pass::stmtsToAddBefore( core, 0); 183 auto stmts_after = __pass::stmtsToAddAfter ( core, 0); 184 auto decls_before = __pass::declsToAddBefore( core, 0); 185 auto decls_after = __pass::declsToAddAfter ( core, 0); 166 186 167 187 // These may be modified by subnode but most be restored once we exit this statemnet. 168 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass:: env( pass, 0) );188 ValueGuardPtr< const ast::TypeSubstitution * > __old_env ( __pass::typeSubs( core, 0 ) ); 169 189 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) >::type > __old_decls_before( stmts_before ); 170 190 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) >::type > __old_decls_after ( stmts_after ); … … 202 222 } 203 223 204 template< typename pass_t >224 template< typename core_t > 205 225 template< template <class...> class container_t > 206 container_t< ptr<Stmt> > ast::Pass< pass_t >::call_accept( const container_t< ptr<Stmt> > & statements ) {226 container_t< ptr<Stmt> > ast::Pass< core_t >::call_accept( const container_t< ptr<Stmt> > & statements ) { 207 227 __pedantic_pass_assert( __visit_children() ); 208 228 if( statements.empty() ) return {}; … … 215 235 216 236 // get the stmts/decls that will need to be spliced in 217 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);218 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0);219 auto decls_before = __pass::declsToAddBefore( pass, 0);220 auto decls_after = __pass::declsToAddAfter ( pass, 0);237 auto stmts_before = __pass::stmtsToAddBefore( core, 0); 238 auto stmts_after = __pass::stmtsToAddAfter ( core, 0); 239 auto decls_before = __pass::declsToAddBefore( core, 0); 240 auto decls_after = __pass::declsToAddAfter ( core, 0); 221 241 222 242 // These may be modified by subnode but most be restored once we exit this statemnet. … … 268 288 } 269 289 270 template< typename pass_t >290 template< typename core_t > 271 291 template< template <class...> class container_t, typename node_t > 272 container_t< ast::ptr<node_t> > ast::Pass< pass_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) {292 container_t< ast::ptr<node_t> > ast::Pass< core_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) { 273 293 __pedantic_pass_assert( __visit_children() ); 274 294 if( container.empty() ) return {}; … … 299 319 } 300 320 301 template< typename pass_t >321 template< typename core_t > 302 322 template<typename node_t, typename parent_t, typename child_t> 303 void ast::Pass< pass_t >::maybe_accept(323 void ast::Pass< core_t >::maybe_accept( 304 324 const node_t * & parent, 305 325 child_t parent_t::*child … … 317 337 318 338 if( __pass::differs(old_val, new_val) ) { 319 auto new_parent = mutate(parent); 339 auto new_parent = __pass::mutate<core_t>(parent); 340 new_parent->*child = new_val; 341 parent = new_parent; 342 } 343 } 344 345 template< typename core_t > 346 template<typename node_t, typename parent_t, typename child_t> 347 void ast::Pass< core_t >::maybe_accept_as_compound( 348 const node_t * & parent, 349 child_t parent_t::*child 350 ) { 351 static_assert( std::is_base_of<parent_t, node_t>::value, "Error deducing member object" ); 352 353 if(__pass::skip(parent->*child)) return; 354 const auto & old_val = __pass::get(parent->*child, 0); 355 356 static_assert( !std::is_same<const ast::Node * &, decltype(old_val)>::value, "ERROR"); 357 358 auto new_val = call_accept_as_compound( old_val ); 359 360 static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR"); 361 362 if( __pass::differs(old_val, new_val) ) { 363 auto new_parent = __pass::mutate<core_t>(parent); 320 364 new_parent->*child = new_val; 321 365 parent = new_parent; … … 333 377 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 334 378 335 template< typename pass_t >336 inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< pass_t > & visitor ) {379 template< typename core_t > 380 inline void ast::accept_all( std::list< ast::ptr<ast::Decl> > & decls, ast::Pass< core_t > & visitor ) { 337 381 // We are going to aggregate errors for all these statements 338 382 SemanticErrorException errors; … … 342 386 343 387 // get the stmts/decls that will need to be spliced in 344 auto decls_before = __pass::declsToAddBefore( visitor. pass, 0);345 auto decls_after = __pass::declsToAddAfter ( visitor. pass, 0);388 auto decls_before = __pass::declsToAddBefore( visitor.core, 0); 389 auto decls_after = __pass::declsToAddAfter ( visitor.core, 0); 346 390 347 391 // update pass statitistics … … 363 407 } 364 408 catch( SemanticErrorException &e ) { 365 errors.append( e ); 409 if (__pass::on_error (visitor.core, *i, 0)) 410 errors.append( e ); 366 411 } 367 412 … … 371 416 pass_visitor_stats.depth--; 372 417 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 ); 373 423 } 374 424 … … 392 442 //-------------------------------------------------------------------------- 393 443 // ObjectDecl 394 template< typename pass_t >395 const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::ObjectDecl * node ) {444 template< typename core_t > 445 const ast::DeclWithType * ast::Pass< core_t >::visit( const ast::ObjectDecl * node ) { 396 446 VISIT_START( node ); 397 447 … … 406 456 ) 407 457 408 __pass::symtab::addId( pass, 0, node );458 __pass::symtab::addId( core, 0, node ); 409 459 410 460 VISIT_END( DeclWithType, node ); … … 413 463 //-------------------------------------------------------------------------- 414 464 // FunctionDecl 415 template< typename pass_t >416 const ast::DeclWithType * ast::Pass< pass_t >::visit( const ast::FunctionDecl * node ) {417 VISIT_START( node ); 418 419 __pass::symtab::addId( pass, 0, node );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 ); 420 470 421 471 VISIT(maybe_accept( node, &FunctionDecl::withExprs );) … … 425 475 // shadow with exprs and not the other way around. 426 476 guard_symtab guard { *this }; 427 __pass::symtab::addWith( pass, 0, node->withExprs, node );477 __pass::symtab::addWith( core, 0, node->withExprs, node ); 428 478 { 429 479 guard_symtab guard { *this }; 430 480 // implicit add __func__ identifier as specified in the C manual 6.4.2.2 431 static ast:: ObjectDecl func(432 node->location, "__func__",433 new ast::ArrayType (434 new ast::BasicType ( ast::BasicType::Char, ast::CV::Qualifiers( ast::CV::Const ) ),481 static ast::ptr< ast::ObjectDecl > func{ new ast::ObjectDecl{ 482 CodeLocation{}, "__func__", 483 new ast::ArrayType{ 484 new ast::BasicType{ ast::BasicType::Char, ast::CV::Const }, 435 485 nullptr, VariableLen, DynamicDim 436 )437 );438 __pass::symtab::addId( pass, 0, &func );486 } 487 } }; 488 __pass::symtab::addId( core, 0, func ); 439 489 VISIT( 440 maybe_accept( node, &FunctionDecl::type ); 441 // function body needs to have the same scope as parameters - CompoundStmt will not enter 442 // a new scope if inFunction is true 490 // parameter declarations 491 maybe_accept( node, &FunctionDecl::params ); 492 maybe_accept( node, &FunctionDecl::returns ); 493 // type params and assertions 494 maybe_accept( node, &FunctionDecl::type_params ); 495 maybe_accept( node, &FunctionDecl::assertions ); 496 // First remember that we are now within a function. 443 497 ValueGuard< bool > oldInFunction( inFunction ); 444 498 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; 445 503 maybe_accept( node, &FunctionDecl::stmts ); 446 504 maybe_accept( node, &FunctionDecl::attributes ); … … 454 512 //-------------------------------------------------------------------------- 455 513 // StructDecl 456 template< typename pass_t >457 const ast::Decl * ast::Pass< pass_t >::visit( const ast::StructDecl * node ) {514 template< typename core_t > 515 const ast::Decl * ast::Pass< core_t >::visit( const ast::StructDecl * node ) { 458 516 VISIT_START( node ); 459 517 460 518 // make up a forward declaration and add it before processing the members 461 519 // needs to be on the heap because addStruct saves the pointer 462 __pass::symtab::addStructFwd( pass, 0, node );520 __pass::symtab::addStructFwd( core, 0, node ); 463 521 464 522 VISIT({ … … 469 527 470 528 // this addition replaces the forward declaration 471 __pass::symtab::addStruct( pass, 0, node );529 __pass::symtab::addStruct( core, 0, node ); 472 530 473 531 VISIT_END( Decl, node ); … … 476 534 //-------------------------------------------------------------------------- 477 535 // UnionDecl 478 template< typename pass_t >479 const ast::Decl * ast::Pass< pass_t >::visit( const ast::UnionDecl * node ) {536 template< typename core_t > 537 const ast::Decl * ast::Pass< core_t >::visit( const ast::UnionDecl * node ) { 480 538 VISIT_START( node ); 481 539 482 540 // make up a forward declaration and add it before processing the members 483 __pass::symtab::addUnionFwd( pass, 0, node );541 __pass::symtab::addUnionFwd( core, 0, node ); 484 542 485 543 VISIT({ … … 489 547 }) 490 548 491 __pass::symtab::addUnion( pass, 0, node );549 __pass::symtab::addUnion( core, 0, node ); 492 550 493 551 VISIT_END( Decl, node ); … … 496 554 //-------------------------------------------------------------------------- 497 555 // EnumDecl 498 template< typename pass_t >499 const ast::Decl * ast::Pass< pass_t >::visit( const ast::EnumDecl * node ) {500 VISIT_START( node ); 501 502 __pass::symtab::addEnum( pass, 0, node );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 ); 503 561 504 562 VISIT( … … 513 571 //-------------------------------------------------------------------------- 514 572 // TraitDecl 515 template< typename pass_t >516 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TraitDecl * node ) {573 template< typename core_t > 574 const ast::Decl * ast::Pass< core_t >::visit( const ast::TraitDecl * node ) { 517 575 VISIT_START( node ); 518 576 … … 523 581 }) 524 582 525 __pass::symtab::addTrait( pass, 0, node );583 __pass::symtab::addTrait( core, 0, node ); 526 584 527 585 VISIT_END( Decl, node ); … … 530 588 //-------------------------------------------------------------------------- 531 589 // TypeDecl 532 template< typename pass_t >533 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TypeDecl * node ) {590 template< typename core_t > 591 const ast::Decl * ast::Pass< core_t >::visit( const ast::TypeDecl * node ) { 534 592 VISIT_START( node ); 535 593 536 594 VISIT({ 537 595 guard_symtab guard { *this }; 538 maybe_accept( node, &TypeDecl::params );539 596 maybe_accept( node, &TypeDecl::base ); 540 597 }) … … 543 600 // note that assertions come after the type is added to the symtab, since they are not part of the type proper 544 601 // and may depend on the type itself 545 __pass::symtab::addType( pass, 0, node );602 __pass::symtab::addType( core, 0, node ); 546 603 547 604 VISIT( … … 559 616 //-------------------------------------------------------------------------- 560 617 // TypedefDecl 561 template< typename pass_t >562 const ast::Decl * ast::Pass< pass_t >::visit( const ast::TypedefDecl * node ) {618 template< typename core_t > 619 const ast::Decl * ast::Pass< core_t >::visit( const ast::TypedefDecl * node ) { 563 620 VISIT_START( node ); 564 621 565 622 VISIT({ 566 623 guard_symtab guard { *this }; 567 maybe_accept( node, &TypedefDecl::params );568 624 maybe_accept( node, &TypedefDecl::base ); 569 625 }) 570 626 571 __pass::symtab::addType( pass, 0, node );627 __pass::symtab::addType( core, 0, node ); 572 628 573 629 VISIT( maybe_accept( node, &TypedefDecl::assertions ); ) … … 578 634 //-------------------------------------------------------------------------- 579 635 // AsmDecl 580 template< typename pass_t >581 const ast::AsmDecl * ast::Pass< pass_t >::visit( const ast::AsmDecl * node ) {636 template< typename core_t > 637 const ast::AsmDecl * ast::Pass< core_t >::visit( const ast::AsmDecl * node ) { 582 638 VISIT_START( node ); 583 639 … … 591 647 //-------------------------------------------------------------------------- 592 648 // StaticAssertDecl 593 template< typename pass_t >594 const ast::StaticAssertDecl * ast::Pass< pass_t >::visit( const ast::StaticAssertDecl * node ) {649 template< typename core_t > 650 const ast::StaticAssertDecl * ast::Pass< core_t >::visit( const ast::StaticAssertDecl * node ) { 595 651 VISIT_START( node ); 596 652 … … 605 661 //-------------------------------------------------------------------------- 606 662 // CompoundStmt 607 template< typename pass_t > 608 const ast::CompoundStmt * ast::Pass< pass_t >::visit( const ast::CompoundStmt * node ) { 609 VISIT_START( node ); 610 VISIT({ 611 // do not enter a new scope if inFunction is true - needs to check old state before the assignment 612 auto guard1 = makeFuncGuard( [this, inFunction = this->inFunction]() { 613 if ( ! inFunction ) __pass::symtab::enter(pass, 0); 614 }, [this, inFunction = this->inFunction]() { 615 if ( ! inFunction ) __pass::symtab::leave(pass, 0); 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 } 616 678 }); 617 ValueGuard< bool > guard2( inFunction ); 679 ValueGuard< bool > guard2( atFunctionTop ); 680 atFunctionTop = false; 618 681 guard_scope guard3 { *this }; 619 inFunction = false;620 682 maybe_accept( node, &CompoundStmt::kids ); 621 })683 ) 622 684 VISIT_END( CompoundStmt, node ); 623 685 } … … 625 687 //-------------------------------------------------------------------------- 626 688 // ExprStmt 627 template< typename pass_t >628 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ExprStmt * node ) {689 template< typename core_t > 690 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ExprStmt * node ) { 629 691 VISIT_START( node ); 630 692 … … 638 700 //-------------------------------------------------------------------------- 639 701 // AsmStmt 640 template< typename pass_t >641 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::AsmStmt * node ) {702 template< typename core_t > 703 const ast::Stmt * ast::Pass< core_t >::visit( const ast::AsmStmt * node ) { 642 704 VISIT_START( node ) 643 705 … … 654 716 //-------------------------------------------------------------------------- 655 717 // DirectiveStmt 656 template< typename pass_t >657 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::DirectiveStmt * node ) {718 template< typename core_t > 719 const ast::Stmt * ast::Pass< core_t >::visit( const ast::DirectiveStmt * node ) { 658 720 VISIT_START( node ) 659 721 … … 663 725 //-------------------------------------------------------------------------- 664 726 // IfStmt 665 template< typename pass_t >666 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::IfStmt * node ) {727 template< typename core_t > 728 const ast::Stmt * ast::Pass< core_t >::visit( const ast::IfStmt * node ) { 667 729 VISIT_START( node ); 668 730 … … 672 734 maybe_accept( node, &IfStmt::inits ); 673 735 maybe_accept( node, &IfStmt::cond ); 674 maybe_accept ( node, &IfStmt::thenPart );675 maybe_accept ( node, &IfStmt::elsePart );736 maybe_accept_as_compound( node, &IfStmt::thenPart ); 737 maybe_accept_as_compound( node, &IfStmt::elsePart ); 676 738 }) 677 739 … … 681 743 //-------------------------------------------------------------------------- 682 744 // WhileStmt 683 template< typename pass_t >684 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::WhileStmt * node ) {745 template< typename core_t > 746 const ast::Stmt * ast::Pass< core_t >::visit( const ast::WhileStmt * node ) { 685 747 VISIT_START( node ); 686 748 … … 690 752 maybe_accept( node, &WhileStmt::inits ); 691 753 maybe_accept( node, &WhileStmt::cond ); 692 maybe_accept ( node, &WhileStmt::body );754 maybe_accept_as_compound( node, &WhileStmt::body ); 693 755 }) 694 756 … … 698 760 //-------------------------------------------------------------------------- 699 761 // ForStmt 700 template< typename pass_t >701 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ForStmt * node ) {762 template< typename core_t > 763 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ForStmt * node ) { 702 764 VISIT_START( node ); 703 765 … … 705 767 // for statements introduce a level of scope (for the initialization) 706 768 guard_symtab guard { *this }; 769 // xxx - old ast does not create WithStmtsToAdd scope for loop inits. should revisit this later. 707 770 maybe_accept( node, &ForStmt::inits ); 708 771 maybe_accept( node, &ForStmt::cond ); 709 772 maybe_accept( node, &ForStmt::inc ); 710 maybe_accept ( node, &ForStmt::body );773 maybe_accept_as_compound( node, &ForStmt::body ); 711 774 }) 712 775 … … 716 779 //-------------------------------------------------------------------------- 717 780 // SwitchStmt 718 template< typename pass_t >719 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::SwitchStmt * node ) {781 template< typename core_t > 782 const ast::Stmt * ast::Pass< core_t >::visit( const ast::SwitchStmt * node ) { 720 783 VISIT_START( node ); 721 784 … … 730 793 //-------------------------------------------------------------------------- 731 794 // CaseStmt 732 template< typename pass_t >733 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::CaseStmt * node ) {795 template< typename core_t > 796 const ast::Stmt * ast::Pass< core_t >::visit( const ast::CaseStmt * node ) { 734 797 VISIT_START( node ); 735 798 … … 744 807 //-------------------------------------------------------------------------- 745 808 // BranchStmt 746 template< typename pass_t >747 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::BranchStmt * node ) {809 template< typename core_t > 810 const ast::Stmt * ast::Pass< core_t >::visit( const ast::BranchStmt * node ) { 748 811 VISIT_START( node ); 749 812 VISIT_END( Stmt, node ); … … 752 815 //-------------------------------------------------------------------------- 753 816 // ReturnStmt 754 template< typename pass_t >755 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ReturnStmt * node ) {817 template< typename core_t > 818 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ReturnStmt * node ) { 756 819 VISIT_START( node ); 757 820 … … 765 828 //-------------------------------------------------------------------------- 766 829 // ThrowStmt 767 template< typename pass_t >768 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ThrowStmt * node ) {830 template< typename core_t > 831 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ThrowStmt * node ) { 769 832 VISIT_START( node ); 770 833 … … 779 842 //-------------------------------------------------------------------------- 780 843 // TryStmt 781 template< typename pass_t >782 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::TryStmt * node ) {844 template< typename core_t > 845 const ast::Stmt * ast::Pass< core_t >::visit( const ast::TryStmt * node ) { 783 846 VISIT_START( node ); 784 847 … … 794 857 //-------------------------------------------------------------------------- 795 858 // CatchStmt 796 template< typename pass_t >797 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::CatchStmt * node ) {859 template< typename core_t > 860 const ast::Stmt * ast::Pass< core_t >::visit( const ast::CatchStmt * node ) { 798 861 VISIT_START( node ); 799 862 … … 803 866 maybe_accept( node, &CatchStmt::decl ); 804 867 maybe_accept( node, &CatchStmt::cond ); 805 maybe_accept ( node, &CatchStmt::body );868 maybe_accept_as_compound( node, &CatchStmt::body ); 806 869 }) 807 870 … … 811 874 //-------------------------------------------------------------------------- 812 875 // FinallyStmt 813 template< typename pass_t >814 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::FinallyStmt * node ) {876 template< typename core_t > 877 const ast::Stmt * ast::Pass< core_t >::visit( const ast::FinallyStmt * node ) { 815 878 VISIT_START( node ); 816 879 … … 823 886 824 887 //-------------------------------------------------------------------------- 888 // FinallyStmt 889 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 //-------------------------------------------------------------------------- 825 901 // WaitForStmt 826 template< typename pass_t >827 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::WaitForStmt * node ) {902 template< typename core_t > 903 const ast::Stmt * ast::Pass< core_t >::visit( const ast::WaitForStmt * node ) { 828 904 VISIT_START( node ); 829 905 // for( auto & clause : node->clauses ) { … … 862 938 863 939 if(mutated) { 864 auto n = mutate(node);940 auto n = __pass::mutate<core_t>(node); 865 941 n->clauses = std::move( new_clauses ); 866 942 node = n; … … 872 948 auto nval = call_accept( node->field ); \ 873 949 if(nval != node->field ) { \ 874 auto nparent = mutate(node); \950 auto nparent = __pass::mutate<core_t>(node); \ 875 951 nparent->field = nval; \ 876 952 node = nparent; \ … … 893 969 //-------------------------------------------------------------------------- 894 970 // WithStmt 895 template< typename pass_t >896 const ast::Decl * ast::Pass< pass_t >::visit( const ast::WithStmt * node ) {971 template< typename core_t > 972 const ast::Decl * ast::Pass< core_t >::visit( const ast::WithStmt * node ) { 897 973 VISIT_START( node ); 898 974 … … 902 978 // catch statements introduce a level of scope (for the caught exception) 903 979 guard_symtab guard { *this }; 904 __pass::symtab::addWith( pass, 0, node->exprs, node );980 __pass::symtab::addWith( core, 0, node->exprs, node ); 905 981 maybe_accept( node, &WithStmt::stmt ); 906 982 } … … 911 987 //-------------------------------------------------------------------------- 912 988 // NullStmt 913 template< typename pass_t >914 const ast::NullStmt * ast::Pass< pass_t >::visit( const ast::NullStmt * node ) {989 template< typename core_t > 990 const ast::NullStmt * ast::Pass< core_t >::visit( const ast::NullStmt * node ) { 915 991 VISIT_START( node ); 916 992 VISIT_END( NullStmt, node ); … … 919 995 //-------------------------------------------------------------------------- 920 996 // DeclStmt 921 template< typename pass_t >922 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::DeclStmt * node ) {997 template< typename core_t > 998 const ast::Stmt * ast::Pass< core_t >::visit( const ast::DeclStmt * node ) { 923 999 VISIT_START( node ); 924 1000 … … 932 1008 //-------------------------------------------------------------------------- 933 1009 // ImplicitCtorDtorStmt 934 template< typename pass_t >935 const ast::Stmt * ast::Pass< pass_t >::visit( const ast::ImplicitCtorDtorStmt * node ) {1010 template< typename core_t > 1011 const ast::Stmt * ast::Pass< core_t >::visit( const ast::ImplicitCtorDtorStmt * node ) { 936 1012 VISIT_START( node ); 937 1013 938 1014 // For now this isn't visited, it is unclear if this causes problem 939 1015 // if all tests are known to pass, remove this code 940 //VISIT(941 //maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );942 //)1016 VISIT( 1017 maybe_accept( node, &ImplicitCtorDtorStmt::callStmt ); 1018 ) 943 1019 944 1020 VISIT_END( Stmt, node ); … … 947 1023 //-------------------------------------------------------------------------- 948 1024 // ApplicationExpr 949 template< typename pass_t >950 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ApplicationExpr * node ) {1025 template< typename core_t > 1026 const ast::Expr * ast::Pass< core_t >::visit( const ast::ApplicationExpr * node ) { 951 1027 VISIT_START( node ); 952 1028 … … 965 1041 //-------------------------------------------------------------------------- 966 1042 // UntypedExpr 967 template< typename pass_t >968 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedExpr * node ) {1043 template< typename core_t > 1044 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedExpr * node ) { 969 1045 VISIT_START( node ); 970 1046 … … 983 1059 //-------------------------------------------------------------------------- 984 1060 // NameExpr 985 template< typename pass_t >986 const ast::Expr * ast::Pass< pass_t >::visit( const ast::NameExpr * node ) {1061 template< typename core_t > 1062 const ast::Expr * ast::Pass< core_t >::visit( const ast::NameExpr * node ) { 987 1063 VISIT_START( node ); 988 1064 … … 997 1073 //-------------------------------------------------------------------------- 998 1074 // CastExpr 999 template< typename pass_t >1000 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CastExpr * node ) {1075 template< typename core_t > 1076 const ast::Expr * ast::Pass< core_t >::visit( const ast::CastExpr * node ) { 1001 1077 VISIT_START( node ); 1002 1078 … … 1013 1089 //-------------------------------------------------------------------------- 1014 1090 // KeywordCastExpr 1015 template< typename pass_t >1016 const ast::Expr * ast::Pass< pass_t >::visit( const ast::KeywordCastExpr * node ) {1091 template< typename core_t > 1092 const ast::Expr * ast::Pass< core_t >::visit( const ast::KeywordCastExpr * node ) { 1017 1093 VISIT_START( node ); 1018 1094 … … 1029 1105 //-------------------------------------------------------------------------- 1030 1106 // VirtualCastExpr 1031 template< typename pass_t >1032 const ast::Expr * ast::Pass< pass_t >::visit( const ast::VirtualCastExpr * node ) {1107 template< typename core_t > 1108 const ast::Expr * ast::Pass< core_t >::visit( const ast::VirtualCastExpr * node ) { 1033 1109 VISIT_START( node ); 1034 1110 … … 1045 1121 //-------------------------------------------------------------------------- 1046 1122 // AddressExpr 1047 template< typename pass_t >1048 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AddressExpr * node ) {1123 template< typename core_t > 1124 const ast::Expr * ast::Pass< core_t >::visit( const ast::AddressExpr * node ) { 1049 1125 VISIT_START( node ); 1050 1126 … … 1061 1137 //-------------------------------------------------------------------------- 1062 1138 // LabelAddressExpr 1063 template< typename pass_t >1064 const ast::Expr * ast::Pass< pass_t >::visit( const ast::LabelAddressExpr * node ) {1139 template< typename core_t > 1140 const ast::Expr * ast::Pass< core_t >::visit( const ast::LabelAddressExpr * node ) { 1065 1141 VISIT_START( node ); 1066 1142 … … 1075 1151 //-------------------------------------------------------------------------- 1076 1152 // UntypedMemberExpr 1077 template< typename pass_t >1078 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedMemberExpr * node ) {1153 template< typename core_t > 1154 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedMemberExpr * node ) { 1079 1155 VISIT_START( node ); 1080 1156 … … 1092 1168 //-------------------------------------------------------------------------- 1093 1169 // MemberExpr 1094 template< typename pass_t >1095 const ast::Expr * ast::Pass< pass_t >::visit( const ast::MemberExpr * node ) {1170 template< typename core_t > 1171 const ast::Expr * ast::Pass< core_t >::visit( const ast::MemberExpr * node ) { 1096 1172 VISIT_START( node ); 1097 1173 … … 1108 1184 //-------------------------------------------------------------------------- 1109 1185 // VariableExpr 1110 template< typename pass_t >1111 const ast::Expr * ast::Pass< pass_t >::visit( const ast::VariableExpr * node ) {1186 template< typename core_t > 1187 const ast::Expr * ast::Pass< core_t >::visit( const ast::VariableExpr * node ) { 1112 1188 VISIT_START( node ); 1113 1189 … … 1122 1198 //-------------------------------------------------------------------------- 1123 1199 // ConstantExpr 1124 template< typename pass_t >1125 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConstantExpr * node ) {1200 template< typename core_t > 1201 const ast::Expr * ast::Pass< core_t >::visit( const ast::ConstantExpr * node ) { 1126 1202 VISIT_START( node ); 1127 1203 … … 1136 1212 //-------------------------------------------------------------------------- 1137 1213 // SizeofExpr 1138 template< typename pass_t >1139 const ast::Expr * ast::Pass< pass_t >::visit( const ast::SizeofExpr * node ) {1214 template< typename core_t > 1215 const ast::Expr * ast::Pass< core_t >::visit( const ast::SizeofExpr * node ) { 1140 1216 VISIT_START( node ); 1141 1217 … … 1156 1232 //-------------------------------------------------------------------------- 1157 1233 // AlignofExpr 1158 template< typename pass_t >1159 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AlignofExpr * node ) {1234 template< typename core_t > 1235 const ast::Expr * ast::Pass< core_t >::visit( const ast::AlignofExpr * node ) { 1160 1236 VISIT_START( node ); 1161 1237 … … 1176 1252 //-------------------------------------------------------------------------- 1177 1253 // UntypedOffsetofExpr 1178 template< typename pass_t >1179 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedOffsetofExpr * node ) {1254 template< typename core_t > 1255 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedOffsetofExpr * node ) { 1180 1256 VISIT_START( node ); 1181 1257 … … 1192 1268 //-------------------------------------------------------------------------- 1193 1269 // OffsetofExpr 1194 template< typename pass_t >1195 const ast::Expr * ast::Pass< pass_t >::visit( const ast::OffsetofExpr * node ) {1270 template< typename core_t > 1271 const ast::Expr * ast::Pass< core_t >::visit( const ast::OffsetofExpr * node ) { 1196 1272 VISIT_START( node ); 1197 1273 … … 1208 1284 //-------------------------------------------------------------------------- 1209 1285 // OffsetPackExpr 1210 template< typename pass_t >1211 const ast::Expr * ast::Pass< pass_t >::visit( const ast::OffsetPackExpr * node ) {1286 template< typename core_t > 1287 const ast::Expr * ast::Pass< core_t >::visit( const ast::OffsetPackExpr * node ) { 1212 1288 VISIT_START( node ); 1213 1289 … … 1224 1300 //-------------------------------------------------------------------------- 1225 1301 // LogicalExpr 1226 template< typename pass_t >1227 const ast::Expr * ast::Pass< pass_t >::visit( const ast::LogicalExpr * node ) {1302 template< typename core_t > 1303 const ast::Expr * ast::Pass< core_t >::visit( const ast::LogicalExpr * node ) { 1228 1304 VISIT_START( node ); 1229 1305 … … 1241 1317 //-------------------------------------------------------------------------- 1242 1318 // ConditionalExpr 1243 template< typename pass_t >1244 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConditionalExpr * node ) {1319 template< typename core_t > 1320 const ast::Expr * ast::Pass< core_t >::visit( const ast::ConditionalExpr * node ) { 1245 1321 VISIT_START( node ); 1246 1322 … … 1259 1335 //-------------------------------------------------------------------------- 1260 1336 // CommaExpr 1261 template< typename pass_t >1262 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CommaExpr * node ) {1337 template< typename core_t > 1338 const ast::Expr * ast::Pass< core_t >::visit( const ast::CommaExpr * node ) { 1263 1339 VISIT_START( node ); 1264 1340 … … 1276 1352 //-------------------------------------------------------------------------- 1277 1353 // TypeExpr 1278 template< typename pass_t >1279 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TypeExpr * node ) {1354 template< typename core_t > 1355 const ast::Expr * ast::Pass< core_t >::visit( const ast::TypeExpr * node ) { 1280 1356 VISIT_START( node ); 1281 1357 … … 1292 1368 //-------------------------------------------------------------------------- 1293 1369 // AsmExpr 1294 template< typename pass_t >1295 const ast::Expr * ast::Pass< pass_t >::visit( const ast::AsmExpr * node ) {1370 template< typename core_t > 1371 const ast::Expr * ast::Pass< core_t >::visit( const ast::AsmExpr * node ) { 1296 1372 VISIT_START( node ); 1297 1373 … … 1309 1385 //-------------------------------------------------------------------------- 1310 1386 // ImplicitCopyCtorExpr 1311 template< typename pass_t >1312 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ImplicitCopyCtorExpr * node ) {1387 template< typename core_t > 1388 const ast::Expr * ast::Pass< core_t >::visit( const ast::ImplicitCopyCtorExpr * node ) { 1313 1389 VISIT_START( node ); 1314 1390 … … 1325 1401 //-------------------------------------------------------------------------- 1326 1402 // ConstructorExpr 1327 template< typename pass_t >1328 const ast::Expr * ast::Pass< pass_t >::visit( const ast::ConstructorExpr * node ) {1403 template< typename core_t > 1404 const ast::Expr * ast::Pass< core_t >::visit( const ast::ConstructorExpr * node ) { 1329 1405 VISIT_START( node ); 1330 1406 … … 1341 1417 //-------------------------------------------------------------------------- 1342 1418 // CompoundLiteralExpr 1343 template< typename pass_t >1344 const ast::Expr * ast::Pass< pass_t >::visit( const ast::CompoundLiteralExpr * node ) {1419 template< typename core_t > 1420 const ast::Expr * ast::Pass< core_t >::visit( const ast::CompoundLiteralExpr * node ) { 1345 1421 VISIT_START( node ); 1346 1422 … … 1357 1433 //-------------------------------------------------------------------------- 1358 1434 // RangeExpr 1359 template< typename pass_t >1360 const ast::Expr * ast::Pass< pass_t >::visit( const ast::RangeExpr * node ) {1435 template< typename core_t > 1436 const ast::Expr * ast::Pass< core_t >::visit( const ast::RangeExpr * node ) { 1361 1437 VISIT_START( node ); 1362 1438 … … 1374 1450 //-------------------------------------------------------------------------- 1375 1451 // UntypedTupleExpr 1376 template< typename pass_t >1377 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedTupleExpr * node ) {1452 template< typename core_t > 1453 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedTupleExpr * node ) { 1378 1454 VISIT_START( node ); 1379 1455 … … 1390 1466 //-------------------------------------------------------------------------- 1391 1467 // TupleExpr 1392 template< typename pass_t >1393 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleExpr * node ) {1468 template< typename core_t > 1469 const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleExpr * node ) { 1394 1470 VISIT_START( node ); 1395 1471 … … 1406 1482 //-------------------------------------------------------------------------- 1407 1483 // TupleIndexExpr 1408 template< typename pass_t >1409 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleIndexExpr * node ) {1484 template< typename core_t > 1485 const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleIndexExpr * node ) { 1410 1486 VISIT_START( node ); 1411 1487 … … 1422 1498 //-------------------------------------------------------------------------- 1423 1499 // TupleAssignExpr 1424 template< typename pass_t >1425 const ast::Expr * ast::Pass< pass_t >::visit( const ast::TupleAssignExpr * node ) {1500 template< typename core_t > 1501 const ast::Expr * ast::Pass< core_t >::visit( const ast::TupleAssignExpr * node ) { 1426 1502 VISIT_START( node ); 1427 1503 … … 1438 1514 //-------------------------------------------------------------------------- 1439 1515 // StmtExpr 1440 template< typename pass_t >1441 const ast::Expr * ast::Pass< pass_t >::visit( const ast::StmtExpr * node ) {1516 template< typename core_t > 1517 const ast::Expr * ast::Pass< core_t >::visit( const ast::StmtExpr * node ) { 1442 1518 VISIT_START( node ); 1443 1519 1444 1520 VISIT(// don't want statements from outer CompoundStmts to be added to this StmtExpr 1445 1521 // get the stmts that will need to be spliced in 1446 auto stmts_before = __pass::stmtsToAddBefore( pass, 0);1447 auto stmts_after = __pass::stmtsToAddAfter ( pass, 0);1522 auto stmts_before = __pass::stmtsToAddBefore( core, 0); 1523 auto stmts_after = __pass::stmtsToAddAfter ( core, 0); 1448 1524 1449 1525 // These may be modified by subnode but most be restored once we exit this statemnet. 1450 ValueGuardPtr< const ast::TypeSubstitution * > __old_env( __pass:: env( pass, 0) );1526 ValueGuardPtr< const ast::TypeSubstitution * > __old_env( __pass::typeSubs( core, 0 ) ); 1451 1527 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_before) >::type > __old_decls_before( stmts_before ); 1452 1528 ValueGuardPtr< typename std::remove_pointer< decltype(stmts_after ) >::type > __old_decls_after ( stmts_after ); … … 1466 1542 //-------------------------------------------------------------------------- 1467 1543 // UniqueExpr 1468 template< typename pass_t >1469 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UniqueExpr * node ) {1544 template< typename core_t > 1545 const ast::Expr * ast::Pass< core_t >::visit( const ast::UniqueExpr * node ) { 1470 1546 VISIT_START( node ); 1471 1547 … … 1482 1558 //-------------------------------------------------------------------------- 1483 1559 // UntypedInitExpr 1484 template< typename pass_t >1485 const ast::Expr * ast::Pass< pass_t >::visit( const ast::UntypedInitExpr * node ) {1560 template< typename core_t > 1561 const ast::Expr * ast::Pass< core_t >::visit( const ast::UntypedInitExpr * node ) { 1486 1562 VISIT_START( node ); 1487 1563 … … 1499 1575 //-------------------------------------------------------------------------- 1500 1576 // InitExpr 1501 template< typename pass_t >1502 const ast::Expr * ast::Pass< pass_t >::visit( const ast::InitExpr * node ) {1577 template< typename core_t > 1578 const ast::Expr * ast::Pass< core_t >::visit( const ast::InitExpr * node ) { 1503 1579 VISIT_START( node ); 1504 1580 … … 1516 1592 //-------------------------------------------------------------------------- 1517 1593 // DeletedExpr 1518 template< typename pass_t >1519 const ast::Expr * ast::Pass< pass_t >::visit( const ast::DeletedExpr * node ) {1594 template< typename core_t > 1595 const ast::Expr * ast::Pass< core_t >::visit( const ast::DeletedExpr * node ) { 1520 1596 VISIT_START( node ); 1521 1597 … … 1533 1609 //-------------------------------------------------------------------------- 1534 1610 // DefaultArgExpr 1535 template< typename pass_t >1536 const ast::Expr * ast::Pass< pass_t >::visit( const ast::DefaultArgExpr * node ) {1611 template< typename core_t > 1612 const ast::Expr * ast::Pass< core_t >::visit( const ast::DefaultArgExpr * node ) { 1537 1613 VISIT_START( node ); 1538 1614 … … 1549 1625 //-------------------------------------------------------------------------- 1550 1626 // GenericExpr 1551 template< typename pass_t >1552 const ast::Expr * ast::Pass< pass_t >::visit( const ast::GenericExpr * node ) {1627 template< typename core_t > 1628 const ast::Expr * ast::Pass< core_t >::visit( const ast::GenericExpr * node ) { 1553 1629 VISIT_START( node ); 1554 1630 … … 1578 1654 1579 1655 if(mutated) { 1580 auto n = mutate(node);1656 auto n = __pass::mutate<core_t>(node); 1581 1657 n->associations = std::move( new_kids ); 1582 1658 node = n; … … 1589 1665 //-------------------------------------------------------------------------- 1590 1666 // VoidType 1591 template< typename pass_t >1592 const ast::Type * ast::Pass< pass_t >::visit( const ast::VoidType * node ) {1667 template< typename core_t > 1668 const ast::Type * ast::Pass< core_t >::visit( const ast::VoidType * node ) { 1593 1669 VISIT_START( node ); 1594 1670 … … 1598 1674 //-------------------------------------------------------------------------- 1599 1675 // BasicType 1600 template< typename pass_t >1601 const ast::Type * ast::Pass< pass_t >::visit( const ast::BasicType * node ) {1676 template< typename core_t > 1677 const ast::Type * ast::Pass< core_t >::visit( const ast::BasicType * node ) { 1602 1678 VISIT_START( node ); 1603 1679 … … 1607 1683 //-------------------------------------------------------------------------- 1608 1684 // PointerType 1609 template< typename pass_t >1610 const ast::Type * ast::Pass< pass_t >::visit( const ast::PointerType * node ) {1685 template< typename core_t > 1686 const ast::Type * ast::Pass< core_t >::visit( const ast::PointerType * node ) { 1611 1687 VISIT_START( node ); 1612 1688 … … 1621 1697 //-------------------------------------------------------------------------- 1622 1698 // ArrayType 1623 template< typename pass_t >1624 const ast::Type * ast::Pass< pass_t >::visit( const ast::ArrayType * node ) {1699 template< typename core_t > 1700 const ast::Type * ast::Pass< core_t >::visit( const ast::ArrayType * node ) { 1625 1701 VISIT_START( node ); 1626 1702 … … 1635 1711 //-------------------------------------------------------------------------- 1636 1712 // ReferenceType 1637 template< typename pass_t >1638 const ast::Type * ast::Pass< pass_t >::visit( const ast::ReferenceType * node ) {1713 template< typename core_t > 1714 const ast::Type * ast::Pass< core_t >::visit( const ast::ReferenceType * node ) { 1639 1715 VISIT_START( node ); 1640 1716 … … 1648 1724 //-------------------------------------------------------------------------- 1649 1725 // QualifiedType 1650 template< typename pass_t >1651 const ast::Type * ast::Pass< pass_t >::visit( const ast::QualifiedType * node ) {1726 template< typename core_t > 1727 const ast::Type * ast::Pass< core_t >::visit( const ast::QualifiedType * node ) { 1652 1728 VISIT_START( node ); 1653 1729 … … 1662 1738 //-------------------------------------------------------------------------- 1663 1739 // FunctionType 1664 template< typename pass_t > 1665 const ast::Type * ast::Pass< pass_t >::visit( const ast::FunctionType * node ) { 1666 VISIT_START( node ); 1667 1668 VISIT( 1669 maybe_accept( node, &FunctionType::forall ); 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 ); 1670 1748 maybe_accept( node, &FunctionType::returns ); 1671 1749 maybe_accept( node, &FunctionType::params ); 1672 )1750 }) 1673 1751 1674 1752 VISIT_END( Type, node ); … … 1677 1755 //-------------------------------------------------------------------------- 1678 1756 // StructInstType 1679 template< typename pass_t >1680 const ast::Type * ast::Pass< pass_t >::visit( const ast::StructInstType * node ) {1681 VISIT_START( node ); 1682 1683 __pass::symtab::addStruct( pass, 0, node->name );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 ); 1684 1762 1685 1763 VISIT({ 1686 1764 guard_symtab guard { *this }; 1687 maybe_accept( node, &StructInstType::forall );1688 1765 maybe_accept( node, &StructInstType::params ); 1689 1766 }) … … 1694 1771 //-------------------------------------------------------------------------- 1695 1772 // UnionInstType 1696 template< typename pass_t >1697 const ast::Type * ast::Pass< pass_t >::visit( const ast::UnionInstType * node ) {1698 VISIT_START( node ); 1699 1700 __pass::symtab::add Struct( pass, 0, node->name );1701 1702 {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::addUnion( core, 0, node->name ); 1778 1779 VISIT({ 1703 1780 guard_symtab guard { *this }; 1704 maybe_accept( node, &UnionInstType::forall );1705 1781 maybe_accept( node, &UnionInstType::params ); 1706 } 1782 }) 1707 1783 1708 1784 VISIT_END( Type, node ); … … 1711 1787 //-------------------------------------------------------------------------- 1712 1788 // EnumInstType 1713 template< typename pass_t > 1714 const ast::Type * ast::Pass< pass_t >::visit( const ast::EnumInstType * node ) { 1715 VISIT_START( node ); 1716 1717 VISIT( 1718 maybe_accept( node, &EnumInstType::forall ); 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({ 1719 1794 maybe_accept( node, &EnumInstType::params ); 1720 )1795 }) 1721 1796 1722 1797 VISIT_END( Type, node ); … … 1725 1800 //-------------------------------------------------------------------------- 1726 1801 // TraitInstType 1727 template< typename pass_t > 1728 const ast::Type * ast::Pass< pass_t >::visit( const ast::TraitInstType * node ) { 1729 VISIT_START( node ); 1730 1731 VISIT( 1732 maybe_accept( node, &TraitInstType::forall ); 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({ 1733 1807 maybe_accept( node, &TraitInstType::params ); 1734 )1808 }) 1735 1809 1736 1810 VISIT_END( Type, node ); … … 1739 1813 //-------------------------------------------------------------------------- 1740 1814 // TypeInstType 1741 template< typename pass_t > 1742 const ast::Type * ast::Pass< pass_t >::visit( const ast::TypeInstType * node ) { 1743 VISIT_START( node ); 1744 1745 VISIT( 1746 maybe_accept( node, &TypeInstType::forall ); 1747 maybe_accept( node, &TypeInstType::params ); 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 ); 1748 1825 ) 1749 1826 … … 1753 1830 //-------------------------------------------------------------------------- 1754 1831 // TupleType 1755 template< typename pass_t >1756 const ast::Type * ast::Pass< pass_t >::visit( const ast::TupleType * node ) {1832 template< typename core_t > 1833 const ast::Type * ast::Pass< core_t >::visit( const ast::TupleType * node ) { 1757 1834 VISIT_START( node ); 1758 1835 … … 1767 1844 //-------------------------------------------------------------------------- 1768 1845 // TypeofType 1769 template< typename pass_t >1770 const ast::Type * ast::Pass< pass_t >::visit( const ast::TypeofType * node ) {1846 template< typename core_t > 1847 const ast::Type * ast::Pass< core_t >::visit( const ast::TypeofType * node ) { 1771 1848 VISIT_START( node ); 1772 1849 … … 1780 1857 //-------------------------------------------------------------------------- 1781 1858 // VarArgsType 1782 template< typename pass_t >1783 const ast::Type * ast::Pass< pass_t >::visit( const ast::VarArgsType * node ) {1859 template< typename core_t > 1860 const ast::Type * ast::Pass< core_t >::visit( const ast::VarArgsType * node ) { 1784 1861 VISIT_START( node ); 1785 1862 … … 1789 1866 //-------------------------------------------------------------------------- 1790 1867 // ZeroType 1791 template< typename pass_t >1792 const ast::Type * ast::Pass< pass_t >::visit( const ast::ZeroType * node ) {1868 template< typename core_t > 1869 const ast::Type * ast::Pass< core_t >::visit( const ast::ZeroType * node ) { 1793 1870 VISIT_START( node ); 1794 1871 … … 1798 1875 //-------------------------------------------------------------------------- 1799 1876 // OneType 1800 template< typename pass_t >1801 const ast::Type * ast::Pass< pass_t >::visit( const ast::OneType * node ) {1877 template< typename core_t > 1878 const ast::Type * ast::Pass< core_t >::visit( const ast::OneType * node ) { 1802 1879 VISIT_START( node ); 1803 1880 … … 1807 1884 //-------------------------------------------------------------------------- 1808 1885 // GlobalScopeType 1809 template< typename pass_t >1810 const ast::Type * ast::Pass< pass_t >::visit( const ast::GlobalScopeType * node ) {1886 template< typename core_t > 1887 const ast::Type * ast::Pass< core_t >::visit( const ast::GlobalScopeType * node ) { 1811 1888 VISIT_START( node ); 1812 1889 … … 1817 1894 //-------------------------------------------------------------------------- 1818 1895 // Designation 1819 template< typename pass_t >1820 const ast::Designation * ast::Pass< pass_t >::visit( const ast::Designation * node ) {1896 template< typename core_t > 1897 const ast::Designation * ast::Pass< core_t >::visit( const ast::Designation * node ) { 1821 1898 VISIT_START( node ); 1822 1899 … … 1828 1905 //-------------------------------------------------------------------------- 1829 1906 // SingleInit 1830 template< typename pass_t >1831 const ast::Init * ast::Pass< pass_t >::visit( const ast::SingleInit * node ) {1907 template< typename core_t > 1908 const ast::Init * ast::Pass< core_t >::visit( const ast::SingleInit * node ) { 1832 1909 VISIT_START( node ); 1833 1910 … … 1841 1918 //-------------------------------------------------------------------------- 1842 1919 // ListInit 1843 template< typename pass_t >1844 const ast::Init * ast::Pass< pass_t >::visit( const ast::ListInit * node ) {1920 template< typename core_t > 1921 const ast::Init * ast::Pass< core_t >::visit( const ast::ListInit * node ) { 1845 1922 VISIT_START( node ); 1846 1923 … … 1855 1932 //-------------------------------------------------------------------------- 1856 1933 // ConstructorInit 1857 template< typename pass_t >1858 const ast::Init * ast::Pass< pass_t >::visit( const ast::ConstructorInit * node ) {1934 template< typename core_t > 1935 const ast::Init * ast::Pass< core_t >::visit( const ast::ConstructorInit * node ) { 1859 1936 VISIT_START( node ); 1860 1937 … … 1870 1947 //-------------------------------------------------------------------------- 1871 1948 // Attribute 1872 template< typename pass_t >1873 const ast::Attribute * ast::Pass< pass_t >::visit( const ast::Attribute * node ) {1949 template< typename core_t > 1950 const ast::Attribute * ast::Pass< core_t >::visit( const ast::Attribute * node ) { 1874 1951 VISIT_START( node ); 1875 1952 … … 1883 1960 //-------------------------------------------------------------------------- 1884 1961 // TypeSubstitution 1885 template< typename pass_t >1886 const ast::TypeSubstitution * ast::Pass< pass_t >::visit( const ast::TypeSubstitution * node ) {1962 template< typename core_t > 1963 const ast::TypeSubstitution * ast::Pass< core_t >::visit( const ast::TypeSubstitution * node ) { 1887 1964 VISIT_START( node ); 1888 1965 … … 1890 1967 { 1891 1968 bool mutated = false; 1892 std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;1969 std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map; 1893 1970 for ( const auto & p : node->typeEnv ) { 1894 1971 guard_symtab guard { *this }; 1895 1972 auto new_node = p.second->accept( *this ); 1896 if (new_node != p.second) mutated = false;1973 if (new_node != p.second) mutated = true; 1897 1974 new_map.insert({ p.first, new_node }); 1898 1975 } 1899 1976 if (mutated) { 1900 auto new_node = mutate( node );1977 auto new_node = __pass::mutate<core_t>( node ); 1901 1978 new_node->typeEnv.swap( new_map ); 1902 1979 node = new_node; 1903 1980 } 1904 1981 } 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 }1921 1982 ) 1922 1983 -
src/AST/Pass.proto.hpp
r3c64c668 r58fe85a 17 17 // IWYU pragma: private, include "Pass.hpp" 18 18 19 #include "Common/Stats/Heap.h" 20 19 21 namespace ast { 20 template<typename pass_type>22 template<typename core_t> 21 23 class Pass; 24 25 struct TranslationUnit; 26 27 struct PureVisitor; 22 28 23 29 namespace __pass { … … 82 88 }; 83 89 84 std::stack< cleanup_t > cleanups;90 std::stack< cleanup_t, std::vector<cleanup_t> > cleanups; 85 91 }; 86 92 … … 111 117 /// "Short hand" to check if this is a valid previsit function 112 118 /// Mostly used to make the static_assert look (and print) prettier 113 template<typename pass_t, typename node_t>119 template<typename core_t, typename node_t> 114 120 struct is_valid_previsit { 115 using ret_t = decltype( (( pass_t*)nullptr)->previsit( (const node_t *)nullptr ) );121 using ret_t = decltype( ((core_t*)nullptr)->previsit( (const node_t *)nullptr ) ); 116 122 117 123 static constexpr bool value = std::is_void< ret_t >::value || … … 127 133 template<> 128 134 struct __assign<true> { 129 template<typename pass_t, typename node_t>130 static inline void result( pass_t & pass, const node_t * & node ) {131 pass.previsit( node );135 template<typename core_t, typename node_t> 136 static inline void result( core_t & core, const node_t * & node ) { 137 core.previsit( node ); 132 138 } 133 139 }; … … 135 141 template<> 136 142 struct __assign<false> { 137 template<typename pass_t, typename node_t>138 static inline void result( pass_t & pass, const node_t * & node ) {139 node = pass.previsit( node );143 template<typename core_t, typename node_t> 144 static inline void result( core_t & core, const node_t * & node ) { 145 node = core.previsit( node ); 140 146 assertf(node, "Previsit must not return NULL"); 141 147 } … … 150 156 template<> 151 157 struct __return<true> { 152 template<typename pass_t, typename node_t>153 static inline const node_t * result( pass_t & pass, const node_t * & node ) {154 pass.postvisit( node );158 template<typename core_t, typename node_t> 159 static inline const node_t * result( core_t & core, const node_t * & node ) { 160 core.postvisit( node ); 155 161 return node; 156 162 } … … 159 165 template<> 160 166 struct __return<false> { 161 template<typename pass_t, typename node_t>162 static inline auto result( pass_t & pass, const node_t * & node ) {163 return pass.postvisit( node );167 template<typename core_t, typename node_t> 168 static inline auto result( core_t & core, const node_t * & node ) { 169 return core.postvisit( node ); 164 170 } 165 171 }; … … 180 186 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 181 187 // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call 182 template<typename pass_t, typename node_t>183 static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) {188 template<typename core_t, typename node_t> 189 static inline auto previsit( core_t & core, const node_t * & node, int ) -> decltype( core.previsit( node ), void() ) { 184 190 static_assert( 185 is_valid_previsit< pass_t, node_t>::value,191 is_valid_previsit<core_t, node_t>::value, 186 192 "Previsit may not change the type of the node. It must return its paremeter or void." 187 193 ); … … 189 195 __assign< 190 196 std::is_void< 191 decltype( pass.previsit( node ) )197 decltype( core.previsit( node ) ) 192 198 >::value 193 >::result( pass, node );194 } 195 196 template<typename pass_t, typename node_t>197 static inline auto previsit( pass_t &, const node_t *, long ) {}199 >::result( core, node ); 200 } 201 202 template<typename core_t, typename node_t> 203 static inline auto previsit( core_t &, const node_t *, long ) {} 198 204 199 205 // PostVisit : never mutates the passed pointer but may return a different node 200 template<typename pass_t, typename node_t>201 static inline auto postvisit( pass_t & pass, const node_t * node, int ) ->202 decltype( pass.postvisit( node ), node->accept( *(Visitor*)nullptr ) )206 template<typename core_t, typename node_t> 207 static inline auto postvisit( core_t & core, const node_t * node, int ) -> 208 decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) ) 203 209 { 204 210 return __return< 205 211 std::is_void< 206 decltype( pass.postvisit( node ) )212 decltype( core.postvisit( node ) ) 207 213 >::value 208 >::result( pass, node );209 } 210 211 template<typename pass_t, typename node_t>212 static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; }214 >::result( core, node ); 215 } 216 217 template<typename core_t, typename node_t> 218 static inline const node_t * postvisit( core_t &, const node_t * node, long ) { return node; } 213 219 214 220 //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- … … 225 231 // The type is not strictly enforced but does match the accessory 226 232 #define FIELD_PTR( name, default_type ) \ 227 template< typename pass_t > \228 static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \233 template< typename core_t > \ 234 static inline auto name( core_t & core, int ) -> decltype( &core.name ) { return &core.name; } \ 229 235 \ 230 template< typename pass_t > \231 static inline default_type * name( pass_t &, long ) { return nullptr; }236 template< typename core_t > \ 237 static inline default_type * name( core_t &, long ) { return nullptr; } 232 238 233 239 // List of fields and their expected types 234 FIELD_PTR( env, const ast::TypeSubstitution * )240 FIELD_PTR( typeSubs, const ast::TypeSubstitution * ) 235 241 FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > ) 236 242 FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > ) … … 239 245 FIELD_PTR( visit_children, __pass::bool_ref ) 240 246 FIELD_PTR( at_cleanup, __pass::at_cleanup_t ) 241 FIELD_PTR( visitor, ast::Pass< pass_t> * const )247 FIELD_PTR( visitor, ast::Pass<core_t> * const ) 242 248 243 249 // Remove the macro to make sure we don't clash 244 250 #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 } 245 278 246 279 // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement. … … 248 281 // detect it using the same strategy 249 282 namespace scope { 250 template<typename pass_t>251 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) {252 pass.beginScope();253 } 254 255 template<typename pass_t>256 static inline void enter( pass_t &, long ) {}257 258 template<typename pass_t>259 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) {260 pass.endScope();261 } 262 263 template<typename pass_t>264 static inline void leave( pass_t &, long ) {}265 } ;266 267 // Finally certain pass desire an up to date symbol table automatically283 template<typename core_t> 284 static inline auto enter( core_t & core, int ) -> decltype( core.beginScope(), void() ) { 285 core.beginScope(); 286 } 287 288 template<typename core_t> 289 static inline void enter( core_t &, long ) {} 290 291 template<typename core_t> 292 static inline auto leave( core_t & core, int ) -> decltype( core.endScope(), void() ) { 293 core.endScope(); 294 } 295 296 template<typename core_t> 297 static inline void leave( core_t &, long ) {} 298 } // namespace scope 299 300 // Certain passes desire an up to date symbol table automatically 268 301 // detect the presence of a member name `symtab` and call all the members appropriately 269 302 namespace symtab { 270 303 // Some simple scoping rules 271 template<typename pass_t>272 static inline auto enter( pass_t & pass, int ) -> decltype( pass.symtab.enterScope(), void() ) {273 pass.symtab.enterScope();274 } 275 276 template<typename pass_t>277 static inline auto enter( pass_t &, long ) {}278 279 template<typename pass_t>280 static inline auto leave( pass_t & pass, int ) -> decltype( pass.symtab.leaveScope(), void() ) {281 pass.symtab.leaveScope();282 } 283 284 template<typename pass_t>285 static inline auto leave( pass_t &, long ) {}304 template<typename core_t> 305 static inline auto enter( core_t & core, int ) -> decltype( core.symtab, void() ) { 306 core.symtab.enterScope(); 307 } 308 309 template<typename core_t> 310 static inline auto enter( core_t &, long ) {} 311 312 template<typename core_t> 313 static inline auto leave( core_t & core, int ) -> decltype( core.symtab, void() ) { 314 core.symtab.leaveScope(); 315 } 316 317 template<typename core_t> 318 static inline auto leave( core_t &, long ) {} 286 319 287 320 // The symbol table has 2 kind of functions mostly, 1 argument and 2 arguments 288 321 // Create macro to condense these common patterns 289 322 #define SYMTAB_FUNC1( func, type ) \ 290 template<typename pass_t> \291 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.symtab.func( arg ), void() ) {\292 pass.symtab.func( arg ); \323 template<typename core_t> \ 324 static inline auto func( core_t & core, int, type arg ) -> decltype( core.symtab.func( arg ), void() ) {\ 325 core.symtab.func( arg ); \ 293 326 } \ 294 327 \ 295 template<typename pass_t> \296 static inline void func( pass_t &, long, type ) {}328 template<typename core_t> \ 329 static inline void func( core_t &, long, type ) {} 297 330 298 331 #define SYMTAB_FUNC2( func, type1, type2 ) \ 299 template<typename pass_t> \300 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.symtab.func( arg1, arg2 ), void () ) {\301 pass.symtab.func( arg1, arg2 ); \332 template<typename core_t> \ 333 static inline auto func( core_t & core, int, type1 arg1, type2 arg2 ) -> decltype( core.symtab.func( arg1, arg2 ), void () ) {\ 334 core.symtab.func( arg1, arg2 ); \ 302 335 } \ 303 336 \ 304 template<typename pass_t> \305 static inline void func( pass_t &, long, type1, type2 ) {}337 template<typename core_t> \ 338 static inline void func( core_t &, long, type1, type2 ) {} 306 339 307 340 SYMTAB_FUNC1( addId , const DeclWithType * ); … … 311 344 SYMTAB_FUNC1( addUnion , const UnionDecl * ); 312 345 SYMTAB_FUNC1( addTrait , const TraitDecl * ); 313 SYMTAB_FUNC2( addWith , const std::vector< ptr<Expr> > &, const Node* );346 SYMTAB_FUNC2( addWith , const std::vector< ptr<Expr> > &, const Decl * ); 314 347 315 348 // A few extra functions have more complicated behaviour, they are hand written 316 template<typename pass_t>317 static inline auto addStructFwd( pass_t & pass, int, const ast::StructDecl * decl ) -> decltype( pass.symtab.addStruct( decl ), void() ) {349 template<typename core_t> 350 static inline auto addStructFwd( core_t & core, int, const ast::StructDecl * decl ) -> decltype( core.symtab.addStruct( decl ), void() ) { 318 351 ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name ); 319 352 fwd->params = decl->params; 320 pass.symtab.addStruct( fwd );321 } 322 323 template<typename pass_t>324 static inline void addStructFwd( pass_t &, long, const ast::StructDecl * ) {}325 326 template<typename pass_t>327 static inline auto addUnionFwd( pass_t & pass, int, const ast::UnionDecl * decl ) -> decltype( pass.symtab.addUnion( decl ), void() ) {353 core.symtab.addStruct( fwd ); 354 } 355 356 template<typename core_t> 357 static inline void addStructFwd( core_t &, long, const ast::StructDecl * ) {} 358 359 template<typename core_t> 360 static inline auto addUnionFwd( core_t & core, int, const ast::UnionDecl * decl ) -> decltype( core.symtab.addUnion( decl ), void() ) { 328 361 UnionDecl * fwd = new UnionDecl( decl->location, decl->name ); 329 362 fwd->params = decl->params; 330 pass.symtab.addUnion( fwd );331 } 332 333 template<typename pass_t>334 static inline void addUnionFwd( pass_t &, long, const ast::UnionDecl * ) {}335 336 template<typename pass_t>337 static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addStruct( str ), void() ) {338 if ( ! pass.symtab.lookupStruct( str ) ) {339 pass.symtab.addStruct( str );363 core.symtab.addUnion( fwd ); 364 } 365 366 template<typename core_t> 367 static inline void addUnionFwd( core_t &, long, const ast::UnionDecl * ) {} 368 369 template<typename core_t> 370 static inline auto addStruct( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addStruct( str ), void() ) { 371 if ( ! core.symtab.lookupStruct( str ) ) { 372 core.symtab.addStruct( str ); 340 373 } 341 374 } 342 375 343 template<typename pass_t>344 static inline void addStruct( pass_t &, long, const std::string & ) {}345 346 template<typename pass_t>347 static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addUnion( str ), void() ) {348 if ( ! pass.symtab.lookupUnion( str ) ) {349 pass.symtab.addUnion( str );376 template<typename core_t> 377 static inline void addStruct( core_t &, long, const std::string & ) {} 378 379 template<typename core_t> 380 static inline auto addUnion( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addUnion( str ), void() ) { 381 if ( ! core.symtab.lookupUnion( str ) ) { 382 core.symtab.addUnion( str ); 350 383 } 351 384 } 352 385 353 template<typename pass_t>354 static inline void addUnion( pass_t &, long, const std::string & ) {}386 template<typename core_t> 387 static inline void addUnion( core_t &, long, const std::string & ) {} 355 388 356 389 #undef SYMTAB_FUNC1 357 390 #undef SYMTAB_FUNC2 358 }; 359 }; 360 }; 391 } // namespace symtab 392 393 // Some passes need to mutate TypeDecl and properly update their pointing TypeInstType. 394 // Detect the presence of a member name `subs` and call all members appropriately 395 namespace forall { 396 // Some simple scoping rules 397 template<typename core_t> 398 static inline auto enter( core_t & core, int, const ast::FunctionType * type ) 399 -> decltype( core.subs, void() ) { 400 if ( ! type->forall.empty() ) core.subs.beginScope(); 401 } 402 403 template<typename core_t> 404 static inline auto enter( core_t &, long, const ast::FunctionType * ) {} 405 406 template<typename core_t> 407 static inline auto leave( core_t & core, int, const ast::FunctionType * type ) 408 -> decltype( core.subs, void() ) { 409 if ( ! type->forall.empty() ) { core.subs.endScope(); } 410 } 411 412 template<typename core_t> 413 static inline auto leave( core_t &, long, const ast::FunctionType * ) {} 414 415 // Replaces a TypeInstType's base TypeDecl according to the table 416 template<typename core_t> 417 static inline auto replace( core_t & core, int, const ast::TypeInstType *& inst ) 418 -> decltype( core.subs, void() ) { 419 inst = ast::mutate_field( 420 inst, &ast::TypeInstType::base, core.subs.replace( inst->base ) ); 421 } 422 423 template<typename core_t> 424 static inline auto replace( core_t &, long, const ast::TypeInstType *& ) {} 425 426 } // namespace forall 427 428 template<typename core_t> 429 static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) { 430 return core.result(); 431 } 432 433 template<typename core_t> 434 static inline auto get_result( core_t & core, int ) -> decltype( core.result ) { 435 return core.result; 436 } 437 438 template<typename core_t> 439 static inline void get_result( core_t &, long ) {} 440 } // namespace __pass 441 } // namespace ast -
src/AST/Print.cpp
r3c64c668 r58fe85a 21 21 #include "Type.hpp" 22 22 #include "TypeSubstitution.hpp" 23 #include "CompilationState.h" 23 24 24 25 #include "Common/utility.h" // for group_iterate … … 29 30 30 31 template <typename C, typename... T> 31 constexpr auto make_array(T&&... values) -> 32 array<C,sizeof...(T)> 32 constexpr array<C,sizeof...(T)> make_array(T&&... values) 33 33 { 34 34 return array<C,sizeof...(T)>{ … … 129 129 130 130 void print( const ast::Expr::InferUnion & inferred, unsigned level = 0 ) { 131 switch ( inferred.mode ) { 132 case ast::Expr::InferUnion::Empty: return; 133 case ast::Expr::InferUnion::Slots: { 134 os << indent << "with " << inferred.data.resnSlots.size() 131 if (inferred.data.resnSlots && !inferred.data.resnSlots->empty()) { 132 os << indent << "with " << inferred.data.resnSlots->size() 135 133 << " pending inference slots" << endl; 136 return; 137 } 138 case ast::Expr::InferUnion::Params: { 134 } 135 if (inferred.data.inferParams && !inferred.data.inferParams->empty()) { 139 136 os << indent << "with inferred parameters " << level << ":" << endl; 140 137 ++indent; 141 for ( const auto & i : inferred.data.inferParams ) {138 for ( const auto & i : *inferred.data.inferParams ) { 142 139 os << indent; 143 short_print( Decl::fromId( i.second.decl ));140 short_print( i.second.declptr ); 144 141 os << endl; 145 142 print( i.second.expr->inferred, level+1 ); 146 143 } 147 144 --indent; 148 return; 149 } 150 } 151 } 152 153 void print( const ast::ParameterizedType::ForallList & forall ) { 145 } 146 } 147 148 void print( const ast::FunctionType::ForallList & forall ) { 154 149 if ( forall.empty() ) return; 155 150 os << "forall" << endl; 156 151 ++indent; 157 152 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); 158 162 os << indent; 159 163 --indent; … … 210 214 211 215 void preprint( const ast::NamedTypeDecl * node ) { 212 if ( ! node->name.empty() ) os << node->name << ": "; 216 if ( ! node->name.empty() ) { 217 os << node->name << ": "; 218 } 213 219 214 220 if ( ! short_mode && node->linkage != Linkage::Cforall ) { … … 226 232 } 227 233 228 if ( ! node->params.empty() ) { 229 os << endl << indent << "... with parameters" << endl; 230 ++indent; 231 printAll( node->params ); 232 --indent; 233 } 234 235 if ( ! short_mode && ! node->assertions.empty() ) { 234 if ( ! node->assertions.empty() ) { 236 235 os << endl << indent << "... with assertions" << endl; 237 236 ++indent; … … 244 243 print( node->inferred ); 245 244 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 246 253 if ( node->env ) { 247 254 os << endl << indent << "... with environment:" << endl; … … 260 267 } 261 268 262 void preprint( const ast:: ParameterizedType * node ) {269 void preprint( const ast::FunctionType * node ) { 263 270 print( node->forall ); 271 print( node->assertions ); 264 272 print( node->qualifiers ); 265 273 } 266 274 267 void preprint( const ast::ReferenceToType * node ) { 268 print( node->forall ); 275 void preprint( const ast::BaseInstType * node ) { 269 276 print( node->attributes ); 270 277 print( node->qualifiers ); … … 678 685 } 679 686 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 680 706 virtual const ast::Stmt * visit( const ast::WaitForStmt * node ) override final { 681 707 os << "Waitfor Statement" << endl; … … 823 849 virtual const ast::Expr * visit( const ast::CastExpr * node ) override final { 824 850 ++indent; 825 os << (node->isGenerated ? "Generated" : "Explicit") << " cast of:" << endl << indent;851 os << (node->isGenerated ? "Generated" : "Explicit") << " Cast of:" << endl << indent; 826 852 safe_print( node->arg ); 827 853 os << endl << indent-1 << "... to:"; … … 1358 1384 virtual const ast::Type * visit( const ast::TypeInstType * node ) override final { 1359 1385 preprint( node ); 1360 os << "instance of type " << node->name 1386 const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->typeString(); 1387 os << "instance of type " << _name 1361 1388 << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)"; 1362 1389 print( node->params ); … … 1484 1511 os << indent << "Types:" << endl; 1485 1512 for ( const auto& i : *node ) { 1486 os << indent+1 << i.first << " -> ";1513 os << indent+1 << i.first.typeString() << " -> "; 1487 1514 indent += 2; 1488 1515 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 );1497 1516 indent -= 2; 1498 1517 os << endl; -
src/AST/Stmt.hpp
r3c64c668 r58fe85a 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 template<typename node_t> friend node_t * mutate(const node_t * node); 29 #define MUTATE_FRIEND \ 30 template<typename node_t> friend node_t * mutate(const node_t * node); \ 31 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 30 32 31 33 namespace ast { … … 342 344 }; 343 345 346 /// Suspend statement 347 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_FRIEND 359 }; 360 344 361 /// Wait for concurrency statement `when (...) waitfor (... , ...) ... timeout(...) ... else ...` 345 362 class WaitForStmt final : public Stmt { … … 397 414 class ImplicitCtorDtorStmt final : public Stmt { 398 415 public: 399 readonly<Stmt> callStmt;416 ptr<Stmt> callStmt; 400 417 401 418 ImplicitCtorDtorStmt( const CodeLocation & loc, const Stmt * callStmt, -
src/AST/SymbolTable.cpp
r3c64c668 r58fe85a 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 97 104 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 98 112 ++*stats().lookup_calls; 99 113 if ( ! idTable ) return {}; … … 107 121 out.push_back( decl.second ); 108 122 } 123 124 if (Stats::Counters::enabled) { 125 if (! lookups_by_name.count(id)) { 126 // leaks some strings, but it is because Counters do not hold them 127 auto lookupCounterName = new std::string(id + "%count"); 128 auto candidatesCounterName = new std::string(id + "%candidate"); 129 lookups_by_name.emplace(id, new Stats::Counters::SimpleCounter(lookupCounterName->c_str(), name_lookup_stats)); 130 candidates_by_name.emplace(id, new Stats::Counters::SimpleCounter(candidatesCounterName->c_str(), name_lookup_stats)); 131 } 132 (*lookups_by_name[id]) ++; 133 *candidates_by_name[id] += out.size(); 134 } 135 136 return out; 137 } 138 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_key 154 = Stats::Counters::build<Stats::Counters::SimpleCounter>("keyed lookups", special_stats); 155 static Stats::Counters::SimpleCounter * num_lookup_without_key 156 = Stats::Counters::build<Stats::Counters::SimpleCounter>("unkeyed lookups", special_stats); 157 158 assert (kind != NUMBER_OF_KINDS); 159 ++*stats().lookup_calls; 160 if ( ! specialFunctionTable[kind] ) return {}; 161 162 std::vector<IdData> out; 163 164 if (otypeKey.empty()) { // returns everything 165 ++*num_lookup_without_key; 166 for (auto & table : *specialFunctionTable[kind]) { 167 for (auto & decl : *table.second) { 168 out.push_back(decl.second); 169 } 170 } 171 } 172 else { 173 ++*num_lookup_with_key; 174 ++*stats().map_lookups; 175 auto decls = specialFunctionTable[kind]->find(otypeKey); 176 if (decls == specialFunctionTable[kind]->end()) return {}; 177 178 for (auto decl : *(decls->second)) { 179 out.push_back(decl.second); 180 } 181 } 182 183 ++*stat_counts[kind]; 184 *stat_candidates[kind] += out.size(); 185 109 186 return out; 110 187 } … … 313 390 if ( ! expr->result ) continue; 314 391 const Type * resTy = expr->result->stripReferences(); 315 auto aggrType = dynamic_cast< const ReferenceToType * >( resTy );392 auto aggrType = dynamic_cast< const BaseInstType * >( resTy ); 316 393 assertf( aggrType, "WithStmt expr has non-aggregate type: %s", 317 394 toString( expr->result ).c_str() ); … … 335 412 } 336 413 337 void SymbolTable::addFunctionType( const FunctionType * ftype ) { 338 addTypes( ftype->forall ); 339 addIds( ftype->returns ); 340 addIds( ftype->params ); 341 } 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 342 427 343 428 void SymbolTable::lazyInitScope() { … … 364 449 namespace { 365 450 /// gets the base type of the first parameter; decl must be a ctor/dtor/assignment function 366 std::string getOtypeKey( const Function Decl * function) {367 const auto & params = f unction->type->params;451 std::string getOtypeKey( const FunctionType * ftype, bool stripParams = true ) { 452 const auto & params = ftype->params; 368 453 assert( ! params.empty() ); 369 454 // use base type of pointer, so that qualifiers on the pointer type aren't considered. 370 const Type * base = InitTweak::getPointerBase( params.front() ->get_type());455 const Type * base = InitTweak::getPointerBase( params.front() ); 371 456 assert( base ); 372 return Mangle::mangle( base ); 457 if (stripParams) { 458 if (dynamic_cast<const PointerType *>(base)) return Mangle::Encoding::pointer; 459 return Mangle::mangle( base, Mangle::Type | Mangle::NoGenericParams ); 460 } 461 else 462 return Mangle::mangle( base ); 373 463 } 374 464 … … 378 468 const DeclWithType * decl, const std::string & otypeKey ) { 379 469 auto func = dynamic_cast< const FunctionDecl * >( decl ); 380 if ( ! func || otypeKey != getOtypeKey( func ) ) return nullptr;470 if ( ! func || otypeKey != getOtypeKey( func->type, false ) ) return nullptr; 381 471 return func; 382 472 } … … 403 493 bool dataIsUserDefinedFunc = ! function->linkage.is_overrideable; 404 494 bool dataIsCopyFunc = InitTweak::isCopyFunction( function ); 405 std::string dataOtypeKey = getOtypeKey( function );495 std::string dataOtypeKey = getOtypeKey( function->type, false ); // requires exact match to override autogen 406 496 407 497 if ( dataIsUserDefinedFunc && dataIsCopyFunc ) { … … 575 665 const DeclWithType * decl, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr, 576 666 const Decl * deleter ) { 667 SpecialFunctionKind kind = getSpecialFunctionKind(decl->name); 668 if (kind == NUMBER_OF_KINDS) { // not a special decl 669 addId(decl, decl->name, idTable, handleConflicts, baseExpr, deleter); 670 } 671 else { 672 std::string key; 673 if (auto func = dynamic_cast<const FunctionDecl *>(decl)) { 674 key = getOtypeKey(func->type); 675 } 676 else if (auto obj = dynamic_cast<const ObjectDecl *>(decl)) { 677 key = getOtypeKey(obj->type.strict_as<PointerType>()->base.strict_as<FunctionType>()); 678 } 679 else { 680 assertf(false, "special decl with non-function type"); 681 } 682 addId(decl, key, specialFunctionTable[kind], handleConflicts, baseExpr, deleter); 683 } 684 } 685 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 ) { 577 689 ++*stats().add_calls; 578 690 const std::string &name = decl->name; … … 605 717 // ensure tables exist and add identifier 606 718 MangleTable::Ptr mangleTable; 607 if ( ! idTable ) {608 idTable = IdTable::new_ptr();719 if ( ! table ) { 720 table = IdTable::new_ptr(); 609 721 mangleTable = MangleTable::new_ptr(); 610 722 } else { 611 723 ++*stats().map_lookups; 612 auto decls = idTable->find( name);613 if ( decls == idTable->end() ) {724 auto decls = table->find( lookupKey ); 725 if ( decls == table->end() ) { 614 726 mangleTable = MangleTable::new_ptr(); 615 727 } else { … … 626 738 lazyInitScope(); 627 739 *stats().map_mutations += 2; 628 idTable = idTable->set(629 name,740 table = table->set( 741 lookupKey, 630 742 mangleTable->set( 631 743 mangleName, … … 642 754 IdData data{ decl, baseExpr, deleter, scope }; 643 755 // Ensure that auto-generated ctor/dtor/assignment are deleted if necessary 644 if ( ! removeSpecialOverrides( data, mangleTable ) ) return; 756 if (table != idTable) { // adding to special table 757 if ( ! removeSpecialOverrides( data, mangleTable ) ) return; 758 } 645 759 *stats().map_mutations += 2; 646 idTable = idTable->set( name, mangleTable->set( mangleName, std::move(data) ) );760 table = table->set( lookupKey, mangleTable->set( mangleName, std::move(data) ) ); 647 761 } 648 762 … … 654 768 if ( dwt->name == "" ) { 655 769 const Type * t = dwt->get_type()->stripReferences(); 656 if ( auto rty = dynamic_cast<const ReferenceToType *>( t ) ) {770 if ( auto rty = dynamic_cast<const BaseInstType *>( t ) ) { 657 771 if ( ! dynamic_cast<const StructInstType *>(rty) 658 772 && ! dynamic_cast<const UnionInstType *>(rty) ) continue; -
src/AST/SymbolTable.hpp
r3c64c668 r58fe85a 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 keys 36 enum SpecialFunctionKind {CTOR, DTOR, ASSIGN, NUMBER_OF_KINDS}; 37 static SpecialFunctionKind getSpecialFunctionKind(const std::string & name); 38 35 39 /// Stored information about a declaration 36 40 struct IdData { … … 77 81 UnionTable::Ptr unionTable; ///< union namespace 78 82 TraitTable::Ptr traitTable; ///< trait namespace 83 IdTable::Ptr specialFunctionTable[NUMBER_OF_KINDS]; 84 85 // using SpecialFuncTable = PersistentMap< std::string, IdTable::Ptr >; // fname (ctor/dtor/assign) - otypekey 86 // SpecialFuncTable::Ptr specialFuncTable; 79 87 80 88 using Ptr = std::shared_ptr<const SymbolTable>; … … 95 103 /// Gets all declarations with the given ID 96 104 std::vector<IdData> lookupId( const std::string &id ) const; 105 /// Gets special functions associated with a type; if no key is given, returns everything 106 std::vector<IdData> specialLookupId( SpecialFunctionKind kind, const std::string & otypeKey = "" ) const; 97 107 /// Gets the top-most type declaration with the given ID 98 108 const NamedTypeDecl * lookupType( const std::string &id ) const; … … 145 155 146 156 /// convenience function for adding all of the declarations in a function type to the indexer 147 void addFunction Type( const FunctionType * ftype);157 void addFunction( const FunctionDecl * ); 148 158 149 159 private: … … 186 196 const Decl * deleter = nullptr ); 187 197 198 /// common code for addId when special decls are placed into separate tables 199 void addId( 200 const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & idTable, OnConflict handleConflicts, 201 const Expr * baseExpr = nullptr, const Decl * deleter = nullptr); 202 188 203 /// adds all of the members of the Aggregate (addWith helper) 189 204 void addMembers( const AggregateDecl * aggr, const Expr * expr, OnConflict handleConflicts ); -
src/AST/Type.cpp
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Mon May 13 15:00:00 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sun Dec 15 16:56:28 201913 // Update Count : 411 // Last Modified By : Andrew Beach 12 // Last Modified On : Thu Jul 23 14:16:00 2020 13 // Update Count : 5 14 14 // 15 15 … … 22 22 #include "Decl.hpp" 23 23 #include "Init.hpp" 24 #include "Common/utility.h" // for copy, move 24 25 #include "InitTweak/InitTweak.h" // for getPointerBase 25 26 #include "Tuples/Tuples.h" // for isTtype … … 91 92 92 93 // --- FunctionType 93 94 94 namespace { 95 bool containsTtype( const std::vector<ptr< DeclWithType>> & l ) {95 bool containsTtype( const std::vector<ptr<Type>> & l ) { 96 96 if ( ! l.empty() ) { 97 return Tuples::isTtype( l.back() ->get_type());97 return Tuples::isTtype( l.back() ); 98 98 } 99 99 return false; … … 105 105 } 106 106 107 // --- ReferenceToType 108 std::vector<readonly<Decl>> ReferenceToType::lookup( const std::string& name ) const { 107 std::vector<readonly<Decl>> BaseInstType::lookup( const std::string& name ) const { 109 108 assertf( aggr(), "Must have aggregate to perform lookup" ); 110 109 … … 116 115 } 117 116 118 // --- S tructInstType117 // --- SueInstType (StructInstType, UnionInstType, EnumInstType) 119 118 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 ) {} 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 ) {} 123 123 124 bool StructInstType::isComplete() const { return base ? base->body : false; } 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 ) {} 125 129 126 // --- UnionInstType 130 template<typename decl_t> 131 bool SueInstType<decl_t>::isComplete() const { 132 return base ? base->body : false; 133 } 127 134 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; } 135 template class SueInstType<StructDecl>; 136 template class SueInstType<UnionDecl>; 137 template class SueInstType<EnumDecl>; 141 138 142 139 // --- TraitInstType 143 140 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 141 TraitInstType::TraitInstType( 142 const TraitDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as ) 143 : BaseInstType( b->name, q, move(as) ), base( b ) {} 149 144 150 145 void TypeInstType::set_base( const TypeDecl * b ) { … … 158 153 159 154 TupleType::TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q ) 160 : Type( q ), types( std::move(ts) ), members() {155 : Type( q ), types( move(ts) ), members() { 161 156 // This constructor is awkward. `TupleType` needs to contain objects so that members can be 162 157 // named, but members without initializer nodes end up getting constructors, which breaks … … 173 168 for ( const Type * ty : types ) { 174 169 members.emplace_back( new ObjectDecl{ 175 CodeLocation{}, "", ty, new ListInit( CodeLocation{}, {}, {}, MaybeConstruct ),170 CodeLocation{}, "", ty, new ListInit( CodeLocation{}, {}, {}, NoConstruct ), 176 171 Storage::Classes{}, Linkage::Cforall } ); 177 172 } 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 check 180 // if the context id is filled. this is a temporary hack for now 181 return typeInst->formal_usage > 0; 182 } 183 return false; 178 184 } 179 185 -
src/AST/Type.hpp
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Thu May 9 10:00:00 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Dec 11 21:56:46 201913 // Update Count : 511 // Last Modified By : Andrew Beach 12 // Last Modified On : Thu Jul 23 14:15:00 2020 13 // Update Count : 6 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 template<typename node_t> friend node_t * mutate(const node_t * node); 31 #define MUTATE_FRIEND \ 32 template<typename node_t> friend node_t * mutate(const node_t * node); \ 33 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 32 34 33 35 namespace ast { 36 37 template< typename T > class Pass; 34 38 35 39 class Type : public Node { … … 44 48 bool is_volatile() const { return qualifiers.is_volatile; } 45 49 bool is_restrict() const { return qualifiers.is_restrict; } 46 bool is_lvalue() const { return qualifiers.is_lvalue; }47 50 bool is_mutex() const { return qualifiers.is_mutex; } 48 51 bool is_atomic() const { return qualifiers.is_atomic; } … … 51 54 Type * set_volatile( bool v ) { qualifiers.is_volatile = v; return this; } 52 55 Type * set_restrict( bool v ) { qualifiers.is_restrict = v; return this; } 53 Type * set_lvalue( bool v ) { qualifiers.is_lvalue = v; return this; }54 56 Type * set_mutex( bool v ) { qualifiers.is_mutex = v; return this; } 55 57 Type * set_atomic( bool v ) { qualifiers.is_atomic = v; return this; } … … 163 165 static const char *typeNames[]; 164 166 165 BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 167 BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 166 168 : Type(q, std::move(as)), kind(k) {} 167 169 … … 263 265 }; 264 266 265 /// Base type for potentially forall-qualified types266 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_FRIEND282 };283 284 267 /// Function variable arguments flag 285 268 enum ArgumentFlag { FixedArgs, VariableArgs }; 286 269 287 270 /// Type of a function `[R1, R2](*)(P1, P2, P3)` 288 class FunctionType final : public ParameterizedType { 289 public: 290 std::vector<ptr<DeclWithType>> returns; 291 std::vector<ptr<DeclWithType>> params; 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; 292 280 293 281 /// Does the function accept a variable number of arguments following the arguments specified … … 299 287 300 288 FunctionType( ArgumentFlag va = FixedArgs, CV::Qualifiers q = {} ) 301 : ParameterizedType(q), returns(), params(), isVarArgs(va) {} 289 : Type(q), returns(), params(), isVarArgs(va) {} 290 291 FunctionType( const FunctionType & o ) = default; 302 292 303 293 /// true if either the parameters or return values contain a tttype … … 313 303 314 304 /// base class for types that refer to types declared elsewhere (aggregates and typedefs) 315 class ReferenceToType : public ParameterizedType {305 class BaseInstType : public Type { 316 306 public: 317 307 std::vector<ptr<Expr>> params; … … 319 309 bool hoistType = false; 320 310 321 ReferenceToType( const std::string& n, CV::Qualifiers q = {}, 322 std::vector<ptr<Attribute>> && as = {} ) 323 : ParameterizedType(q, std::move(as)), params(), name(n) {} 311 BaseInstType( 312 const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 313 : Type(q, std::move(as)), params(), name(n) {} 314 315 BaseInstType( 316 const std::string& n, std::vector<ptr<Expr>> && params, 317 CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 318 : Type(q, std::move(as)), params(std::move(params)), name(n) {} 319 320 BaseInstType( const BaseInstType & o ) = default; 324 321 325 322 /// Gets aggregate declaration this type refers to … … 329 326 330 327 private: 331 virtual ReferenceToType * clone() const override = 0; 332 MUTATE_FRIEND 333 }; 334 335 /// instance of struct type 336 class StructInstType final : public ReferenceToType { 337 public: 338 readonly<StructDecl> base; 339 340 StructInstType( const std::string& n, CV::Qualifiers q = {}, 341 std::vector<ptr<Attribute>> && as = {} ) 342 : ReferenceToType( n, q, std::move(as) ), base() {} 343 StructInstType( const StructDecl * b, CV::Qualifiers q = {}, 344 std::vector<ptr<Attribute>> && as = {} ); 328 virtual BaseInstType * clone() const override = 0; 329 MUTATE_FRIEND 330 }; 331 332 // Common implementation for the SUE instance types. Not to be used directly. 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 = {} ); 345 349 346 350 bool isComplete() const override; 347 351 348 const StructDecl * aggr() const override { return base; } 349 350 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 351 private: 352 StructInstType * clone() const override { return new StructInstType{ *this }; } 353 MUTATE_FRIEND 354 }; 355 356 /// instance of union type 357 class UnionInstType final : public ReferenceToType { 358 public: 359 readonly<UnionDecl> base; 360 361 UnionInstType( const std::string& n, CV::Qualifiers q = {}, 362 std::vector<ptr<Attribute>> && as = {} ) 363 : ReferenceToType( n, q, std::move(as) ), base() {} 364 UnionInstType( const UnionDecl * b, CV::Qualifiers q = {}, 365 std::vector<ptr<Attribute>> && as = {} ); 366 367 bool isComplete() const override; 368 369 const UnionDecl * aggr() const override { return base; } 370 371 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 372 private: 373 UnionInstType * clone() const override { return new UnionInstType{ *this }; } 374 MUTATE_FRIEND 375 }; 376 377 /// instance of enum type 378 class EnumInstType final : public ReferenceToType { 379 public: 380 readonly<EnumDecl> base; 381 382 EnumInstType( const std::string& n, CV::Qualifiers q = {}, 383 std::vector<ptr<Attribute>> && as = {} ) 384 : ReferenceToType( n, q, std::move(as) ), base() {} 385 EnumInstType( const EnumDecl * b, CV::Qualifiers q = {}, 386 std::vector<ptr<Attribute>> && as = {} ); 387 388 bool isComplete() const override; 389 390 const EnumDecl * aggr() const override { return base; } 391 392 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 393 private: 394 EnumInstType * clone() const override { return new EnumInstType{ *this }; } 395 MUTATE_FRIEND 396 }; 397 398 /// instance of trait type 399 class TraitInstType final : public ReferenceToType { 352 const decl_t * aggr() const override { return base; } 353 354 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 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 { 400 371 public: 401 372 readonly<TraitDecl> base; 402 373 403 TraitInstType( const std::string& n, CV::Qualifiers q = {}, 404 std::vector<ptr<Attribute>> && as = {} ) 405 : ReferenceToType( n, q, std::move(as) ), base() {} 406 TraitInstType( const TraitDecl * b, CV::Qualifiers q = {}, 407 std::vector<ptr<Attribute>> && as = {} ); 374 TraitInstType( 375 const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 376 : BaseInstType( n, q, std::move(as) ), base() {} 377 378 TraitInstType( 379 const TraitDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ); 408 380 409 381 // not meaningful for TraitInstType … … 419 391 420 392 /// instance of named type alias (typedef or variable) 421 class TypeInstType final : public ReferenceToType {393 class TypeInstType final : public BaseInstType { 422 394 public: 423 395 readonly<TypeDecl> base; 396 // previously from renameTyVars; now directly use integer fields instead of synthesized strings 397 // a nonzero value of formal_usage indicates a formal type (only used in function type) 398 // a zero value of formal_usage indicates an actual type (referenced inside body of parametric structs and functions) 424 399 TypeDecl::Kind kind; 425 426 TypeInstType( const std::string& n, const TypeDecl * b, CV::Qualifiers q = {}, 400 int formal_usage = 0; 401 int expr_id = 0; 402 403 // compact representation used for map lookups. 404 struct TypeEnvKey { 405 const TypeDecl * base; 406 int formal_usage; 407 int expr_id; 408 409 TypeEnvKey() = default; 410 TypeEnvKey(const TypeDecl * base, int formal_usage = 0, int expr_id = 0): base(base), formal_usage(formal_usage), expr_id(expr_id) {} 411 TypeEnvKey(const TypeInstType & inst): base(inst.base), formal_usage(inst.formal_usage), expr_id(inst.expr_id) {} 412 std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; } 413 bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 414 415 }; 416 417 bool operator==(const TypeInstType & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 418 419 TypeInstType( 420 const std::string& n, const TypeDecl * b, CV::Qualifiers q = {}, 427 421 std::vector<ptr<Attribute>> && as = {} ) 428 : ReferenceToType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}422 : BaseInstType( n, q, std::move(as) ), base( b ), kind( b->kind ) {} 429 423 TypeInstType( const std::string& n, TypeDecl::Kind k, CV::Qualifiers q = {}, 430 424 std::vector<ptr<Attribute>> && as = {} ) 431 : ReferenceToType( n, q, std::move(as) ), base(), kind( k ) {} 425 : BaseInstType( n, q, std::move(as) ), base(), kind( k ) {} 426 427 TypeInstType( const TypeInstType & o ) = default; 428 429 TypeInstType( const TypeEnvKey & key ) 430 : BaseInstType(key.base->name), base(key.base), kind(key.base->kind), formal_usage(key.formal_usage), expr_id(key.expr_id) {} 432 431 433 432 /// sets `base`, updating `kind` correctly … … 440 439 441 440 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 } 442 446 private: 443 447 TypeInstType * clone() const override { return new TypeInstType{ *this }; } … … 531 535 }; 532 536 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 }; 533 552 } 534 553 -
src/AST/TypeEnvironment.cpp
r3c64c668 r58fe85a 34 34 #include "ResolvExpr/Unify.h" // for unifyInexact 35 35 #include "Tuples/Tuples.h" // for isTtype 36 #include "CompilationState.h" 36 37 37 38 using ResolvExpr::WidenMode; … … 51 52 for ( const auto & i : open ) { 52 53 if ( first ) { first = false; } else { out << ' '; } 53 out << i.first << "(" << i.second << ")";54 out << i.first.typeString() << "(" << i.second << ")"; 54 55 } 55 56 } 56 57 57 58 void print( std::ostream & out, const EqvClass & clz, Indenter indent ) { 58 out << "( "; 59 std::copy( clz.vars.begin(), clz.vars.end(), std::ostream_iterator< std::string >( out, " " ) ); 59 out << "("; 60 bool first = true; 61 for(const auto & var : clz.vars) { 62 if(first) first = false; 63 else out << " "; 64 65 if( deterministic_output ) out << "[unbound]"; 66 else out << "_" << var.formal_usage << "_" << var.expr_id << "_"; 67 68 out << var.base->name; 69 } 60 70 out << ")"; 61 71 62 72 if ( clz.bound ) { 63 73 out << " -> "; … … 72 82 } 73 83 74 const EqvClass * TypeEnvironment::lookup( const std::string& var ) const {84 const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey & var ) const { 75 85 for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) { 76 86 if ( i->vars.find( var ) != i->vars.end() ) return &*i; … … 92 102 } 93 103 } 94 104 95 105 i = next; // go to next node even if this removed 96 106 } … … 98 108 } 99 109 100 void TypeEnvironment::add( const ParameterizedType::ForallList & tyDecls ) {101 for ( const TypeDecl *tyDecl : tyDecls ) {110 void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) { 111 for ( auto & tyDecl : tyDecls ) { 102 112 env.emplace_back( tyDecl ); 103 113 } … … 112 122 void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const { 113 123 for ( const auto & clz : env ) { 114 std::string clzRep; 124 TypeInstType::TypeEnvKey clzRep; 125 bool first = true; 115 126 for ( const auto & var : clz.vars ) { 116 127 if ( clz.bound ) { 117 128 sub.add( var, clz.bound ); 118 } else if ( clzRep.empty()) {129 } else if ( first ) { 119 130 clzRep = var; 131 first = false; 120 132 } else { 121 sub.add( var, new TypeInstType{ clzRep , clz.data.kind} );133 sub.add( var, new TypeInstType{ clzRep } ); 122 134 } 123 135 } … … 134 146 struct Occurs : public ast::WithVisitorRef<Occurs> { 135 147 bool result; 136 std:: set< std::string> vars;148 std::unordered_set< TypeInstType::TypeEnvKey > vars; 137 149 const TypeEnvironment & tenv; 138 150 139 Occurs( const std::string& var, const TypeEnvironment & env )151 Occurs( const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) 140 152 : result( false ), vars(), tenv( env ) { 141 153 if ( const EqvClass * clz = tenv.lookup( var ) ) { … … 147 159 148 160 void previsit( const TypeInstType * typeInst ) { 149 if ( vars.count( typeInst->name) ) {161 if ( vars.count( *typeInst ) ) { 150 162 result = true; 151 } else if ( const EqvClass * clz = tenv.lookup( typeInst->name) ) {163 } else if ( const EqvClass * clz = tenv.lookup( *typeInst ) ) { 152 164 if ( clz->bound ) { 153 165 clz->bound->accept( *visitor ); … … 158 170 159 171 /// true if `var` occurs in `ty` under `env` 160 bool occurs( const Type * ty, const std::string& var, const TypeEnvironment & env ) {172 bool occurs( const Type * ty, const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) { 161 173 Pass<Occurs> occur{ var, env }; 162 174 maybe_accept( ty, occur ); 163 return occur. pass.result;164 } 165 } 166 167 bool TypeEnvironment::combine( 175 return occur.core.result; 176 } 177 } 178 179 bool TypeEnvironment::combine( 168 180 const TypeEnvironment & o, OpenVarSet & open, const SymbolTable & symtab ) { 169 181 // short-circuit easy cases … … 199 211 auto st = internal_lookup( *vt ); 200 212 if ( st == env.end() ) { 201 // unbound, safe to add if occurs 213 // unbound, safe to add if occurs 202 214 if ( r.bound && occurs( r.bound, *vt, *this ) ) return false; 203 215 r.vars.emplace( *vt ); … … 266 278 } 267 279 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 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 272 284 ) { 273 285 // remove references from bound type, so that type variables can only bind to value types 274 286 ptr<Type> target = bindTo->stripReferences(); 275 auto tyvar = open.find( typeInst->name);287 auto tyvar = open.find( *typeInst ); 276 288 assert( tyvar != open.end() ); 277 289 if ( ! tyVarCompatible( tyvar->second, target ) ) return false; 278 if ( occurs( target, typeInst->name, *this ) ) return false;279 280 auto it = internal_lookup( typeInst->name);290 if ( occurs( target, *typeInst, *this ) ) return false; 291 292 auto it = internal_lookup( *typeInst ); 281 293 if ( it != env.end() ) { 282 294 if ( it->bound ) { … … 286 298 ptr<Type> newType = it->bound; 287 299 reset_qualifiers( newType, typeInst->qualifiers ); 288 if ( unifyInexact( 289 newType, target, *this, need, have, open, 300 if ( unifyInexact( 301 newType, target, *this, need, have, open, 290 302 widen & WidenMode{ it->allowWidening, true }, symtab, common ) ) { 291 303 if ( common ) { … … 300 312 } 301 313 } else { 302 env.emplace_back( 303 typeInst->name, target, widen.first && widen.second, data );314 env.emplace_back( 315 *typeInst, target, widen.first && widen.second, data ); 304 316 } 305 317 return true; 306 318 } 307 319 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 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 312 324 ) { 313 auto c1 = internal_lookup( var1->name);314 auto c2 = internal_lookup( var2->name);315 325 auto c1 = internal_lookup( *var1 ); 326 auto c2 = internal_lookup( *var2 ); 327 316 328 // exit early if variables already bound together 317 329 if ( c1 != env.end() && c1 == c2 ) { … … 326 338 if ( c1 != env.end() ) { 327 339 if ( c1->bound ) { 328 if ( occurs( c1->bound, var2->name, *this ) ) return false;340 if ( occurs( c1->bound, *var2, *this ) ) return false; 329 341 type1 = c1->bound; 330 342 } … … 333 345 if ( c2 != env.end() ) { 334 346 if ( c2->bound ) { 335 if ( occurs( c2->bound, var1->name, *this ) ) return false;347 if ( occurs( c2->bound, *var1, *this ) ) return false; 336 348 type2 = c2->bound; 337 349 } … … 371 383 } else if ( c1 != env.end() ) { 372 384 // var2 unbound, add to env[c1] 373 c1->vars.emplace( var2->name);385 c1->vars.emplace( *var2 ); 374 386 c1->allowWidening = widen1; 375 387 c1->data.isComplete |= data.isComplete; 376 388 } else if ( c2 != env.end() ) { 377 389 // var1 unbound, add to env[c2] 378 c2->vars.emplace( var1->name);390 c2->vars.emplace( *var1 ); 379 391 c2->allowWidening = widen2; 380 392 c2->data.isComplete |= data.isComplete; 381 393 } else { 382 394 // neither var bound, create new class 383 env.emplace_back( var1->name, var2->name, widen1 && widen2, data );395 env.emplace_back( *var1, *var2, widen1 && widen2, data ); 384 396 } 385 397 … … 396 408 } 397 409 398 bool TypeEnvironment::mergeBound( 410 bool TypeEnvironment::mergeBound( 399 411 EqvClass & to, const EqvClass & from, OpenVarSet & open, const SymbolTable & symtab ) { 400 412 if ( from.bound ) { … … 406 418 AssertionSet need, have; 407 419 408 if ( unifyInexact( 420 if ( unifyInexact( 409 421 toType, fromType, *this, need, have, open, widen, symtab, common ) ) { 410 422 // unifies, set common type if necessary … … 424 436 } 425 437 426 bool TypeEnvironment::mergeClasses( 438 bool TypeEnvironment::mergeClasses( 427 439 ClassList::iterator to, ClassList::iterator from, OpenVarSet & open, const SymbolTable & symtab 428 440 ) { … … 445 457 } 446 458 447 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string& var ) {459 TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey & var ) { 448 460 for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) { 449 461 if ( i->vars.count( var ) ) return i; -
src/AST/TypeEnvironment.hpp
r3c64c668 r58fe85a 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 DeclWithType * d1, const DeclWithType* d2 ) const {58 int cmp = d1-> name.compare( d2->name );59 return cmp < 0 || ( cmp == 0 && d1-> get_type() < d2->get_type());57 bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const { 58 int cmp = d1->var->name.compare( d2->var->name ); 59 return cmp < 0 || ( cmp == 0 && d1->result < d2->result ); 60 60 } 61 61 }; … … 70 70 71 71 /// Set of assertions pending satisfaction 72 using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >;72 using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >; 73 73 74 74 /// Set of open variables 75 using OpenVarSet = std::unordered_map< std::string, TypeDecl::Data >;75 using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, 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:: set< std::string> vars;91 std::unordered_set< TypeInstType::TypeEnvKey > 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 Decl * decl)104 : vars{ decl->name }, bound(), allowWidening( true ), data( decl) {}103 EqvClass( const TypeInstType * inst ) 104 : vars{ *inst }, bound(), allowWidening( true ), data( inst->base ) {} 105 105 106 106 /// Singleton class constructor from substitution 107 EqvClass( const std::string& v, const Type * b )107 EqvClass( const TypeInstType::TypeEnvKey & 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 std::string& v, const Type * b, bool w, const TypeDecl::Data & d )111 EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b, bool w, const TypeDecl::Data & d ) 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 std::string & v, const std::string& u, bool w, const TypeDecl::Data & d )117 EqvClass( const TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey & u, bool w, const TypeDecl::Data & d ) 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 std::string& var ) const;133 const EqvClass * lookup( const TypeInstType::TypeEnvKey & var ) const; 134 134 135 135 /// Add a new equivalence class for each type variable 136 void add( const ParameterizedType::ForallList & tyDecls );136 void add( const FunctionType::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 , enum Node::ref_type ref_t>145 int apply( ptr_base< node_t, ref_t >& type ) const {144 template< typename node_t > 145 auto apply( node_t && type ) const { 146 146 TypeSubstitution sub; 147 147 writeToSubstitution( sub ); 148 return sub.apply( type);149 } 150 151 template< typename node_t , enum Node::ref_type ref_t>152 int applyFree( ptr_base< node_t, ref_t >& type ) const {148 return sub.apply( std::forward<node_t>(type) ); 149 } 150 151 template< typename node_t > 152 auto applyFree( node_t && type ) const { 153 153 TypeSubstitution sub; 154 154 writeToSubstitution( sub ); 155 return sub.applyFree( type);155 return sub.applyFree( std::forward<node_t>(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 std::string& );209 ClassList::iterator internal_lookup( const TypeInstType::TypeEnvKey & ); 210 210 }; 211 211 -
src/AST/TypeSubstitution.cpp
r3c64c668 r58fe85a 19 19 namespace ast { 20 20 21 22 // size_t TypeSubstitution::Substituter::traceId = Stats::Heap::new_stacktrace_id("TypeSubstitution"); 23 21 24 TypeSubstitution::TypeSubstitution() { 22 25 } … … 36 39 void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) { 37 40 dest.typeEnv.clear(); 38 dest.varEnv.clear();39 41 dest.add( src ); 40 42 } … … 44 46 typeEnv[ i->first ] = i->second; 45 47 } // for 46 for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) {47 varEnv[ i->first ] = i->second;48 } // for49 48 } 50 49 51 void TypeSubstitution::add( std::stringformalType, const Type *actualType ) {52 typeEnv[ formalType ] = actualType;50 void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) { 51 typeEnv[ *formalType ] = actualType; 53 52 } 54 53 55 void TypeSubstitution::add Var( std::string formalExpr, const Expr *actualExpr) {56 varEnv[ formalExpr ] = actualExpr;54 void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) { 55 typeEnv[ key ] = actualType; 57 56 } 58 57 59 void TypeSubstitution::remove( std::stringformalType ) {60 TypeEnvType::iterator i = typeEnv.find( formalType );58 void TypeSubstitution::remove( const TypeInstType * formalType ) { 59 TypeEnvType::iterator i = typeEnv.find( *formalType ); 61 60 if ( i != typeEnv.end() ) { 62 typeEnv.erase( formalType );61 typeEnv.erase( *formalType ); 63 62 } // if 64 63 } 65 64 66 const Type *TypeSubstitution::lookup( std::stringformalType ) const {67 TypeEnvType::const_iterator i = typeEnv.find( formalType );65 const Type *TypeSubstitution::lookup( const TypeInstType * formalType ) const { 66 TypeEnvType::const_iterator i = typeEnv.find( *formalType ); 68 67 69 68 // break on not in substitution set … … 72 71 // attempt to transitively follow TypeInstType links. 73 72 while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) { 74 const std::string& typeName = actualType->name;75 76 73 // break cycles in the transitive follow 77 if ( formalType == typeName ) break;74 if ( *formalType == *actualType ) break; 78 75 79 76 // Look for the type this maps to, returning previous mapping if none-such 80 i = typeEnv.find( typeName );77 i = typeEnv.find( *actualType ); 81 78 if ( i == typeEnv.end() ) return actualType; 82 79 } … … 87 84 88 85 bool TypeSubstitution::empty() const { 89 return typeEnv.empty() && varEnv.empty();86 return typeEnv.empty(); 90 87 } 91 88 92 89 namespace { 93 90 struct EnvTrimmer { 94 ptr<TypeSubstitution>env;91 const TypeSubstitution * env; 95 92 TypeSubstitution * newEnv; 96 93 EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){} 97 void previsit( TypeDecl * tyDecl) {94 void previsit( FunctionType * ftype ) { 98 95 // transfer known bindings for seen type variables 99 if ( const Type * t = env->lookup( tyDecl->name ) ) { 100 newEnv->add( tyDecl->name, t ); 96 for (auto & formal : ftype->forall) { 97 if ( const Type * t = env->lookup( formal ) ) { 98 newEnv->add( formal, t ); 99 } 101 100 } 102 101 } … … 108 107 if ( env ) { 109 108 TypeSubstitution * newEnv = new TypeSubstitution(); 110 #if TIME_TO_CONVERT_PASSES111 109 Pass<EnvTrimmer> trimmer( env, newEnv ); 112 110 expr->accept( trimmer ); 113 #else114 (void)expr;115 (void)env;116 #endif117 111 return newEnv; 118 112 } … … 121 115 122 116 void TypeSubstitution::normalize() { 123 #if TIME_TO_CONVERT_PASSES 124 PassVisitor<Substituter> sub( *this, true ); 117 Pass<Substituter> sub( *this, true ); 125 118 do { 126 sub. pass.subCount = 0;127 sub. pass.freeOnly = true;119 sub.core.subCount = 0; 120 sub.core.freeOnly = true; 128 121 for ( TypeEnvType::iterator i = typeEnv.begin(); i != typeEnv.end(); ++i ) { 129 i->second = i->second->accept Mutator( sub );122 i->second = i->second->accept( sub ); 130 123 } 131 } while ( sub.pass.subCount ); 132 #endif 124 } while ( sub.core.subCount ); 133 125 } 134 126 135 #if TIME_TO_CONVERT_PASSES 136 137 Type * TypeSubstitution::Substituter::postmutate( TypeInstType *inst ) { 138 BoundVarsType::const_iterator bound = boundVars.find( inst->name ); 127 const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) { 128 BoundVarsType::const_iterator bound = boundVars.find( *inst ); 139 129 if ( bound != boundVars.end() ) return inst; 140 130 141 TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name);131 TypeEnvType::const_iterator i = sub.typeEnv.find( *inst ); 142 132 if ( i == sub.typeEnv.end() ) { 143 133 return inst; … … 146 136 // Note: this does not prevent cycles in the general case, so it may be necessary to do something more sophisticated here. 147 137 // TODO: investigate preventing type variables from being bound to themselves in the first place. 148 if ( TypeInstType * replacement = i->second.as<TypeInstType>() ) {149 if ( inst->name == replacement->name) {138 if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) { 139 if ( *inst == *replacement ) { 150 140 return inst; 151 141 } … … 153 143 // std::cerr << "found " << inst->name << ", replacing with " << i->second << std::endl; 154 144 subCount++; 155 Type * newtype = i->second->clone(); 156 newtype->get_qualifiers() |= inst->get_qualifiers(); 157 delete inst; 158 // Note: need to recursively apply substitution to the new type because normalize does not substitute bound vars, but bound vars must be substituted when not in freeOnly mode. 159 return newtype->acceptMutator( *visitor ); 145 ptr<Type> newType = i->second; // force clone if needed 146 add_qualifiers( newType, inst->qualifiers ); 147 // Note: need to recursively apply substitution to the new type because normalize does not 148 // substitute bound vars, but bound vars must be substituted when not in freeOnly mode. 149 newType = newType->accept( *visitor ); 150 return newType.release(); 160 151 } // if 161 152 } 162 153 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 ) { 154 void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) { 175 155 GuardValue( boundVars ); 176 156 // bind type variables from forall-qualifiers 177 157 if ( freeOnly ) { 178 for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar) {179 boundVars.insert( (*tyvar)->name);158 for ( auto & tyvar : ptype->forall ) { 159 boundVars.insert( *tyvar ); 180 160 } // for 181 161 } // if 182 162 } 183 163 184 template< typename TypeClass > 185 void TypeSubstitution::Substituter::handleAggregateType( TypeClass* type ) {164 /* 165 void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType * type ) { 186 166 GuardValue( boundVars ); 187 167 // bind type variables from forall-qualifiers 188 168 if ( freeOnly ) { 189 for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) {190 boundVars.insert( (*tyvar)->name );191 } // for192 169 // bind type variables from generic type instantiations 193 std::list< TypeDecl* > *baseParameters = type->get_baseParameters(); 194 if ( baseParameters && ! type->parameters.empty() ) { 195 for ( std::list< TypeDecl* >::const_iterator tyvar = baseParameters->begin(); tyvar != baseParameters->end(); ++tyvar ) { 196 boundVars.insert( (*tyvar)->name ); 197 } // for 198 } // if 170 if ( auto decl = type->aggr() ) { 171 if ( ! type->params.empty() ) { 172 for ( const TypeDecl * tyvar : decl->params ) { 173 boundVars.insert( *tyvar ); 174 } // for 175 } // if 176 } 199 177 } // if 200 178 } 201 179 202 void TypeSubstitution::Substituter::pre mutate(StructInstType * aggregateUseType ) {180 void TypeSubstitution::Substituter::previsit( const StructInstType * aggregateUseType ) { 203 181 handleAggregateType( aggregateUseType ); 204 182 } 205 183 206 void TypeSubstitution::Substituter::pre mutate(UnionInstType *aggregateUseType ) {184 void TypeSubstitution::Substituter::previsit( const UnionInstType *aggregateUseType ) { 207 185 handleAggregateType( aggregateUseType ); 208 186 } 209 210 #endif 187 */ 211 188 212 189 } // namespace ast -
src/AST/TypeSubstitution.hpp
r3c64c668 r58fe85a 44 44 TypeSubstitution &operator=( const TypeSubstitution &other ); 45 45 46 template< typename SynTreeClass > int apply( const SynTreeClass *& input ) const; 47 template< typename SynTreeClass > int applyFree( const SynTreeClass *& input ) const; 46 template< typename SynTreeClass > 47 struct ApplyResult { 48 ast::ptr<SynTreeClass> node; 49 int count; 50 }; 51 52 template< typename SynTreeClass > ApplyResult<SynTreeClass> apply( const SynTreeClass * input ) const; 53 template< typename SynTreeClass > ApplyResult<SynTreeClass> applyFree( const SynTreeClass * input ) const; 48 54 49 55 template< typename node_t, enum Node::ref_type ref_t > 50 56 int apply( ptr_base< node_t, ref_t > & input ) const { 51 57 const node_t * p = input.get(); 52 intret = apply(p);53 input = p;54 return ret ;58 auto ret = apply(p); 59 input = ret.node; 60 return ret.count; 55 61 } 56 62 … … 58 64 int applyFree( ptr_base< node_t, ref_t > & input ) const { 59 65 const node_t * p = input.get(); 60 intret = applyFree(p);61 input = p;62 return ret ;66 auto ret = applyFree(p); 67 input = ret.node; 68 return ret.count; 63 69 } 64 70 65 void add( std::string formalType, const Type *actualType ); 71 void add( const TypeInstType * formalType, const Type *actualType ); 72 void add( const TypeInstType::TypeEnvKey & key, const Type *actualType ); 66 73 void add( const TypeSubstitution &other ); 67 void remove( std::stringformalType );68 const Type *lookup( std::stringformalType ) const;74 void remove( const TypeInstType * formalType ); 75 const Type *lookup( const TypeInstType * formalType ) const; 69 76 bool empty() const; 70 71 void addVar( std::string formalExpr, const Expr *actualExpr );72 77 73 78 template< typename FormalIterator, typename ActualIterator > … … 92 97 void initialize( const TypeSubstitution &src, TypeSubstitution &dest ); 93 98 94 template<typename pass_type>99 template<typename core_t> 95 100 friend class Pass; 96 101 97 typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType; 98 typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType; 102 typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType; 99 103 TypeEnvType typeEnv; 100 VarEnvType varEnv;101 104 102 105 public: … … 107 110 auto end() const -> decltype( typeEnv. end() ) { return typeEnv. end(); } 108 111 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(); }113 112 }; 114 113 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 ->name] = actual->type;125 typeEnv[ formal ] = actual->type; 126 126 } // if 127 127 } else { … … 129 129 } // if 130 130 } else { 131 // TODO: type check the formal and actual parameters 132 if ( (*formalIt)->name != "" ) { 133 varEnv[ (*formalIt)->name ] = *actualIt; 134 } // if 131 135 132 } // if 136 133 } // for 137 134 } 135 136 138 137 139 138 template< typename FormalIterator, typename ActualIterator > … … 142 141 } 143 142 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" 149 150 150 151 namespace ast { 151 152 152 153 // definitition must happen after PassVisitor is included so that WithGuards can be used 153 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter> { 154 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter>, public PureVisitor { 155 static size_t traceId; 154 156 155 157 Substituter( const TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {} 156 158 157 #if TIME_TO_CONVERT_PASSES 158 159 Type * postmutate( TypeInstType * aggregateUseType ); 160 Expression * postmutate( NameExpr * nameExpr ); 159 const Type * postvisit( const TypeInstType * aggregateUseType ); 161 160 162 161 /// Records type variable bindings from forall-statements 163 void pre mutate(Type * type );162 void previsit( const FunctionType * type ); 164 163 /// Records type variable bindings from forall-statements and instantiations of generic types 165 template< typename TypeClass > void handleAggregateType( TypeClass* type );164 // void handleAggregateType( const BaseInstType * type ); 166 165 167 void premutate( StructInstType * aggregateUseType ); 168 void premutate( UnionInstType * aggregateUseType ); 169 170 #endif 166 // void previsit( const StructInstType * aggregateUseType ); 167 // void previsit( const UnionInstType * aggregateUseType ); 171 168 172 169 const TypeSubstitution & sub; 173 170 int subCount = 0; 174 171 bool freeOnly; 175 typedef std::unordered_set< std::string> BoundVarsType;172 typedef std::unordered_set< TypeInstType::TypeEnvKey > BoundVarsType; 176 173 BoundVarsType boundVars; 177 174 … … 179 176 180 177 template< typename SynTreeClass > 181 int TypeSubstitution::apply( const SynTreeClass *&input ) const {178 TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::apply( const SynTreeClass * input ) const { 182 179 assert( input ); 183 180 Pass<Substituter> sub( *this, false ); 184 181 input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) ); 185 /// std::cerr << "substitution result is: "; 186 /// newType->print( std::cerr ); 187 /// std::cerr << std::endl; 188 return sub.pass.subCount; 182 return { input, sub.core.subCount }; 189 183 } 190 184 191 185 template< typename SynTreeClass > 192 int TypeSubstitution::applyFree( const SynTreeClass *&input ) const {186 TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::applyFree( const SynTreeClass * input ) const { 193 187 assert( input ); 194 188 Pass<Substituter> sub( *this, true ); 195 189 input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) ); 196 /// std::cerr << "substitution result is: "; 197 /// newType->print( std::cerr ); 198 /// std::cerr << std::endl; 199 return sub.pass.subCount; 190 return { input, sub.core.subCount }; 200 191 } 201 192 -
src/AST/Visitor.hpp
r3c64c668 r58fe85a 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; 49 50 virtual const ast::Stmt * visit( const ast::WaitForStmt * ) = 0; 50 51 virtual const ast::Decl * visit( const ast::WithStmt * ) = 0; -
src/AST/module.mk
r3c64c668 r58fe85a 17 17 SRC_AST = \ 18 18 AST/AssertAcyclic.cpp \ 19 AST/AssertAcyclic.hpp \ 19 20 AST/Attribute.cpp \ 21 AST/Attribute.hpp \ 22 AST/Bitfield.hpp \ 23 AST/Chain.hpp \ 20 24 AST/Convert.cpp \ 25 AST/Convert.hpp \ 26 AST/Copy.hpp \ 27 AST/CVQualifiers.hpp \ 21 28 AST/Decl.cpp \ 29 AST/Decl.hpp \ 22 30 AST/DeclReplacer.cpp \ 31 AST/DeclReplacer.hpp \ 32 AST/Eval.hpp \ 23 33 AST/Expr.cpp \ 34 AST/Expr.hpp \ 35 AST/FunctionSpec.hpp \ 36 AST/Fwd.hpp \ 24 37 AST/GenericSubstitution.cpp \ 38 AST/GenericSubstitution.hpp \ 25 39 AST/Init.cpp \ 40 AST/Init.hpp \ 41 AST/Label.hpp \ 26 42 AST/LinkageSpec.cpp \ 43 AST/LinkageSpec.hpp \ 27 44 AST/Node.cpp \ 45 AST/Node.hpp \ 46 AST/ParseNode.hpp \ 28 47 AST/Pass.cpp \ 48 AST/Pass.hpp \ 49 AST/Pass.impl.hpp \ 50 AST/Pass.proto.hpp \ 29 51 AST/Print.cpp \ 52 AST/Print.hpp \ 30 53 AST/Stmt.cpp \ 54 AST/Stmt.hpp \ 55 AST/StorageClasses.hpp \ 31 56 AST/SymbolTable.cpp \ 57 AST/SymbolTable.hpp \ 58 AST/TranslationUnit.hpp \ 32 59 AST/Type.cpp \ 60 AST/Type.hpp \ 33 61 AST/TypeEnvironment.cpp \ 34 AST/TypeSubstitution.cpp 62 AST/TypeEnvironment.hpp \ 63 AST/TypeSubstitution.cpp \ 64 AST/TypeSubstitution.hpp \ 65 AST/Visitor.hpp 35 66 36 67 SRC += $(SRC_AST) -
src/AST/porting.md
r3c64c668 r58fe85a 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` 32 36 33 37 ## Structural Changes ## … … 47 51 template<typename node_t> 48 52 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. 49 58 50 59 All leaves of the `Node` inheritance tree are now declared `final` … … 141 150 * allows `newObject` as just default settings 142 151 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 143 157 `NamedTypeDecl` 144 158 * `parameters` => `params` … … 149 163 `AggregateDecl` 150 164 * `parameters` => `params` 165 166 `StructDecl` 167 * `makeInst` replaced by better constructor on `StructInstType`. 151 168 152 169 `Expr` … … 240 257 * **TODO** move `kind`, `typeNames` into code generator 241 258 242 `ReferenceToType` 259 `ReferenceToType` => `BaseInstType` 243 260 * deleted `get_baseParameters()` from children 244 261 * replace with `aggr() ? aggr()->params : nullptr` … … 256 273 * `returnVals` => `returns` 257 274 * `parameters` => `params` 275 * Both now just point at types. 258 276 * `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` 259 281 260 282 `TypeInstType` -
src/CodeGen/CodeGenerator.cc
r3c64c668 r58fe85a 120 120 // GCC builtins should always be printed unmangled 121 121 if ( options.pretty || decl->linkage.is_gcc_builtin ) return decl->name; 122 if ( decl->mangleName != "" ) {122 if ( LinkageSpec::isMangled(decl->linkage) && decl->mangleName != "" ) { 123 123 // need to incorporate scope level in order to differentiate names for destructors 124 124 return decl->get_scopedMangleName(); -
src/CodeGen/FixMain.cc
r3c64c668 r58fe85a 26 26 #include "SynTree/Declaration.h" // for FunctionDecl, operator<< 27 27 #include "SynTree/Type.h" // for FunctionType 28 #include "SymTab/Mangler.h" 28 29 29 30 namespace CodeGen { … … 47 48 if( main_signature ) { 48 49 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()); 49 51 50 52 os << main_signature->get_scopedMangleName() << "("; -
src/CodeGen/FixNames.cc
r3c64c668 r58fe85a 31 31 #include "SynTree/Type.h" // for Type, BasicType, Type::Qualifiers 32 32 #include "SynTree/Visitor.h" // for Visitor, acceptAll 33 #include "CompilationState.h" 33 34 34 35 namespace CodeGen { … … 102 103 if ( dwt->get_name() != "" ) { 103 104 if ( LinkageSpec::isMangled( dwt->get_linkage() ) ) { 104 dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) ); 105 if (!useNewAST) { 106 dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) ); 107 } 105 108 dwt->set_scopeLevel( scopeLevel ); 106 109 } // if -
src/CodeGen/module.mk
r3c64c668 r58fe85a 20 20 SRC_CODEGEN = \ 21 21 CodeGen/CodeGenerator.cc \ 22 CodeGen/CodeGenerator.h \ 22 23 CodeGen/FixMain.cc \ 24 CodeGen/FixMain.h \ 23 25 CodeGen/GenType.cc \ 24 CodeGen/OperatorTable.cc 26 CodeGen/GenType.h \ 27 CodeGen/OperatorTable.cc \ 28 CodeGen/OperatorTable.h \ 29 CodeGen/Options.h 25 30 26 SRC += $(SRC_CODEGEN) CodeGen/Generate.cc CodeGen/ FixNames.cc31 SRC += $(SRC_CODEGEN) CodeGen/Generate.cc CodeGen/Generate.h CodeGen/FixNames.cc CodeGen/FixNames.h 27 32 SRCDEMANGLE += $(SRC_CODEGEN) -
src/CodeTools/TrackLoc.cc
r3c64c668 r58fe85a 10 10 // Created On : Tues May 2 15:46:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed May 3 14:43:00 201713 // Update Count : 012 // Last Modified On : Fri Nov 27 18:00:00 2020 13 // Update Count : 1 14 14 // 15 15 … … 22 22 #include <string> // for operator<<, string 23 23 #include <typeindex> // for type_index 24 #include <vector> // for vector 24 25 25 26 #include "Common/PassVisitor.h" // for PassVisitor … … 37 38 CodeLocation *lastNode; 38 39 39 std::stack< CodeLocation * > parents;40 std::stack< CodeLocation *, std::vector< CodeLocation * > > parents; 40 41 public: 41 42 LocationPrinter(size_t printLevel) : -
src/CodeTools/module.mk
r3c64c668 r58fe85a 15 15 ############################################################################### 16 16 17 SRC += CodeTools/DeclStats.cc \ 17 SRC += \ 18 CodeTools/DeclStats.cc \ 19 CodeTools/DeclStats.h \ 18 20 CodeTools/ResolvProtoDump.cc \ 19 CodeTools/TrackLoc.cc 21 CodeTools/ResolvProtoDump.h \ 22 CodeTools/TrackLoc.cc \ 23 CodeTools/TrackLoc.h -
src/Common/CodeLocation.h
r3c64c668 r58fe85a 42 42 } 43 43 44 bool followedBy( CodeLocation const & other, int seperation ) { 44 bool startsBefore( CodeLocation const & other ) const { 45 if( filename < other.filename ) return true; 46 if( filename > other.filename ) return false; 47 48 if( first_line < other.first_line ) return true; 49 if( first_line > other.first_line ) return false; 50 51 if( last_line < other.last_line ) return true; 52 return false; 53 } 54 55 bool followedBy( CodeLocation const & other, int seperation ) const { 45 56 return (first_line + seperation == other.first_line && 46 57 filename == other.filename); 47 58 } 48 59 49 bool operator==( CodeLocation const & other ) {60 bool operator==( CodeLocation const & other ) const { 50 61 return followedBy( other, 0 ); 51 62 } 52 63 53 bool operator!=( CodeLocation const & other ) {64 bool operator!=( CodeLocation const & other ) const { 54 65 return !(*this == other); 55 66 } -
src/Common/Eval.cc
r3c64c668 r58fe85a 168 168 if (expr) { 169 169 expr->accept(ev); 170 return std::make_pair(ev. pass.value, ev.pass.valid);170 return std::make_pair(ev.core.value, ev.core.valid); 171 171 } else { 172 172 return std::make_pair(0, false); -
src/Common/PassVisitor.h
r3c64c668 r58fe85a 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; 112 114 virtual void visit( WaitForStmt * waitforStmt ) override final; 113 115 virtual void visit( const WaitForStmt * waitforStmt ) override final; … … 276 278 virtual Statement * mutate( CatchStmt * catchStmt ) override final; 277 279 virtual Statement * mutate( FinallyStmt * finallyStmt ) override final; 280 virtual Statement * mutate( SuspendStmt * suspendStmt ) override final; 278 281 virtual Statement * mutate( WaitForStmt * waitforStmt ) override final; 279 282 virtual Declaration * mutate( WithStmt * withStmt ) override final; … … 351 354 virtual TypeSubstitution * mutate( TypeSubstitution * sub ) final; 352 355 356 bool isInFunction() const { 357 return inFunction; 358 } 359 353 360 private: 354 361 bool inFunction = false; 362 bool atFunctionTop = false; 355 363 356 364 template<typename pass_t> friend void acceptAll( std::list< Declaration* > &decls, PassVisitor< pass_t >& visitor ); … … 523 531 public: 524 532 PassVisitor<pass_type> * const visitor = nullptr; 533 534 bool isInFunction() const { 535 return visitor->isInFunction(); 536 } 525 537 }; 526 538 -
src/Common/PassVisitor.impl.h
r3c64c668 r58fe85a 532 532 indexerAddId( &func ); 533 533 maybeAccept_impl( node->type, *this ); 534 // function body needs to have the same scope as parameters - CompoundStmt will not enter 535 // a new scope if inFunction is true 534 // First remember that we are now within a function. 536 535 ValueGuard< bool > oldInFunction( inFunction ); 537 536 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; 538 541 maybeAccept_impl( node->statements, *this ); 539 542 maybeAccept_impl( node->attributes, *this ); … … 567 570 indexerAddId( &func ); 568 571 maybeAccept_impl( node->type, *this ); 569 // function body needs to have the same scope as parameters - CompoundStmt will not enter 570 // a new scope if inFunction is true 572 // First remember that we are now within a function. 571 573 ValueGuard< bool > oldInFunction( inFunction ); 572 574 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; 573 579 maybeAccept_impl( node->statements, *this ); 574 580 maybeAccept_impl( node->attributes, *this ); … … 601 607 indexerAddId( &func ); 602 608 maybeMutate_impl( node->type, *this ); 603 // function body needs to have the same scope as parameters - CompoundStmt will not enter 604 // a new scope if inFunction is true 609 // First remember that we are now within a function. 605 610 ValueGuard< bool > oldInFunction( inFunction ); 606 611 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; 607 616 maybeMutate_impl( node->statements, *this ); 608 617 maybeMutate_impl( node->attributes, *this ); … … 826 835 { 827 836 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 828 maybeAccept_impl( node->parameters, *this );829 837 maybeAccept_impl( node->base , *this ); 830 838 } … … 849 857 { 850 858 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 851 maybeAccept_impl( node->parameters, *this );852 859 maybeAccept_impl( node->base , *this ); 853 860 } … … 871 878 { 872 879 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 873 maybeMutate_impl( node->parameters, *this );874 880 maybeMutate_impl( node->base , *this ); 875 881 } … … 895 901 { 896 902 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 897 maybeAccept_impl( node->parameters, *this );898 903 maybeAccept_impl( node->base , *this ); 899 904 } … … 912 917 { 913 918 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 914 maybeAccept_impl( node->parameters, *this );915 919 maybeAccept_impl( node->base , *this ); 916 920 } … … 929 933 { 930 934 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } ); 931 maybeMutate_impl( node->parameters, *this );932 935 maybeMutate_impl( node->base , *this ); 933 936 } … … 1007 1010 VISIT_START( node ); 1008 1011 { 1009 // do not enter a new scope if inFunction is true - needs to check old state before the assignment1010 ValueGuard< bool > old InFunction( inFunction);1011 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old) indexerScopeLeave(); } );1012 // Do not enter a new scope if atFunctionTop is true, don't leave one either. 1013 ValueGuard< bool > oldAtFunctionTop( atFunctionTop ); 1014 auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } ); 1012 1015 auto guard2 = makeFuncGuard( [this]() { call_beginScope(); }, [this]() { call_endScope(); } ); 1013 inFunction= false;1016 atFunctionTop = false; 1014 1017 visitStatementList( node->kids ); 1015 1018 } … … 1021 1024 VISIT_START( node ); 1022 1025 { 1023 // do not enter a new scope if inFunction is true - needs to check old state before the assignment1024 ValueGuard< bool > old InFunction( inFunction);1025 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old) indexerScopeLeave(); } );1026 // Do not enter a new scope if atFunctionTop is true, don't leave one either. 1027 ValueGuard< bool > oldAtFunctionTop( atFunctionTop ); 1028 auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } ); 1026 1029 auto guard2 = makeFuncGuard( [this]() { call_beginScope(); }, [this]() { call_endScope(); } ); 1027 inFunction= false;1030 atFunctionTop = false; 1028 1031 visitStatementList( node->kids ); 1029 1032 } … … 1035 1038 MUTATE_START( node ); 1036 1039 { 1037 // do not enter a new scope if inFunction is true - needs to check old state before the assignment1038 ValueGuard< bool > old InFunction( inFunction);1039 auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old) indexerScopeLeave(); } );1040 // Do not enter a new scope if atFunctionTop is true, don't leave one either. 1041 ValueGuard< bool > oldAtFunctionTop( atFunctionTop ); 1042 auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } ); 1040 1043 auto guard2 = makeFuncGuard( [this]() { call_beginScope(); }, [this]() { call_endScope(); } ); 1041 inFunction= false;1044 atFunctionTop = false; 1042 1045 mutateStatementList( node->kids ); 1043 1046 } … … 1517 1520 1518 1521 maybeMutate_impl( node->block, *this ); 1522 1523 MUTATE_END( Statement, node ); 1524 } 1525 1526 //-------------------------------------------------------------------------- 1527 // SuspendStmt 1528 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 ); 1519 1551 1520 1552 MUTATE_END( Statement, node ); -
src/Common/PassVisitor.proto.h
r3c64c668 r58fe85a 38 38 }; 39 39 40 std::stack< cleanup_t > cleanups;40 std::stack< cleanup_t, std::vector< cleanup_t > > cleanups; 41 41 }; 42 42 -
src/Common/ScopedMap.h
r3c64c668 r58fe85a 93 93 94 94 reference operator* () { return *it; } 95 pointer operator-> () { return it.operator->(); }95 pointer operator-> () const { 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; } 251 253 Note& getNote( size_type i ) { return scopes[i].note; } 252 254 const Note& getNote( size_type i ) const { return scopes[i].note; } -
src/Common/SemanticError.cc
r3c64c668 r58fe85a 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 92 100 for( auto err : errors ) { 93 101 std::cerr << ErrorHelpers::bold() << err.location << ErrorHelpers::error_str() << ErrorHelpers::reset_font() << err.description << std::endl; -
src/Common/Stats/Heap.cc
r3c64c668 r58fe85a 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 } 55 79 56 80 void newPass( const char * const name ) { … … 116 140 for(size_t i = 0; i < passes_cnt; i++) { 117 141 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); 118 151 } 119 152 print('-', nct); … … 188 221 = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs); 189 222 } 223 224 if ( stacktrace_stats_enabled && stacktrace_depth > 0) { 225 stacktrace_stats[trace[stacktrace_depth]].mallocs++; 226 } 190 227 return __malloc( size ); 191 228 } … … 196 233 passes[passes_cnt - 1].frees++; 197 234 passes[passes_cnt - 1].n_allocs--; 235 } 236 if ( stacktrace_stats_enabled && stacktrace_depth > 0) { 237 stacktrace_stats[trace[stacktrace_depth]].frees++; 198 238 } 199 239 return __free( ptr ); … … 208 248 = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs); 209 249 } 250 if ( stacktrace_stats_enabled && stacktrace_depth > 0) { 251 stacktrace_stats[trace[stacktrace_depth]].mallocs++; 252 } 210 253 return __calloc( nelem, size ); 211 254 } … … 218 261 passes[passes_cnt - 1].frees++; 219 262 } // if 263 if ( stacktrace_stats_enabled && stacktrace_depth > 0) { 264 stacktrace_stats[trace[stacktrace_depth]].mallocs++; 265 stacktrace_stats[trace[stacktrace_depth]].frees++; 266 } 220 267 return s; 221 268 } -
src/Common/Stats/Heap.h
r3c64c668 r58fe85a 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(); 22 26 } 23 27 } -
src/Common/Stats/Stats.cc
r3c64c668 r58fe85a 35 35 } 36 36 37 namespace ResolveTime { 38 bool enabled = false; 39 } 40 37 41 struct { 38 42 const char * const opt; … … 43 47 { "heap" , Heap::enabled }, 44 48 { "time" , Time::enabled }, 49 { "resolve" , ResolveTime::enabled }, 45 50 }; 46 51 -
src/Common/module.mk
r3c64c668 r58fe85a 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 \ 19 25 Common/Eval.cc \ 26 Common/Examine.cc \ 27 Common/Examine.h \ 28 Common/FilterCombos.h \ 29 Common/Indenter.h \ 20 30 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 \ 21 36 Common/SemanticError.cc \ 37 Common/SemanticError.h \ 38 Common/Stats.h \ 39 Common/Stats/Base.h \ 22 40 Common/Stats/Counter.cc \ 41 Common/Stats/Counter.h \ 23 42 Common/Stats/Heap.cc \ 43 Common/Stats/Heap.h \ 44 Common/Stats/ResolveTime.cc \ 45 Common/Stats/ResolveTime.h \ 24 46 Common/Stats/Stats.cc \ 25 47 Common/Stats/Time.cc \ 26 Common/UniqueName.cc 48 Common/Stats/Time.h \ 49 Common/UnimplementedError.h \ 50 Common/UniqueName.cc \ 51 Common/UniqueName.h \ 52 Common/utility.h \ 53 Common/VectorMap.h 27 54 28 55 SRC += $(SRC_COMMON) Common/DebugMalloc.cc -
src/Common/utility.h
r3c64c668 r58fe85a 360 360 reverse_iterate_t( T & ref ) : ref(ref) {} 361 361 362 typedef typename T::reverse_iterator iterator; 363 iterator begin() { return ref.rbegin(); } 364 iterator end() { return ref.rend(); } 362 // this does NOT work on const T!!! 363 // typedef typename T::reverse_iterator iterator; 364 auto begin() { return ref.rbegin(); } 365 auto end() { return ref.rend(); } 365 366 }; 366 367 -
src/CompilationState.cc
r3c64c668 r58fe85a 14 14 // 15 15 16 #include "config.h" 17 16 18 int 17 19 astp = false, … … 27 29 nopreludep = false, 28 30 genproto = false, 31 deterministic_output = false, 32 useNewAST = CFA_USE_NEW_AST, 29 33 nomainp = false, 30 34 parsep = false, -
src/CompilationState.h
r3c64c668 r58fe85a 28 28 nopreludep, 29 29 genproto, 30 deterministic_output, 31 useNewAST, 30 32 nomainp, 31 33 parsep, -
src/Concurrency/Keywords.cc
r3c64c668 r58fe85a 16 16 #include "Concurrency/Keywords.h" 17 17 18 #include <cassert> // for assert 19 #include <string> // for string, operator== 20 21 #include "Common/PassVisitor.h" // for PassVisitor 22 #include "Common/SemanticError.h" // for SemanticError 23 #include "Common/utility.h" // for deleteAll, map_range 24 #include "CodeGen/OperatorTable.h" // for isConstructor 25 #include "InitTweak/InitTweak.h" // for getPointerBase 26 #include "SynTree/LinkageSpec.h" // for Cforall 27 #include "SynTree/Constant.h" // for Constant 28 #include "SynTree/Declaration.h" // for StructDecl, FunctionDecl, ObjectDecl 29 #include "SynTree/Expression.h" // for VariableExpr, ConstantExpr, Untype... 30 #include "SynTree/Initializer.h" // for SingleInit, ListInit, Initializer ... 31 #include "SynTree/Label.h" // for Label 32 #include "SynTree/Statement.h" // for CompoundStmt, DeclStmt, ExprStmt 33 #include "SynTree/Type.h" // for StructInstType, Type, PointerType 34 #include "SynTree/Visitor.h" // for Visitor, acceptAll 18 #include <cassert> // for assert 19 #include <string> // for string, operator== 20 21 #include <iostream> 22 23 #include "Common/Examine.h" // for isMainFor 24 #include "Common/PassVisitor.h" // for PassVisitor 25 #include "Common/SemanticError.h" // for SemanticError 26 #include "Common/utility.h" // for deleteAll, map_range 27 #include "CodeGen/OperatorTable.h" // for isConstructor 28 #include "ControlStruct/LabelGenerator.h" // for LebelGenerator 29 #include "InitTweak/InitTweak.h" // for getPointerBase 30 #include "SynTree/LinkageSpec.h" // for Cforall 31 #include "SynTree/Constant.h" // for Constant 32 #include "SynTree/Declaration.h" // for StructDecl, FunctionDecl, ObjectDecl 33 #include "SynTree/Expression.h" // for VariableExpr, ConstantExpr, Untype... 34 #include "SynTree/Initializer.h" // for SingleInit, ListInit, Initializer ... 35 #include "SynTree/Label.h" // for Label 36 #include "SynTree/Statement.h" // for CompoundStmt, DeclStmt, ExprStmt 37 #include "SynTree/Type.h" // for StructInstType, Type, PointerType 38 #include "SynTree/Visitor.h" // for Visitor, acceptAll 39 #include "Virtual/Tables.h" 35 40 36 41 class Attribute; 37 42 38 43 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 39 56 //============================================================================================= 40 57 // Pass declarations … … 53 70 public: 54 71 55 ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name, std::string&& getter_name, std::string&& context_error, bool needs_main, AggregateDecl::Aggregate cast_target ) : 56 type_name( type_name ), field_name( field_name ), getter_name( getter_name ), context_error( context_error ), needs_main( needs_main ), cast_target( cast_target ) {} 72 ConcurrentSueKeyword( std::string&& type_name, std::string&& field_name, 73 std::string&& getter_name, std::string&& context_error, std::string&& exception_name, 74 bool needs_main, AggregateDecl::Aggregate cast_target ) : 75 type_name( type_name ), field_name( field_name ), getter_name( getter_name ), 76 context_error( context_error ), exception_name( exception_name ), 77 vtable_name( getVTableName( exception_name ) ), 78 needs_main( needs_main ), cast_target( cast_target ) {} 57 79 58 80 virtual ~ConcurrentSueKeyword() {} … … 62 84 63 85 void handle( StructDecl * ); 86 void addVtableForward( StructDecl * ); 64 87 FunctionDecl * forwardDeclare( StructDecl * ); 65 88 ObjectDecl * addField( StructDecl * ); … … 75 98 const std::string getter_name; 76 99 const std::string context_error; 100 const std::string exception_name; 101 const std::string vtable_name; 77 102 bool needs_main; 78 103 AggregateDecl::Aggregate cast_target; … … 80 105 StructDecl * type_decl = nullptr; 81 106 FunctionDecl * dtor_decl = nullptr; 107 StructDecl * except_decl = nullptr; 108 StructDecl * vtable_decl = nullptr; 82 109 }; 83 110 … … 100 127 "get_thread", 101 128 "thread keyword requires threads to be in scope, add #include <thread.hfa>\n", 129 "ThreadCancelled", 102 130 true, 103 131 AggregateDecl::Thread … … 132 160 "get_coroutine", 133 161 "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n", 162 "CoroutineCancelled", 134 163 true, 135 164 AggregateDecl::Coroutine … … 146 175 } 147 176 }; 177 178 148 179 149 180 //----------------------------------------------------------------------------- … … 164 195 "get_monitor", 165 196 "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n", 197 "", 166 198 false, 167 199 AggregateDecl::Monitor … … 177 209 mutateAll( translationUnit, impl ); 178 210 } 211 }; 212 213 //----------------------------------------------------------------------------- 214 //Handles generator type declarations : 215 // generator MyGenerator { struct MyGenerator { 216 // int data; int data; 217 // a_struct_t more_data; a_struct_t more_data; 218 // => int __gen_next; 219 // }; }; 220 // 221 class GeneratorKeyword final : public ConcurrentSueKeyword { 222 public: 223 224 GeneratorKeyword() : ConcurrentSueKeyword( 225 "$generator", 226 "__generator_state", 227 "get_generator", 228 "Unable to find builtin type $generator\n", 229 "", 230 true, 231 AggregateDecl::Generator 232 ) 233 {} 234 235 virtual ~GeneratorKeyword() {} 236 237 virtual bool is_target( StructDecl * decl ) override final { return decl->is_generator(); } 238 239 static void implement( std::list< Declaration * > & translationUnit ) { 240 PassVisitor< GeneratorKeyword > impl; 241 mutateAll( translationUnit, impl ); 242 } 243 }; 244 245 246 //----------------------------------------------------------------------------- 247 class SuspendKeyword final : public WithStmtsToAdd, public WithGuards { 248 public: 249 SuspendKeyword() = default; 250 virtual ~SuspendKeyword() = default; 251 252 void premutate( FunctionDecl * ); 253 DeclarationWithType * postmutate( FunctionDecl * ); 254 255 Statement * postmutate( SuspendStmt * ); 256 257 static void implement( std::list< Declaration * > & translationUnit ) { 258 PassVisitor< SuspendKeyword > impl; 259 mutateAll( translationUnit, impl ); 260 } 261 262 private: 263 bool is_real_suspend( FunctionDecl * ); 264 265 Statement * make_generator_suspend( SuspendStmt * ); 266 Statement * make_coroutine_suspend( SuspendStmt * ); 267 268 struct LabelPair { 269 Label obj; 270 int idx; 271 }; 272 273 LabelPair make_label() { 274 labels.push_back( gen.newLabel("generator") ); 275 return { labels.back(), int(labels.size()) }; 276 } 277 278 DeclarationWithType * in_generator = nullptr; 279 FunctionDecl * decl_suspend = nullptr; 280 std::vector<Label> labels; 281 ControlStruct::LabelGenerator & gen = *ControlStruct::LabelGenerator::getGenerator(); 179 282 }; 180 283 … … 195 298 std::list<DeclarationWithType*> findMutexArgs( FunctionDecl*, bool & first ); 196 299 void validate( DeclarationWithType * ); 197 void addDtorStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 198 void addStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 300 void addDtorStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 301 void addStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &); 302 void addThreadDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ); 199 303 200 304 static void implement( std::list< Declaration * > & translationUnit ) { … … 207 311 StructDecl* guard_decl = nullptr; 208 312 StructDecl* dtor_guard_decl = nullptr; 313 StructDecl* thread_guard_decl = nullptr; 209 314 210 315 static std::unique_ptr< Type > generic_func; … … 251 356 CoroutineKeyword ::implement( translationUnit ); 252 357 MonitorKeyword ::implement( translationUnit ); 358 GeneratorKeyword ::implement( translationUnit ); 359 SuspendKeyword ::implement( translationUnit ); 253 360 } 254 361 … … 283 390 handle( decl ); 284 391 } 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) ); 285 400 return decl; 286 401 } 287 402 288 403 DeclarationWithType * ConcurrentSueKeyword::postmutate( FunctionDecl * decl ) { 289 if( !type_decl ) return decl; 290 if( !CodeGen::isDestructor( decl->name ) ) return decl; 291 292 auto params = decl->type->parameters; 293 if( params.size() != 1 ) return decl; 294 295 auto type = dynamic_cast<ReferenceType*>( params.front()->get_type() ); 296 if( !type ) return decl; 297 298 auto stype = dynamic_cast<StructInstType*>( type->base ); 299 if( !stype ) return decl; 300 if( stype->baseStruct != type_decl ) return decl; 301 302 if( !dtor_decl ) dtor_decl = decl; 404 if ( type_decl && isDestructorFor( decl, type_decl ) ) 405 dtor_decl = decl; 406 else if ( vtable_name.empty() ) 407 ; 408 else if( !decl->has_body() ) 409 ; 410 else if ( auto param = isMainFor( decl, cast_target ) ) { 411 // This should never trigger. 412 assert( vtable_decl ); 413 // Should be safe because of isMainFor. 414 StructInstType * struct_type = static_cast<StructInstType *>( 415 static_cast<ReferenceType *>( param->get_type() )->base ); 416 assert( struct_type ); 417 418 std::list< Expression * > poly_args = { new TypeExpr( struct_type->clone() ) }; 419 ObjectDecl * vtable_object = Virtual::makeVtableInstance( 420 vtable_decl->makeInst( poly_args ), struct_type, nullptr ); 421 declsToAddAfter.push_back( vtable_object ); 422 declsToAddAfter.push_back( Virtual::makeGetExceptionFunction( 423 vtable_object, except_decl->makeInst( std::move( poly_args ) ) 424 ) ); 425 } 426 303 427 return decl; 304 428 } … … 324 448 if( !dtor_decl ) SemanticError( decl, context_error ); 325 449 450 addVtableForward( decl ); 326 451 FunctionDecl * func = forwardDeclare( decl ); 327 452 ObjectDecl * field = addField( decl ); 328 453 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 } 329 471 } 330 472 … … 434 576 new CastExpr( 435 577 new VariableExpr( func->get_functionType()->get_parameters().front() ), 436 func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone() 578 func->get_functionType()->get_parameters().front()->get_type()->stripReferences()->clone(), 579 false 437 580 ) 438 581 ) … … 446 589 447 590 declsToAddAfter.push_back( get_decl ); 448 449 // get_decl->fixUniqueId(); 450 } 591 } 592 593 //============================================================================================= 594 // Suspend keyword implementation 595 //============================================================================================= 596 bool SuspendKeyword::is_real_suspend( FunctionDecl * func ) { 597 if(isMangled(func->linkage)) return false; // the real suspend isn't mangled 598 if(func->name != "__cfactx_suspend") return false; // the real suspend has a specific name 599 if(func->type->parameters.size() != 0) return false; // Too many parameters 600 if(func->type->returnVals.size() != 0) return false; // Too many return values 601 602 return true; 603 } 604 605 void SuspendKeyword::premutate( FunctionDecl * func ) { 606 GuardValue(in_generator); 607 in_generator = nullptr; 608 609 // Is this the real suspend? 610 if(is_real_suspend(func)) { 611 decl_suspend = decl_suspend ? decl_suspend : func; 612 return; 613 } 614 615 // Is this the main of a generator? 616 auto param = isMainFor( func, AggregateDecl::Aggregate::Generator ); 617 if(!param) return; 618 619 if(func->type->returnVals.size() != 0) SemanticError(func->location, "Generator main must return void"); 620 621 in_generator = param; 622 GuardValue(labels); 623 labels.clear(); 624 } 625 626 DeclarationWithType * SuspendKeyword::postmutate( FunctionDecl * func ) { 627 if( !func->statements ) return func; // Not the actual definition, don't do anything 628 if( !in_generator ) return func; // Not in a generator, don't do anything 629 if( labels.empty() ) return func; // Generator has no states, nothing to do, could throw a warning 630 631 // This is a generator main, we need to add the following code to the top 632 // static void * __generator_labels[] = {&&s0, &&s1, ...}; 633 // goto * __generator_labels[gen.__generator_state]; 634 const auto & loc = func->location; 635 636 const auto first_label = gen.newLabel("generator"); 637 638 // for each label add to declaration 639 std::list<Initializer*> inits = { new SingleInit( new LabelAddressExpr( first_label ) ) }; 640 for(const auto & label : labels) { 641 inits.push_back( 642 new SingleInit( 643 new LabelAddressExpr( label ) 644 ) 645 ); 646 } 647 auto init = new ListInit(std::move(inits), noDesignators, true); 648 labels.clear(); 649 650 // create decl 651 auto decl = new ObjectDecl( 652 "__generator_labels", 653 Type::StorageClasses( Type::Static ), 654 LinkageSpec::AutoGen, 655 nullptr, 656 new ArrayType( 657 Type::Qualifiers(), 658 new PointerType( 659 Type::Qualifiers(), 660 new VoidType( Type::Qualifiers() ) 661 ), 662 nullptr, 663 false, false 664 ), 665 init 666 ); 667 668 // create the goto 669 assert(in_generator); 670 671 auto go_decl = new ObjectDecl( 672 "__generator_label", 673 noStorageClasses, 674 LinkageSpec::AutoGen, 675 nullptr, 676 new PointerType( 677 Type::Qualifiers(), 678 new VoidType( Type::Qualifiers() ) 679 ), 680 new SingleInit( 681 new UntypedExpr( 682 new NameExpr("?[?]"), 683 { 684 new NameExpr("__generator_labels"), 685 new UntypedMemberExpr( 686 new NameExpr("__generator_state"), 687 new VariableExpr( in_generator ) 688 ) 689 } 690 ) 691 ) 692 ); 693 go_decl->location = loc; 694 695 auto go = new BranchStmt( 696 new VariableExpr( go_decl ), 697 BranchStmt::Goto 698 ); 699 go->location = loc; 700 go->computedTarget->location = loc; 701 702 auto noop = new NullStmt({ first_label }); 703 noop->location = loc; 704 705 // wrap everything in a nice compound 706 auto body = new CompoundStmt({ 707 new DeclStmt( decl ), 708 new DeclStmt( go_decl ), 709 go, 710 noop, 711 func->statements 712 }); 713 body->location = loc; 714 func->statements = body; 715 716 return func; 717 } 718 719 Statement * SuspendKeyword::postmutate( SuspendStmt * stmt ) { 720 SuspendStmt::Type type = stmt->type; 721 if(type == SuspendStmt::None) { 722 // This suspend has a implicit target, find it 723 type = in_generator ? SuspendStmt::Generator : SuspendStmt::Coroutine; 724 } 725 726 // Check that the target makes sense 727 if(!in_generator && type == SuspendStmt::Generator) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type."); 728 729 // Act appropriately 730 switch(type) { 731 case SuspendStmt::Generator: return make_generator_suspend(stmt); 732 case SuspendStmt::Coroutine: return make_coroutine_suspend(stmt); 733 default: abort(); 734 } 735 } 736 737 Statement * SuspendKeyword::make_generator_suspend( SuspendStmt * stmt ) { 738 assert(in_generator); 739 // Target code is : 740 // gen.__generator_state = X; 741 // { THEN } 742 // return; 743 // __gen_X:; 744 745 // Save the location and delete the old statement, we only need the location from this point on 746 auto loc = stmt->location; 747 748 // Build the label and get its index 749 auto label = make_label(); 750 751 // Create the context saving statement 752 auto save = new ExprStmt( new UntypedExpr( 753 new NameExpr( "?=?" ), 754 { 755 new UntypedMemberExpr( 756 new NameExpr("__generator_state"), 757 new VariableExpr( in_generator ) 758 ), 759 new ConstantExpr( 760 Constant::from_int( label.idx ) 761 ) 762 } 763 )); 764 assert(save->expr); 765 save->location = loc; 766 stmtsToAddBefore.push_back( save ); 767 768 // if we have a then add it here 769 auto then = stmt->then; 770 stmt->then = nullptr; 771 delete stmt; 772 if(then) stmtsToAddBefore.push_back( then ); 773 774 // Create the return statement 775 auto ret = new ReturnStmt( nullptr ); 776 ret->location = loc; 777 stmtsToAddBefore.push_back( ret ); 778 779 // Create the null statement with the created label 780 auto noop = new NullStmt({ label.obj }); 781 noop->location = loc; 782 783 // Return the null statement to take the place of the previous statement 784 return noop; 785 } 786 787 Statement * SuspendKeyword::make_coroutine_suspend( SuspendStmt * stmt ) { 788 if(stmt->then) SemanticError( stmt->location, "Compound statement following coroutines is not implemented."); 789 790 // Save the location and delete the old statement, we only need the location from this point on 791 auto loc = stmt->location; 792 delete stmt; 793 794 // Create the call expression 795 if(!decl_suspend) SemanticError( loc, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n"); 796 auto expr = new UntypedExpr( VariableExpr::functionPointer( decl_suspend ) ); 797 expr->location = loc; 798 799 // Change this statement into a regular expr 800 assert(expr); 801 auto nstmt = new ExprStmt( expr ); 802 nstmt->location = loc; 803 return nstmt; 804 } 805 451 806 452 807 //============================================================================================= … … 458 813 bool first = false; 459 814 std::list<DeclarationWithType*> mutexArgs = findMutexArgs( decl, first ); 460 bool isDtor = CodeGen::isDestructor( decl->name );815 bool const isDtor = CodeGen::isDestructor( decl->name ); 461 816 462 817 // Is this function relevant to monitors … … 506 861 507 862 // Instrument the body 508 if( isDtor ) { 509 addDtorStatments( decl, body, mutexArgs ); 863 if ( isDtor && isThread( mutexArgs.front() ) ) { 864 if( !thread_guard_decl ) { 865 SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" ); 866 } 867 addThreadDtorStatements( decl, body, mutexArgs ); 868 } 869 else if ( isDtor ) { 870 addDtorStatements( decl, body, mutexArgs ); 510 871 } 511 872 else { 512 addStat ments( decl, body, mutexArgs );873 addStatements( decl, body, mutexArgs ); 513 874 } 514 875 } … … 527 888 assert( !dtor_guard_decl ); 528 889 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; 529 894 } 530 895 } … … 565 930 } 566 931 567 void MutexKeyword::addDtorStat ments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {932 void MutexKeyword::addDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) { 568 933 Type * arg_type = args.front()->get_type()->clone(); 569 934 arg_type->set_mutex( false ); … … 583 948 new SingleInit( new UntypedExpr( 584 949 new NameExpr( "get_monitor" ), 585 { new CastExpr( new VariableExpr( args.front() ), arg_type ) }950 { new CastExpr( new VariableExpr( args.front() ), arg_type, false ) } 586 951 )) 587 952 ); … … 604 969 { 605 970 new SingleInit( new AddressExpr( new VariableExpr( monitors ) ) ), 606 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() ) ) 971 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) ), 972 new SingleInit( new ConstantExpr( Constant::from_bool( false ) ) ) 607 973 }, 608 974 noDesignators, … … 613 979 614 980 //$monitor * __monitors[] = { get_monitor(a), get_monitor(b) }; 615 body->push_front( new DeclStmt( monitors) ); 616 } 617 618 void MutexKeyword::addStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) { 981 body->push_front( new DeclStmt( monitors ) ); 982 } 983 984 void MutexKeyword::addThreadDtorStatements( 985 FunctionDecl*, CompoundStmt * body, 986 const std::list<DeclarationWithType * > & args ) { 987 assert( args.size() == 1 ); 988 DeclarationWithType * arg = args.front(); 989 Type * arg_type = arg->get_type()->clone(); 990 assert( arg_type->get_mutex() ); 991 arg_type->set_mutex( false ); 992 993 // thread_dtor_guard_t __guard = { this, intptr( 0 ) }; 994 body->push_front( 995 new DeclStmt( new ObjectDecl( 996 "__guard", 997 noStorageClasses, 998 LinkageSpec::Cforall, 999 nullptr, 1000 new StructInstType( 1001 noQualifiers, 1002 thread_guard_decl 1003 ), 1004 new ListInit( 1005 { 1006 new SingleInit( new CastExpr( new VariableExpr( arg ), arg_type ) ), 1007 new SingleInit( new UntypedExpr( 1008 new NameExpr( "intptr" ), { 1009 new ConstantExpr( Constant::from_int( 0 ) ), 1010 } 1011 ) ), 1012 }, 1013 noDesignators, 1014 true 1015 ) 1016 )) 1017 ); 1018 } 1019 1020 void MutexKeyword::addStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) { 619 1021 ObjectDecl * monitors = new ObjectDecl( 620 1022 "__monitors", … … 641 1043 return new SingleInit( new UntypedExpr( 642 1044 new NameExpr( "get_monitor" ), 643 { new CastExpr( new VariableExpr( var ), type ) }1045 { new CastExpr( new VariableExpr( var ), type, false ) } 644 1046 ) ); 645 1047 }) … … 665 1067 new SingleInit( new VariableExpr( monitors ) ), 666 1068 new SingleInit( new ConstantExpr( Constant::from_ulong( args.size() ) ) ), 667 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone() ) )1069 new SingleInit( new CastExpr( new VariableExpr( func ), generic_func->clone(), false ) ) 668 1070 }, 669 1071 noDesignators, … … 727 1129 // tab-width: 4 // 728 1130 // End: // 1131 -
src/Concurrency/Waitfor.cc
r3c64c668 r58fe85a 384 384 decl_monitor 385 385 ) 386 ) 386 ), 387 false 387 388 ); 388 389 … … 408 409 new CompoundStmt({ 409 410 makeAccStatement( acceptables, index, "is_dtor", detectIsDtor( clause.target.function ) , indexer ), 410 makeAccStatement( acceptables, index, "func" , new CastExpr( clause.target.function, fptr_t ), indexer ),411 makeAccStatement( acceptables, index, "func" , new CastExpr( clause.target.function, fptr_t, false ) , indexer ), 411 412 makeAccStatement( acceptables, index, "data" , new VariableExpr( monitors ) , indexer ), 412 413 makeAccStatement( acceptables, index, "size" , new ConstantExpr( Constant::from_ulong( clause.target.arguments.size() ) ), indexer ), … … 531 532 decl_mask 532 533 ) 533 ) 534 ), 535 false 534 536 ), 535 537 timeout -
src/Concurrency/module.mk
r3c64c668 r58fe85a 15 15 ############################################################################### 16 16 17 SRC += Concurrency/Keywords.cc Concurrency/ Waitfor.cc17 SRC += Concurrency/Keywords.cc Concurrency/Keywords.h Concurrency/Waitfor.cc Concurrency/Waitfor.h 18 18 SRCDEMANGLE += Concurrency/Keywords.cc 19 19 -
src/ControlStruct/ExceptTranslate.cc
r3c64c668 r58fe85a 9 9 // Author : Andrew Beach 10 10 // Created On : Wed Jun 14 16:49:00 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Fri Dec 13 23:40:15 201913 // Update Count : 1 211 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jun 24 11:18:00 2020 13 // Update Count : 17 14 14 // 15 15 … … 64 64 } 65 65 66 class ExceptionMutatorCore : public WithGuards { 67 enum Context { NoHandler, TerHandler, ResHandler }; 68 69 // Also need to handle goto, break & continue. 70 // They need to be cut off in a ResHandler, until we enter another 71 // loop, switch or the goto stays within the function. 72 73 Context cur_context; 74 75 // The current (innermost) termination handler exception declaration. 76 ObjectDecl * handler_except_decl; 77 66 class ThrowMutatorCore : public WithGuards { 67 ObjectDecl * terminate_handler_except; 68 enum Context { NoHandler, TerHandler, ResHandler } cur_context; 69 70 // The helper functions for code/syntree generation. 71 Statement * create_either_throw( 72 ThrowStmt * throwStmt, const char * throwFunc ); 73 Statement * create_terminate_rethrow( ThrowStmt * throwStmt ); 74 75 public: 76 ThrowMutatorCore() : 77 terminate_handler_except( nullptr ), 78 cur_context( NoHandler ) 79 {} 80 81 void premutate( CatchStmt *catchStmt ); 82 Statement * postmutate( ThrowStmt *throwStmt ); 83 }; 84 85 // ThrowStmt Mutation Helpers 86 87 Statement * ThrowMutatorCore::create_either_throw( 88 ThrowStmt * throwStmt, const char * throwFunc ) { 89 // `throwFunc`( `throwStmt->get_name()` ); 90 UntypedExpr * call = new UntypedExpr( new NameExpr( throwFunc ) ); 91 call->get_args().push_back( throwStmt->get_expr() ); 92 throwStmt->set_expr( nullptr ); 93 delete throwStmt; 94 return new ExprStmt( call ); 95 } 96 97 Statement * ThrowMutatorCore::create_terminate_rethrow( 98 ThrowStmt *throwStmt ) { 99 // { `terminate_handler_except` = 0p; __rethrow_terminate(); } 100 assert( nullptr == throwStmt->get_expr() ); 101 assert( terminate_handler_except ); 102 103 CompoundStmt * result = new CompoundStmt(); 104 result->labels = throwStmt->labels; 105 result->push_back( new ExprStmt( UntypedExpr::createAssign( 106 nameOf( terminate_handler_except ), 107 new ConstantExpr( Constant::null( 108 terminate_handler_except->get_type()->clone() 109 ) ) 110 ) ) ); 111 result->push_back( new ExprStmt( 112 new UntypedExpr( new NameExpr( "__cfaehm_rethrow_terminate" ) ) 113 ) ); 114 delete throwStmt; 115 return result; 116 } 117 118 // Visiting/Mutating Functions 119 120 void ThrowMutatorCore::premutate( CatchStmt *catchStmt ) { 121 // Validate the statement's form. 122 ObjectDecl * decl = dynamic_cast<ObjectDecl *>( catchStmt->get_decl() ); 123 // Also checking the type would be nice. 124 if ( !decl || !dynamic_cast<PointerType *>( decl->type ) ) { 125 std::string kind = (CatchStmt::Terminate == catchStmt->kind) ? "catch" : "catchResume"; 126 SemanticError( catchStmt->location, kind + " must have pointer to an exception type" ); 127 } 128 129 // Track the handler context. 130 GuardValue( cur_context ); 131 if ( CatchStmt::Terminate == catchStmt->get_kind() ) { 132 cur_context = TerHandler; 133 134 GuardValue( terminate_handler_except ); 135 terminate_handler_except = decl; 136 } else { 137 cur_context = ResHandler; 138 } 139 } 140 141 Statement * ThrowMutatorCore::postmutate( ThrowStmt *throwStmt ) { 142 // Ignoring throwStmt->get_target() for now. 143 if ( ThrowStmt::Terminate == throwStmt->get_kind() ) { 144 if ( throwStmt->get_expr() ) { 145 return create_either_throw( throwStmt, "$throw" ); 146 } else if ( TerHandler == cur_context ) { 147 return create_terminate_rethrow( throwStmt ); 148 } else { 149 abort("Invalid throw in %s at %i\n", 150 throwStmt->location.filename.c_str(), 151 throwStmt->location.first_line); 152 } 153 } else { 154 if ( throwStmt->get_expr() ) { 155 return create_either_throw( throwStmt, "$throwResume" ); 156 } else if ( ResHandler == cur_context ) { 157 // This has to be handled later. 158 return throwStmt; 159 } else { 160 abort("Invalid throwResume in %s at %i\n", 161 throwStmt->location.filename.c_str(), 162 throwStmt->location.first_line); 163 } 164 } 165 } 166 167 class TryMutatorCore { 78 168 // The built in types used in translation. 79 169 StructDecl * except_decl; … … 82 172 83 173 // 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 );90 174 CompoundStmt * take_try_block( TryStmt * tryStmt ); 91 175 FunctionDecl * create_try_wrapper( CompoundStmt * body ); … … 101 185 FunctionDecl * create_finally_wrapper( TryStmt * tryStmt ); 102 186 ObjectDecl * create_finally_hook( FunctionDecl * finally_wrapper ); 187 Statement * create_resume_rethrow( ThrowStmt * throwStmt ); 103 188 104 189 // Types used in translation, make sure to use clone. … … 121 206 122 207 public: 123 ExceptionMutatorCore() : 124 cur_context( NoHandler ), 125 handler_except_decl( nullptr ), 208 TryMutatorCore() : 126 209 except_decl( nullptr ), node_decl( nullptr ), hook_decl( nullptr ), 127 210 try_func_t( noQualifiers, false ), … … 132 215 {} 133 216 134 void premutate( CatchStmt *catchStmt );135 217 void premutate( StructDecl *structDecl ); 218 Statement * postmutate( TryStmt *tryStmt ); 136 219 Statement * postmutate( ThrowStmt *throwStmt ); 137 Statement * postmutate( TryStmt *tryStmt );138 220 }; 139 221 140 void ExceptionMutatorCore::init_func_types() {222 void TryMutatorCore::init_func_types() { 141 223 assert( except_decl ); 142 224 … … 196 278 } 197 279 198 // ThrowStmt Mutation Helpers199 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 257 280 // TryStmt Mutation Helpers 258 281 259 CompoundStmt * ExceptionMutatorCore::take_try_block( TryStmt *tryStmt ) {282 CompoundStmt * TryMutatorCore::take_try_block( TryStmt *tryStmt ) { 260 283 CompoundStmt * block = tryStmt->get_block(); 261 284 tryStmt->set_block( nullptr ); … … 263 286 } 264 287 265 FunctionDecl * ExceptionMutatorCore::create_try_wrapper(288 FunctionDecl * TryMutatorCore::create_try_wrapper( 266 289 CompoundStmt *body ) { 267 290 … … 270 293 } 271 294 272 FunctionDecl * ExceptionMutatorCore::create_terminate_catch(295 FunctionDecl * TryMutatorCore::create_terminate_catch( 273 296 CatchList &handlers ) { 274 297 std::list<CaseStmt *> handler_wrappers; … … 309 332 local_except->get_attributes().push_back( new Attribute( 310 333 "cleanup", 311 { new NameExpr( "__cfa abi_ehm__cleanup_terminate" ) }334 { new NameExpr( "__cfaehm_cleanup_terminate" ) } 312 335 ) ); 313 336 … … 350 373 // Create a single check from a moddified handler. 351 374 // except_obj is referenced, modded_handler will be freed. 352 CompoundStmt * ExceptionMutatorCore::create_single_matcher(375 CompoundStmt * TryMutatorCore::create_single_matcher( 353 376 DeclarationWithType * except_obj, CatchStmt * modded_handler ) { 354 377 // { … … 388 411 } 389 412 390 FunctionDecl * ExceptionMutatorCore::create_terminate_match(413 FunctionDecl * TryMutatorCore::create_terminate_match( 391 414 CatchList &handlers ) { 392 415 // int match(exception * except) { … … 425 448 } 426 449 427 CompoundStmt * ExceptionMutatorCore::create_terminate_caller(450 CompoundStmt * TryMutatorCore::create_terminate_caller( 428 451 FunctionDecl * try_wrapper, 429 452 FunctionDecl * terminate_catch, 430 453 FunctionDecl * terminate_match ) { 431 // { __cfa abi_ehm__try_terminate(`try`, `catch`, `match`); }454 // { __cfaehm_try_terminate(`try`, `catch`, `match`); } 432 455 433 456 UntypedExpr * caller = new UntypedExpr( new NameExpr( 434 "__cfa abi_ehm__try_terminate" ) );457 "__cfaehm_try_terminate" ) ); 435 458 std::list<Expression *>& args = caller->get_args(); 436 459 args.push_back( nameOf( try_wrapper ) ); … … 443 466 } 444 467 445 FunctionDecl * ExceptionMutatorCore::create_resume_handler(468 FunctionDecl * TryMutatorCore::create_resume_handler( 446 469 CatchList &handlers ) { 447 470 // bool handle(exception * except) { … … 480 503 } 481 504 482 CompoundStmt * ExceptionMutatorCore::create_resume_wrapper(505 CompoundStmt * TryMutatorCore::create_resume_wrapper( 483 506 Statement * wraps, 484 507 FunctionDecl * resume_handler ) { … … 486 509 487 510 // struct __try_resume_node __resume_node 488 // __attribute__((cleanup( __cfa abi_ehm__try_resume_cleanup )));511 // __attribute__((cleanup( __cfaehm_try_resume_cleanup ))); 489 512 // ** unwinding of the stack here could cause problems ** 490 513 // ** however I don't think that can happen currently ** 491 // __cfa abi_ehm__try_resume_setup( &__resume_node, resume_handler );514 // __cfaehm_try_resume_setup( &__resume_node, resume_handler ); 492 515 493 516 std::list< Attribute * > attributes; … … 495 518 std::list< Expression * > attr_params; 496 519 attr_params.push_back( new NameExpr( 497 "__cfa abi_ehm__try_resume_cleanup" ) );520 "__cfaehm_try_resume_cleanup" ) ); 498 521 attributes.push_back( new Attribute( "cleanup", attr_params ) ); 499 522 } … … 514 537 515 538 UntypedExpr *setup = new UntypedExpr( new NameExpr( 516 "__cfa abi_ehm__try_resume_setup" ) );539 "__cfaehm_try_resume_setup" ) ); 517 540 setup->get_args().push_back( new AddressExpr( nameOf( obj ) ) ); 518 541 setup->get_args().push_back( nameOf( resume_handler ) ); … … 524 547 } 525 548 526 FunctionDecl * ExceptionMutatorCore::create_finally_wrapper(549 FunctionDecl * TryMutatorCore::create_finally_wrapper( 527 550 TryStmt * tryStmt ) { 528 // void finally() { <finally code>}551 // void finally() { `finally->block` } 529 552 FinallyStmt * finally = tryStmt->get_finally(); 530 553 CompoundStmt * body = finally->get_block(); … … 537 560 } 538 561 539 ObjectDecl * ExceptionMutatorCore::create_finally_hook(562 ObjectDecl * TryMutatorCore::create_finally_hook( 540 563 FunctionDecl * finally_wrapper ) { 541 // struct __cfa abi_ehm__cleanup_hook __finally_hook542 // __attribute__((cleanup( finally_wrapper)));564 // struct __cfaehm_cleanup_hook __finally_hook 565 // __attribute__((cleanup( `finally_wrapper` ))); 543 566 544 567 // Make Cleanup Attribute. … … 564 587 } 565 588 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 566 599 // Visiting/Mutating Functions 567 void ExceptionMutatorCore::premutate( CatchStmt *catchStmt ) { 568 // Validate the Statement's form. 569 ObjectDecl * decl = 570 dynamic_cast<ObjectDecl *>( catchStmt->get_decl() ); 571 if ( decl && true /* check decl->get_type() */ ) { 572 // Pass. 573 } else if ( CatchStmt::Terminate == catchStmt->get_kind() ) { 574 SemanticError(catchStmt->location, "catch must have exception type"); 575 } else { 576 SemanticError(catchStmt->location, "catchResume must have exception type"); 577 } 578 579 // Track the handler context. 580 GuardValue( cur_context ); 581 if ( CatchStmt::Terminate == catchStmt->get_kind() ) { 582 cur_context = TerHandler; 583 584 GuardValue( handler_except_decl ); 585 handler_except_decl = decl; 586 } else { 587 cur_context = ResHandler; 588 } 589 } 590 591 void ExceptionMutatorCore::premutate( StructDecl *structDecl ) { 600 void TryMutatorCore::premutate( StructDecl *structDecl ) { 592 601 if ( !structDecl->has_body() ) { 593 602 // Skip children? 594 603 return; 595 } else if ( structDecl->get_name() == "__cfa abi_ehm__base_exception_t" ) {604 } else if ( structDecl->get_name() == "__cfaehm_base_exception_t" ) { 596 605 assert( nullptr == except_decl ); 597 606 except_decl = structDecl; 598 607 init_func_types(); 599 } else if ( structDecl->get_name() == "__cfa abi_ehm__try_resume_node" ) {608 } else if ( structDecl->get_name() == "__cfaehm_try_resume_node" ) { 600 609 assert( nullptr == node_decl ); 601 610 node_decl = structDecl; 602 } else if ( structDecl->get_name() == "__cfa abi_ehm__cleanup_hook" ) {611 } else if ( structDecl->get_name() == "__cfaehm_cleanup_hook" ) { 603 612 assert( nullptr == hook_decl ); 604 613 hook_decl = structDecl; 605 614 } 606 // Later we might get the exception type as well. 607 } 608 609 Statement * ExceptionMutatorCore::postmutate( ThrowStmt *throwStmt ) { 610 assert( except_decl ); 611 612 // Ignoring throwStmt->get_target() for now. 613 if ( ThrowStmt::Terminate == throwStmt->get_kind() ) { 614 if ( throwStmt->get_expr() ) { 615 return create_terminate_throw( throwStmt ); 616 } else if ( TerHandler == cur_context ) { 617 return create_terminate_rethrow( throwStmt ); 618 } else { 619 abort("Invalid throw in %s at %i\n", 620 throwStmt->location.filename.c_str(), 621 throwStmt->location.first_line); 622 } 623 } else { 624 if ( throwStmt->get_expr() ) { 625 return create_resume_throw( throwStmt ); 626 } else if ( ResHandler == cur_context ) { 627 return create_resume_rethrow( throwStmt ); 628 } else { 629 abort("Invalid throwResume in %s at %i\n", 630 throwStmt->location.filename.c_str(), 631 throwStmt->location.first_line); 632 } 633 } 634 } 635 636 Statement * ExceptionMutatorCore::postmutate( TryStmt *tryStmt ) { 615 } 616 617 Statement * TryMutatorCore::postmutate( TryStmt *tryStmt ) { 637 618 assert( except_decl ); 638 619 assert( node_decl ); … … 688 669 } 689 670 690 void translateEHM( std::list< Declaration *> & translationUnit ) { 691 PassVisitor<ExceptionMutatorCore> translator; 671 Statement * TryMutatorCore::postmutate( ThrowStmt *throwStmt ) { 672 // Only valid `throwResume;` statements should remain. (2/3 checks) 673 assert( ThrowStmt::Resume == throwStmt->kind && ! throwStmt->expr ); 674 return create_resume_rethrow( throwStmt ); 675 } 676 677 void translateThrows( std::list< Declaration *> & translationUnit ) { 678 PassVisitor<ThrowMutatorCore> translator; 692 679 mutateAll( translationUnit, translator ); 693 680 } 681 682 void translateTries( std::list< Declaration *> & translationUnit ) { 683 PassVisitor<TryMutatorCore> translator; 684 mutateAll( translationUnit, translator ); 685 } 694 686 } -
src/ControlStruct/ExceptTranslate.h
r3c64c668 r58fe85a 9 9 // Author : Andrew Beach 10 10 // Created On : Tus Jun 06 10:13:00 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Jul 22 09:19:23 201713 // Update Count : 411 // Last Modified By : Andrew Beach 12 // Last Modified On : Tus May 19 11:47:00 2020 13 // Update Count : 5 14 14 // 15 15 … … 21 21 22 22 namespace ControlStruct { 23 void translateEHM( std::list< Declaration *> & translationUnit ); 24 // Converts exception handling structures into their underlying C code. Translation does use the exception 25 // handling header, make sure it is visible wherever translation occurs. 23 void translateThrows( std::list< Declaration *> & translationUnit ); 24 /* Replaces all throw & throwResume statements with function calls. 25 * These still need to be resolved, so call this before the reslover. 26 */ 27 28 void translateTries( std::list< Declaration *> & translationUnit ); 29 /* Replaces all try blocks (and their many clauses) with function definitions and calls. 30 * This uses the exception built-ins to produce typed output and should take place after 31 * the resolver. It also produces virtual casts and should happen before they are expanded. 32 */ 26 33 } 27 34 -
src/ControlStruct/module.mk
r3c64c668 r58fe85a 17 17 SRC_CONTROLSTRUCT = \ 18 18 ControlStruct/ForExprMutator.cc \ 19 ControlStruct/ForExprMutator.h \ 19 20 ControlStruct/LabelFixer.cc \ 21 ControlStruct/LabelFixer.h \ 20 22 ControlStruct/LabelGenerator.cc \ 23 ControlStruct/LabelGenerator.h \ 21 24 ControlStruct/MLEMutator.cc \ 22 ControlStruct/Mutate.cc 25 ControlStruct/MLEMutator.h \ 26 ControlStruct/Mutate.cc \ 27 ControlStruct/Mutate.h 23 28 24 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc 29 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h 25 30 SRCDEMANGLE += $(SRC_CONTROLSTRUCT) 26 31 -
src/GenPoly/GenPoly.cc
r3c64c668 r58fe85a 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 48 56 /// Checks a parameter list for polymorphic parameters from tyVars; will substitute according to env if present 49 57 bool hasPolyParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) { … … 56 64 } 57 65 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 58 74 /// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present 59 75 bool hasDynParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) { … … 92 108 Type *newType = env->lookup( typeInst->get_name() ); 93 109 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; 94 119 } 95 120 return type; … … 111 136 } 112 137 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 113 153 Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env ) { 114 154 type = replaceTypeInst( type, env ); … … 126 166 } 127 167 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; 128 183 } 129 184 … … 449 504 } 450 505 506 namespace { 507 // temporary hack to avoid re-implementing anything related to TyVarMap 508 // does this work? these two structs have identical definitions. 509 inline TypeDecl::Data convData(const ast::TypeDecl::Data & data) { 510 return *reinterpret_cast<const TypeDecl::Data *>(&data); 511 } 512 } 513 451 514 bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ) { 452 515 // is parameter is not polymorphic, don't need to box … … 459 522 } 460 523 524 bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env) { 525 // is parameter is not polymorphic, don't need to box 526 if ( ! isPolyType( param, exprTyVars ) ) return false; 527 ast::ptr<ast::Type> newType = arg; 528 if ( env ) env->apply( newType ); 529 // if the argument's type is polymorphic, we don't need to box again! 530 return ! isPolyType( newType ); 531 } 532 461 533 bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env ) { 462 534 FunctionType * function = getFunctionType( appExpr->function->result ); … … 467 539 } 468 540 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 469 550 void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) { 470 551 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})); 471 556 } 472 557 … … 478 563 if ( PointerType *pointer = dynamic_cast< PointerType* >( type ) ) { 479 564 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); 480 577 } 481 578 } -
src/GenPoly/GenPoly.h
r3c64c668 r58fe85a 26 26 27 27 namespace GenPoly { 28 28 29 typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap; 29 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); 35 36 36 37 /// returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided 37 38 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); 38 40 39 41 /// returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided … … 84 86 /// true if arg requires boxing given exprTyVars 85 87 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); 86 89 87 90 /// true if arg requires boxing in the call to appExpr 88 91 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); 89 93 90 94 /// Adds the type variable `tyVar` to `tyVarMap` … … 93 97 /// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `tyVarMap` 94 98 void makeTyVarMap( Type *type, TyVarMap &tyVarMap ); 99 void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap); 95 100 96 101 /// Prints type variable map -
src/GenPoly/InstantiateGeneric.cc
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Thu Aug 04 18:33:00 2016 11 // Last Modified By : A aron B. Moss12 // Last Modified On : Thu Aug 04 18:33:00 201613 // Update Count : 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jul 16 10:17:00 2020 13 // Update Count : 2 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 ScopedSet< AggregateDecl*> dtypeStatics;174 std::set<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 299 311 void GenericInstantiator::stripDtypeParams( AggregateDecl *base, std::list< TypeDecl* >& baseParams, const std::list< TypeExpr* >& typeSubs ) { 300 312 substituteMembers( base->get_members(), baseParams, typeSubs ); … … 373 385 concDecl->set_body( inst->get_baseStruct()->has_body() ); 374 386 substituteMembers( inst->get_baseStruct()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() ); 375 insert( inst, typeSubs, concDecl ); // must insert before recursion 387 // Forward declare before recursion. (TODO: Only when needed, #199.) 388 insert( inst, typeSubs, concDecl ); 389 if ( StructDecl *forwardDecl = asForward( concDecl ) ) { 390 declsToAddBefore.push_back( forwardDecl ); 391 } 376 392 concDecl->acceptMutator( *visitor ); // recursively instantiate members 377 393 declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first … … 423 439 concDecl->set_body( inst->get_baseUnion()->has_body() ); 424 440 substituteMembers( inst->get_baseUnion()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() ); 425 insert( inst, typeSubs, concDecl ); // must insert before recursion 441 // Forward declare before recursion. (TODO: Only when needed, #199.) 442 insert( inst, typeSubs, concDecl ); 443 if ( UnionDecl *forwardDecl = asForward( concDecl ) ) { 444 declsToAddBefore.push_back( forwardDecl ); 445 } 426 446 concDecl->acceptMutator( *visitor ); // recursively instantiate members 427 447 declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first … … 485 505 void GenericInstantiator::beginScope() { 486 506 instantiations.beginScope(); 487 dtypeStatics.beginScope();488 507 } 489 508 490 509 void GenericInstantiator::endScope() { 491 510 instantiations.endScope(); 492 dtypeStatics.endScope();493 511 } 494 512 -
src/GenPoly/Specialize.cc
r3c64c668 r58fe85a 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Fri Dec 13 23:40:49 201913 // Update Count : 3 211 // Last Modified By : Andrew Beach 12 // Last Modified On : Thr Jul 2 17:42:00 2020 13 // Update Count : 33 14 14 // 15 15 … … 42 42 43 43 namespace GenPoly { 44 struct Specialize final : public WithConstTypeSubstitution, public WithStmtsToAdd, public WithVisitorRef<Specialize> { 44 struct Specialize final : public WithConstTypeSubstitution, 45 public WithDeclsToAdd, public WithVisitorRef<Specialize> { 45 46 Expression * postmutate( ApplicationExpr *applicationExpr ); 46 47 Expression * postmutate( CastExpr *castExpr ); … … 217 218 thunkFunc->get_attributes().push_back( new Attribute( "unused" ) ); 218 219 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 219 224 // thread thunk parameters into call to actual function, naming thunk parameters as we go 220 225 UniqueName paramNamer( paramPrefix ); … … 248 253 } // if 249 254 250 // handle any specializations that may still be present 251 std::string oldParamPrefix = paramPrefix; 252 paramPrefix += "p"; 253 // save stmtsToAddBefore in oldStmts 254 std::list< Statement* > oldStmts; 255 oldStmts.splice( oldStmts.end(), stmtsToAddBefore ); 256 appExpr->acceptMutator( *visitor ); 257 paramPrefix = oldParamPrefix; 258 // write any statements added for recursive specializations into the thunk body 259 thunkFunc->statements->kids.splice( thunkFunc->statements->kids.end(), stmtsToAddBefore ); 260 // restore oldStmts into stmtsToAddBefore 261 stmtsToAddBefore.splice( stmtsToAddBefore.end(), oldStmts ); 255 // Handle any specializations that may still be present. 256 { 257 std::string oldParamPrefix = paramPrefix; 258 paramPrefix += "p"; 259 std::list< Declaration * > oldDecls; 260 oldDecls.splice( oldDecls.end(), declsToAddBefore ); 261 262 appExpr->acceptMutator( *visitor ); 263 // Write recursive specializations into the thunk body. 264 for ( Declaration * decl : declsToAddBefore ) { 265 thunkFunc->statements->kids.push_back( new DeclStmt( decl ) ); 266 } 267 268 declsToAddBefore = std::move( oldDecls ); 269 paramPrefix = oldParamPrefix; 270 } 262 271 263 272 // add return (or valueless expression) to the thunk … … 270 279 thunkFunc->statements->kids.push_back( appStmt ); 271 280 272 // add thunk definition to queue of statements to add273 stmtsToAddBefore.push_back( new DeclStmt( thunkFunc ));281 // Add the thunk definition (converted to DeclStmt if appproprate). 282 declsToAddBefore.push_back( thunkFunc ); 274 283 // return address of thunk function as replacement expression 275 284 return new AddressExpr( new VariableExpr( thunkFunc ) ); -
src/GenPoly/module.mk
r3c64c668 r58fe85a 16 16 17 17 SRC += GenPoly/Box.cc \ 18 GenPoly/Box.h \ 19 GenPoly/ErasableScopedMap.h \ 20 GenPoly/FindFunction.cc \ 21 GenPoly/FindFunction.h \ 18 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 \ 19 29 GenPoly/ScrubTyVars.cc \ 20 GenPoly/ Lvalue.cc\30 GenPoly/ScrubTyVars.h \ 21 31 GenPoly/Specialize.cc \ 22 GenPoly/FindFunction.cc \ 23 GenPoly/InstantiateGeneric.cc 32 GenPoly/Specialize.h 24 33 25 SRCDEMANGLE += GenPoly/GenPoly.cc GenPoly/ Lvalue.cc34 SRCDEMANGLE += GenPoly/GenPoly.cc GenPoly/GenPoly.h GenPoly/Lvalue.cc GenPoly/Lvalue.h 26 35 -
src/InitTweak/FixGlobalInit.cc
r3c64c668 r58fe85a 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 36 40 namespace InitTweak { 37 41 class GlobalFixer : public WithShortCircuiting { … … 50 54 FunctionDecl * initFunction; 51 55 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; 52 70 }; 53 71 … … 91 109 } 92 110 111 void fixGlobalInit(ast::TranslationUnit & translationUnit, bool inLibrary) { 112 ast::Pass<GlobalFixer_new> fixer; 113 accept_all(translationUnit, fixer); 114 115 if ( !fixer.core.initStmts.empty() ) { 116 std::vector<ast::ptr<ast::Expr>> ctorParams; 117 if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int({}, 200)); 118 auto initFunction = new ast::FunctionDecl({}, "__global_init__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.initStmts)), 119 ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("constructor", std::move(ctorParams))}); 120 121 translationUnit.decls.emplace_back( initFunction ); 122 } // if 123 124 if ( !fixer.core.destroyStmts.empty() ) { 125 std::vector<ast::ptr<ast::Expr>> dtorParams; 126 if (inLibrary) dtorParams.emplace_back(ast::ConstantExpr::from_int({}, 200)); 127 auto destroyFunction = new ast::FunctionDecl({}, "__global_destroy__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.destroyStmts)), 128 ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("destructor", std::move(dtorParams))}); 129 130 translationUnit.decls.emplace_back(destroyFunction); 131 } // if 132 } 133 93 134 void GlobalFixer::previsit( ObjectDecl *objDecl ) { 94 135 std::list< Statement * > & initStatements = initFunction->get_statements()->get_kids(); … … 112 153 } // if 113 154 if ( Statement * ctor = ctorInit->ctor ) { 155 addDataSectonAttribute( objDecl ); 114 156 initStatements.push_back( ctor ); 115 157 objDecl->init = nullptr; … … 126 168 } 127 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-NULL 175 assert( ! ctorInit->ctor || ! ctorInit->init ); 176 177 const ast::Stmt * dtor = ctorInit->dtor; 178 if ( dtor && ! isIntrinsicSingleArgCallStmt( dtor ) ) { 179 // don't need to call intrinsic dtor, because it does nothing, but 180 // non-intrinsic dtors must be called 181 destroyStmts.push_front( dtor ); 182 // ctorInit->dtor = nullptr; 183 } // if 184 if ( const ast::Stmt * ctor = ctorInit->ctor ) { 185 addDataSectionAttribute(mutDecl); 186 initStmts.push_back( ctor ); 187 mutDecl->init = nullptr; 188 // ctorInit->ctor = nullptr; 189 } else if ( const ast::Init * init = ctorInit->init ) { 190 mutDecl->init = init; 191 // ctorInit->init = nullptr; 192 } else { 193 // no constructor and no initializer, which is okay 194 mutDecl->init = nullptr; 195 } // if 196 // delete ctorInit; 197 } // if 198 } 199 128 200 // only modify global variables 129 201 void GlobalFixer::previsit( FunctionDecl * ) { visit_children = false; } -
src/InitTweak/FixGlobalInit.h
r3c64c668 r58fe85a 19 19 #include <string> // for string 20 20 21 #include <AST/Fwd.hpp> 22 23 21 24 class Declaration; 22 25 … … 26 29 /// function is for library code. 27 30 void fixGlobalInit( std::list< Declaration * > & translationUnit, bool inLibrary ); 31 void fixGlobalInit( ast::TranslationUnit & translationUnit, bool inLibrary ); 28 32 } // namespace 29 33 -
src/InitTweak/FixInit.cc
r3c64c668 r58fe85a 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 ); … … 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 804 810 // originally wanted to take advantage of gcc nested functions, but 805 811 // we get memory errors with this approach. To remedy this, the static -
src/InitTweak/FixInit.h
r3c64c668 r58fe85a 20 20 21 21 class Declaration; 22 namespace ast { 23 struct TranslationUnit; 24 } 22 25 23 26 namespace InitTweak { 24 27 /// replace constructor initializers with expression statements and unwrap basic C-style initializers 25 28 void fix( std::list< Declaration * > & translationUnit, bool inLibrary ); 29 30 void fix( ast::TranslationUnit & translationUnit, bool inLibrary); 26 31 } // namespace 27 32 -
src/InitTweak/GenInit.cc
r3c64c668 r58fe85a 26 26 #include "AST/Node.hpp" 27 27 #include "AST/Stmt.hpp" 28 #include "CompilationState.h" 28 29 #include "CodeGen/OperatorTable.h" 29 30 #include "Common/PassVisitor.h" // for PassVisitor, WithGuards, WithShort... … … 121 122 }; 122 123 124 struct HoistArrayDimension_NoResolve final : public WithDeclsToAdd, public WithShortCircuiting, public WithGuards { 125 /// hoist dimension from array types in object declaration so that it uses a single 126 /// const variable of type size_t, so that side effecting array dimensions are only 127 /// computed once. 128 static void hoistArrayDimension( std::list< Declaration * > & translationUnit ); 129 130 void premutate( ObjectDecl * objectDecl ); 131 DeclarationWithType * postmutate( ObjectDecl * objectDecl ); 132 void premutate( FunctionDecl *functionDecl ); 133 // should not traverse into any of these declarations to find objects 134 // that need to be constructed or destructed 135 void premutate( AggregateDecl * ) { visit_children = false; } 136 void premutate( NamedTypeDecl * ) { visit_children = false; } 137 void premutate( FunctionType * ) { visit_children = false; } 138 139 void hoist( Type * type ); 140 141 Type::StorageClasses storageClasses; 142 bool inFunction = false; 143 }; 144 123 145 void genInit( std::list< Declaration * > & translationUnit ) { 146 if (!useNewAST) { 147 HoistArrayDimension::hoistArrayDimension( translationUnit ); 148 } 149 else { 150 HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit ); 151 } 124 152 fixReturnStatements( translationUnit ); 125 HoistArrayDimension::hoistArrayDimension( translationUnit ); 126 CtorDtor::generateCtorDtor( translationUnit ); 153 154 if (!useNewAST) { 155 CtorDtor::generateCtorDtor( translationUnit ); 156 } 127 157 } 128 158 … … 196 226 arrayType->isVarLen = ! isConstExpr( arrayType->dimension ); 197 227 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects. 228 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve 229 // still try to detect constant expressions 198 230 if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return; 199 231 … … 210 242 211 243 void HoistArrayDimension::premutate( FunctionDecl * ) { 244 GuardValue( inFunction ); 245 inFunction = true; 246 } 247 248 // precompute array dimension expression, because constructor generation may duplicate it, 249 // which would be incorrect if it is a side-effecting computation. 250 void HoistArrayDimension_NoResolve::hoistArrayDimension( std::list< Declaration * > & translationUnit ) { 251 PassVisitor<HoistArrayDimension_NoResolve> hoister; 252 mutateAll( translationUnit, hoister ); 253 } 254 255 void HoistArrayDimension_NoResolve::premutate( ObjectDecl * objectDecl ) { 256 GuardValue( storageClasses ); 257 storageClasses = objectDecl->get_storageClasses(); 258 } 259 260 DeclarationWithType * HoistArrayDimension_NoResolve::postmutate( ObjectDecl * objectDecl ) { 261 hoist( objectDecl->get_type() ); 262 return objectDecl; 263 } 264 265 void HoistArrayDimension_NoResolve::hoist( Type * type ) { 266 // if in function, generate const size_t var 267 static UniqueName dimensionName( "_array_dim" ); 268 269 // C doesn't allow variable sized arrays at global scope or for static variables, so don't hoist dimension. 270 if ( ! inFunction ) return; 271 if ( storageClasses.is_static ) return; 272 273 if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) { 274 if ( ! arrayType->get_dimension() ) return; // xxx - recursive call to hoist? 275 // don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects. 276 // xxx - hoisting has no side effects anyways, so don't skip since we delay resolve 277 // still try to detect constant expressions 278 if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return; 279 280 ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) ); 281 arrayDimension->get_type()->set_const( true ); 282 283 arrayType->set_dimension( new VariableExpr( arrayDimension ) ); 284 declsToAddBefore.push_back( arrayDimension ); 285 286 hoist( arrayType->get_base() ); 287 return; 288 } 289 } 290 291 void HoistArrayDimension_NoResolve::premutate( FunctionDecl * ) { 212 292 GuardValue( inFunction ); 213 293 inFunction = true; … … 245 325 } 246 326 327 // why is this not just on FunctionDecl? 247 328 void ManagedTypes::handleDWT( DeclarationWithType * dwt ) { 248 329 // if this function is a user-defined constructor or destructor, mark down the type as "managed" … … 275 356 void ManagedTypes::endScope() { managedTypes.endScope(); } 276 357 358 bool ManagedTypes_new::isManaged( const ast::Type * type ) const { 359 // references are never constructed 360 if ( dynamic_cast< const ast::ReferenceType * >( type ) ) return false; 361 if ( auto tupleType = dynamic_cast< const ast::TupleType * > ( type ) ) { 362 // tuple is also managed if any of its components are managed 363 for (auto & component : tupleType->types) { 364 if (isManaged(component)) return true; 365 } 366 } 367 // need to clear and reset qualifiers when determining if a type is managed 368 // ValueGuard< Type::Qualifiers > qualifiers( type->get_qualifiers() ); 369 auto tmp = shallowCopy(type); 370 tmp->qualifiers = {}; 371 // delete tmp at return 372 ast::ptr<ast::Type> guard = tmp; 373 // a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable) 374 return managedTypes.find( Mangle::mangle( tmp, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ) != managedTypes.end() || GenPoly::isPolyType( tmp ); 375 } 376 377 bool ManagedTypes_new::isManaged( const ast::ObjectDecl * objDecl ) const { 378 const ast::Type * type = objDecl->type; 379 while ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) { 380 // must always construct VLAs with an initializer, since this is an error in C 381 if ( at->isVarLen && objDecl->init ) return true; 382 type = at->base; 383 } 384 return isManaged( type ); 385 } 386 387 void ManagedTypes_new::handleDWT( const ast::DeclWithType * dwt ) { 388 // if this function is a user-defined constructor or destructor, mark down the type as "managed" 389 if ( ! dwt->linkage.is_overrideable && CodeGen::isCtorDtor( dwt->name ) ) { 390 auto & params = GenPoly::getFunctionType( dwt->get_type())->params; 391 assert( ! params.empty() ); 392 // Type * type = InitTweak::getPointerBase( params.front() ); 393 // assert( type ); 394 managedTypes.insert( Mangle::mangle( params.front(), {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ); 395 } 396 } 397 398 void ManagedTypes_new::handleStruct( const ast::StructDecl * aggregateDecl ) { 399 // don't construct members, but need to take note if there is a managed member, 400 // because that means that this type is also managed 401 for ( auto & member : aggregateDecl->members ) { 402 if ( auto field = member.as<ast::ObjectDecl>() ) { 403 if ( isManaged( field ) ) { 404 // generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that 405 // polymorphic constructors make generic types managed types 406 ast::StructInstType inst( aggregateDecl ); 407 managedTypes.insert( Mangle::mangle( &inst, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ); 408 break; 409 } 410 } 411 } 412 } 413 414 void ManagedTypes_new::beginScope() { managedTypes.beginScope(); } 415 void ManagedTypes_new::endScope() { managedTypes.endScope(); } 416 277 417 ImplicitCtorDtorStmt * genCtorDtor( const std::string & fname, ObjectDecl * objDecl, Expression * arg ) { 278 418 // call into genImplicitCall from Autogen.h to generate calls to ctor/dtor … … 283 423 assert( stmts.size() <= 1 ); 284 424 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); 285 432 } 286 433 … … 363 510 // constructable object 364 511 InitExpander_new srcParam{ objDecl->init }, nullParam{ (const ast::Init *)nullptr }; 512 ast::ptr< ast::Expr > dstParam = new ast::VariableExpr(loc, objDecl); 365 513 366 514 ast::ptr< ast::Stmt > ctor = SymTab::genImplicitCall( 367 srcParam, new ast::VariableExpr{ loc, objDecl }, loc, "?{}", objDecl );515 srcParam, dstParam, loc, "?{}", objDecl ); 368 516 ast::ptr< ast::Stmt > dtor = SymTab::genImplicitCall( 369 nullParam, new ast::VariableExpr{ loc, objDecl }, loc, "^?{}", objDecl,517 nullParam, dstParam, loc, "^?{}", objDecl, 370 518 SymTab::LoopBackward ); 371 519 -
src/InitTweak/GenInit.h
r3c64c668 r58fe85a 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); 35 36 36 37 /// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer … … 51 52 GenPoly::ScopedSet< std::string > managedTypes; 52 53 }; 54 55 class ManagedTypes_new { 56 public: 57 bool isManaged( const ast::ObjectDecl * objDecl ) const ; // determine if object is managed 58 bool isManaged( const ast::Type * type ) const; // determine if type is managed 59 60 void handleDWT( const ast::DeclWithType * dwt ); // add type to managed if ctor/dtor 61 void handleStruct( const ast::StructDecl * aggregateDecl ); // add type to managed if child is managed 62 63 void beginScope(); 64 void endScope(); 65 private: 66 GenPoly::ScopedSet< std::string > managedTypes; 67 }; 53 68 } // namespace 54 69 -
src/InitTweak/InitTweak.cc
r3c64c668 r58fe85a 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 designations 94 if ( result ) visit_children = false; 95 } 96 97 void previsit( const ast::Designation * des ) { 98 // short circuit if we already know there are designations 99 if ( result ) visit_children = false; 100 else if ( ! des->designators.empty() ) { 101 result = true; 102 visit_children = false; 103 } 104 } 105 }; 106 107 struct InitDepthChecker_new : public ast::WithGuards { 108 bool result = true; 109 const ast::Type * type; 110 int curDepth = 0, maxDepth = 0; 111 InitDepthChecker_new( const ast::Type * type ) : type( type ) { 112 const ast::Type * t = type; 113 while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) { 114 maxDepth++; 115 t = at->base; 116 } 117 maxDepth++; 118 } 119 void previsit( ListInit * ) { 120 curDepth++; 121 GuardAction( [this]() { curDepth--; } ); 122 if ( curDepth > maxDepth ) result = false; 123 } 124 }; 125 89 126 struct InitFlattener_old : public WithShortCircuiting { 90 127 void previsit( SingleInit * singleInit ) { … … 124 161 } 125 162 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 126 175 std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) { 127 176 ast::Pass< InitFlattener_new > flattener; 128 177 maybe_accept( init, flattener ); 129 return std::move( flattener. pass.argList );178 return std::move( flattener.core.argList ); 130 179 } 131 180 … … 358 407 if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) { 359 408 for ( const ast::Init * init : *listInit ) { 360 buildCallExpr( callExpr, index, dimension, init, out );409 buildCallExpr( shallowCopy(callExpr), index, dimension, init, out ); 361 410 } 362 411 } else { 363 buildCallExpr( callExpr, index, dimension, init, out );412 buildCallExpr( shallowCopy(callExpr), index, dimension, init, out ); 364 413 } 365 414 } else { … … 498 547 } 499 548 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 500 556 bool tryConstruct( DeclarationWithType * dwt ) { 501 557 ObjectDecl * objDecl = dynamic_cast< ObjectDecl * >( dwt ); … … 511 567 } 512 568 569 bool tryConstruct( const ast::DeclWithType * dwt ) { 570 auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt ); 571 if ( ! objDecl ) return false; 572 return (objDecl->init == nullptr || 573 ( objDecl->init != nullptr && objDecl->init->maybeConstructed )) 574 && ! objDecl->storage.is_extern 575 && isConstructable( objDecl->type ); 576 } 577 578 bool isConstructable( const ast::Type * type ) { 579 return ! dynamic_cast< const ast::VarArgsType * >( type ) && ! dynamic_cast< const ast::ReferenceType * >( type ) 580 && ! dynamic_cast< const ast::FunctionType * >( type ) && ! Tuples::isTtype( type ); 581 } 582 513 583 struct CallFinder_old { 514 584 CallFinder_old( const std::list< std::string > & names ) : names( names ) {} … … 536 606 537 607 struct CallFinder_new final { 538 std::vector< ast::ptr< ast::Expr >> matches;608 std::vector< const ast::Expr * > matches; 539 609 const std::vector< std::string > names; 540 610 … … 558 628 } 559 629 560 std::vector< ast::ptr< ast::Expr >> collectCtorDtorCalls( const ast::Stmt * stmt ) {630 std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) { 561 631 ast::Pass< CallFinder_new > finder{ std::vector< std::string >{ "?{}", "^?{}" } }; 562 632 maybe_accept( stmt, finder ); 563 return std::move( finder. pass.matches );633 return std::move( finder.core.matches ); 564 634 } 565 635 … … 696 766 template <typename Predicate> 697 767 bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) { 698 std::vector< ast::ptr< ast::Expr >> callExprs = collectCtorDtorCalls( stmt );768 std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt ); 699 769 return std::all_of( callExprs.begin(), callExprs.end(), pred ); 700 770 } … … 939 1009 } 940 1010 1011 // looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards 1012 // following passes may accidentally resolve this expression if returned as untyped... 1013 ast::Expr * createBitwiseAssignment (const ast::Expr * dst, const ast::Expr * src) { 1014 static ast::ptr<ast::FunctionDecl> assign = nullptr; 1015 if (!assign) { 1016 auto td = new ast::TypeDecl({}, "T", {}, nullptr, ast::TypeDecl::Dtype, true); 1017 assign = new ast::FunctionDecl({}, "?=?", {}, 1018 { new ast::ObjectDecl({}, "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))), 1019 new ast::ObjectDecl({}, "_src", new ast::TypeInstType("T", td))}, 1020 { new ast::ObjectDecl({}, "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic); 1021 } 1022 if (dst->result.as<ast::ReferenceType>()) { 1023 for (int depth = dst->result->referenceDepth(); depth > 0; depth--) { 1024 dst = new ast::AddressExpr(dst); 1025 } 1026 } 1027 else { 1028 dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {})); 1029 } 1030 if (src->result.as<ast::ReferenceType>()) { 1031 for (int depth = src->result->referenceDepth(); depth > 0; depth--) { 1032 src = new ast::AddressExpr(src); 1033 } 1034 } 1035 return new ast::ApplicationExpr(dst->location, ast::VariableExpr::functionPointer(dst->location, assign), {dst, src}); 1036 } 1037 941 1038 struct ConstExprChecker : public WithShortCircuiting { 942 1039 // most expressions are not const expr … … 979 1076 }; 980 1077 1078 struct ConstExprChecker_new : public ast::WithShortCircuiting { 1079 // most expressions are not const expr 1080 void previsit( const ast::Expr * ) { result = false; visit_children = false; } 1081 1082 void previsit( const ast::AddressExpr *addressExpr ) { 1083 visit_children = false; 1084 const ast::Expr * arg = addressExpr->arg; 1085 1086 // address of a variable or member expression is constexpr 1087 if ( ! dynamic_cast< const ast::NameExpr * >( arg ) 1088 && ! dynamic_cast< const ast::VariableExpr * >( arg ) 1089 && ! dynamic_cast< const ast::MemberExpr * >( arg ) 1090 && ! dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false; 1091 } 1092 1093 // these expressions may be const expr, depending on their children 1094 void previsit( const ast::SizeofExpr * ) {} 1095 void previsit( const ast::AlignofExpr * ) {} 1096 void previsit( const ast::UntypedOffsetofExpr * ) {} 1097 void previsit( const ast::OffsetofExpr * ) {} 1098 void previsit( const ast::OffsetPackExpr * ) {} 1099 void previsit( const ast::CommaExpr * ) {} 1100 void previsit( const ast::LogicalExpr * ) {} 1101 void previsit( const ast::ConditionalExpr * ) {} 1102 void previsit( const ast::CastExpr * ) {} 1103 void previsit( const ast::ConstantExpr * ) {} 1104 1105 void previsit( const ast::VariableExpr * varExpr ) { 1106 visit_children = false; 1107 1108 if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) { 1109 long long int value; 1110 if ( inst->base->valueOf( varExpr->var, value ) ) { 1111 // enumerators are const expr 1112 return; 1113 } 1114 } 1115 result = false; 1116 } 1117 1118 bool result = true; 1119 }; 1120 981 1121 bool isConstExpr( Expression * expr ) { 982 1122 if ( expr ) { … … 998 1138 } 999 1139 1140 bool isConstExpr( const ast::Expr * expr ) { 1141 if ( expr ) { 1142 ast::Pass<ConstExprChecker_new> checker; 1143 expr->accept( checker ); 1144 return checker.core.result; 1145 } 1146 return true; 1147 } 1148 1149 bool isConstExpr( const ast::Init * init ) { 1150 if ( init ) { 1151 ast::Pass<ConstExprChecker_new> checker; 1152 init->accept( checker ); 1153 return checker.core.result; 1154 } // if 1155 // for all intents and purposes, no initializer means const expr 1156 return true; 1157 } 1158 1000 1159 bool isConstructor( const std::string & str ) { return str == "?{}"; } 1001 1160 bool isDestructor( const std::string & str ) { return str == "^?{}"; } … … 1026 1185 if ( ftype->params.size() != 2 ) return false; 1027 1186 1028 const ast::Type * t1 = getPointerBase( ftype->params.front() ->get_type());1187 const ast::Type * t1 = getPointerBase( ftype->params.front() ); 1029 1188 if ( ! t1 ) return false; 1030 const ast::Type * t2 = ftype->params.back() ->get_type();1189 const ast::Type * t2 = ftype->params.back(); 1031 1190 1032 1191 return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} ); … … 1055 1214 return isCopyFunction( decl, "?{}" ); 1056 1215 } 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 1057 1231 } -
src/InitTweak/InitTweak.h
r3c64c668 r58fe85a 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); 40 41 41 42 /// generate a bitwise assignment operation. 42 43 ApplicationExpr * createBitwiseAssignment( Expression * dst, Expression * src ); 44 45 ast::Expr * createBitwiseAssignment( const ast::Expr * dst, const ast::Expr * src); 43 46 44 47 /// transform Initializer into an argument list that can be passed to a call expression … … 48 51 /// True if the resolver should try to construct dwt 49 52 bool tryConstruct( DeclarationWithType * dwt ); 53 bool tryConstruct( const ast::DeclWithType * dwt ); 50 54 51 55 /// True if the type can have a user-defined constructor 52 56 bool isConstructable( Type * t ); 57 bool isConstructable( const ast::Type * t ); 53 58 54 59 /// True if the Initializer contains designations 55 60 bool isDesignated( Initializer * init ); 61 bool isDesignated( const ast::Init * init ); 56 62 57 63 /// True if the ObjectDecl's Initializer nesting level is not deeper than the depth of its 58 64 /// type, where the depth of its type is the number of nested ArrayTypes + 1 59 65 bool checkInitDepth( ObjectDecl * objDecl ); 66 bool checkInitDepth( const ast::ObjectDecl * objDecl ); 60 67 61 68 /// returns the declaration of the function called by the expr (must be ApplicationExpr or UntypedExpr) … … 79 86 /// get all Ctor/Dtor call expressions from a Statement 80 87 void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches ); 81 std::vector< ast::ptr< ast::Expr >> collectCtorDtorCalls( const ast::Stmt * stmt );88 std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ); 82 89 83 90 /// get the Ctor/Dtor call expression from a Statement that looks like a generated ctor/dtor call … … 102 109 bool isConstExpr( Expression * expr ); 103 110 bool isConstExpr( Initializer * init ); 111 112 bool isConstExpr( const ast::Expr * expr ); 113 bool isConstExpr( const ast::Init * init ); 114 115 /// Modifies objDecl to have: 116 /// __attribute__((section (".data#"))) 117 /// which makes gcc put the declared variable in the data section, 118 /// which is helpful for global constants on newer gcc versions, 119 /// so that CFA's generated initialization won't segfault when writing it via a const cast. 120 /// The trailing # is an injected assembly comment, to suppress the "a" in 121 /// .section .data,"a" 122 /// .section .data#,"a" 123 /// to avoid assembler warning "ignoring changed section attributes for .data" 124 void addDataSectonAttribute( ObjectDecl * objDecl ); 125 126 void addDataSectionAttribute( ast::ObjectDecl * objDecl ); 104 127 105 128 class InitExpander_old { -
src/InitTweak/module.mk
r3c64c668 r58fe85a 15 15 ############################################################################### 16 16 17 SRC += InitTweak/GenInit.cc \ 17 SRC += \ 18 InitTweak/FixGlobalInit.cc \ 19 InitTweak/FixGlobalInit.h \ 18 20 InitTweak/FixInit.cc \ 19 InitTweak/FixGlobalInit.cc \ 20 InitTweak/InitTweak.cc 21 InitTweak/FixInit.h \ 22 InitTweak/GenInit.cc \ 23 InitTweak/GenInit.h \ 24 InitTweak/InitTweak.cc \ 25 InitTweak/InitTweak.h \ 26 InitTweak/FixInitNew.cpp 21 27 22 SRCDEMANGLE += InitTweak/GenInit.cc \ 23 InitTweak/InitTweak.cc 28 SRCDEMANGLE += \ 29 InitTweak/GenInit.cc \ 30 InitTweak/GenInit.h \ 31 InitTweak/InitTweak.cc \ 32 InitTweak/InitTweak.h 24 33 -
src/Makefile.am
r3c64c668 r58fe85a 20 20 21 21 SRC = main.cc \ 22 CompilationState.cc \ 23 CompilationState.h \ 22 24 MakeLibCfa.cc \ 23 CompilationState.cc 25 MakeLibCfa.h 24 26 25 27 SRCDEMANGLE = CompilationState.cc … … 66 68 ___driver_cfa_cpp_SOURCES = $(SRC) 67 69 ___driver_cfa_cpp_LDADD = -ldl $(LIBPROFILER) $(LIBTCMALLOC) 70 EXTRA_DIST = include/cassert include/optional BasicTypes-gen.cc 68 71 69 72 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
r3c64c668 r58fe85a 10 10 // Created On : Sat May 16 12:34:05 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 16 15:32:22 201913 // Update Count : 113 312 // Last Modified On : Thu Oct 8 08:03:38 2020 13 // Update Count : 1135 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 ne _Noreturn struct S { ... }; // disallowed1118 // inl ne _Noreturn enum E { ... }; // disallowed1117 // inline _Noreturn struct S { ... }; // disallowed 1118 // inline _Noreturn enum E { ... }; // disallowed 1119 1119 if ( funcSpecs.any() ) { 1120 1120 SemanticError( this, "invalid function specifier for " ); -
src/Parser/ExpressionNode.cc
r3c64c668 r58fe85a 10 10 // Created On : Sat May 16 13:17:07 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Dec 18 21:14:58 201913 // Update Count : 98112 // Last Modified On : Thu Aug 20 14:01:46 2020 13 // Update Count : 1076 14 14 // 15 15 … … 65 65 66 66 void lnthSuffix( string & str, int & type, int & ltype ) { 67 // 'u' can appear before or after length suffix 67 68 string::size_type posn = str.find_last_of( "lL" ); 68 69 69 70 if ( posn == string::npos ) return; // no suffix 70 if ( posn == str.length() - 1 ) { type = 3; return; } // no length => long 71 71 size_t end = str.length() - 1; 72 if ( posn == end ) { type = 3; return; } // no length after 'l' => long 73 72 74 string::size_type next = posn + 1; // advance to length 73 75 if ( str[next] == '3' ) { // 32 … … 84 86 } // if 85 87 } // if 86 // remove "lL" for these cases because it may not imply long 87 str.erase( posn ); // remove length 88 89 char fix = '\0'; 90 if ( str[end] == 'u' || str[end] == 'U' ) fix = str[end]; // ends with 'uU' ? 91 str.erase( posn ); // remove length suffix and possibly uU 92 if ( type == 5 ) { // L128 does not need uU 93 end = str.length() - 1; 94 if ( str[end] == 'u' || str[end] == 'U' ) str.erase( end ); // ends with 'uU' ? remove 95 } else if ( fix != '\0' ) str += fix; // put 'uU' back if removed 88 96 } // lnthSuffix 89 97 … … 108 116 } // valueToType 109 117 118 static void scanbin( string & str, unsigned long long int & v ) { 119 v = 0; 120 size_t last = str.length() - 1; // last subscript of constant 121 for ( unsigned int i = 2;; ) { // ignore prefix 122 if ( str[i] == '1' ) v |= 1; 123 i += 1; 124 if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break; 125 v <<= 1; 126 } // for 127 } // scanbin 128 110 129 Expression * build_constantInteger( string & str ) { 111 130 static const BasicType::Kind kind[2][6] = { 112 131 // short (h) must be before char (hh) because shorter type has the longer suffix 113 { BasicType::ShortSignedInt, BasicType::SignedChar, BasicType::SignedInt, BasicType::LongSignedInt, BasicType::LongLongSignedInt, BasicType::SignedInt128, },114 { BasicType::ShortUnsignedInt, BasicType::UnsignedChar, BasicType::UnsignedInt, BasicType::LongUnsignedInt, BasicType::LongLongUnsignedInt, BasicType::UnsignedInt128, },132 { BasicType::ShortSignedInt, BasicType::SignedChar, BasicType::SignedInt, BasicType::LongSignedInt, BasicType::LongLongSignedInt, /* BasicType::SignedInt128 */ BasicType::LongLongSignedInt, }, 133 { BasicType::ShortUnsignedInt, BasicType::UnsignedChar, BasicType::UnsignedInt, BasicType::LongUnsignedInt, BasicType::LongLongUnsignedInt, /* BasicType::UnsignedInt128 */ BasicType::LongLongUnsignedInt, }, 115 134 }; 116 135 … … 120 139 }; // lnthsInt 121 140 122 unsigned long long int v; // converted integral value 123 size_t last = str.length() - 1; // last subscript of constant 124 Expression * ret; 125 //string fred( str ); 141 string str2( "0x0" ); 142 unsigned long long int v, v2 = 0; // converted integral value 143 Expression * ret, * ret2; 126 144 127 145 int type = -1; // 0 => short, 1 => char, 2 => int, 3 => long int, 4 => long long int, 5 => int128 … … 139 157 } // if 140 158 159 string::size_type posn; 160 161 // 'u' can appear before or after length suffix 162 if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true; 163 164 if ( isdigit( str[str.length() - 1] ) ) { // no suffix ? 165 lnthSuffix( str, type, ltype ); // could have length suffix 166 } else { 167 // At least one digit in integer constant, so safe to backup while looking for suffix. 168 169 posn = str.find_last_of( "pP" ); // pointer value 170 if ( posn != string::npos ) { ltype = 5; str.erase( posn, 1 ); goto FINI; } 171 172 posn = str.find_last_of( "zZ" ); // size_t 173 if ( posn != string::npos ) { Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 ); goto FINI; } 174 175 posn = str.rfind( "hh" ); // char 176 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; } 177 178 posn = str.rfind( "HH" ); // char 179 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; } 180 181 posn = str.find_last_of( "hH" ); // short 182 if ( posn != string::npos ) { type = 0; str.erase( posn, 1 ); goto FINI; } 183 184 posn = str.find_last_of( "nN" ); // int (natural number) 185 if ( posn != string::npos ) { type = 2; str.erase( posn, 1 ); goto FINI; } 186 187 if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) { type = 4; goto FINI; } 188 189 lnthSuffix( str, type, ltype ); // must be after check for "ll" 190 FINI: ; 191 } // if 192 141 193 // Cannot be just "0"/"1"; sscanf stops at the suffix, if any; value goes over the wall => always generate 142 194 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 143 199 if ( str[0] == '0' ) { // radix character ? 144 200 dec = false; 145 201 if ( checkX( str[1] ) ) { // hex constant ? 146 sscanf( (char *)str.c_str(), "%llx", &v ); 202 if ( type < 5 ) { // not L128 ? 203 sscanf( (char *)str.c_str(), "%llx", &v ); 204 #if defined(__SIZEOF_INT128__) 205 } else { // hex int128 constant 206 unsigned int len = str.length(); 207 if ( len > (2 + 16 + 16) ) SemanticError( yylloc, "128-bit hexadecimal constant to large " + str ); 208 if ( len <= (2 + 16) ) goto FHEX1; // hex digits < 2^64 209 str2 = "0x" + str.substr( len - 16 ); 210 sscanf( (char *)str2.c_str(), "%llx", &v2 ); 211 str = str.substr( 0, len - 16 ); 212 FHEX1: ; 213 sscanf( (char *)str.c_str(), "%llx", &v ); 214 #endif // __SIZEOF_INT128__ 215 } // if 147 216 //printf( "%llx %llu\n", v, v ); 148 217 } else if ( checkB( str[1] ) ) { // binary constant ? 149 v = 0; // compute value 150 for ( unsigned int i = 2;; ) { // ignore prefix 151 if ( str[i] == '1' ) v |= 1; 152 i += 1; 153 if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break; 154 v <<= 1; 155 } // for 218 #if defined(__SIZEOF_INT128__) 219 unsigned int len = str.length(); 220 if ( type == 5 && len > 2 + 64 ) { 221 if ( len > 2 + 64 + 64 ) SemanticError( yylloc, "128-bit binary constant to large " + str ); 222 str2 = "0b" + str.substr( len - 64 ); 223 str = str.substr( 0, len - 64 ); 224 scanbin( str2, v2 ); 225 } // if 226 #endif // __SIZEOF_INT128__ 227 scanbin( str, v ); 156 228 //printf( "%#llx %llu\n", v, v ); 157 229 } else { // octal constant 158 sscanf( (char *)str.c_str(), "%llo", &v ); 230 if ( type < 5 ) { // not L128 ? 231 sscanf( (char *)str.c_str(), "%llo", &v ); 232 #if defined(__SIZEOF_INT128__) 233 } else { // octal int128 constant 234 unsigned int len = str.length(); 235 if ( len > 1 + 43 || (len == 1 + 43 && str[0] > '3') ) SemanticError( yylloc, "128-bit octal constant to large " + str ); 236 char buf[32]; 237 if ( len <= 1 + 21 ) { // value < 21 octal digitis 238 sscanf( (char *)str.c_str(), "%llo", &v ); 239 } else { 240 sscanf( &str[len - 21], "%llo", &v ); 241 __int128 val = v; // accumulate bits 242 str[len - 21] ='\0'; // shorten string 243 sscanf( &str[len == 43 ? 1 : 0], "%llo", &v ); 244 val |= (__int128)v << 63; // store bits 245 if ( len == 1 + 43 ) { // most significant 2 bits ? 246 str[2] = '\0'; // shorten string 247 sscanf( &str[1], "%llo", &v ); // process most significant 2 bits 248 val |= (__int128)v << 126; // store bits 249 } // if 250 v = val >> 64; v2 = (uint64_t)val; // replace octal constant with 2 hex constants 251 sprintf( buf, "%#llx", v2 ); 252 str2 = buf; 253 } // if 254 sprintf( buf, "%#llx", v ); 255 str = buf; 256 #endif // __SIZEOF_INT128__ 257 } // if 159 258 //printf( "%#llo %llu\n", v, v ); 160 259 } // if 161 260 } else { // decimal constant ? 162 sscanf( (char *)str.c_str(), "%llu", &v ); 261 if ( type < 5 ) { // not L128 ? 262 sscanf( (char *)str.c_str(), "%llu", &v ); 263 #if defined(__SIZEOF_INT128__) 264 } else { // decimal int128 constant 265 #define P10_UINT64 10'000'000'000'000'000'000ULL // 19 zeroes 266 unsigned int len = str.length(); 267 if ( str.length() == 39 && str > (Unsigned ? "340282366920938463463374607431768211455" : "170141183460469231731687303715884105727") ) 268 SemanticError( yylloc, "128-bit decimal constant to large " + str ); 269 char buf[32]; 270 if ( len <= 19 ) { // value < 19 decimal digitis 271 sscanf( (char *)str.c_str(), "%llu", &v ); 272 } else { 273 sscanf( &str[len - 19], "%llu", &v ); 274 __int128 val = v; // accumulate bits 275 str[len - 19] ='\0'; // shorten string 276 sscanf( &str[len == 39 ? 1 : 0], "%llu", &v ); 277 val += (__int128)v * (__int128)P10_UINT64; // store bits 278 if ( len == 39 ) { // most significant 2 bits ? 279 str[1] = '\0'; // shorten string 280 sscanf( &str[0], "%llu", &v ); // process most significant 2 bits 281 val += (__int128)v * (__int128)P10_UINT64 * (__int128)P10_UINT64; // store bits 282 } // if 283 v = val >> 64; v2 = (uint64_t)val; // replace decimal constant with 2 hex constants 284 sprintf( buf, "%#llx", v2 ); 285 str2 = buf; 286 } // if 287 sprintf( buf, "%#llx", v ); 288 str = buf; 289 #endif // __SIZEOF_INT128__ 290 } // if 163 291 //printf( "%llu\n", v ); 164 292 } // if 165 293 166 string::size_type posn; 167 168 if ( isdigit( str[last] ) ) { // no suffix ? 169 lnthSuffix( str, type, ltype ); // could have length suffix 170 if ( type == -1 ) { // no suffix 171 valueToType( v, dec, type, Unsigned ); 172 } // if 173 } else { 174 // At least one digit in integer constant, so safe to backup while looking for suffix. 175 176 posn = str.find_last_of( "pP" ); 177 if ( posn != string::npos ) { valueToType( v, dec, type, Unsigned ); ltype = 5; str.erase( posn, 1 ); goto FINI; } 178 179 posn = str.find_last_of( "zZ" ); 180 if ( posn != string::npos ) { Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 ); goto FINI; } 181 182 // 'u' can appear before or after length suffix 183 if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true; 184 185 posn = str.rfind( "hh" ); 186 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; } 187 188 posn = str.rfind( "HH" ); 189 if ( posn != string::npos ) { type = 1; str.erase( posn, 2 ); goto FINI; } 190 191 posn = str.find_last_of( "hH" ); 192 if ( posn != string::npos ) { type = 0; str.erase( posn, 1 ); goto FINI; } 193 194 posn = str.find_last_of( "nN" ); 195 if ( posn != string::npos ) { type = 2; str.erase( posn, 1 ); goto FINI; } 196 197 if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) { type = 4; goto FINI; } 198 199 lnthSuffix( str, type, ltype ); // must be after check for "ll" 200 if ( type == -1 ) { // only 'u' suffix ? 201 valueToType( v, dec, type, Unsigned ); 202 } // if 203 FINI: ; 204 } // if 294 if ( type == -1 ) { // no suffix => determine type from value size 295 valueToType( v, dec, type, Unsigned ); 296 } // if 297 /* printf( "%s %llo %s %llo\n", str.c_str(), v, str2.c_str(), v2 ); */ 205 298 206 299 //if ( !( 0 <= type && type <= 6 ) ) { printf( "%s %lu %d %s\n", fred.c_str(), fred.length(), type, str.c_str() ); } … … 214 307 } else if ( ltype != -1 ) { // explicit length ? 215 308 if ( ltype == 6 ) { // int128, (int128)constant 216 ret = new CastExpr( ret, new BasicType( Type::Qualifiers(), kind[Unsigned][type] ), false ); 309 // ret = new CastExpr( ret, new BasicType( Type::Qualifiers(), kind[Unsigned][type] ), false ); 310 ret2 = new ConstantExpr( Constant( new BasicType( noQualifiers, BasicType::LongLongSignedInt ), str2, v2 ) ); 311 ret = build_compoundLiteral( DeclarationNode::newBasicType( DeclarationNode::Int128 )->addType( DeclarationNode::newSignedNess( DeclarationNode::Unsigned ) ), 312 new InitializerNode( (InitializerNode *)(new InitializerNode( new ExpressionNode( v2 == 0 ? ret2 : ret ) ))->set_last( new InitializerNode( new ExpressionNode( v2 == 0 ? ret : ret2 ) ) ), true ) ); 217 313 } else { // explicit length, (length_type)constant 218 314 ret = new CastExpr( ret, new TypeInstType( Type::Qualifiers(), lnthsInt[Unsigned][ltype], false ), false ); … … 342 438 if ( str[1] == '8' ) goto Default; // utf-8 characters => array of char 343 439 // lookup type of associated typedef 344 strtype = new TypeInstType( Type::Qualifiers( Type::Const), "char16_t", false );440 strtype = new TypeInstType( Type::Qualifiers( ), "char16_t", false ); 345 441 break; 346 442 case 'U': 347 strtype = new TypeInstType( Type::Qualifiers( Type::Const), "char32_t", false );443 strtype = new TypeInstType( Type::Qualifiers( ), "char32_t", false ); 348 444 break; 349 445 case 'L': 350 strtype = new TypeInstType( Type::Qualifiers( Type::Const), "wchar_t", false );446 strtype = new TypeInstType( Type::Qualifiers( ), "wchar_t", false ); 351 447 break; 352 448 Default: // char default string type 353 449 default: 354 strtype = new BasicType( Type::Qualifiers( Type::Const), BasicType::Char );450 strtype = new BasicType( Type::Qualifiers( ), BasicType::Char ); 355 451 } // switch 356 452 ArrayType * at = new ArrayType( noQualifiers, strtype, -
src/Parser/ParseNode.h
r3c64c668 r58fe85a 10 10 // Created On : Sat May 16 13:28:16 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 7 17:56:02202013 // Update Count : 89 112 // Last Modified On : Sat Oct 24 03:53:54 2020 13 // Update Count : 895 14 14 // 15 15 … … 37 37 class Attribute; 38 38 class Declaration; 39 classDeclarationNode;39 struct DeclarationNode; 40 40 class DeclarationWithType; 41 41 class ExpressionNode; 42 42 class Initializer; 43 classStatementNode;43 struct 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 class DeclarationNode : public ParseNode { 208 public: 207 struct DeclarationNode : public ParseNode { 209 208 // These enumerations must harmonize with their names in DeclarationNode.cc. 210 209 enum BasicType { Void, Bool, Char, Int, Int128, … … 304 303 bool get_inLine() const { return inLine; } 305 304 DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; } 306 public: 305 307 306 DeclarationNode * get_last() { return (DeclarationNode *)ParseNode::get_last(); } 308 307 … … 360 359 //############################################################################## 361 360 362 class StatementNode final : public ParseNode { 363 public: 361 struct StatementNode final : public ParseNode { 364 362 StatementNode() { stmt = nullptr; } 365 363 StatementNode( Statement * stmt ) : stmt( stmt ) {} … … 382 380 os << stmt.get() << std::endl; 383 381 } 384 private: 382 385 383 std::unique_ptr<Statement> stmt; 386 384 }; // StatementNode … … 426 424 Statement * build_finally( StatementNode * stmt ); 427 425 Statement * build_compound( StatementNode * first ); 426 StatementNode * maybe_build_compound( StatementNode * first ); 428 427 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output = nullptr, ExpressionNode * input = nullptr, ExpressionNode * clobber = nullptr, LabelNode * gotolabels = nullptr ); 429 428 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 ); -
src/Parser/StatementNode.cc
r3c64c668 r58fe85a 10 10 // Created On : Sat May 16 14:59:41 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Aug 4 09:39:25 201813 // Update Count : 3 6312 // Last Modified On : Sat Oct 24 04:20:55 2020 13 // Update Count : 383 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 251 266 WaitForStmt * build_waitfor( ExpressionNode * targetExpr, StatementNode * stmt, ExpressionNode * when ) { 252 267 auto node = new WaitForStmt(); … … 330 345 } // build_compound 331 346 347 // A single statement in a control structure is always converted to a compound statement so subsequent generated code 348 // can be placed within this compound statement. Otherwise, code generation has to constantly check for a single 349 // statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a 350 // conical form for code generation. 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_ptr 355 CompoundStmt * cs = new CompoundStmt(); 356 buildMoveList( first, cs->get_kids() ); 357 return new StatementNode( cs ); 358 } // if 359 return first; 360 } // maybe_build_compound 361 332 362 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) { 333 363 std::list< Expression * > out, in; -
src/Parser/TypeData.cc
r3c64c668 r58fe85a 769 769 case AggregateDecl::Struct: 770 770 case AggregateDecl::Coroutine: 771 case AggregateDecl::Generator: 771 772 case AggregateDecl::Monitor: 772 773 case AggregateDecl::Thread: … … 899 900 ret = new TypeDecl( name, scs, typebuild( td->base ), TypeDecl::Dtype, true ); 900 901 } // 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/lex.ll
r3c64c668 r58fe85a 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:05:50202013 * Update Count : 7 3712 * Last Modified On : Tue Oct 6 18:15:41 2020 13 * Update Count : 743 14 14 */ 15 15 … … 62 62 #define IDENTIFIER_RETURN() RETURN_VAL( typedefTable.isKind( yytext ) ) 63 63 64 #ifdef HAVE_KEYWORDS_FLOATXX // GCC >= 7 => keyword, otherwise typedef64 #ifdef HAVE_KEYWORDS_FLOATXX // GCC >= 7 => keyword, otherwise typedef 65 65 #define FLOATXX(v) KEYWORD_RETURN(v); 66 66 #else 67 #define FLOATXX(v) IDENTIFIER_RETURN(); 67 #define FLOATXX(v) IDENTIFIER_RETURN(); 68 68 #endif // HAVE_KEYWORDS_FLOATXX 69 69 … … 292 292 __restrict__ { KEYWORD_RETURN(RESTRICT); } // GCC 293 293 return { KEYWORD_RETURN(RETURN); } 294 /* resume { KEYWORD_RETURN(RESUME); } // CFA */294 /* resume { KEYWORD_RETURN(RESUME); } // CFA */ 295 295 short { KEYWORD_RETURN(SHORT); } 296 296 signed { KEYWORD_RETURN(SIGNED); } … … 301 301 _Static_assert { KEYWORD_RETURN(STATICASSERT); } // C11 302 302 struct { KEYWORD_RETURN(STRUCT); } 303 /* suspend { KEYWORD_RETURN(SUSPEND); } // CFA */ 303 suspend { KEYWORD_RETURN(SUSPEND); } // CFA 304 304 switch { KEYWORD_RETURN(SWITCH); } 305 305 thread { KEYWORD_RETURN(THREAD); } // C11 -
src/Parser/module.mk
r3c64c668 r58fe85a 17 17 BUILT_SOURCES = Parser/parser.hh 18 18 19 AM_YFLAGS = -d -t -v 19 AM_YFLAGS = -d -t -v -Wno-yacc 20 20 21 21 SRC += \ … … 23 23 Parser/ExpressionNode.cc \ 24 24 Parser/InitializerNode.cc \ 25 Parser/lex.ll \ 25 26 Parser/ParseNode.cc \ 27 Parser/ParseNode.h \ 28 Parser/parser.yy \ 29 Parser/ParserTypes.h \ 30 Parser/parserutility.cc \ 31 Parser/parserutility.h \ 26 32 Parser/StatementNode.cc \ 27 33 Parser/TypeData.cc \ 34 Parser/TypeData.h \ 28 35 Parser/TypedefTable.cc \ 29 Parser/lex.ll \ 30 Parser/parser.yy \ 31 Parser/parserutility.cc 36 Parser/TypedefTable.h 32 37 33 38 MOSTLYCLEANFILES += Parser/lex.cc Parser/parser.cc Parser/parser.hh Parser/parser.output -
src/Parser/parser.yy
r3c64c668 r58fe85a 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 21 14:47:29202013 // Update Count : 4 46812 // Last Modified On : Sat Oct 24 08:21:14 2020 13 // Update Count : 4624 14 14 // 15 15 … … 204 204 return forCtrl( type, new string( identifier->name ), start, compop, comp, inc ); 205 205 } else { 206 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed " ); return nullptr;206 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed." ); return nullptr; 207 207 } // if 208 208 } else { 209 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed " ); return nullptr;209 SemanticError( yylloc, "Expression disallowed. Only loop-index name allowed." ); return nullptr; 210 210 } // if 211 211 } // forCtrl … … 278 278 %token OTYPE FTYPE DTYPE TTYPE TRAIT // CFA 279 279 %token SIZEOF OFFSETOF 280 // %token SUSPEND RESUME // CFA 280 // %token RESUME // CFA 281 %token SUSPEND // CFA 281 282 %token ATTRIBUTE EXTENSION // GCC 282 283 %token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN … … 328 329 %type<en> conditional_expression constant_expression assignment_expression assignment_expression_opt 329 330 %type<en> comma_expression comma_expression_opt 330 %type<en> argument_expression_list argument_expression default_initialize_opt331 %type<en> argument_expression_list_opt argument_expression default_initialize_opt 331 332 %type<ifctl> if_control_expression 332 333 %type<fctl> for_control_expression for_control_expression_list … … 369 370 %type<decl> assertion assertion_list assertion_list_opt 370 371 371 %type<en> bit_subrange_size_opt bit_subrange_size372 %type<en> bit_subrange_size_opt bit_subrange_size 372 373 373 374 %type<decl> basic_declaration_specifier basic_type_name basic_type_specifier direct_type indirect_type … … 623 624 // equivalent to the old x[i,j]. 624 625 { $$ = new ExpressionNode( build_binary_val( OperKinds::Index, $1, $3 ) ); } 625 | postfix_expression '{' argument_expression_list '}' // CFA, constructor call626 | postfix_expression '{' argument_expression_list_opt '}' // CFA, constructor call 626 627 { 627 628 Token fn; … … 629 630 $$ = new ExpressionNode( new ConstructorExpr( build_func( new ExpressionNode( build_varref( fn ) ), (ExpressionNode *)( $1 )->set_last( $3 ) ) ) ); 630 631 } 631 | postfix_expression '(' argument_expression_list ')'632 | postfix_expression '(' argument_expression_list_opt ')' 632 633 { $$ = new ExpressionNode( build_func( $1, $3 ) ); } 633 634 | postfix_expression '`' identifier // CFA, postfix call … … 661 662 | '(' type_no_function ')' '@' '{' initializer_list_opt comma_opt '}' // CFA, explicit C compound-literal 662 663 { $$ = new ExpressionNode( build_compoundLiteral( $2, (new InitializerNode( $6, true ))->set_maybeConstructed( false ) ) ); } 663 | '^' primary_expression '{' argument_expression_list '}' // CFA, destructor call664 | '^' primary_expression '{' argument_expression_list_opt '}' // CFA, destructor call 664 665 { 665 666 Token fn; … … 669 670 ; 670 671 671 argument_expression_list :672 argument_expression_list_opt: 672 673 // empty 673 674 { $$ = nullptr; } 674 675 | argument_expression 675 | argument_expression_list ',' argument_expression676 | argument_expression_list_opt ',' argument_expression 676 677 { $$ = (ExpressionNode *)($1->set_last( $3 )); } 677 678 ; … … 792 793 | '(' aggregate_control '&' ')' cast_expression // CFA 793 794 { $$ = new ExpressionNode( build_keyword_cast( $2, $5 ) ); } 794 // VIRTUAL cannot be opt because of look ahead issues795 795 | '(' VIRTUAL ')' cast_expression // CFA 796 796 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression >( $4 ), maybeMoveBuildType( nullptr ) ) ); } … … 918 918 conditional_expression 919 919 | unary_expression assignment_operator assignment_expression 920 { $$ = new ExpressionNode( build_binary_val( $2, $1, $3 ) ); } 920 { 921 // if ( $2 == OperKinds::AtAssn ) { 922 // SemanticError( yylloc, "C @= assignment is currently unimplemented." ); $$ = nullptr; 923 // } else { 924 $$ = new ExpressionNode( build_binary_val( $2, $1, $3 ) ); 925 // } // if 926 } 921 927 | unary_expression '=' '{' initializer_list_opt comma_opt '}' 922 928 { SemanticError( yylloc, "Initializer assignment is currently unimplemented." ); $$ = nullptr; } … … 959 965 960 966 tuple_expression_list: 961 assignment_expression_opt 962 | tuple_expression_list ',' assignment_expression_opt 967 assignment_expression 968 | '@' // CFA 969 { SemanticError( yylloc, "Eliding tuple element with '@' is currently unimplemented." ); $$ = nullptr; } 970 | tuple_expression_list ',' assignment_expression 963 971 { $$ = (ExpressionNode *)($1->set_last( $3 )); } 972 | tuple_expression_list ',' '@' 973 { SemanticError( yylloc, "Eliding tuple element with '@' is currently unimplemented." ); $$ = nullptr; } 964 974 ; 965 975 … … 1070 1080 IF '(' if_control_expression ')' statement %prec THEN 1071 1081 // explicitly deal with the shift/reduce conflict on if/else 1072 { $$ = new StatementNode( build_if( $3, $5, nullptr ) ); }1082 { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), nullptr ) ); } 1073 1083 | IF '(' if_control_expression ')' statement ELSE statement 1074 { $$ = new StatementNode( build_if( $3, $5, $7) ); }1084 { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), maybe_build_compound( $7 ) ) ); } 1075 1085 ; 1076 1086 … … 1120 1130 1121 1131 case_clause: // CFA 1122 case_label_list statement { $$ = $1->append_last_case( new StatementNode( build_compound( $2 )) ); }1132 case_label_list statement { $$ = $1->append_last_case( maybe_build_compound( $2 ) ); } 1123 1133 ; 1124 1134 … … 1138 1148 iteration_statement: 1139 1149 WHILE '(' push if_control_expression ')' statement pop 1140 { $$ = new StatementNode( build_while( $4, $6) ); }1150 { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); } 1141 1151 | WHILE '(' ')' statement // CFA => while ( 1 ) 1142 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), $4) ); }1152 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); } 1143 1153 | DO statement WHILE '(' comma_expression ')' ';' 1144 { $$ = new StatementNode( build_do_while( $5, $2) ); }1154 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); } 1145 1155 | DO statement WHILE '(' ')' ';' // CFA => do while( 1 ) 1146 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), $2) ); }1156 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); } 1147 1157 | FOR '(' push for_control_expression_list ')' statement pop 1148 { $$ = new StatementNode( build_for( $4, $6) ); }1158 { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); } 1149 1159 | FOR '(' ')' statement // CFA => for ( ;; ) 1150 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), $4) ); }1160 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); } 1151 1161 ; 1152 1162 … … 1185 1195 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1186 1196 OperKinds::LThan, $1->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } 1187 | '=' comma_expression // CFA1197 | '=' comma_expression // CFA 1188 1198 { $$ = forCtrl( $2, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1189 1199 OperKinds::LEThan, $2->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } … … 1192 1202 | comma_expression inclexcl comma_expression '~' comma_expression // CFA 1193 1203 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), $1->clone(), $2, $3, $5 ); } 1204 | comma_expression ';' // CFA 1205 { $$ = forCtrl( new ExpressionNode( build_constantInteger( *new string( "0u" ) ) ), $1, nullptr, OperKinds::LThan, nullptr, nullptr ); } 1194 1206 | comma_expression ';' comma_expression // CFA 1195 1207 { $$ = forCtrl( $3, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1196 1208 OperKinds::LThan, $3->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } 1197 | comma_expression ';' '=' comma_expression // CFA1209 | comma_expression ';' '=' comma_expression // CFA 1198 1210 { $$ = forCtrl( $4, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), 1199 1211 OperKinds::LEThan, $4->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } … … 1259 1271 | RETURN '{' initializer_list_opt comma_opt '}' ';' 1260 1272 { SemanticError( yylloc, "Initializer return is currently unimplemented." ); $$ = nullptr; } 1261 // | SUSPEND ';' 1262 // { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; } 1263 // | SUSPEND compound_statement ';' 1264 // { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; } 1273 | SUSPEND ';' 1274 { $$ = new StatementNode( build_suspend( nullptr ) ); } 1275 | SUSPEND compound_statement 1276 { $$ = new StatementNode( build_suspend( $2 ) ); } 1277 | SUSPEND COROUTINE ';' 1278 { $$ = new StatementNode( build_suspend( nullptr, SuspendStmt::Coroutine ) ); } 1279 | SUSPEND COROUTINE compound_statement 1280 { $$ = new StatementNode( build_suspend( $3, SuspendStmt::Coroutine ) ); } 1281 | SUSPEND GENERATOR ';' 1282 { $$ = new StatementNode( build_suspend( nullptr, SuspendStmt::Generator ) ); } 1283 | SUSPEND GENERATOR compound_statement 1284 { $$ = new StatementNode( build_suspend( $3, SuspendStmt::Generator ) ); } 1265 1285 | THROW assignment_expression_opt ';' // handles rethrow 1266 1286 { $$ = new StatementNode( build_throw( $2 ) ); } … … 1285 1305 // If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex". 1286 1306 mutex_statement: 1287 MUTEX '(' argument_expression_list ')' statement1307 MUTEX '(' argument_expression_list_opt ')' statement 1288 1308 { SemanticError( yylloc, "Mutex statement is currently unimplemented." ); $$ = nullptr; } 1289 1309 ; … … 1302 1322 WAITFOR '(' cast_expression ')' 1303 1323 { $$ = $3; } 1304 // | WAITFOR '(' cast_expression ',' argument_expression_list ')'1324 // | WAITFOR '(' cast_expression ',' argument_expression_list_opt ')' 1305 1325 // { $$ = (ExpressionNode *)$3->set_last( $5 ); } 1306 | WAITFOR '(' cast_expression_list ':' argument_expression_list ')'1326 | WAITFOR '(' cast_expression_list ':' argument_expression_list_opt ')' 1307 1327 { $$ = (ExpressionNode *)($3->set_last( $5 )); } 1308 1328 ; … … 1311 1331 cast_expression 1312 1332 | cast_expression_list ',' cast_expression 1313 { $$ = (ExpressionNode *)($1->set_last( $3 )); } 1333 // { $$ = (ExpressionNode *)($1->set_last( $3 )); } 1334 { SemanticError( yylloc, "List of mutex member is currently unimplemented." ); $$ = nullptr; } 1314 1335 ; 1315 1336 … … 1320 1341 waitfor_clause: 1321 1342 when_clause_opt waitfor statement %prec THEN 1322 { $$ = build_waitfor( $2, $3, $1 ); }1343 { $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1 ); } 1323 1344 | when_clause_opt waitfor statement WOR waitfor_clause 1324 { $$ = build_waitfor( $2, $3, $1, $5 ); }1345 { $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1, $5 ); } 1325 1346 | when_clause_opt timeout statement %prec THEN 1326 { $$ = build_waitfor_timeout( $2, $3, $1 ); }1347 { $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1 ); } 1327 1348 | when_clause_opt ELSE statement 1328 { $$ = build_waitfor_timeout( nullptr, $3, $1 ); }1349 { $$ = build_waitfor_timeout( nullptr, maybe_build_compound( $3 ), $1 ); } 1329 1350 // "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless) 1330 1351 | when_clause_opt timeout statement WOR ELSE statement 1331 1352 { SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; } 1332 1353 | when_clause_opt timeout statement WOR when_clause ELSE statement 1333 { $$ = build_waitfor_timeout( $2, $3, $1, $7, $5 ); }1354 { $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1, maybe_build_compound( $7 ), $5 ); } 1334 1355 ; 1335 1356 … … 1654 1675 1655 1676 typedef_expression: 1656 // GCC, naming expression type: typedef name = exp; gives a name to the type of an expression1677 // deprecated GCC, naming expression type: typedef name = exp; gives a name to the type of an expression 1657 1678 TYPEDEF identifier '=' assignment_expression 1658 1679 { 1659 // $$ = DeclarationNode::newName( 0 ); // unimplemented 1660 SemanticError( yylloc, "Typedef expression is currently unimplemented." ); $$ = nullptr; 1680 SemanticError( yylloc, "Typedef expression is deprecated, use typeof(...) instead." ); $$ = nullptr; 1661 1681 } 1662 1682 | typedef_expression pop ',' push identifier '=' assignment_expression 1663 1683 { 1664 // $$ = DeclarationNode::newName( 0 ); // unimplemented 1665 SemanticError( yylloc, "Typedef expression is currently unimplemented." ); $$ = nullptr; 1666 } 1667 ; 1668 1669 //c_declaration: 1670 // declaring_list pop ';' 1671 // | typedef_declaration pop ';' 1672 // | typedef_expression pop ';' // GCC, naming expression type 1673 // | sue_declaration_specifier pop ';' 1674 // ; 1675 // 1676 //declaring_list: 1677 // // A semantic check is required to ensure asm_name only appears on declarations with implicit or explicit static 1678 // // storage-class 1679 // declarator asm_name_opt initializer_opt 1680 // { 1681 // typedefTable.addToEnclosingScope( IDENTIFIER ); 1682 // $$ = ( $2->addType( $1 ))->addAsmName( $3 )->addInitializer( $4 ); 1683 // } 1684 // | declaring_list ',' attribute_list_opt declarator asm_name_opt initializer_opt 1685 // { 1686 // typedefTable.addToEnclosingScope( IDENTIFIER ); 1687 // $$ = $1->appendList( $1->cloneBaseType( $4->addAsmName( $5 )->addInitializer( $6 ) ) ); 1688 // } 1689 // ; 1684 SemanticError( yylloc, "Typedef expression is deprecated, use typeof(...) instead." ); $$ = nullptr; 1685 } 1686 ; 1690 1687 1691 1688 c_declaration: … … 1693 1690 { $$ = distAttr( $1, $2 ); } 1694 1691 | typedef_declaration 1695 | typedef_expression // GCC, naming expression type1692 | typedef_expression // deprecated GCC, naming expression type 1696 1693 | sue_declaration_specifier 1697 1694 ; … … 2072 2069 { yyy = true; $$ = AggregateDecl::Union; } 2073 2070 | EXCEPTION // CFA 2074 { yyy = true; $$ = AggregateDecl::Exception; } 2071 // { yyy = true; $$ = AggregateDecl::Exception; } 2072 { SemanticError( yylloc, "exception aggregate is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; } 2075 2073 ; 2076 2074 2077 2075 aggregate_control: // CFA 2078 GENERATOR 2079 { yyy = true; $$ = AggregateDecl::Coroutine; } 2076 MONITOR 2077 { yyy = true; $$ = AggregateDecl::Monitor; } 2078 | MUTEX STRUCT 2079 { yyy = true; $$ = AggregateDecl::Monitor; } 2080 | GENERATOR 2081 { yyy = true; $$ = AggregateDecl::Generator; } 2082 | MUTEX GENERATOR 2083 { SemanticError( yylloc, "monitor generator is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; } 2080 2084 | COROUTINE 2081 2085 { yyy = true; $$ = AggregateDecl::Coroutine; } 2082 | M ONITOR2083 { yyy = true; $$ = AggregateDecl::Monitor; }2086 | MUTEX COROUTINE 2087 { SemanticError( yylloc, "monitor coroutine is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; } 2084 2088 | THREAD 2085 2089 { yyy = true; $$ = AggregateDecl::Thread; } 2090 | MUTEX THREAD 2091 { SemanticError( yylloc, "monitor thread is currently unimplemented." ); $$ = AggregateDecl::NoAggregate; } 2086 2092 ; 2087 2093 … … 2406 2412 // Overloading: function, data, and operator identifiers may be overloaded. 2407 2413 // 2408 // Type declarations: " type" is used to generate new types for declaring objects. Similarly, "dtype" is used for object2414 // Type declarations: "otype" is used to generate new types for declaring objects. Similarly, "dtype" is used for object 2409 2415 // and incomplete types, and "ftype" is used for function types. Type declarations with initializers provide 2410 2416 // definitions of new types. Type declarations with storage class "extern" provide opaque types. … … 2435 2441 type_class identifier_or_type_name 2436 2442 { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); } 2437 type_initializer_opt assertion_list_opt2443 type_initializer_opt assertion_list_opt 2438 2444 { $$ = DeclarationNode::newTypeParam( $1, $2 )->addTypeInitializer( $4 )->addAssertions( $5 ); } 2439 2445 | type_specifier identifier_parameter_declarator … … 2462 2468 assertion 2463 2469 | assertion_list assertion 2464 { $$ = $1 ? $1->appendList( $2 ) : $2; }2470 { $$ = $1->appendList( $2 ); } 2465 2471 ; 2466 2472 … … 2749 2755 | attr_name 2750 2756 { $$ = DeclarationNode::newAttribute( $1 ); } 2751 | attr_name '(' argument_expression_list ')'2757 | attr_name '(' argument_expression_list_opt ')' 2752 2758 { $$ = DeclarationNode::newAttribute( $1, $3 ); } 2753 2759 ; -
src/ResolvExpr/AdjustExprType.cc
r3c64c668 r58fe85a 100 100 101 101 namespace { 102 struct AdjustExprType_new final : public ast::WithShortCircuiting { 102 class AdjustExprType_new final : public ast::WithShortCircuiting { 103 const ast::SymbolTable & symtab; 104 public: 103 105 const ast::TypeEnvironment & tenv; 104 const ast::SymbolTable & symtab;105 106 106 107 AdjustExprType_new( const ast::TypeEnvironment & e, const ast::SymbolTable & syms ) 107 : tenv( e ), symtab( syms) {}108 : symtab( syms ), tenv( e ) {} 108 109 109 void pre mutate( const ast::VoidType * ) { visit_children = false; }110 void pre mutate( const ast::BasicType * ) { visit_children = false; }111 void pre mutate( const ast::PointerType * ) { visit_children = false; }112 void pre mutate( const ast::ArrayType * ) { visit_children = false; }113 void pre mutate( const ast::FunctionType * ) { visit_children = false; }114 void pre mutate( const ast::StructInstType * ) { visit_children = false; }115 void pre mutate( const ast::UnionInstType * ) { visit_children = false; }116 void pre mutate( const ast::EnumInstType * ) { visit_children = false; }117 void pre mutate( const ast::TraitInstType * ) { visit_children = false; }118 void pre mutate( const ast::TypeInstType * ) { visit_children = false; }119 void pre mutate( const ast::TupleType * ) { visit_children = false; }120 void pre mutate( const ast::VarArgsType * ) { visit_children = false; }121 void pre mutate( const ast::ZeroType * ) { visit_children = false; }122 void pre mutate( const ast::OneType * ) { visit_children = false; }110 void previsit( const ast::VoidType * ) { visit_children = false; } 111 void previsit( const ast::BasicType * ) { visit_children = false; } 112 void previsit( const ast::PointerType * ) { visit_children = false; } 113 void previsit( const ast::ArrayType * ) { visit_children = false; } 114 void previsit( const ast::FunctionType * ) { visit_children = false; } 115 void previsit( const ast::StructInstType * ) { visit_children = false; } 116 void previsit( const ast::UnionInstType * ) { visit_children = false; } 117 void previsit( const ast::EnumInstType * ) { visit_children = false; } 118 void previsit( const ast::TraitInstType * ) { visit_children = false; } 119 void previsit( const ast::TypeInstType * ) { visit_children = false; } 120 void previsit( const ast::TupleType * ) { visit_children = false; } 121 void previsit( const ast::VarArgsType * ) { visit_children = false; } 122 void previsit( const ast::ZeroType * ) { visit_children = false; } 123 void previsit( const ast::OneType * ) { visit_children = false; } 123 124 124 const ast::Type * post mutate( const ast::ArrayType * at ) {125 const ast::Type * postvisit( const ast::ArrayType * at ) { 125 126 return new ast::PointerType{ at->base, at->qualifiers }; 126 127 } 127 128 128 const ast::Type * post mutate( const ast::FunctionType * ft ) {129 const ast::Type * postvisit( const ast::FunctionType * ft ) { 129 130 return new ast::PointerType{ ft }; 130 131 } 131 132 132 const ast::Type * post mutate( const ast::TypeInstType * inst ) {133 const ast::Type * postvisit( const ast::TypeInstType * inst ) { 133 134 // replace known function-type-variables with pointer-to-function 134 if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name) ) {135 if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) { 135 136 if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) { 136 137 return new ast::PointerType{ inst }; -
src/ResolvExpr/AlternativeFinder.cc
r3c64c668 r58fe85a 131 131 132 132 void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt ) { 133 Indenter indent = { indentAmt }; 134 for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) { 135 i->print( os, indent ); 136 os << std::endl; 133 std::vector<std::string> sorted; 134 sorted.reserve(list.size()); 135 for(const auto & c : list) { 136 std::stringstream ss; 137 c.print( ss, indentAmt ); 138 sorted.push_back(ss.str()); 139 } 140 141 std::sort(sorted.begin(), sorted.end()); 142 143 for ( const auto & s : sorted ) { 144 os << s << std::endl; 137 145 } 138 146 } … … 251 259 SemanticError( expr, "No reasonable alternatives for expression " ); 252 260 } 253 if ( mode. satisfyAssns || mode.prune ) {261 if ( mode.prune ) { 254 262 // trim candidates just to those where the assertions resolve 255 263 // - necessary pre-requisite to pruning … … 1216 1224 unify( castExpr->result, alt.expr->result, alt.env, needAssertions, 1217 1225 haveAssertions, openVars, indexer ); 1218 Cost thisCost = castCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), 1219 indexer, alt.env ); 1226 Cost thisCost = 1227 castExpr->isGenerated 1228 ? conversionCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), indexer, alt.env ) 1229 : castCost( alt.expr->result, castExpr->result, alt.expr->get_lvalue(), indexer, alt.env ); 1220 1230 PRINT( 1221 1231 std::cerr << "working on cast with result: " << castExpr->result << std::endl; … … 1698 1708 1699 1709 // unification run for side-effects 1700 unify( toType, alt.expr->result, newEnv, need, have, openVars, indexer ); 1710 bool canUnify = unify( toType, alt.expr->result, newEnv, need, have, openVars, indexer ); 1711 (void) canUnify; 1701 1712 // xxx - do some inspecting on this line... why isn't result bound to initAlt.type? 1702 1713 1703 Cost thisCost = c astCost( alt.expr->result, toType, alt.expr->get_lvalue(),1714 Cost thisCost = computeConversionCost( alt.expr->result, toType, alt.expr->get_lvalue(), 1704 1715 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 1705 1729 if ( thisCost != Cost::infinity ) { 1706 1730 // count one safe conversion for each value that is thrown away -
src/ResolvExpr/Candidate.cpp
r3c64c668 r58fe85a 41 41 42 42 void print( std::ostream & os, const CandidateList & cands, Indenter indent ) { 43 for ( const CandidateRef & cand : cands ) { 44 print( os, *cand, indent ); 45 os << std::endl; 43 std::vector<std::string> sorted; 44 sorted.reserve(cands.size()); 45 for(const auto & c : cands) { 46 std::stringstream ss; 47 print( ss, *c, indent ); 48 sorted.push_back(ss.str()); 49 } 50 51 std::sort(sorted.begin(), sorted.end()); 52 53 for ( const auto & s : sorted ) { 54 os << s << std::endl; 46 55 } 47 56 } -
src/ResolvExpr/Candidate.hpp
r3c64c668 r58fe85a 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() {} 53 : expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() { 54 assert(x->result); 55 } 54 56 55 57 Candidate( const Candidate & o, const ast::Expr * x, const Cost & addedCost = Cost::zero ) 56 58 : expr( x ), cost( o.cost + addedCost ), cvtCost( Cost::zero ), env( o.env ), open( o.open ), 57 need( o.need ) {} 59 need( o.need ) { 60 assert(x->result); 61 } 58 62 59 63 Candidate( 60 const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 64 const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 61 65 const ast::AssertionSet & n, const Cost & c, const Cost & cvt = Cost::zero ) 62 : expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) {} 66 : expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) { 67 assert(x->result); 68 } 63 69 64 70 Candidate( … … 66 72 ast::AssertionSet && n, const Cost & c, const Cost & cvt = Cost::zero ) 67 73 : expr( x ), cost( c ), cvtCost( cvt ), env( std::move( e ) ), open( std::move( o ) ), 68 need( n.begin(), n.end() ) {} 74 need( n.begin(), n.end() ) { 75 assert(x->result); 76 } 69 77 }; 70 78 -
src/ResolvExpr/CandidateFinder.cpp
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Wed Jun 5 14:30:00 2019 11 // Last Modified By : A aron B. Moss12 // Last Modified On : Wed Jun 5 14:30:00 201913 // Update Count : 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 1 14:55:00 2019 13 // Update Count : 2 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 getPointerBase 46 47 #include "Common/Stats/Counter.h" 45 48 46 49 #define PRINT( text ) if ( resolvep ) { text } … … 54 57 return new ast::CastExpr{ expr, expr->result->stripReferences() }; 55 58 } 56 59 57 60 return expr; 58 61 } … … 61 64 UniqueId globalResnSlot = 0; 62 65 63 Cost computeConversionCost( 64 const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab,65 const ast:: TypeEnvironment & env66 Cost computeConversionCost( 67 const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue, 68 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env 66 69 ) { 67 70 PRINT( … … 74 77 std::cerr << std::endl; 75 78 ) 76 Cost convCost = conversionCost( argType, paramType, symtab, env );79 Cost convCost = conversionCost( argType, paramType, argIsLvalue, symtab, env ); 77 80 PRINT( 78 81 std::cerr << std::endl << "cost is " << convCost << std::endl; … … 107 110 108 111 /// Computes conversion cost for a given expression to a given type 109 const ast::Expr * computeExpressionConversionCost( 110 const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost 112 const ast::Expr * computeExpressionConversionCost( 113 const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost 111 114 ) { 112 Cost convCost = computeConversionCost( arg->result, paramType, symtab, env ); 115 Cost convCost = computeConversionCost( 116 arg->result, paramType, arg->get_lvalue(), symtab, env ); 113 117 outCost += convCost; 114 118 115 // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires 116 // conversion. Ignore poly cost for now, since this requires resolution of the cast to 119 // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires 120 // conversion. Ignore poly cost for now, since this requires resolution of the cast to 117 121 // infer parameters and this does not currently work for the reason stated below 118 122 Cost tmpCost = convCost; … … 123 127 return new ast::CastExpr{ arg, newType }; 124 128 125 // xxx - *should* be able to resolve this cast, but at the moment pointers are not 126 // castable to zero_t, but are implicitly convertible. This is clearly inconsistent, 129 // xxx - *should* be able to resolve this cast, but at the moment pointers are not 130 // castable to zero_t, but are implicitly convertible. This is clearly inconsistent, 127 131 // once this is fixed it should be possible to resolve the cast. 128 // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable, 129 // but it shouldn't be because this makes the conversion from DT* to DT* since 132 // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable, 133 // but it shouldn't be because this makes the conversion from DT* to DT* since 130 134 // commontype(zero_t, DT*) is DT*, rather than nothing 131 135 132 136 // CandidateFinder finder{ symtab, env }; 133 137 // finder.find( arg, ResolvMode::withAdjustment() ); 134 // assertf( finder.candidates.size() > 0, 138 // assertf( finder.candidates.size() > 0, 135 139 // "Somehow castable expression failed to find alternatives." ); 136 // assertf( finder.candidates.size() == 1, 140 // assertf( finder.candidates.size() == 1, 137 141 // "Somehow got multiple alternatives for known cast expression." ); 138 142 // return finder.candidates.front()->expr; … … 143 147 144 148 /// Computes conversion cost for a given candidate 145 Cost computeApplicationConversionCost( 146 CandidateRef cand, const ast::SymbolTable & symtab 149 Cost computeApplicationConversionCost( 150 CandidateRef cand, const ast::SymbolTable & symtab 147 151 ) { 148 152 auto appExpr = cand->expr.strict_as< ast::ApplicationExpr >(); … … 167 171 if ( function->isVarArgs ) { 168 172 convCost.incUnsafe(); 169 PRINT( std::cerr << "end of params with varargs function: inc unsafe: " 173 PRINT( std::cerr << "end of params with varargs function: inc unsafe: " 170 174 << convCost << std::endl; ; ) 171 175 // convert reference-typed expressions into value-typed expressions 172 cand->expr = ast::mutate_field_index( 173 appExpr, &ast::ApplicationExpr::args, i, 176 cand->expr = ast::mutate_field_index( 177 appExpr, &ast::ApplicationExpr::args, i, 174 178 referenceToRvalueConversion( args[i], convCost ) ); 175 179 continue; … … 180 184 // Default arguments should be free - don't include conversion cost. 181 185 // Unwrap them here because they are not relevant to the rest of the system 182 cand->expr = ast::mutate_field_index( 186 cand->expr = ast::mutate_field_index( 183 187 appExpr, &ast::ApplicationExpr::args, i, def->expr ); 184 188 ++param; … … 187 191 188 192 // mark conversion cost and also specialization cost of param type 189 const ast::Type * paramType = (*param)->get_type();190 cand->expr = ast::mutate_field_index( 191 appExpr, &ast::ApplicationExpr::args, i, 192 computeExpressionConversionCost( 193 args[i], paramType, symtab, cand->env, convCost ) );194 convCost.decSpec( specCost( paramType) );193 // const ast::Type * paramType = (*param)->get_type(); 194 cand->expr = ast::mutate_field_index( 195 appExpr, &ast::ApplicationExpr::args, i, 196 computeExpressionConversionCost( 197 args[i], *param, symtab, cand->env, convCost ) ); 198 convCost.decSpec( specCost( *param ) ); 195 199 ++param; // can't be in for-loop update because of the continue 196 200 } … … 198 202 if ( param != params.end() ) return Cost::infinity; 199 203 200 // specialization cost of return types can't be accounted for directly, it disables 204 // specialization cost of return types can't be accounted for directly, it disables 201 205 // otherwise-identical calls, like this example based on auto-newline in the I/O lib: 202 206 // … … 208 212 // mark type variable and specialization cost of forall clause 209 213 convCost.incVar( function->forall.size() ); 210 for ( const ast::TypeDecl * td : function->forall ) { 211 convCost.decSpec( td->assertions.size() ); 212 } 214 convCost.decSpec( function->assertions.size() ); 213 215 214 216 return convCost; 215 217 } 216 218 217 void makeUnifiableVars( 218 const ast:: ParameterizedType * type, ast::OpenVarSet & unifiableVars,219 ast::AssertionSet & need 219 void makeUnifiableVars( 220 const ast::FunctionType * type, ast::OpenVarSet & unifiableVars, 221 ast::AssertionSet & need 220 222 ) { 221 for ( const ast::TypeDecl *tyvar : type->forall ) {222 unifiableVars[ tyvar->name ] = ast::TypeDecl::Data{ tyvar};223 for ( const ast::DeclWithType * assn : tyvar->assertions ) {224 need[ assn ].isUsed = true;225 }223 for ( auto & tyvar : type->forall ) { 224 unifiableVars[ *tyvar ] = ast::TypeDecl::Data{ tyvar->base }; 225 } 226 for ( auto & assn : type->assertions ) { 227 need[ assn ].isUsed = true; 226 228 } 227 229 } … … 254 256 255 257 ArgPack() 256 : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ), 258 : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ), 257 259 tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {} 258 259 ArgPack( 260 const ast::TypeEnvironment & env, const ast::AssertionSet & need, 260 261 ArgPack( 262 const ast::TypeEnvironment & env, const ast::AssertionSet & need, 261 263 const ast::AssertionSet & have, const ast::OpenVarSet & open ) 262 : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ), 264 : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ), 263 265 open( open ), nextArg( 0 ), tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {} 264 266 265 267 ArgPack( 266 std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env, 267 ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open, 268 unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero, 268 std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env, 269 ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open, 270 unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero, 269 271 unsigned nextExpl = 0, unsigned explAlt = 0 ) 270 272 : parent(parent), expr( expr ), cost( cost ), env( move( env ) ), need( move( need ) ), 271 273 have( move( have ) ), open( move( open ) ), nextArg( nextArg ), tupleStart( tupleStart ), 272 274 nextExpl( nextExpl ), explAlt( explAlt ) {} 273 275 274 276 ArgPack( 275 const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need, 277 const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need, 276 278 ast::AssertionSet && have, ast::OpenVarSet && open, unsigned nextArg, Cost added ) 277 : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ), 278 need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ), 279 : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ), 280 need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ), 279 281 tupleStart( o.tupleStart ), nextExpl( 0 ), explAlt( 0 ) {} 280 282 281 283 /// true if this pack is in the middle of an exploded argument 282 284 bool hasExpl() const { return nextExpl > 0; } … … 286 288 return args[ nextArg-1 ][ explAlt ]; 287 289 } 288 290 289 291 /// Ends a tuple expression, consolidating the appropriate args 290 292 void endTuple( const std::vector< ArgPack > & packs ) { … … 307 309 308 310 /// Instantiates an argument to match a parameter, returns false if no matching results left 309 bool instantiateArgument( 310 const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args, 311 std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab, 312 unsigned nTuples = 0 311 bool instantiateArgument( 312 const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args, 313 std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab, 314 unsigned nTuples = 0 313 315 ) { 314 316 if ( auto tupleType = dynamic_cast< const ast::TupleType * >( paramType ) ) { … … 318 320 // xxx - dropping initializer changes behaviour from previous, but seems correct 319 321 // ^^^ need to handle the case where a tuple has a default argument 320 if ( ! instantiateArgument( 322 if ( ! instantiateArgument( 321 323 type, nullptr, args, results, genStart, symtab, nTuples ) ) return false; 322 324 nTuples = 0; … … 329 331 } else if ( const ast::TypeInstType * ttype = Tuples::isTtype( paramType ) ) { 330 332 // paramType is a ttype, consumes all remaining arguments 331 333 332 334 // completed tuples; will be spliced to end of results to finish 333 335 std::vector< ArgPack > finalResults{}; … … 342 344 for ( std::size_t i = genStart; i < genEnd; ++i ) { 343 345 unsigned nextArg = results[i].nextArg; 344 346 345 347 // use next element of exploded tuple if present 346 348 if ( results[i].hasExpl() ) { … … 352 354 results.emplace_back( 353 355 i, expl.exprs[ results[i].nextExpl ], copy( results[i].env ), 354 copy( results[i].need ), copy( results[i].have ), 356 copy( results[i].need ), copy( results[i].have ), 355 357 copy( results[i].open ), nextArg, nTuples, Cost::zero, nextExpl, 356 358 results[i].explAlt ); … … 370 372 // push empty tuple expression 371 373 newResult.parent = i; 372 std::vector< ast::ptr< ast::Expr > > emptyList; 373 newResult.expr = 374 new ast::TupleExpr{ CodeLocation{}, move( emptyList ) }; 374 newResult.expr = new ast::TupleExpr{ CodeLocation{}, {} }; 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(); 550 return genEnd != results.size(); // were any new results added? 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 struct Finder final : public ast::WithShortCircuiting { 596 class Finder final : public ast::WithShortCircuiting { 597 const ast::SymbolTable & symtab; 598 public: 599 static size_t traceId; 597 600 CandidateFinder & selfFinder; 598 const ast::SymbolTable & symtab;599 601 CandidateList & candidates; 600 602 const ast::TypeEnvironment & tenv; 601 603 ast::ptr< ast::Type > & targetType; 602 604 605 enum Errors { 606 NotFound, 607 NoMatch, 608 ArgsToFew, 609 ArgsToMany, 610 RetsToFew, 611 RetsToMany, 612 NoReason 613 }; 614 615 struct { 616 Errors code = NotFound; 617 } reason; 618 603 619 Finder( CandidateFinder & f ) 604 : s elfFinder( f ), symtab( f.symtab ), candidates( f.candidates ), tenv( f.env ),620 : symtab( f.localSyms ), selfFinder( f ), candidates( f.candidates ), tenv( f.env ), 605 621 targetType( f.targetType ) {} 606 622 607 623 void previsit( const ast::Node * ) { visit_children = false; } 608 624 … … 611 627 void addCandidate( Args &&... args ) { 612 628 candidates.emplace_back( new Candidate{ std::forward<Args>( args )... } ); 629 reason.code = NoReason; 613 630 } 614 631 … … 639 656 640 657 /// Completes a function candidate with arguments located 641 void validateFunctionCandidate( 642 const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results, 643 CandidateList & out 658 void validateFunctionCandidate( 659 const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results, 660 CandidateList & out 644 661 ) { 645 ast::ApplicationExpr * appExpr = 662 ast::ApplicationExpr * appExpr = 646 663 new ast::ApplicationExpr{ func->expr->location, func->expr }; 647 664 // sum cost and accumulate arguments … … 657 674 appExpr->args = move( vargs ); 658 675 // build and validate new candidate 659 auto newCand = 676 auto newCand = 660 677 std::make_shared<Candidate>( appExpr, result.env, result.open, result.need, cost ); 661 678 PRINT( … … 669 686 /// Builds a list of candidates for a function, storing them in out 670 687 void makeFunctionCandidates( 671 const CandidateRef & func, const ast::FunctionType * funcType, 688 const CandidateRef & func, const ast::FunctionType * funcType, 672 689 const ExplodedArgs_new & args, CandidateList & out 673 690 ) { … … 676 693 ast::TypeEnvironment funcEnv{ func->env }; 677 694 makeUnifiableVars( funcType, funcOpen, funcNeed ); 678 // add all type variables as open variables now so that those not used in the parameter679 // list are still considered open695 // add all type variables as open variables now so that those not used in the 696 // parameter list are still considered open 680 697 funcEnv.add( funcType->forall ); 681 698 682 699 if ( targetType && ! targetType->isVoid() && ! funcType->returns.empty() ) { 683 700 // attempt to narrow based on expected target type 684 const ast::Type * returnType = funcType->returns.front() ->get_type();685 if ( ! unify( 686 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 701 const ast::Type * returnType = funcType->returns.front(); 702 if ( ! unify( 703 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 687 704 ) { 688 705 // unification failed, do not pursue this candidate … … 696 713 std::size_t genStart = 0; 697 714 698 for ( const ast::DeclWithType * param : funcType->params ) { 699 auto obj = strict_dynamic_cast< const ast::ObjectDecl * >( param ); 700 // Try adding the arguments corresponding to the current parameter to the existing 715 // xxx - how to handle default arg after change to ftype representation? 716 if (const ast::VariableExpr * varExpr = func->expr.as<ast::VariableExpr>()) { 717 if (const ast::FunctionDecl * funcDecl = varExpr->var.as<ast::FunctionDecl>()) { 718 // function may have default args only if directly calling by name 719 // must use types on candidate however, due to RenameVars substitution 720 auto nParams = funcType->params.size(); 721 722 for (size_t i=0; i<nParams; ++i) { 723 auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>(); 724 if (!instantiateArgument( 725 funcType->params[i], obj->init, args, results, genStart, symtab)) return; 726 } 727 goto endMatch; 728 } 729 } 730 for ( const auto & param : funcType->params ) { 731 // Try adding the arguments corresponding to the current parameter to the existing 701 732 // matches 702 if ( ! instantiateArgument( 703 obj->type, obj->init, args, results, genStart, symtab ) ) return; 704 } 705 733 // no default args for indirect calls 734 if ( ! instantiateArgument( 735 param, nullptr, args, results, genStart, symtab ) ) return; 736 } 737 738 endMatch: 706 739 if ( funcType->isVarArgs ) { 707 740 // append any unused arguments to vararg pack … … 750 783 if ( expl.exprs.empty() ) { 751 784 results.emplace_back( 752 results[i], move( env ), copy( results[i].need ), 753 copy( results[i].have ), move( open ), nextArg + 1, 785 results[i], move( env ), copy( results[i].need ), 786 copy( results[i].have ), move( open ), nextArg + 1, 754 787 expl.cost ); 755 788 … … 760 793 results.emplace_back( 761 794 i, expl.exprs.front(), move( env ), copy( results[i].need ), 762 copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost, 795 copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost, 763 796 expl.exprs.size() == 1 ? 0 : 1, j ); 764 797 } … … 780 813 /// Adds implicit struct-conversions to the alternative list 781 814 void addAnonConversions( const CandidateRef & cand ) { 782 // adds anonymous member interpretations whenever an aggregate value type is seen. 783 // it's okay for the aggregate expression to have reference type -- cast it to the 815 // adds anonymous member interpretations whenever an aggregate value type is seen. 816 // it's okay for the aggregate expression to have reference type -- cast it to the 784 817 // base type to treat the aggregate as the referenced value 785 818 ast::ptr< ast::Expr > aggrExpr( cand->expr ); 786 819 ast::ptr< ast::Type > & aggrType = aggrExpr.get_and_mutate()->result; 787 820 cand->env.apply( aggrType ); 788 821 789 822 if ( aggrType.as< ast::ReferenceType >() ) { 790 823 aggrExpr = new ast::CastExpr{ aggrExpr, aggrType->stripReferences() }; … … 799 832 800 833 /// Adds aggregate member interpretations 801 void addAggMembers( 802 const ast:: ReferenceToType * aggrInst, const ast::Expr * expr,803 const Candidate & cand, const Cost & addedCost, const std::string & name 834 void addAggMembers( 835 const ast::BaseInstType * aggrInst, const ast::Expr * expr, 836 const Candidate & cand, const Cost & addedCost, const std::string & name 804 837 ) { 805 838 for ( const ast::Decl * decl : aggrInst->lookup( name ) ) { 806 839 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl ); 807 CandidateRef newCand = std::make_shared<Candidate>( 840 CandidateRef newCand = std::make_shared<Candidate>( 808 841 cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost ); 809 // add anonymous member interpretations whenever an aggregate value type is seen 842 // add anonymous member interpretations whenever an aggregate value type is seen 810 843 // as a member expression 811 844 addAnonConversions( newCand ); … … 815 848 816 849 /// Adds tuple member interpretations 817 void addTupleMembers( 818 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 819 const Cost & addedCost, const ast::Expr * member 850 void addTupleMembers( 851 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 852 const Cost & addedCost, const ast::Expr * member 820 853 ) { 821 854 if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) { 822 // get the value of the constant expression as an int, must be between 0 and the 855 // get the value of the constant expression as an int, must be between 0 and the 823 856 // length of the tuple to have meaning 824 857 long long val = constantExpr->intValue(); 825 858 if ( val >= 0 && (unsigned long long)val < tupleType->size() ) { 826 859 addCandidate( 827 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 860 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 828 861 addedCost ); 829 862 } … … 832 865 833 866 void postvisit( const ast::UntypedExpr * untypedExpr ) { 834 CandidateFinder funcFinder{ symtab, tenv }; 835 funcFinder.find( untypedExpr->func, ResolvMode::withAdjustment() ); 836 // short-circuit if no candidates 837 if ( funcFinder.candidates.empty() ) return; 838 839 std::vector< CandidateFinder > argCandidates = 867 std::vector< CandidateFinder > argCandidates = 840 868 selfFinder.findSubExprs( untypedExpr->args ); 841 869 842 870 // take care of possible tuple assignments 843 871 // if not tuple assignment, handled as normal function call 844 872 Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates ); 873 874 CandidateFinder funcFinder{ symtab, tenv }; 875 if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) { 876 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name); 877 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) { 878 assertf(!argCandidates.empty(), "special function call without argument"); 879 for (auto & firstArgCand: argCandidates[0]) { 880 ast::ptr<ast::Type> argType = firstArgCand->expr->result; 881 firstArgCand->env.apply(argType); 882 // strip references 883 // xxx - is this correct? 884 while (argType.as<ast::ReferenceType>()) argType = argType.as<ast::ReferenceType>()->base; 885 886 // convert 1-tuple to plain type 887 if (auto tuple = argType.as<ast::TupleType>()) { 888 if (tuple->size() == 1) { 889 argType = tuple->types[0]; 890 } 891 } 892 893 // if argType is an unbound type parameter, all special functions need to be searched. 894 if (isUnboundType(argType)) { 895 funcFinder.otypeKeys.clear(); 896 break; 897 } 898 899 if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer); 900 else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type)); 901 } 902 } 903 } 904 // if candidates are already produced, do not fail 905 // xxx - is it possible that handleTupleAssignment and main finder both produce candidates? 906 // this means there exists ctor/assign functions with a tuple as first parameter. 907 ResolvMode mode = { 908 true, // adjust 909 !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name 910 selfFinder.candidates.empty() // failfast if other options are not found 911 }; 912 funcFinder.find( untypedExpr->func, mode ); 913 // short-circuit if no candidates 914 // if ( funcFinder.candidates.empty() ) return; 915 916 reason.code = NoMatch; 845 917 846 918 // find function operators … … 877 949 if ( auto function = pointer->base.as< ast::FunctionType >() ) { 878 950 CandidateRef newFunc{ new Candidate{ *func } }; 879 newFunc->expr = 951 newFunc->expr = 880 952 referenceToRvalueConversion( newFunc->expr, newFunc->cost ); 881 953 makeFunctionCandidates( newFunc, function, argExpansions, found ); 882 954 } 883 } else if ( 884 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 955 } else if ( 956 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 885 957 ) { 886 if ( const ast::EqvClass * clz = func->env.lookup( inst->name) ) {958 if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) { 887 959 if ( auto function = clz->bound.as< ast::FunctionType >() ) { 888 960 CandidateRef newFunc{ new Candidate{ *func } }; 889 newFunc->expr = 961 newFunc->expr = 890 962 referenceToRvalueConversion( newFunc->expr, newFunc->cost ); 891 963 makeFunctionCandidates( newFunc, function, argExpansions, found ); … … 901 973 std::vector< ExplodedArg > funcE; 902 974 funcE.reserve( funcFinder.candidates.size() ); 903 for ( const CandidateRef & func : funcFinder ) { 975 for ( const CandidateRef & func : funcFinder ) { 904 976 funcE.emplace_back( *func, symtab ); 905 977 } … … 913 985 if ( auto function = pointer->base.as< ast::FunctionType >() ) { 914 986 CandidateRef newOp{ new Candidate{ *op} }; 915 newOp->expr = 987 newOp->expr = 916 988 referenceToRvalueConversion( newOp->expr, newOp->cost ); 917 989 makeFunctionCandidates( newOp, function, argExpansions, found ); … … 922 994 } 923 995 924 // Implement SFINAE; resolution errors are only errors if there aren't any non-error 996 // Implement SFINAE; resolution errors are only errors if there aren't any non-error 925 997 // candidates 926 998 if ( found.empty() && ! errors.isEmpty() ) { throw errors; } … … 934 1006 auto pointer = appExpr->func->result.strict_as< ast::PointerType >(); 935 1007 auto function = pointer->base.strict_as< ast::FunctionType >(); 936 1008 937 1009 std::cerr << "Case +++++++++++++ " << appExpr->func << std::endl; 938 1010 std::cerr << "parameters are:" << std::endl; … … 957 1029 promoteCvtCost( winners ); 958 1030 959 // function may return a struct/union value, in which case we need to add candidates 960 // for implicit conversions to each of the anonymous members, which must happen after 1031 // function may return a struct/union value, in which case we need to add candidates 1032 // for implicit conversions to each of the anonymous members, which must happen after 961 1033 // `findMinCost`, since anon conversions are never the cheapest 962 1034 for ( const CandidateRef & c : winners ) { … … 966 1038 967 1039 if ( candidates.empty() && targetType && ! targetType->isVoid() ) { 968 // If resolution is unsuccessful with a target type, try again without, since it 1040 // If resolution is unsuccessful with a target type, try again without, since it 969 1041 // will sometimes succeed when it wouldn't with a target type binding. 970 1042 // For example: … … 983 1055 /// true if expression is an lvalue 984 1056 static bool isLvalue( const ast::Expr * x ) { 985 return x->result && ( x-> result->is_lvalue() || x->result.as< ast::ReferenceType >() );1057 return x->result && ( x->get_lvalue() || x->result.as< ast::ReferenceType >() ); 986 1058 } 987 1059 … … 989 1061 CandidateFinder finder{ symtab, tenv }; 990 1062 finder.find( addressExpr->arg ); 1063 1064 if( finder.candidates.empty() ) return; 1065 1066 reason.code = NoMatch; 1067 991 1068 for ( CandidateRef & r : finder.candidates ) { 992 1069 if ( ! isLvalue( r->expr ) ) continue; … … 1003 1080 assert( toType ); 1004 1081 toType = resolveTypeof( toType, symtab ); 1005 toType = SymTab::validateType( castExpr->location, toType, symtab );1082 // toType = SymTab::validateType( castExpr->location, toType, symtab ); 1006 1083 toType = adjustExprType( toType, tenv, symtab ); 1007 1084 1008 1085 CandidateFinder finder{ symtab, tenv, toType }; 1009 1086 finder.find( castExpr->arg, ResolvMode::withAdjustment() ); 1087 1088 if( !finder.candidates.empty() ) reason.code = NoMatch; 1010 1089 1011 1090 CandidateList matches; … … 1016 1095 cand->env.extractOpenVars( open ); 1017 1096 1018 // It is possible that a cast can throw away some values in a multiply-valued 1019 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the 1020 // subexpression results that are cast directly. The candidate is invalid if it 1097 // It is possible that a cast can throw away some values in a multiply-valued 1098 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the 1099 // subexpression results that are cast directly. The candidate is invalid if it 1021 1100 // has fewer results than there are types to cast to. 1022 1101 int discardedValues = cand->expr->result->size() - toType->size(); … … 1025 1104 // unification run for side-effects 1026 1105 unify( toType, cand->expr->result, cand->env, need, have, open, symtab ); 1027 Cost thisCost = castCost( cand->expr->result, toType, symtab, cand->env ); 1106 Cost thisCost = 1107 (castExpr->isGenerated == ast::GeneratedFlag::GeneratedCast) 1108 ? conversionCost( cand->expr->result, toType, cand->expr->get_lvalue(), symtab, cand->env ) 1109 : castCost( cand->expr->result, toType, cand->expr->get_lvalue(), symtab, cand->env ); 1110 1028 1111 PRINT( 1029 1112 std::cerr << "working on cast with result: " << toType << std::endl; … … 1037 1120 // count one safe conversion for each value that is thrown away 1038 1121 thisCost.incSafe( discardedValues ); 1039 CandidateRef newCand = std::make_shared<Candidate>( 1040 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1041 copy( cand->env ), move( open ), move( need ), cand->cost, 1122 CandidateRef newCand = std::make_shared<Candidate>( 1123 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1124 copy( cand->env ), move( open ), move( need ), cand->cost, 1042 1125 cand->cost + thisCost ); 1043 1126 inferParameters( newCand, matches ); … … 1057 1140 finder.find( castExpr->arg, ResolvMode::withoutPrune() ); 1058 1141 for ( CandidateRef & r : finder.candidates ) { 1059 addCandidate( 1060 *r, 1142 addCandidate( 1143 *r, 1061 1144 new ast::VirtualCastExpr{ castExpr->location, r->expr, castExpr->result } ); 1062 1145 } 1146 } 1147 1148 void postvisit( const ast::KeywordCastExpr * castExpr ) { 1149 const auto & loc = castExpr->location; 1150 assertf( castExpr->result, "Cast target should have been set in Validate." ); 1151 auto ref = castExpr->result.strict_as<ast::ReferenceType>(); 1152 auto inst = ref->base.strict_as<ast::StructInstType>(); 1153 auto target = inst->base.get(); 1154 1155 CandidateFinder finder{ symtab, tenv }; 1156 1157 auto pick_alternatives = [target, this](CandidateList & found, bool expect_ref) { 1158 for(auto & cand : found) { 1159 const ast::Type * expr = cand->expr->result.get(); 1160 if(expect_ref) { 1161 auto res = dynamic_cast<const ast::ReferenceType*>(expr); 1162 if(!res) { continue; } 1163 expr = res->base.get(); 1164 } 1165 1166 if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) { 1167 auto td = cand->env.lookup(*insttype); 1168 if(!td) { continue; } 1169 expr = td->bound.get(); 1170 } 1171 1172 if(auto base = dynamic_cast<const ast::StructInstType*>(expr)) { 1173 if(base->base == target) { 1174 candidates.push_back( std::move(cand) ); 1175 reason.code = NoReason; 1176 } 1177 } 1178 } 1179 }; 1180 1181 try { 1182 // Attempt 1 : turn (thread&)X into ($thread&)X.__thrd 1183 // Clone is purely for memory management 1184 std::unique_ptr<const ast::Expr> tech1 { new ast::UntypedMemberExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.field), castExpr->arg) }; 1185 1186 // don't prune here, since it's guaranteed all alternatives will have the same type 1187 finder.find( tech1.get(), ResolvMode::withoutPrune() ); 1188 pick_alternatives(finder.candidates, false); 1189 1190 return; 1191 } catch(SemanticErrorException & ) {} 1192 1193 // Fallback : turn (thread&)X into ($thread&)get_thread(X) 1194 std::unique_ptr<const ast::Expr> fallback { ast::UntypedExpr::createDeref(loc, new ast::UntypedExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.getter), { castExpr->arg })) }; 1195 // don't prune here, since it's guaranteed all alternatives will have the same type 1196 finder.find( fallback.get(), ResolvMode::withoutPrune() ); 1197 1198 pick_alternatives(finder.candidates, true); 1199 1200 // Whatever happens here, we have no more fallbacks 1063 1201 } 1064 1202 … … 1067 1205 aggFinder.find( memberExpr->aggregate, ResolvMode::withAdjustment() ); 1068 1206 for ( CandidateRef & agg : aggFinder.candidates ) { 1069 // it's okay for the aggregate expression to have reference type -- cast it to the 1207 // it's okay for the aggregate expression to have reference type -- cast it to the 1070 1208 // base type to treat the aggregate as the referenced value 1071 1209 Cost addedCost = Cost::zero; … … 1074 1212 // find member of the given type 1075 1213 if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) { 1076 addAggMembers( 1214 addAggMembers( 1077 1215 structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) ); 1078 1216 } else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) { 1079 addAggMembers( 1217 addAggMembers( 1080 1218 unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) ); 1081 1219 } else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) { … … 1090 1228 1091 1229 void postvisit( const ast::NameExpr * nameExpr ) { 1092 std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( nameExpr->name ); 1230 std::vector< ast::SymbolTable::IdData > declList; 1231 if (!selfFinder.otypeKeys.empty()) { 1232 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name); 1233 assertf(kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS, "special lookup with non-special target: %s", nameExpr->name.c_str()); 1234 1235 for (auto & otypeKey: selfFinder.otypeKeys) { 1236 auto result = symtab.specialLookupId(kind, otypeKey); 1237 declList.insert(declList.end(), std::make_move_iterator(result.begin()), std::make_move_iterator(result.end())); 1238 } 1239 } 1240 else { 1241 declList = symtab.lookupId( nameExpr->name ); 1242 } 1093 1243 PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; ) 1244 1245 if( declList.empty() ) return; 1246 1247 reason.code = NoMatch; 1248 1094 1249 for ( auto & data : declList ) { 1095 1250 Cost cost = Cost::zero; … … 1097 1252 1098 1253 CandidateRef newCand = std::make_shared<Candidate>( 1099 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 1254 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 1100 1255 cost ); 1101 1256 PRINT( … … 1107 1262 std::cerr << std::endl; 1108 1263 ) 1109 newCand->expr = ast::mutate_field( 1110 newCand->expr.get(), &ast::Expr::result, 1264 newCand->expr = ast::mutate_field( 1265 newCand->expr.get(), &ast::Expr::result, 1111 1266 renameTyVars( newCand->expr->result ) ); 1112 // add anonymous member interpretations whenever an aggregate value type is seen 1267 // add anonymous member interpretations whenever an aggregate value type is seen 1113 1268 // as a name expression 1114 1269 addAnonConversions( newCand ); … … 1120 1275 // not sufficient to just pass `variableExpr` here, type might have changed since 1121 1276 // creation 1122 addCandidate( 1277 addCandidate( 1123 1278 new ast::VariableExpr{ variableExpr->location, variableExpr->var }, tenv ); 1124 1279 } … … 1130 1285 void postvisit( const ast::SizeofExpr * sizeofExpr ) { 1131 1286 if ( sizeofExpr->type ) { 1132 addCandidate( 1133 new ast::SizeofExpr{ 1134 sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 1287 addCandidate( 1288 new ast::SizeofExpr{ 1289 sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 1135 1290 tenv ); 1136 1291 } else { … … 1141 1296 CandidateList winners = findMinCost( finder.candidates ); 1142 1297 if ( winners.size() != 1 ) { 1143 SemanticError( 1298 SemanticError( 1144 1299 sizeofExpr->expr.get(), "Ambiguous expression in sizeof operand: " ); 1145 1300 } … … 1154 1309 void postvisit( const ast::AlignofExpr * alignofExpr ) { 1155 1310 if ( alignofExpr->type ) { 1156 addCandidate( 1157 new ast::AlignofExpr{ 1158 alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 1311 addCandidate( 1312 new ast::AlignofExpr{ 1313 alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 1159 1314 tenv ); 1160 1315 } else { … … 1165 1320 CandidateList winners = findMinCost( finder.candidates ); 1166 1321 if ( winners.size() != 1 ) { 1167 SemanticError( 1322 SemanticError( 1168 1323 alignofExpr->expr.get(), "Ambiguous expression in alignof operand: " ); 1169 1324 } … … 1172 1327 choice->expr = referenceToRvalueConversion( choice->expr, choice->cost ); 1173 1328 choice->cost = Cost::zero; 1174 addCandidate( 1329 addCandidate( 1175 1330 *choice, new ast::AlignofExpr{ alignofExpr->location, choice->expr } ); 1176 1331 } … … 1178 1333 1179 1334 void postvisit( const ast::UntypedOffsetofExpr * offsetofExpr ) { 1180 const ast:: ReferenceToType * aggInst;1335 const ast::BaseInstType * aggInst; 1181 1336 if (( aggInst = offsetofExpr->type.as< ast::StructInstType >() )) ; 1182 1337 else if (( aggInst = offsetofExpr->type.as< ast::UnionInstType >() )) ; … … 1185 1340 for ( const ast::Decl * member : aggInst->lookup( offsetofExpr->member ) ) { 1186 1341 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( member ); 1187 addCandidate( 1342 addCandidate( 1188 1343 new ast::OffsetofExpr{ offsetofExpr->location, aggInst, dwt }, tenv ); 1189 1344 } … … 1206 1361 finder2.find( logicalExpr->arg2, ResolvMode::withAdjustment() ); 1207 1362 if ( finder2.candidates.empty() ) return; 1363 1364 reason.code = NoMatch; 1208 1365 1209 1366 for ( const CandidateRef & r1 : finder1.candidates ) { … … 1218 1375 1219 1376 addCandidate( 1220 new ast::LogicalExpr{ 1377 new ast::LogicalExpr{ 1221 1378 logicalExpr->location, r1->expr, r2->expr, logicalExpr->isAnd }, 1222 1379 move( env ), move( open ), move( need ), r1->cost + r2->cost ); … … 1240 1397 finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() ); 1241 1398 if ( finder3.candidates.empty() ) return; 1399 1400 reason.code = NoMatch; 1242 1401 1243 1402 for ( const CandidateRef & r1 : finder1.candidates ) { … … 1256 1415 ast::AssertionSet have; 1257 1416 1258 // unify true and false results, then infer parameters to produce new 1417 // unify true and false results, then infer parameters to produce new 1259 1418 // candidates 1260 1419 ast::ptr< ast::Type > common; 1261 if ( 1262 unify( 1263 r2->expr->result, r3->expr->result, env, need, have, open, symtab, 1264 common ) 1420 if ( 1421 unify( 1422 r2->expr->result, r3->expr->result, env, need, have, open, symtab, 1423 common ) 1265 1424 ) { 1266 1425 // generate typed expression 1267 ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 1426 ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 1268 1427 conditionalExpr->location, r1->expr, r2->expr, r3->expr }; 1269 1428 newExpr->result = common ? common : r2->expr->result; 1270 1429 // convert both options to result type 1271 1430 Cost cost = r1->cost + r2->cost + r3->cost; 1272 newExpr->arg2 = computeExpressionConversionCost( 1431 newExpr->arg2 = computeExpressionConversionCost( 1273 1432 newExpr->arg2, newExpr->result, symtab, env, cost ); 1274 1433 newExpr->arg3 = computeExpressionConversionCost( … … 1287 1446 ast::TypeEnvironment env{ tenv }; 1288 1447 ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, symtab, env ); 1289 1448 1290 1449 CandidateFinder finder2{ symtab, env }; 1291 1450 finder2.find( commaExpr->arg2, ResolvMode::withAdjustment() ); … … 1317 1476 finder2.find( rangeExpr->high, ResolvMode::withAdjustment() ); 1318 1477 if ( finder2.candidates.empty() ) return; 1478 1479 reason.code = NoMatch; 1319 1480 1320 1481 for ( const CandidateRef & r1 : finder1.candidates ) { … … 1330 1491 1331 1492 ast::ptr< ast::Type > common; 1332 if ( 1333 unify( 1334 r1->expr->result, r2->expr->result, env, need, have, open, symtab, 1335 common ) 1493 if ( 1494 unify( 1495 r1->expr->result, r2->expr->result, env, need, have, open, symtab, 1496 common ) 1336 1497 ) { 1337 1498 // generate new expression 1338 ast::RangeExpr * newExpr = 1499 ast::RangeExpr * newExpr = 1339 1500 new ast::RangeExpr{ rangeExpr->location, r1->expr, r2->expr }; 1340 1501 newExpr->result = common ? common : r1->expr->result; 1341 1502 // add candidate 1342 1503 CandidateRef newCand = std::make_shared<Candidate>( 1343 newExpr, move( env ), move( open ), move( need ), 1504 newExpr, move( env ), move( open ), move( need ), 1344 1505 r1->cost + r2->cost ); 1345 1506 inferParameters( newCand, candidates ); … … 1350 1511 1351 1512 void postvisit( const ast::UntypedTupleExpr * tupleExpr ) { 1352 std::vector< CandidateFinder > subCandidates = 1513 std::vector< CandidateFinder > subCandidates = 1353 1514 selfFinder.findSubExprs( tupleExpr->exprs ); 1354 1515 std::vector< CandidateList > possibilities; … … 1370 1531 1371 1532 addCandidate( 1372 new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 1533 new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 1373 1534 move( env ), move( open ), move( need ), sumCost( subs ) ); 1374 1535 } … … 1410 1571 // calculate target type 1411 1572 const ast::Type * toType = resolveTypeof( initAlt.type, symtab ); 1412 toType = SymTab::validateType( initExpr->location, toType, symtab );1573 // toType = SymTab::validateType( initExpr->location, toType, symtab ); 1413 1574 toType = adjustExprType( toType, tenv, symtab ); 1414 // The call to find must occur inside this loop, otherwise polymorphic return 1415 // types are not bound to the initialization type, since return type variables are 1416 // only open for the duration of resolving the UntypedExpr. 1575 // The call to find must occur inside this loop, otherwise polymorphic return 1576 // types are not bound to the initialization type, since return type variables are 1577 // only open for the duration of resolving the UntypedExpr. 1417 1578 CandidateFinder finder{ symtab, tenv, toType }; 1418 1579 finder.find( initExpr->expr, ResolvMode::withAdjustment() ); 1419 1580 for ( CandidateRef & cand : finder.candidates ) { 1581 if(reason.code == NotFound) reason.code = NoMatch; 1582 1420 1583 ast::TypeEnvironment env{ cand->env }; 1421 1584 ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have; … … 1426 1589 ) 1427 1590 1428 // It is possible that a cast can throw away some values in a multiply-valued 1429 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of 1430 // the subexpression results that are cast directly. The candidate is invalid 1591 // It is possible that a cast can throw away some values in a multiply-valued 1592 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of 1593 // the subexpression results that are cast directly. The candidate is invalid 1431 1594 // if it has fewer results than there are types to cast to. 1432 1595 int discardedValues = cand->expr->result->size() - toType->size(); … … 1434 1597 1435 1598 // unification run for side-effects 1436 unify( toType, cand->expr->result, env, need, have, open, symtab ); 1437 Cost thisCost = castCost( cand->expr->result, toType, symtab, env ); 1438 1599 bool canUnify = unify( toType, cand->expr->result, env, need, have, open, symtab ); 1600 (void) canUnify; 1601 Cost thisCost = computeConversionCost( cand->expr->result, toType, cand->expr->get_lvalue(), 1602 symtab, env ); 1603 PRINT( 1604 Cost legacyCost = castCost( cand->expr->result, toType, cand->expr->get_lvalue(), 1605 symtab, env ); 1606 std::cerr << "Considering initialization:"; 1607 std::cerr << std::endl << " FROM: " << cand->expr->result << std::endl; 1608 std::cerr << std::endl << " TO: " << toType << std::endl; 1609 std::cerr << std::endl << " Unification " << (canUnify ? "succeeded" : "failed"); 1610 std::cerr << std::endl << " Legacy cost " << legacyCost; 1611 std::cerr << std::endl << " New cost " << thisCost; 1612 std::cerr << std::endl; 1613 ) 1439 1614 if ( thisCost != Cost::infinity ) { 1440 1615 // count one safe conversion for each value that is thrown away 1441 1616 thisCost.incSafe( discardedValues ); 1442 CandidateRef newCand = std::make_shared<Candidate>( 1443 new ast::InitExpr{ 1444 initExpr->location, restructureCast( cand->expr, toType ), 1445 initAlt.designation }, 1446 copy( cand->env), move( open ), move( need ), cand->cost, thisCost );1617 CandidateRef newCand = std::make_shared<Candidate>( 1618 new ast::InitExpr{ 1619 initExpr->location, restructureCast( cand->expr, toType ), 1620 initAlt.designation }, 1621 move(env), move( open ), move( need ), cand->cost, thisCost ); 1447 1622 inferParameters( newCand, matches ); 1448 1623 } 1449 1624 } 1625 1450 1626 } 1451 1627 … … 1469 1645 }; 1470 1646 1471 /// Prunes a list of candidates down to those that have the minimum conversion cost for a given 1647 // size_t Finder::traceId = Stats::Heap::new_stacktrace_id("Finder"); 1648 /// Prunes a list of candidates down to those that have the minimum conversion cost for a given 1472 1649 /// return type. Skips ambiguous candidates. 1473 CandidateList pruneCandidates( CandidateList & candidates ) { 1474 struct PruneStruct { 1475 CandidateRef candidate; 1476 bool ambiguous; 1477 1478 PruneStruct() = default; 1479 PruneStruct( const CandidateRef & c ) : candidate( c ), ambiguous( false ) {} 1480 }; 1481 1482 // find lowest-cost candidate for each type 1483 std::unordered_map< std::string, PruneStruct > selected; 1484 for ( CandidateRef & candidate : candidates ) { 1485 std::string mangleName; 1650 1651 } // anonymous namespace 1652 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) 1486 1702 { 1487 ast::ptr< ast::Type > newType = candidate->expr->result; 1488 candidate->env.apply( newType ); 1703 ast::ptr< ast::Type > newType = newCand->expr->result; 1704 assertf(newCand->expr->result, "Result of expression %p for candidate is null", newCand->expr.get()); 1705 newCand->env.apply( newType ); 1489 1706 mangleName = Mangle::mangle( newType ); 1490 1707 } 1491 1492 1708 auto found = selected.find( mangleName ); 1493 1709 if ( found != selected.end() ) { 1494 if ( candidate->cost < found->second.candidate->cost ) {1710 if ( newCand->cost < found->second.candidate->cost ) { 1495 1711 PRINT( 1496 std::cerr << "cost " << candidate->cost << " beats "1712 std::cerr << "cost " << newCand->cost << " beats " 1497 1713 << found->second.candidate->cost << std::endl; 1498 1714 ) 1499 1715 1500 found->second = PruneStruct{ candidate};1501 } else if ( candidate->cost == found->second.candidate->cost ) {1502 // if one of the candidates contains a deleted identifier, can pick the other, 1503 // since deleted expressions should not be ambiguous if there is another option 1716 found->second = PruneStruct{ newCand }; 1717 } else if ( newCand->cost == found->second.candidate->cost ) { 1718 // if one of the candidates contains a deleted identifier, can pick the other, 1719 // since deleted expressions should not be ambiguous if there is another option 1504 1720 // that is at least as good 1505 if ( findDeletedExpr( candidate->expr ) ) {1721 if ( findDeletedExpr( newCand->expr ) ) { 1506 1722 // do nothing 1507 1723 PRINT( std::cerr << "candidate is deleted" << std::endl; ) 1508 1724 } else if ( findDeletedExpr( found->second.candidate->expr ) ) { 1509 1725 PRINT( std::cerr << "current is deleted" << std::endl; ) 1510 found->second = PruneStruct{ candidate};1726 found->second = PruneStruct{ newCand }; 1511 1727 } else { 1512 1728 PRINT( std::cerr << "marking ambiguous" << std::endl; ) 1513 1729 found->second.ambiguous = true; 1514 1730 } 1515 } else { 1731 } else { 1732 // xxx - can satisfyAssertions increase the cost? 1516 1733 PRINT( 1517 std::cerr << "cost " << candidate->cost << " loses to "1734 std::cerr << "cost " << newCand->cost << " loses to " 1518 1735 << found->second.candidate->cost << std::endl; 1519 ) 1736 ) 1520 1737 } 1521 1738 } else { 1522 selected.emplace_hint( found, mangleName, candidate ); 1523 } 1524 } 1525 1526 // report unambiguous min-cost candidates 1527 CandidateList out; 1528 for ( auto & target : selected ) { 1529 if ( target.second.ambiguous ) continue; 1530 1531 CandidateRef cand = target.second.candidate; 1532 1533 ast::ptr< ast::Type > newResult = cand->expr->result; 1534 cand->env.applyFree( newResult ); 1535 cand->expr = ast::mutate_field( 1536 cand->expr.get(), &ast::Expr::result, move( newResult ) ); 1537 1538 out.emplace_back( cand ); 1539 } 1540 return out; 1739 selected.emplace_hint( found, mangleName, newCand ); 1740 } 1741 } 1541 1742 } 1542 1743 1543 } // anonymous namespace 1744 // report unambiguous min-cost candidates 1745 // CandidateList out; 1746 for ( auto & target : selected ) { 1747 if ( target.second.ambiguous ) continue; 1748 1749 CandidateRef cand = target.second.candidate; 1750 1751 ast::ptr< ast::Type > newResult = cand->expr->result; 1752 cand->env.applyFree( newResult ); 1753 cand->expr = ast::mutate_field( 1754 cand->expr.get(), &ast::Expr::result, move( newResult ) ); 1755 1756 out.emplace_back( cand ); 1757 } 1758 // if everything is lost in satisfyAssertions, report the error 1759 return !selected.empty(); 1760 } 1544 1761 1545 1762 void CandidateFinder::find( const ast::Expr * expr, ResolvMode mode ) { … … 1549 1766 1550 1767 if ( mode.failFast && candidates.empty() ) { 1551 SemanticError( expr, "No reasonable alternatives for expression " ); 1768 switch(finder.core.reason.code) { 1769 case Finder::NotFound: 1770 { SemanticError( expr, "No alternatives for expression " ); break; } 1771 case Finder::NoMatch: 1772 { SemanticError( expr, "Invalid application of existing declaration(s) in expression " ); break; } 1773 case Finder::ArgsToFew: 1774 case Finder::ArgsToMany: 1775 case Finder::RetsToFew: 1776 case Finder::RetsToMany: 1777 case Finder::NoReason: 1778 default: 1779 { SemanticError( expr->location, "No reasonable alternatives for expression : reasons unkown" ); } 1780 } 1552 1781 } 1553 1782 1783 /* 1554 1784 if ( mode.satisfyAssns || mode.prune ) { 1555 1785 // trim candidates to just those where the assertions are satisfiable … … 1558 1788 std::vector< std::string > errors; 1559 1789 for ( CandidateRef & candidate : candidates ) { 1560 satisfyAssertions( candidate, symtab, satisfied, errors );1790 satisfyAssertions( candidate, localSyms, satisfied, errors ); 1561 1791 } 1562 1792 … … 1574 1804 candidates = move( satisfied ); 1575 1805 } 1806 */ 1576 1807 1577 1808 if ( mode.prune ) { … … 1582 1813 ) 1583 1814 1584 CandidateList pruned = pruneCandidates( candidates ); 1585 1815 CandidateList pruned; 1816 std::vector<std::string> errors; 1817 bool found = pruneCandidates( candidates, pruned, errors ); 1818 1586 1819 if ( mode.failFast && pruned.empty() ) { 1587 1820 std::ostringstream stream; 1588 CandidateList winners = findMinCost( candidates ); 1589 stream << "Cannot choose between " << winners.size() << " alternatives for " 1590 "expression\n"; 1591 ast::print( stream, expr ); 1592 stream << " Alternatives are:\n"; 1593 print( stream, winners, 1 ); 1594 SemanticError( expr->location, stream.str() ); 1821 if (found) { 1822 CandidateList winners = findMinCost( candidates ); 1823 stream << "Cannot choose between " << winners.size() << " alternatives for " 1824 "expression\n"; 1825 ast::print( stream, expr ); 1826 stream << " Alternatives are:\n"; 1827 print( stream, winners, 1 ); 1828 SemanticError( expr->location, stream.str() ); 1829 } 1830 else { 1831 stream << "No alternatives with satisfiable assertions for " << expr << "\n"; 1832 for ( const auto& err : errors ) { 1833 stream << err; 1834 } 1835 SemanticError( expr->location, stream.str() ); 1836 } 1595 1837 } 1596 1838 … … 1602 1844 ) 1603 1845 PRINT( 1604 std::cerr << "there are " << candidates.size() << " alternatives after elimination" 1846 std::cerr << "there are " << candidates.size() << " alternatives after elimination" 1605 1847 << std::endl; 1606 1848 ) 1607 1849 } 1608 1850 1609 // adjust types after pruning so that types substituted by pruneAlternatives are correctly 1851 // adjust types after pruning so that types substituted by pruneAlternatives are correctly 1610 1852 // adjusted 1611 1853 if ( mode.adjust ) { 1612 1854 for ( CandidateRef & r : candidates ) { 1613 r->expr = ast::mutate_field( 1614 r->expr.get(), &ast::Expr::result, 1615 adjustExprType( r->expr->result, r->env, symtab) );1855 r->expr = ast::mutate_field( 1856 r->expr.get(), &ast::Expr::result, 1857 adjustExprType( r->expr->result, r->env, localSyms ) ); 1616 1858 } 1617 1859 } … … 1625 1867 } 1626 1868 1627 std::vector< CandidateFinder > CandidateFinder::findSubExprs( 1628 const std::vector< ast::ptr< ast::Expr > > & xs 1869 std::vector< CandidateFinder > CandidateFinder::findSubExprs( 1870 const std::vector< ast::ptr< ast::Expr > > & xs 1629 1871 ) { 1630 1872 std::vector< CandidateFinder > out; 1631 1873 1632 1874 for ( const auto & x : xs ) { 1633 out.emplace_back( symtab, env );1875 out.emplace_back( localSyms, env ); 1634 1876 out.back().find( x, ResolvMode::withAdjustment() ); 1635 1877 1636 1878 PRINT( 1637 1879 std::cerr << "findSubExprs" << std::endl; -
src/ResolvExpr/CandidateFinder.hpp
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Wed Jun 5 14:30:00 2019 11 // Last Modified By : A aron B. Moss12 // Last Modified On : Wed Jun 5 14:30:00 201913 // Update Count : 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 1 9:51:00 2019 13 // Update Count : 2 14 14 // 15 15 … … 28 28 struct CandidateFinder { 29 29 CandidateList candidates; ///< List of candidate resolutions 30 const ast::SymbolTable & symtab; ///< Symbol table to lookup candidates30 const ast::SymbolTable & localSyms; ///< 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 key 33 34 34 CandidateFinder( 35 const ast::SymbolTable & sym tab, const ast::TypeEnvironment & env,35 CandidateFinder( 36 const ast::SymbolTable & syms, const ast::TypeEnvironment & env, 36 37 const ast::Type * tt = nullptr ) 37 : candidates(), symtab( symtab), env( env ), targetType( tt ) {}38 : candidates(), localSyms( syms ), env( env ), targetType( tt ) {} 38 39 39 40 /// Fill candidates with feasible resolutions for `expr` 40 41 void find( const ast::Expr * expr, ResolvMode mode = {} ); 42 bool pruneCandidates( CandidateList & candidates, CandidateList & out, std::vector<std::string> & errors ); 41 43 42 44 /// Runs new candidate finder on each element in xs, returning the list of finders … … 49 51 iterator begin() { return candidates.begin(); } 50 52 const_iterator begin() const { return candidates.begin(); } 51 53 52 54 iterator end() { return candidates.end(); } 53 55 const_iterator end() const { return candidates.end(); } … … 55 57 56 58 /// Computes conversion cost between two types 57 Cost computeConversionCost( 58 const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab,59 const ast:: TypeEnvironment & env );59 Cost computeConversionCost( 60 const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue, 61 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ); 60 62 61 63 } // namespace ResolvExpr -
src/ResolvExpr/CastCost.cc
r3c64c668 r58fe85a 10 10 // Created On : Sun May 17 06:57:43 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T hu Aug 8 16:12:00 201913 // Update Count : 812 // Last Modified On : Tue Oct 4 15:00:00 2019 13 // Update Count : 9 14 14 // 15 15 … … 142 142 143 143 CastCost_new( 144 const ast::Type * dst, const ast::SymbolTable & symtab,144 const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab, 145 145 const ast::TypeEnvironment & env, CostCalculation costFunc ) 146 : ConversionCost_new( dst, s ymtab, env, costFunc ) {}146 : ConversionCost_new( dst, srcIsLvalue, 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 ymtab, env );154 cost = conversionCost( basicType, dst, srcIsLvalue, symtab, env ); 155 155 } 156 156 } … … 165 165 } else { 166 166 ast::TypeEnvironment newEnv{ env }; 167 if ( auto wParams = pointerType->base.as< ast:: ParameterizedType >() ) {167 if ( auto wParams = pointerType->base.as< ast::FunctionType >() ) { 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 & env 194 ) { return castCost( src, dst, srcIsLvalue, symtab, env ); } 185 195 } // anonymous namespace 186 196 197 198 187 199 Cost castCost( 188 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,189 const ast:: TypeEnvironment & env200 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 201 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env 190 202 ) { 191 203 if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 192 if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name) ) {204 if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) { 193 205 // check cast cost against bound type, if present 194 206 if ( eqvClass->bound ) { 195 return castCost( src, eqvClass->bound, s ymtab, env );207 return castCost( src, eqvClass->bound, srcIsLvalue, symtab, env ); 196 208 } else { 197 209 return Cost::infinity; … … 201 213 auto type = strict_dynamic_cast< const ast::TypeDecl * >( named ); 202 214 if ( type->base ) { 203 return castCost( src, type->base, s ymtab, env ) + Cost::safe;215 return castCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe; 204 216 } 205 217 } … … 224 236 #warning cast on ptrsCastable artifact of having two functions, remove when port done 225 237 return convertToReferenceCost( 226 src, refType, symtab, env, 227 ( int (*)( 228 const ast::Type *, const ast::Type *, const ast::SymbolTable &, 229 const ast::TypeEnvironment & ) 230 ) ptrsCastable ); 238 src, refType, srcIsLvalue, symtab, env, localPtrsCastable ); 231 239 } else { 232 240 #warning cast on castCost artifact of having two functions, remove when port done 233 ast::Pass< CastCost_new > converter{ 234 dst, symtab, env, 235 ( Cost (*)( 236 const ast::Type *, const ast::Type *, const ast::SymbolTable &, 237 const ast::TypeEnvironment & ) 238 ) castCost }; 241 ast::Pass< CastCost_new > converter( 242 dst, srcIsLvalue, symtab, env, localCastCost ); 239 243 src->accept( converter ); 240 return converter. pass.cost;244 return converter.core.cost; 241 245 } 242 246 } -
src/ResolvExpr/CommonType.cc
r3c64c668 r58fe85a 666 666 const ast::OpenVarSet & open; 667 667 public: 668 static size_t traceId; 668 669 ast::ptr< ast::Type > result; 669 670 … … 712 713 const ast::Type * base = oPtr->base; 713 714 if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) { 714 auto entry = open.find( var->name);715 auto entry = open.find( *var ); 715 716 if ( entry != open.end() ) { 716 717 ast::AssertionSet need, have; … … 893 894 }; 894 895 896 // size_t CommonType_new::traceId = Stats::Heap::new_stacktrace_id("CommonType_new"); 895 897 namespace { 896 898 ast::ptr< ast::Type > handleReference( … … 939 941 ast::ptr< ast::Type > result; 940 942 const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >(); 941 const ast::ReferenceType * ref2 = type 1.as< ast::ReferenceType >();943 const ast::ReferenceType * ref2 = type2.as< ast::ReferenceType >(); 942 944 943 945 if ( depth1 > depth2 ) { … … 966 968 ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open }; 967 969 type1->accept( visitor ); 968 ast::ptr< ast::Type > result = visitor. pass.result;970 ast::ptr< ast::Type > result = visitor.core.result; 969 971 970 972 // handling for opaque type declarations (?) -
src/ResolvExpr/ConversionCost.cc
r3c64c668 r58fe85a 10 10 // Created On : Sun May 17 07:06:19 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Aug 12 10:21:00 201913 // Update Count : 2 712 // Last Modified On : Wed Jul 29 16:11:00 2020 13 // Update Count : 28 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 } // if399 } // if400 }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 } // if407 } // if408 }409 410 394 void ConversionCost::postvisit( const EnumInstType * ) { 411 395 static Type::Qualifiers q; … … 497 481 } 498 482 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 & env509 ) { return conversionCost( src, dst, symtab, env );}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 & env 492 ) { return conversionCost( src, dst, srcIsLvalue, symtab, env ); } 493 } 510 494 511 495 Cost conversionCost( 512 const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,513 const ast:: TypeEnvironment & env496 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 497 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env 514 498 ) { 515 499 if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) { 516 if ( const ast::EqvClass * eqv = env.lookup( inst->name) ) {500 if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) { 517 501 if ( eqv->bound ) { 518 return conversionCost(src, eqv->bound, s ymtab, env );502 return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env ); 519 503 } else { 520 504 return Cost::infinity; … … 524 508 assertf( type, "Unexpected typedef." ); 525 509 if ( type->base ) { 526 return conversionCost( src, type->base, s ymtab, env ) + Cost::safe;510 return conversionCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe; 527 511 } 528 512 } … … 534 518 } else if ( const ast::ReferenceType * refType = 535 519 dynamic_cast< const ast::ReferenceType * >( dst ) ) { 536 return convertToReferenceCost( src, refType, s ymtab, env, localPtrsAssignable );520 return convertToReferenceCost( src, refType, srcIsLvalue, symtab, env, localPtrsAssignable ); 537 521 } else { 538 ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost ); 539 src->accept( converter ); 540 return converter.pass.cost; 541 } 542 } 543 544 static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, 522 return ast::Pass<ConversionCost_new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost ); 523 } 524 } 525 526 static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 545 527 int diff, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, 546 NumCostCalculation func ) {528 PtrsCalculation func ) { 547 529 if ( 0 < diff ) { 548 530 Cost cost = convertToReferenceCost( 549 strict_dynamic_cast< const ast::ReferenceType * >( src )->base, 550 dst, (diff - 1), symtab, env, func );531 strict_dynamic_cast< const ast::ReferenceType * >( src )->base, dst, 532 srcIsLvalue, (diff - 1), symtab, env, func ); 551 533 cost.incReference(); 552 534 return cost; … … 554 536 Cost cost = convertToReferenceCost( 555 537 src, strict_dynamic_cast< const ast::ReferenceType * >( dst )->base, 556 (diff + 1), symtab, env, func );538 srcIsLvalue, (diff + 1), symtab, env, func ); 557 539 cost.incReference(); 558 540 return cost; … … 579 561 } 580 562 } else { 581 ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost ); 582 src->accept( converter ); 583 return converter.pass.cost; 563 return ast::Pass<ConversionCost_new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost ); 584 564 } 585 565 } else { … … 588 568 assert( dstAsRef ); 589 569 if ( typesCompatibleIgnoreQualifiers( src, dstAsRef->base, symtab, env ) ) { 590 if ( src ->is_lvalue()) {570 if ( srcIsLvalue ) { 591 571 if ( src->qualifiers == dstAsRef->base->qualifiers ) { 592 572 return Cost::reference; … … 607 587 608 588 Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dst, 609 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,610 NumCostCalculation func ) {589 bool srcIsLvalue, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, 590 PtrsCalculation func ) { 611 591 int sdepth = src->referenceDepth(), ddepth = dst->referenceDepth(); 612 return convertToReferenceCost( src, dst, s depth - ddepth, symtab, env, func );592 return convertToReferenceCost( src, dst, srcIsLvalue, sdepth - ddepth, symtab, env, func ); 613 593 } 614 594 … … 667 647 assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) ); 668 648 669 cost = costCalc( refType->base, dst, s ymtab, env );649 cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env ); 670 650 if ( refType->base->qualifiers == dst->qualifiers ) { 671 651 cost.incReference(); … … 681 661 } 682 662 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 701 663 void ConversionCost_new::postvisit( const ast::EnumInstType * enumInstType ) { 702 664 (void)enumInstType; 703 static const ast::BasicType integer( ast::BasicType::SignedInt );704 cost = costCalc( &integer, dst, symtab, env );665 static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicType::SignedInt ) }; 666 cost = costCalc( integer, dst, srcIsLvalue, symtab, env ); 705 667 if ( cost < Cost::unsafe ) { 706 668 cost.incSafe(); … … 713 675 714 676 void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) { 715 if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {716 cost = costCalc( eqv->bound, dst, s ymtab, env );677 if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) { 678 cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env ); 717 679 } else if ( const ast::TypeInstType * dstAsInst = 718 680 dynamic_cast< const ast::TypeInstType * >( dst ) ) { 719 if ( typeInstType->name == dstAsInst->name) {681 if ( *typeInstType == *dstAsInst ) { 720 682 cost = Cost::zero; 721 683 } … … 724 686 assertf( type, "Unexpected typedef."); 725 687 if ( type->base ) { 726 cost = costCalc( type->base, dst, s ymtab, env ) + Cost::safe;688 cost = costCalc( type->base, dst, srcIsLvalue, symtab, env ) + Cost::safe; 727 689 } 728 690 } … … 737 699 auto dstEnd = dstAsTuple->types.end(); 738 700 while ( srcIt != srcEnd && dstIt != dstEnd ) { 739 Cost newCost = costCalc( * srcIt++, * dstIt++, s ymtab, env );701 Cost newCost = costCalc( * srcIt++, * dstIt++, srcIsLvalue, symtab, env ); 740 702 if ( newCost == Cost::infinity ) { 741 703 return; … … 772 734 cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] ); 773 735 } 736 } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) { 737 cost = Cost::zero; 738 // +1 for zero_t ->, +1 for disambiguation 739 cost.incSafe( maxIntCost + 2 ); 774 740 } 775 741 } … … 789 755 cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] ); 790 756 } 791 } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) { 792 cost = Cost::zero; 793 cost.incSafe( maxIntCost + 2 ); 794 } 795 } 796 757 } 758 } 759 // size_t ConversionCost_new::traceId = Stats::Heap::new_stacktrace_id("ConversionCost"); 797 760 798 761 } // namespace ResolvExpr -
src/ResolvExpr/ConversionCost.h
r3c64c668 r58fe85a 10 10 // Created On : Sun May 17 09:37:28 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Thu Aug 8 16:13:00 201913 // Update Count : 612 // Last Modified On : Wed Jul 29 16:12:00 2020 13 // Update Count : 7 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 );55 53 void postvisit( const EnumInstType * aggregateUseType ); 56 54 void postvisit( const TraitInstType * aggregateUseType ); … … 74 72 75 73 // Some function pointer types, differ in return type. 76 using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, 74 using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, bool, 77 75 const ast::SymbolTable &, const ast::TypeEnvironment &)>; 78 using NumCostCalculation = std::function<int(const ast::Type *, const ast::Type *,76 using PtrsCalculation = std::function<int(const ast::Type *, const ast::Type *, 79 77 const ast::SymbolTable &, const ast::TypeEnvironment &)>; 80 78 … … 83 81 protected: 84 82 const ast::Type * dst; 83 bool srcIsLvalue; 85 84 const ast::SymbolTable & symtab; 86 85 const ast::TypeEnvironment & env; 87 86 CostCalculation costCalc; 88 87 public: 88 static size_t traceId; 89 89 Cost cost; 90 Cost result() { return cost; } 90 91 91 ConversionCost_new( const ast::Type * dst, const ast::SymbolTable & symtab,92 ConversionCost_new( const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab, 92 93 const ast::TypeEnvironment & env, CostCalculation costCalc ) : 93 dst( dst ), symtab( symtab ), env( env ), costCalc( costCalc ), cost( Cost::infinity ) 94 dst( dst ), srcIsLvalue( srcIsLvalue ), symtab( symtab ), env( env ), 95 costCalc( costCalc ), cost( Cost::infinity ) 94 96 {} 95 97 … … 102 104 void postvisit( const ast::ReferenceType * refType ); 103 105 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 const ast::SymbolTable & indexer, const ast::TypeEnvironment & env, NumCostCalculation func ); 116 bool srcIsLvalue, const ast::SymbolTable & indexer, const ast::TypeEnvironment & env, 117 PtrsCalculation func ); 117 118 118 119 } // namespace ResolvExpr -
src/ResolvExpr/CurrentObject.cc
r3c64c668 r58fe85a 21 21 #include <string> // for string, operator<<, allocator 22 22 23 #include "AST/Copy.hpp" // for shallowCopy 23 24 #include "AST/Expr.hpp" // for InitAlternative 24 25 #include "AST/GenericSubstitution.hpp" // for genericSubstitution 25 26 #include "AST/Init.hpp" // for Designation 26 27 #include "AST/Node.hpp" // for readonly 28 #include "AST/Print.hpp" // for readonly 27 29 #include "AST/Type.hpp" 28 30 #include "Common/Indenter.h" // for Indenter, operator<< … … 592 594 class SimpleIterator final : public MemberIterator { 593 595 CodeLocation location; 594 readonly< Type >type = nullptr;596 const Type * type = nullptr; 595 597 public: 596 598 SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {} 597 599 598 void setPosition( 599 std::deque< ptr< Expr > >::const_iterator begin, 600 void setPosition( 601 std::deque< ptr< Expr > >::const_iterator begin, 600 602 std::deque< ptr< Expr > >::const_iterator end 601 603 ) override { … … 628 630 class ArrayIterator final : public MemberIterator { 629 631 CodeLocation location; 630 readonly< ArrayType >array = nullptr;631 readonly< Type >base = nullptr;632 const ArrayType * array = nullptr; 633 const Type * base = nullptr; 632 634 size_t index = 0; 633 635 size_t size = 0; … … 637 639 auto res = eval(expr); 638 640 if ( ! res.second ) { 639 SemanticError( location, 641 SemanticError( location, 640 642 toString("Array designator must be a constant expression: ", expr ) ); 641 643 } … … 644 646 645 647 public: 646 ArrayIterator( const CodeLocation & loc, const ArrayType * at ) 648 ArrayIterator( const CodeLocation & loc, const ArrayType * at ) 647 649 : location( loc ), array( at ), base( at->base ) { 648 650 PRINT( std::cerr << "Creating array iterator: " << at << std::endl; ) … … 655 657 656 658 void setPosition( const Expr * expr ) { 657 // need to permit integer-constant-expressions, including: integer constants, 658 // enumeration constants, character constants, sizeof expressions, alignof expressions, 659 // need to permit integer-constant-expressions, including: integer constants, 660 // enumeration constants, character constants, sizeof expressions, alignof expressions, 659 661 // cast expressions 660 662 if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) { … … 662 664 index = constExpr->intValue(); 663 665 } catch ( SemanticErrorException & ) { 664 SemanticError( expr, 666 SemanticError( expr, 665 667 "Constant expression of non-integral type in array designator: " ); 666 668 } 667 669 } else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) { 668 670 setPosition( castExpr->arg ); 669 } else if ( 670 dynamic_cast< const SizeofExpr * >( expr ) 671 || dynamic_cast< const AlignofExpr * >( expr ) 671 } else if ( 672 dynamic_cast< const SizeofExpr * >( expr ) 673 || dynamic_cast< const AlignofExpr * >( expr ) 672 674 ) { 673 675 index = 0; 674 676 } else { 675 assertf( false, 677 assertf( false, 676 678 "bad designator given to ArrayIterator: %s", toString( expr ).c_str() ); 677 679 } 678 680 } 679 681 680 void setPosition( 681 std::deque< ptr< Expr > >::const_iterator begin, 682 void setPosition( 683 std::deque< ptr< Expr > >::const_iterator begin, 682 684 std::deque< ptr< Expr > >::const_iterator end 683 685 ) override { … … 758 760 } 759 761 760 AggregateIterator( 761 const CodeLocation & loc, const std::string k, const std::string & n, const Type * i, 762 AggregateIterator( 763 const CodeLocation & loc, const std::string k, const std::string & n, const Type * i, 762 764 const MemberList & ms ) 763 : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ), 765 : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ), 764 766 sub( genericSubstitution( i ) ) { 765 767 PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; ) … … 768 770 769 771 public: 770 void setPosition( 771 std::deque< ptr< Expr > >::const_iterator begin, 772 void setPosition( 773 std::deque< ptr< Expr > >::const_iterator begin, 772 774 std::deque< ptr< Expr > >::const_iterator end 773 775 ) final { … … 786 788 return; 787 789 } 788 assertf( false, 790 assertf( false, 789 791 "could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() ); 790 792 } else { 791 assertf( false, 793 assertf( false, 792 794 "bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() ); 793 795 } … … 803 805 new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } ); 804 806 // need to substitute for generic types so that casts are to concrete types 807 alt.type = shallowCopy(alt.type.get()); 805 808 PRINT( std::cerr << " type is: " << alt.type; ) 806 809 sub.apply( alt.type ); // also apply to designation?? … … 842 845 for ( InitAlternative & alt : ret ) { 843 846 PRINT( std::cerr << "iterating and adding designators" << std::endl; ) 844 alt.designation.get_and_mutate()->designators.emplace_front( 847 alt.designation.get_and_mutate()->designators.emplace_front( 845 848 new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } ); 846 849 } … … 897 900 class TupleIterator final : public AggregateIterator { 898 901 public: 899 TupleIterator( const CodeLocation & loc, const TupleType * inst ) 900 : AggregateIterator( 901 loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members 902 TupleIterator( const CodeLocation & loc, const TupleType * inst ) 903 : AggregateIterator( 904 loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members 902 905 ) {} 903 906 … … 920 923 921 924 MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type ) { 922 if ( auto aggr = dynamic_cast< const ReferenceToType * >( type ) ) {925 if ( auto aggr = dynamic_cast< const BaseInstType * >( type ) ) { 923 926 if ( auto sit = dynamic_cast< const StructInstType * >( aggr ) ) { 924 927 return new StructIterator{ loc, sit }; … … 926 929 return new UnionIterator{ loc, uit }; 927 930 } else { 928 assertf( 929 dynamic_cast< const EnumInstType * >( aggr )930 || dynamic_cast< const TypeInstType * >( aggr ),931 "Encountered unhandled ReferenceToType in createMemberIterator: %s",931 assertf( 932 dynamic_cast< const EnumInstType * >( type ) 933 || dynamic_cast< const TypeInstType * >( type ), 934 "Encountered unhandled BaseInstType in createMemberIterator: %s", 932 935 toString( type ).c_str() ); 933 936 return new SimpleIterator{ loc, type }; … … 949 952 using DesignatorChain = std::deque< ptr< Expr > >; 950 953 PRINT( std::cerr << "___findNext" << std::endl; ) 951 954 952 955 // find all the d's 953 956 std::vector< DesignatorChain > desigAlts{ {} }, newDesigAlts; … … 962 965 DesignatorChain & d = *dit; 963 966 PRINT( std::cerr << "____actual: " << t << std::endl; ) 964 if ( auto refType = dynamic_cast< const ReferenceToType * >( t ) ) {967 if ( auto refType = dynamic_cast< const BaseInstType * >( t ) ) { 965 968 // concatenate identical field names 966 969 for ( const Decl * mem : refType->lookup( nexpr->name ) ) { … … 1013 1016 // set new designators 1014 1017 assertf( ! objStack.empty(), "empty object stack when setting designation" ); 1015 Designation * actualDesignation = 1018 Designation * actualDesignation = 1016 1019 new Designation{ designation->location, DesignatorChain{d} }; 1017 1020 objStack.back()->setPosition( d ); // destroys d -
src/ResolvExpr/FindOpenVars.cc
r3c64c668 r58fe85a 112 112 // mark open/closed variables 113 113 if ( nextIsOpen ) { 114 for ( const ast::TypeDecl *decl : type->forall ) {115 open[ decl->name ] = ast::TypeDecl::Data{ decl};116 for ( const ast::DeclWithType * assert : decl->assertions ) {117 need[ assert ].isUsed = false;118 }114 for ( auto & decl : type->forall ) { 115 open[ *decl ] = ast::TypeDecl::Data{ decl->base }; 116 } 117 for ( auto & assert : type->assertions ) { 118 need[ assert ].isUsed = false; 119 119 } 120 120 } else { 121 for ( const ast::TypeDecl *decl : type->forall ) {122 closed[ decl->name ] = ast::TypeDecl::Data{ decl };123 for ( const ast::DeclWithType * assert : decl->assertions ) {124 have[ assert ].isUsed = false;125 }121 for ( auto & decl : type->forall ) { 122 closed[ *decl ] = ast::TypeDecl::Data{ decl->base }; 123 } 124 for ( auto & assert : type->assertions ) { 125 have[ assert ].isUsed = false; 126 126 } 127 127 } -
src/ResolvExpr/PolyCost.cc
r3c64c668 r58fe85a 58 58 59 59 // TODO: When the old PolyCost is torn out get rid of the _new suffix. 60 struct PolyCost_new { 60 class PolyCost_new { 61 const ast::SymbolTable &symtab; 62 public: 61 63 int result; 62 const ast::SymbolTable &symtab;63 64 const ast::TypeEnvironment &env_; 64 65 65 PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) :66 result( 0 ), symtab( symtab), env_( env ) {}66 PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) 67 : symtab( symtab ), result( 0 ), env_( env ) {} 67 68 68 69 void previsit( const ast::TypeInstType * type ) { 69 if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ if ( eqv->bound ) {70 if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) { 70 71 if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) { 71 72 if ( symtab.lookupType( otherType->name ) ) { … … 86 87 ast::Pass<PolyCost_new> costing( symtab, env ); 87 88 type->accept( costing ); 88 return costing. pass.result;89 return costing.core.result; 89 90 } 90 91 -
src/ResolvExpr/PtrsAssignable.cc
r3c64c668 r58fe85a 134 134 } 135 135 void postvisit( const ast::TypeInstType * inst ) { 136 if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name) ) {136 if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) { 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->name) ) {148 if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) { 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. pass.result;157 return visitor.core.result; 158 158 } 159 159 -
src/ResolvExpr/PtrsCastable.cc
r3c64c668 r58fe85a 180 180 } 181 181 } 182 } else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name) ) {182 } else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) { 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->name) ) {285 if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) { 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. pass.result;295 return ptrs.core.result; 296 296 } 297 297 } -
src/ResolvExpr/RenameVars.cc
r3c64c668 r58fe85a 30 30 #include "SynTree/Visitor.h" // for acceptAll, maybeAccept 31 31 32 #include "AST/Copy.hpp" 33 32 34 namespace ResolvExpr { 33 35 … … 36 38 int level = 0; 37 39 int resetCount = 0; 40 41 int next_expr_id = 1; 42 int next_usage_id = 1; 38 43 ScopedMap< std::string, std::string > nameMap; 39 44 ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap; 40 45 public: 41 46 void reset() { … … 44 49 } 45 50 46 using mapConstIterator = ScopedMap< std::string, std::string >::const_iterator;47 48 51 void rename( TypeInstType * type ) { 49 mapConstIteratorit = nameMap.find( type->name );52 auto it = nameMap.find( type->name ); 50 53 if ( it != nameMap.end() ) { 51 54 type->name = it->second; 52 55 } 56 } 57 58 void nextUsage() { 59 ++next_usage_id; 53 60 } 54 61 … … 65 72 // ditto for assertion names, the next level in 66 73 level++; 67 // acceptAll( td->assertions, *this ); 68 } // for 69 } // if 74 } 75 } 70 76 } 71 77 … … 77 83 78 84 const ast::TypeInstType * rename( const ast::TypeInstType * type ) { 79 mapConstIterator it = nameMap.find( type->name ); 80 if ( it != nameMap.end() ) { 81 ast::TypeInstType * mutType = ast::mutate( type ); 82 mutType->name = it->second; 83 type = mutType; 84 } 85 // rename 86 auto it = idMap.find( type->name ); 87 if ( it != idMap.end() ) { 88 // unconditionally mutate because map will *always* have different name 89 ast::TypeInstType * mut = ast::shallowCopy( type ); 90 // reconcile base node since some copies might have been made 91 mut->base = it->second.base; 92 mut->formal_usage = it->second.formal_usage; 93 mut->expr_id = it->second.expr_id; 94 type = mut; 95 } 96 85 97 return type; 86 98 } 87 99 88 template<typename NodeT> 89 const NodeT * openLevel( const NodeT * type ) { 90 if ( !type->forall.empty() ) { 91 nameMap.beginScope(); 92 // Load new names from this forall clause and perform renaming. 93 NodeT * mutType = ast::mutate( type ); 94 for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) { 95 std::ostringstream output; 96 output << "_" << resetCount << "_" << level << "_" << td->name; 97 std::string newname( output.str() ); 98 nameMap[ td->name ] = newname; 99 ++level; 100 101 ast::TypeDecl * decl = ast::mutate( td.get() ); 102 decl->name = newname; 103 td = decl; 104 } 105 } 106 return type; 107 } 108 109 template<typename NodeT> 110 const NodeT * closeLevel( const NodeT * type ) { 111 if ( !type->forall.empty() ) { 112 nameMap.endScope(); 113 } 114 return type; 100 const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) { 101 if ( type->forall.empty() ) return type; 102 idMap.beginScope(); 103 104 // Load new names from this forall clause and perform renaming. 105 auto mutType = ast::shallowCopy( type ); 106 // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" ); 107 for ( auto & td : mutType->forall ) { 108 auto mut = ast::shallowCopy( td.get() ); 109 // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" ); 110 111 if (mode == GEN_EXPR_ID) { 112 mut->expr_id = next_expr_id; 113 mut->formal_usage = -1; 114 ++next_expr_id; 115 } 116 else if (mode == GEN_USAGE) { 117 assertf(mut->expr_id, "unfilled expression id in generating candidate type"); 118 mut->formal_usage = next_usage_id; 119 } 120 else { 121 assert(false); 122 } 123 idMap[ td->name ] = ast::TypeInstType::TypeEnvKey(*mut); 124 125 td = mut; 126 } 127 128 return mutType; 129 } 130 131 void closeLevel( const ast::FunctionType * type ) { 132 if ( type->forall.empty() ) return; 133 idMap.endScope(); 115 134 } 116 135 }; … … 119 138 RenamingData renaming; 120 139 121 struct RenameVars {140 struct RenameVars_old { 122 141 void previsit( TypeInstType * instType ) { 123 142 renaming.openLevel( (Type*)instType ); … … 130 149 renaming.closeLevel( type ); 131 150 } 151 }; 152 153 struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ { 154 RenameMode mode; 132 155 133 156 const ast::FunctionType * previsit( const ast::FunctionType * type ) { 134 return renaming.openLevel( type ); 135 } 157 return renaming.openLevel( type, mode ); 158 } 159 160 /* 136 161 const ast::StructInstType * previsit( const ast::StructInstType * type ) { 137 162 return renaming.openLevel( type ); … … 143 168 return renaming.openLevel( type ); 144 169 } 170 */ 171 145 172 const ast::TypeInstType * previsit( const ast::TypeInstType * type ) { 146 return renaming.rename( renaming.openLevel( type ) ); 147 } 148 const ast::ParameterizedType * postvisit( const ast::ParameterizedType * type ) { 149 return renaming.closeLevel( type ); 173 if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type 174 return renaming.rename( type ); 175 } 176 void postvisit( const ast::FunctionType * type ) { 177 renaming.closeLevel( type ); 150 178 } 151 179 }; … … 154 182 155 183 void renameTyVars( Type * t ) { 156 PassVisitor<RenameVars > renamer;184 PassVisitor<RenameVars_old> renamer; 157 185 t->accept( renamer ); 158 186 } 159 187 160 const ast::Type * renameTyVars( const ast::Type * t ) { 161 ast::Pass<RenameVars> renamer; 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 } 162 195 return t->accept( renamer ); 163 196 } … … 165 198 void resetTyVarRenaming() { 166 199 renaming.reset(); 200 renaming.nextUsage(); 167 201 } 168 202 -
src/ResolvExpr/RenameVars.h
r3c64c668 r58fe85a 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 const ast::Type * renameTyVars( const ast::Type * ); 32 33 enum RenameMode { 34 GEN_USAGE, // for type in VariableExpr 35 GEN_EXPR_ID // for type in decl 36 }; 37 const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true ); 38 33 39 34 40 /// resets internal state of renamer to avoid overflow 35 41 void resetTyVarRenaming(); 42 43 36 44 } // namespace ResolvExpr 37 45 -
src/ResolvExpr/ResolvMode.h
r3c64c668 r58fe85a 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]25 24 26 private: 27 constexpr ResolvMode(bool a, bool p, bool ff, bool sa) 28 : adjust(a), prune(p), failFast(ff), satisfyAssns(sa) {} 25 constexpr ResolvMode(bool a, bool p, bool ff) 26 : adjust(a), prune(p), failFast(ff) {} 29 27 30 public:31 28 /// Default settings 32 constexpr ResolvMode() : adjust(false), prune(true), failFast(true) , satisfyAssns(false){}29 constexpr ResolvMode() : adjust(false), prune(true), failFast(true) {} 33 30 34 31 /// With adjust flag set; turns array and function types into equivalent pointers 35 static constexpr ResolvMode withAdjustment() { return { true, true, true , false}; }32 static constexpr ResolvMode withAdjustment() { return { true, true, true }; } 36 33 37 34 /// With adjust flag set but prune unset; pruning ensures there is at least one alternative 38 35 /// per result type 39 static constexpr ResolvMode withoutPrune() { return { true, false, true , false}; }36 static constexpr ResolvMode withoutPrune() { return { true, false, true }; } 40 37 41 38 /// With adjust and prune flags set but failFast unset; failFast ensures there is at least 42 39 /// one resulting alternative 43 static constexpr ResolvMode withoutFailFast() { return { true, true, false , false}; }40 static constexpr ResolvMode withoutFailFast() { return { true, true, false }; } 44 41 45 42 /// The same mode, but with satisfyAssns turned on; for top-level calls 46 ResolvMode atTopLevel() const { return { adjust, prune, failFast, true}; }43 ResolvMode atTopLevel() const { return { adjust, true, failFast }; } 47 44 }; 48 45 } // namespace ResolvExpr -
src/ResolvExpr/ResolveAssertions.cc
r3c64c668 r58fe85a 277 277 const DeclarationWithType * candidate = cdata.id; 278 278 279 // build independent unification context for candidate 279 // ignore deleted candidates. 280 // NOTE: this behavior is different from main resolver. 281 // further investigations might be needed to determine 282 // if we should implement the same rule here 283 // (i.e. error if unique best match is deleted) 284 if (candidate->isDeleted) continue; 285 286 // build independent unification context. for candidate 280 287 AssertionSet have, newNeed; 281 288 TypeEnvironment newEnv{ resn.alt.env }; … … 390 397 391 398 /// Limit to depth of recursion of assertion satisfaction 392 static const int recursionLimit = 4;399 static const int recursionLimit = 7; 393 400 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 394 401 static const int deferLimit = 10; -
src/ResolvExpr/ResolveTypeof.cc
r3c64c668 r58fe85a 15 15 16 16 #include "ResolveTypeof.h" 17 #include "RenameVars.h" 17 18 18 19 #include <cassert> // for assert … … 29 30 #include "SynTree/Mutator.h" // for Mutator 30 31 #include "SynTree/Type.h" // for TypeofType, Type 32 #include "SymTab/Mangler.h" 33 #include "InitTweak/InitTweak.h" // for isConstExpr 31 34 32 35 namespace SymTab { … … 99 102 // replace basetypeof(<enum>) by int 100 103 if ( dynamic_cast<EnumInstType*>(newType) ) { 101 Type* newerType = 102 new BasicType{ newType->get_qualifiers(), BasicType::SignedInt, 104 Type* newerType = 105 new BasicType{ newType->get_qualifiers(), BasicType::SignedInt, 103 106 newType->attributes }; 104 107 delete newType; 105 108 newType = newerType; 106 109 } 107 newType->get_qualifiers().val 110 newType->get_qualifiers().val 108 111 = ( newType->get_qualifiers().val & ~Type::Qualifiers::Mask ) | oldQuals; 109 112 } else { 110 113 newType->get_qualifiers().val |= oldQuals; 111 114 } 112 115 113 116 return newType; 114 117 } … … 120 123 ResolveTypeof_new( const ast::SymbolTable & syms ) : localSymtab( syms ) {} 121 124 122 void pre mutate( const ast::TypeofType * ) { visit_children = false; }123 124 const ast::Type * post mutate( const ast::TypeofType * typeofType ) {125 void previsit( const ast::TypeofType * ) { visit_children = false; } 126 127 const ast::Type * postvisit( const ast::TypeofType * typeofType ) { 125 128 // pass on null expression 126 129 if ( ! typeofType->expr ) return typeofType; … … 133 136 // typeof wrapping expression 134 137 ast::TypeEnvironment dummy; 135 ast::ptr< ast::Expr > newExpr = 138 ast::ptr< ast::Expr > newExpr = 136 139 resolveInVoidContext( typeofType->expr, localSymtab, dummy ); 137 140 assert( newExpr->result && ! newExpr->result->isVoid() ); … … 143 146 // replace basetypeof(<enum>) by int 144 147 if ( newType.as< ast::EnumInstType >() ) { 145 newType = new ast::BasicType{ 148 newType = new ast::BasicType{ 146 149 ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) }; 147 150 } 148 reset_qualifiers( 149 newType, 151 reset_qualifiers( 152 newType, 150 153 ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers ); 151 154 } else { … … 153 156 } 154 157 155 return newType ;158 return newType.release(); 156 159 } 157 160 }; … … 161 164 ast::Pass< ResolveTypeof_new > mutator{ symtab }; 162 165 return type->accept( mutator ); 166 } 167 168 struct FixArrayDimension { 169 // should not require a mutable symbol table - prevent pass template instantiation 170 const ast::SymbolTable & _symtab; 171 FixArrayDimension(const ast::SymbolTable & symtab): _symtab(symtab) {} 172 173 const ast::ArrayType * previsit (const ast::ArrayType * arrayType) { 174 if (!arrayType->dimension) return arrayType; 175 auto mutType = mutate(arrayType); 176 ast::ptr<ast::Type> sizetype = ast::sizeType ? ast::sizeType : new ast::BasicType(ast::BasicType::LongUnsignedInt); 177 mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, _symtab); 178 179 if (InitTweak::isConstExpr(mutType->dimension)) { 180 mutType->isVarLen = ast::LengthFlag::FixedLen; 181 } 182 else { 183 mutType->isVarLen = ast::LengthFlag::VariableLen; 184 } 185 return mutType; 186 } 187 }; 188 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 previously 208 if (InitTweak::isConstExpr(varexpr->var.strict_as<ast::ObjectDecl>()->init)) { 209 auto mutType = mutate(arrayType); 210 mutType->isVarLen = ast::LengthFlag::VariableLen; 211 mutDecl->type = mutType; 212 } 213 } 214 } 215 */ 216 217 218 if (!mutDecl->name.empty()) 219 mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables 220 221 mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID); 222 mutDecl->isTypeFixed = true; 223 return mutDecl; 224 } 225 return decl; 163 226 } 164 227 -
src/ResolvExpr/ResolveTypeof.h
r3c64c668 r58fe85a 23 23 class Type; 24 24 class SymbolTable; 25 class ObjectDecl; 25 26 } 26 27 … … 28 29 Type *resolveTypeof( Type*, const SymTab::Indexer &indexer ); 29 30 const ast::Type * resolveTypeof( const ast::Type *, const ast::SymbolTable & ); 31 const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ast::SymbolTable & symtab ); 30 32 } // namespace ResolvExpr 31 33 -
src/ResolvExpr/Resolver.cc
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Sun May 17 12:17:01 2015 11 // Last Modified By : A aron B. Moss12 // Last Modified On : Wed May 29 11:00:00 201913 // Update Count : 24 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Mar 27 11:58:00 2020 13 // Update Count : 242 14 14 // 15 15 … … 26 26 #include "RenameVars.h" // for RenameVars, global_renamer 27 27 #include "Resolver.h" 28 #include "ResolveTypeof.h" 28 29 #include "ResolvMode.h" // for ResolvMode 29 30 #include "typeops.h" // for extractResultType 30 31 #include "Unify.h" // for unify 32 #include "CompilationState.h" 31 33 #include "AST/Chain.hpp" 32 34 #include "AST/Decl.hpp" … … 38 40 #include "Common/PassVisitor.h" // for PassVisitor 39 41 #include "Common/SemanticError.h" // for SemanticError 42 #include "Common/Stats/ResolveTime.h" // for ResolveTime::start(), ResolveTime::stop() 40 43 #include "Common/utility.h" // for ValueGuard, group_iterate 41 44 #include "InitTweak/GenInit.h" … … 44 47 #include "SymTab/Autogen.h" // for SizeType 45 48 #include "SymTab/Indexer.h" // for Indexer 49 #include "SymTab/Mangler.h" // for Mangler 46 50 #include "SynTree/Declaration.h" // for ObjectDecl, TypeDecl, Declar... 47 51 #include "SynTree/Expression.h" // for Expression, CastExpr, InitExpr … … 560 564 // TODO: Replace *exception type with &exception type. 561 565 if ( throwStmt->get_expr() ) { 562 const StructDecl * exception_decl = indexer.lookupStruct( "__cfa abi_ehm__base_exception_t" );566 const StructDecl * exception_decl = indexer.lookupStruct( "__cfaehm_base_exception_t" ); 563 567 assert( exception_decl ); 564 568 Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, const_cast<StructDecl *>(exception_decl) ) ); … … 964 968 namespace { 965 969 /// Finds deleted expressions in an expression tree 966 struct DeleteFinder_new final : public ast::WithShortCircuiting {967 const ast::DeletedExpr * delExpr= nullptr;970 struct DeleteFinder_new final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder_new> { 971 const ast::DeletedExpr * result = nullptr; 968 972 969 973 void previsit( const ast::DeletedExpr * expr ) { 970 if ( delExpr ) { visit_children = false; } 971 else { delExpr = expr; } 972 } 973 974 void previsit( const ast::Expr * ) { 975 if ( delExpr ) { visit_children = false; } 974 if ( result ) { visit_children = false; } 975 else { result = expr; } 976 } 977 978 void previsit( const ast::Expr * expr ) { 979 if ( result ) { visit_children = false; } 980 if (expr->inferred.hasParams()) { 981 for (auto & imp : expr->inferred.inferParams() ) { 982 imp.second.expr->accept(*visitor); 983 } 984 } 976 985 } 977 986 }; 978 987 } // anonymous namespace 979 980 988 /// Check if this expression is or includes a deleted expression 981 989 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) { 982 ast::Pass<DeleteFinder_new> finder; 983 expr->accept( finder ); 984 return finder.pass.delExpr; 990 return ast::Pass<DeleteFinder_new>::read( expr ); 985 991 } 986 992 … … 1072 1078 /// Strips extraneous casts out of an expression 1073 1079 struct StripCasts_new final { 1074 const ast::Expr * post mutate( const ast::CastExpr * castExpr ) {1080 const ast::Expr * postvisit( const ast::CastExpr * castExpr ) { 1075 1081 if ( 1076 castExpr->isGenerated 1082 castExpr->isGenerated == ast::GeneratedCast 1077 1083 && typesCompatible( castExpr->arg->result, castExpr->result ) 1078 1084 ) { … … 1106 1112 } 1107 1113 1108 /// Establish post-resolver invariants for expressions 1114 1115 } // anonymous namespace 1116 /// Establish post-resolver invariants for expressions 1109 1117 void finishExpr( 1110 1118 ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env, … … 1119 1127 StripCasts_new::strip( expr ); 1120 1128 } 1121 } // anonymous namespace1122 1123 1129 1124 1130 ast::ptr< ast::Expr > resolveInVoidContext( … … 1128 1134 1129 1135 // set up and resolve expression cast to void 1130 ast:: CastExpr *untyped = new ast::CastExpr{ expr };1136 ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr }; 1131 1137 CandidateRef choice = findUnfinishedKindExpression( 1132 1138 untyped, symtab, "", anyCandidate, ResolvMode::withAdjustment() ); … … 1140 1146 } 1141 1147 1142 namespace { 1143 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 1148 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 1144 1149 /// context. 1145 1150 ast::ptr< ast::Expr > findVoidExpression( 1146 1151 const ast::Expr * untyped, const ast::SymbolTable & symtab 1147 1152 ) { 1148 resetTyVarRenaming();1149 1153 ast::TypeEnvironment env; 1150 1154 ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, symtab, env ); … … 1152 1156 return newExpr; 1153 1157 } 1158 1159 namespace { 1160 1154 1161 1155 1162 /// resolve `untyped` to the expression whose candidate satisfies `pred` with the … … 1163 1170 CandidateRef choice = 1164 1171 findUnfinishedKindExpression( untyped, symtab, kind, pred, mode ); 1165 finishExpr( choice->expr, choice->env, untyped->env );1172 ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env ); 1166 1173 return std::move( choice->expr ); 1167 1174 } … … 1171 1178 const ast::Expr * untyped, const ast::SymbolTable & symtab 1172 1179 ) { 1173 return findKindExpression( untyped, symtab ); 1180 Stats::ResolveTime::start( untyped ); 1181 auto res = findKindExpression( untyped, symtab ); 1182 Stats::ResolveTime::stop(); 1183 return res; 1174 1184 } 1175 1185 } // anonymous namespace 1176 1186 1177 ast::ptr< ast::Expr > findSingleExpression(1178 const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab1179 ) {1180 assert( untyped && type );1181 ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };1182 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab );1183 removeExtraneousCast( newExpr, symtab );1184 return newExpr;1185 }1187 ast::ptr< ast::Expr > findSingleExpression( 1188 const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab 1189 ) { 1190 assert( untyped && type ); 1191 ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type }; 1192 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, symtab ); 1193 removeExtraneousCast( newExpr, symtab ); 1194 return newExpr; 1195 } 1186 1196 1187 1197 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 } 1188 1202 /// Predicate for "Candidate has integral type" 1189 1203 bool hasIntegralType( const Candidate & i ) { … … 1221 1235 template<typename Iter> 1222 1236 inline bool nextMutex( Iter & it, const Iter & end ) { 1223 while ( it != end && ! (*it)-> get_type()->is_mutex() ) { ++it; }1237 while ( it != end && ! (*it)->is_mutex() ) { ++it; } 1224 1238 return it != end; 1225 1239 } … … 1233 1247 ast::ptr< ast::Type > functionReturn = nullptr; 1234 1248 ast::CurrentObject currentObject; 1249 // for work previously in GenInit 1250 static InitTweak::ManagedTypes_new managedTypes; 1251 1235 1252 bool inEnumDecl = false; 1236 1253 1237 1254 public: 1255 static size_t traceId; 1238 1256 Resolver_new() = default; 1239 1257 Resolver_new( const ast::SymbolTable & syms ) { symtab = syms; } 1240 1258 1241 voidprevisit( const ast::FunctionDecl * );1259 const ast::FunctionDecl * previsit( const ast::FunctionDecl * ); 1242 1260 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * ); 1243 void previsit( const ast::ObjectDecl * ); 1261 const ast::ObjectDecl * previsit( const ast::ObjectDecl * ); 1262 void previsit( const ast::AggregateDecl * ); 1263 void previsit( const ast::StructDecl * ); 1244 1264 void previsit( const ast::EnumDecl * ); 1245 1265 const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * ); … … 1260 1280 const ast::ThrowStmt * previsit( const ast::ThrowStmt * ); 1261 1281 const ast::CatchStmt * previsit( const ast::CatchStmt * ); 1282 const ast::CatchStmt * postvisit( const ast::CatchStmt * ); 1262 1283 const ast::WaitForStmt * previsit( const ast::WaitForStmt * ); 1284 const ast::WithStmt * previsit( const ast::WithStmt * ); 1263 1285 1264 1286 const ast::SingleInit * previsit( const ast::SingleInit * ); 1265 1287 const ast::ListInit * previsit( const ast::ListInit * ); 1266 1288 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); 1267 1295 }; 1268 1269 void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit ) { 1270 ast::Pass< Resolver_new > resolver; 1271 accept_all( translationUnit, resolver ); 1296 // size_t Resolver_new::traceId = Stats::Heap::new_stacktrace_id("Resolver"); 1297 1298 InitTweak::ManagedTypes_new Resolver_new::managedTypes; 1299 1300 void resolve( ast::TranslationUnit& translationUnit ) { 1301 ast::Pass< Resolver_new >::run( translationUnit ); 1272 1302 } 1273 1303 … … 1280 1310 } 1281 1311 1282 ast::ptr< ast::Expr >resolveStmtExpr(1312 const ast::Expr * resolveStmtExpr( 1283 1313 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab 1284 1314 ) { 1285 1315 assert( stmtExpr ); 1286 1316 ast::Pass< Resolver_new > resolver{ symtab }; 1287 ast::ptr< ast::Expr > ret = stmtExpr; 1288 ret = ret->accept( resolver ); 1289 strict_dynamic_cast< ast::StmtExpr * >( ret.get_and_mutate() )->computeResult(); 1317 auto ret = mutate(stmtExpr->accept(resolver)); 1318 strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult(); 1290 1319 return ret; 1291 1320 } 1292 1321 1293 void Resolver_new::previsit( const ast::FunctionDecl * functionDecl ) { 1322 namespace { 1323 const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ast::SymbolTable & symtab) { 1324 std::string name = attr->normalizedName(); 1325 if (name == "constructor" || name == "destructor") { 1326 if (attr->params.size() == 1) { 1327 auto arg = attr->params.front(); 1328 auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), symtab ); 1329 auto result = eval(arg); 1330 1331 auto mutAttr = mutate(attr); 1332 mutAttr->params.front() = resolved; 1333 if (! result.second) { 1334 SemanticWarning(loc, Warning::GccAttributes, 1335 toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) ); 1336 } 1337 else { 1338 auto priority = result.first; 1339 if (priority < 101) { 1340 SemanticWarning(loc, Warning::GccAttributes, 1341 toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) ); 1342 } else if (priority < 201 && ! buildingLibrary()) { 1343 SemanticWarning(loc, Warning::GccAttributes, 1344 toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) ); 1345 } 1346 } 1347 return mutAttr; 1348 } else if (attr->params.size() > 1) { 1349 SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) ); 1350 } else { 1351 SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) ); 1352 } 1353 } 1354 return attr; 1355 } 1356 } 1357 1358 const ast::FunctionDecl * Resolver_new::previsit( const ast::FunctionDecl * functionDecl ) { 1294 1359 GuardValue( functionReturn ); 1360 1361 assert (functionDecl->unique()); 1362 if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) { 1363 SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations"); 1364 } 1365 1366 if (!functionDecl->isTypeFixed) { 1367 auto mutDecl = mutate(functionDecl); 1368 auto mutType = mutDecl->type.get_and_mutate(); 1369 1370 for (auto & attr: mutDecl->attributes) { 1371 attr = handleAttribute(mutDecl->location, attr, symtab); 1372 } 1373 1374 // handle assertions 1375 1376 symtab.enterScope(); 1377 mutType->forall.clear(); 1378 mutType->assertions.clear(); 1379 for (auto & typeParam : mutDecl->type_params) { 1380 symtab.addType(typeParam); 1381 mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam)); 1382 } 1383 for (auto & asst : mutDecl->assertions) { 1384 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab); 1385 symtab.addId(asst); 1386 mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst)); 1387 } 1388 1389 // temporarily adds params to symbol table. 1390 // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl) 1391 1392 std::vector<ast::ptr<ast::Type>> paramTypes; 1393 std::vector<ast::ptr<ast::Type>> returnTypes; 1394 1395 for (auto & param : mutDecl->params) { 1396 param = fixObjectType(param.strict_as<ast::ObjectDecl>(), symtab); 1397 symtab.addId(param); 1398 paramTypes.emplace_back(param->get_type()); 1399 } 1400 for (auto & ret : mutDecl->returns) { 1401 ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), symtab); 1402 returnTypes.emplace_back(ret->get_type()); 1403 } 1404 // since function type in decl is just a view of param types, need to update that as well 1405 mutType->params = std::move(paramTypes); 1406 mutType->returns = std::move(returnTypes); 1407 1408 auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID)); 1409 1410 std::list<ast::ptr<ast::Stmt>> newStmts; 1411 resolveWithExprs (mutDecl->withExprs, newStmts); 1412 1413 if (mutDecl->stmts) { 1414 auto mutStmt = mutDecl->stmts.get_and_mutate(); 1415 mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts)); 1416 mutDecl->stmts = mutStmt; 1417 } 1418 1419 symtab.leaveScope(); 1420 1421 mutDecl->type = renamedType; 1422 mutDecl->mangleName = Mangle::mangle(mutDecl); 1423 mutDecl->isTypeFixed = true; 1424 functionDecl = mutDecl; 1425 } 1426 managedTypes.handleDWT(functionDecl); 1427 1295 1428 functionReturn = extractResultType( functionDecl->type ); 1429 return functionDecl; 1296 1430 } 1297 1431 … … 1299 1433 // default value expressions have an environment which shouldn't be there and trips up 1300 1434 // later passes. 1301 as t::ptr< ast::FunctionDecl > ret = functionDecl;1302 for ( unsigned i = 0; i < functionDecl->type->params.size(); ++i ) {1303 const ast::ptr<ast::DeclWithType> & d = functionDecl->type->params[i]; 1304 1305 if ( const ast::ObjectDecl * obj = d.as< ast::ObjectDecl >() ) {1435 assert( functionDecl->unique() ); 1436 ast::FunctionType * mutType = mutate( functionDecl->type.get() ); 1437 1438 for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) { 1439 if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) { 1306 1440 if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) { 1307 1441 if ( init->value->env == nullptr ) continue; 1308 1442 // clone initializer minus the initializer environment 1309 ast::chain_mutate( ret ) 1310 ( &ast::FunctionDecl::type ) 1311 ( &ast::FunctionType::params )[i] 1312 ( &ast::ObjectDecl::init ) 1313 ( &ast::SingleInit::value )->env = nullptr; 1314 1315 assert( functionDecl != ret.get() || functionDecl->unique() ); 1316 assert( ! ret->type->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env ); 1443 auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() ); 1444 auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() ); 1445 auto mutValue = mutate( mutInit->value.get() ); 1446 1447 mutValue->env = nullptr; 1448 mutInit->value = mutValue; 1449 mutParam->init = mutInit; 1450 mutType->params[i] = mutParam; 1451 1452 assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env); 1317 1453 } 1318 1454 } 1319 1455 } 1320 return ret.get(); 1321 } 1322 1323 void Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) { 1456 mutate_field(functionDecl, &ast::FunctionDecl::type, mutType); 1457 return functionDecl; 1458 } 1459 1460 const ast::ObjectDecl * Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) { 1324 1461 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], 1325 1462 // class-variable `initContext` is changed multiple times because the LHS is analyzed … … 1329 1466 // selecting the RHS. 1330 1467 GuardValue( currentObject ); 1331 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() }; 1468 1332 1469 if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) { 1333 1470 // enumerator initializers should not use the enum type to initialize, since the 1334 1471 // enum type is still incomplete at this point. Use `int` instead. 1472 objectDecl = fixObjectType(objectDecl, symtab); 1335 1473 currentObject = ast::CurrentObject{ 1336 1474 objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } }; 1337 1475 } 1476 else { 1477 if (!objectDecl->isTypeFixed) { 1478 auto newDecl = fixObjectType(objectDecl, symtab); 1479 auto mutDecl = mutate(newDecl); 1480 1481 // generate CtorInit wrapper when necessary. 1482 // in certain cases, fixObjectType is called before reaching 1483 // this object in visitor pass, thus disabling CtorInit codegen. 1484 // this happens on aggregate members and function parameters. 1485 if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) { 1486 // constructed objects cannot be designated 1487 if ( InitTweak::isDesignated( mutDecl->init ) ) SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object. If this is really what you want, then initialize with @=.\n" ); 1488 // constructed objects should not have initializers nested too deeply 1489 if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " ); 1490 1491 mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl ); 1492 } 1493 1494 objectDecl = mutDecl; 1495 } 1496 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() }; 1497 } 1498 1499 return objectDecl; 1500 } 1501 1502 void Resolver_new::previsit( const ast::AggregateDecl * _aggDecl ) { 1503 auto aggDecl = mutate(_aggDecl); 1504 assertf(aggDecl == _aggDecl, "type declarations must be unique"); 1505 1506 for (auto & member: aggDecl->members) { 1507 // nested type decls are hoisted already. no need to do anything 1508 if (auto obj = member.as<ast::ObjectDecl>()) { 1509 member = fixObjectType(obj, symtab); 1510 } 1511 } 1512 } 1513 1514 void Resolver_new::previsit( const ast::StructDecl * structDecl ) { 1515 previsit(static_cast<const ast::AggregateDecl *>(structDecl)); 1516 managedTypes.handleStruct(structDecl); 1338 1517 } 1339 1518 … … 1341 1520 // in case we decide to allow nested enums 1342 1521 GuardValue( inEnumDecl ); 1343 inEnumDecl = false; 1344 } 1522 inEnumDecl = true; 1523 // don't need to fix types for enum fields 1524 } 1525 1345 1526 1346 1527 const ast::StaticAssertDecl * Resolver_new::previsit( … … 1355 1536 const PtrType * handlePtrType( const PtrType * type, const ast::SymbolTable & symtab ) { 1356 1537 if ( type->dimension ) { 1357 #warning should use new equivalent to Validate::SizeType rather than sizeType here 1358 ast::ptr< ast::Type > sizeType = new ast::BasicType{ ast::BasicType::LongUnsignedInt }; 1538 ast::ptr< ast::Type > sizeType = ast::sizeType; 1359 1539 ast::mutate_field( 1360 1540 type, &PtrType::dimension, … … 1477 1657 if ( throwStmt->expr ) { 1478 1658 const ast::StructDecl * exceptionDecl = 1479 symtab.lookupStruct( "__cfa abi_ehm__base_exception_t" );1659 symtab.lookupStruct( "__cfaehm_base_exception_t" ); 1480 1660 assert( exceptionDecl ); 1481 1661 ast::ptr< ast::Type > exceptType = … … 1489 1669 1490 1670 const ast::CatchStmt * Resolver_new::previsit( const ast::CatchStmt * catchStmt ) { 1491 // TODO: This will need a fix for the decl/cond scoping problem. 1671 // Until we are very sure this invarent (ifs that move between passes have thenPart) 1672 // holds, check it. This allows a check for when to decode the mangling. 1673 if ( auto ifStmt = catchStmt->body.as<ast::IfStmt>() ) { 1674 assert( ifStmt->thenPart ); 1675 } 1676 // Encode the catchStmt so the condition can see the declaration. 1492 1677 if ( catchStmt->cond ) { 1493 ast::ptr< ast::Type > boolType = new ast::BasicType{ ast::BasicType::Bool }; 1494 catchStmt = ast::mutate_field( 1495 catchStmt, &ast::CatchStmt::cond, 1496 findSingleExpression( catchStmt->cond, boolType, symtab ) ); 1678 ast::CatchStmt * stmt = mutate( catchStmt ); 1679 stmt->body = new ast::IfStmt( stmt->location, stmt->cond, nullptr, stmt->body ); 1680 stmt->cond = nullptr; 1681 return stmt; 1682 } 1683 return catchStmt; 1684 } 1685 1686 const ast::CatchStmt * Resolver_new::postvisit( const ast::CatchStmt * catchStmt ) { 1687 // Decode the catchStmt so everything is stored properly. 1688 const ast::IfStmt * ifStmt = catchStmt->body.as<ast::IfStmt>(); 1689 if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) { 1690 assert( ifStmt->cond ); 1691 assert( ifStmt->elsePart ); 1692 ast::CatchStmt * stmt = ast::mutate( catchStmt ); 1693 stmt->cond = ifStmt->cond; 1694 stmt->body = ifStmt->elsePart; 1695 // ifStmt should be implicately deleted here. 1696 return stmt; 1497 1697 } 1498 1698 return catchStmt; … … 1611 1811 // Check if the argument matches the parameter type in the current 1612 1812 // scope 1613 ast::ptr< ast::Type > paramType = (*param)->get_type();1813 // ast::ptr< ast::Type > paramType = (*param)->get_type(); 1614 1814 if ( 1615 1815 ! unify( 1616 arg->expr->result, paramType, resultEnv, need, have, open,1816 arg->expr->result, *param, resultEnv, need, have, open, 1617 1817 symtab ) 1618 1818 ) { … … 1621 1821 ss << "candidate function not viable: no known conversion " 1622 1822 "from '"; 1623 ast::print( ss, (*param)->get_type());1823 ast::print( ss, *param ); 1624 1824 ss << "' to '"; 1625 1825 ast::print( ss, arg->expr->result ); … … 1751 1951 } 1752 1952 1953 const ast::WithStmt * Resolver_new::previsit( const ast::WithStmt * withStmt ) { 1954 auto mutStmt = mutate(withStmt); 1955 resolveWithExprs(mutStmt->exprs, stmtsToAddBefore); 1956 return mutStmt; 1957 } 1958 1959 void Resolver_new::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) { 1960 for (auto & expr : exprs) { 1961 // only struct- and union-typed expressions are viable candidates 1962 expr = findKindExpression( expr, symtab, structOrUnion, "with expression" ); 1963 1964 // if with expression might be impure, create a temporary so that it is evaluated once 1965 if ( Tuples::maybeImpure( expr ) ) { 1966 static UniqueName tmpNamer( "_with_tmp_" ); 1967 const CodeLocation loc = expr->location; 1968 auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) ); 1969 expr = new ast::VariableExpr( loc, tmp ); 1970 stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) ); 1971 if ( InitTweak::isConstructable( tmp->type ) ) { 1972 // generate ctor/dtor and resolve them 1973 tmp->init = InitTweak::genCtorInit( loc, tmp ); 1974 } 1975 // since tmp is freshly created, this should modify tmp in-place 1976 tmp->accept( *visitor ); 1977 } 1978 } 1979 } 1753 1980 1754 1981 … … 1846 2073 } 1847 2074 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 1848 2090 } // namespace ResolvExpr 1849 2091 -
src/ResolvExpr/Resolver.h
r3c64c668 r58fe85a 35 35 class StmtExpr; 36 36 class SymbolTable; 37 struct TranslationUnit; 37 38 class Type; 38 39 class TypeEnvironment; … … 55 56 56 57 /// Checks types and binds syntactic constructs to typed representations 57 void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit );58 void resolve( ast::TranslationUnit& translationUnit ); 58 59 /// Searches expr and returns the first DeletedExpr found, otherwise nullptr 59 60 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ); … … 62 63 ast::ptr< ast::Expr > resolveInVoidContext( 63 64 const ast::Expr * expr, const ast::SymbolTable & symtab, ast::TypeEnvironment & env ); 64 /// Resolve `untyped` to the single expression whose candidate is the best match for the 65 /// Resolve `untyped` to the single expression whose candidate is the best match for the 65 66 /// given type. 66 67 ast::ptr< ast::Expr > findSingleExpression( 67 68 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); 68 71 /// Resolves a constructor init expression 69 ast::ptr< ast::Init > resolveCtorInit( 72 ast::ptr< ast::Init > resolveCtorInit( 70 73 const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab ); 71 /// Resolves a statement expression 72 ast::ptr< ast::Expr > resolveStmtExpr(74 /// Resolves a statement expression 75 const ast::Expr * resolveStmtExpr( 73 76 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab ); 74 77 } // namespace ResolvExpr -
src/ResolvExpr/SatisfyAssertions.cpp
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Mon Jun 10 17:45:00 2019 11 // Last Modified By : A aron B. Moss12 // Last Modified On : Mon Jun 10 17:45:00 201913 // Update Count : 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 1 13:56:00 2019 13 // Update Count : 2 14 14 // 15 15 … … 57 57 ast::UniqueId resnSlot; ///< Slot for any recursive assertion IDs 58 58 59 AssnCandidate( 60 const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 59 AssnCandidate( 60 const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 61 61 ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs ) 62 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 62 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 63 63 need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {} 64 64 }; … … 69 69 /// Reference to a single deferred item 70 70 struct DeferRef { 71 const ast:: DeclWithType * decl;71 const ast::VariableExpr * expr; 72 72 const ast::AssertionSetValue & info; 73 73 const AssnCandidate & match; 74 74 }; 75 76 /// Wrapper for the deferred items from a single assertion satisfaction. 75 76 /// Wrapper for the deferred items from a single assertion satisfaction. 77 77 /// Acts like an indexed list of DeferRef 78 78 struct DeferItem { 79 const ast:: DeclWithType * decl;79 const ast::VariableExpr * expr; 80 80 const ast::AssertionSetValue & info; 81 81 AssnCandidateList matches; 82 82 83 DeferItem( 84 const ast:: DeclWithType* d, const ast::AssertionSetValue & i, AssnCandidateList && ms )85 : decl( d ), info( i ), matches( std::move( ms ) ) {}83 DeferItem( 84 const ast::VariableExpr * d, const ast::AssertionSetValue & i, AssnCandidateList && ms ) 85 : expr( d ), info( i ), matches( std::move( ms ) ) {} 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 { decl, info, matches[i] }; }91 DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; } 92 92 }; 93 93 … … 117 117 /// Initial satisfaction state for a candidate 118 118 SatState( CandidateRef & c, const ast::SymbolTable & syms ) 119 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 119 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 120 120 symtab( syms ) { need.swap( c->need ); } 121 121 122 122 /// Update satisfaction state for next step after previous state 123 123 SatState( SatState && o, IterateFlag ) 124 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 125 deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 124 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 125 deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 126 126 symtab( o.symtab ) { costs.emplace_back( Cost::zero ); } 127 127 128 128 /// Field-wise next step constructor 129 129 SatState( 130 CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 130 CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 131 131 ast::SymbolTable && syms ) 132 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 132 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 133 133 inferred( std::move( i ) ), costs( std::move( cs ) ), symtab( std::move( syms ) ) 134 134 { costs.emplace_back( Cost::zero ); } … … 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 ); }140 if ( i.second.isUsed ) { symtab.addId( i.first->var ); } 141 141 } 142 142 } 143 143 144 144 /// Binds a single assertion, updating satisfaction state 145 void bindAssertion( 146 const ast:: DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand,145 void bindAssertion( 146 const ast::VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand, 147 147 AssnCandidate & match, InferCache & inferred 148 148 ) { 149 149 const ast::DeclWithType * candidate = match.cdata.id; 150 assertf( candidate->uniqueId, 150 assertf( candidate->uniqueId, 151 151 "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() ); 152 152 153 153 ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost ); 154 154 varExpr->result = match.adjType; … … 156 156 157 157 // place newly-inferred assertion in proper location in cache 158 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{159 candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr };158 inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{ 159 candidate->uniqueId, candidate, match.adjType, expr->result, varExpr }; 160 160 } 161 161 … … 167 167 // find candidates that unify with the desired type 168 168 AssnCandidateList matches; 169 for ( const ast::SymbolTable::IdData & cdata : sat.symtab.lookupId( assn.first->name ) ) { 169 170 std::vector<ast::SymbolTable::IdData> candidates; 171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name); 172 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) { 173 // prefilter special decls by argument type, if already known 174 ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base 175 .strict_as<ast::FunctionType>()->params[0] 176 .strict_as<ast::ReferenceType>()->base; 177 sat.cand->env.apply(thisArgType); 178 179 std::string otypeKey = ""; 180 if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer; 181 else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams); 182 183 candidates = sat.symtab.specialLookupId(kind, otypeKey); 184 } 185 else { 186 candidates = sat.symtab.lookupId(assn.first->var->name); 187 } 188 for ( const ast::SymbolTable::IdData & cdata : candidates ) { 170 189 const ast::DeclWithType * candidate = cdata.id; 190 191 // ignore deleted candidates. 192 // NOTE: this behavior is different from main resolver. 193 // further investigations might be needed to determine 194 // if we should implement the same rule here 195 // (i.e. error if unique best match is deleted) 196 if (candidate->isDeleted && candidate->linkage == ast::Linkage::AutoGen) continue; 171 197 172 198 // build independent unification context for candidate … … 174 200 ast::TypeEnvironment newEnv{ sat.cand->env }; 175 201 ast::OpenVarSet newOpen{ sat.cand->open }; 176 ast::ptr< ast::Type > toType = assn.first-> get_type();177 ast::ptr< ast::Type > adjType = 178 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) );202 ast::ptr< ast::Type > toType = assn.first->result; 203 ast::ptr< ast::Type > adjType = 204 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ), GEN_USAGE, false ); 179 205 180 206 // only keep candidates which unify … … 187 213 } 188 214 189 matches.emplace_back( 190 cdata, adjType, std::move( newEnv ), std::move( newNeed ), std::move( have ),215 matches.emplace_back( 216 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ), 191 217 std::move( newOpen ), crntResnSlot ); 192 218 } … … 229 255 InferMatcher( InferCache & inferred ) : inferred( inferred ) {} 230 256 231 const ast::Expr * post mutate( const ast::Expr * expr ) {257 const ast::Expr * postvisit( const ast::Expr * expr ) { 232 258 // Skip if no slots to find 233 if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr; 234 259 if ( !expr->inferred.hasSlots() ) return expr; 260 // if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr; 261 std::vector<UniqueId> missingSlots; 235 262 // find inferred parameters for resolution slots 236 ast::InferredParams newInferred;263 ast::InferredParams * newInferred = new ast::InferredParams(); 237 264 for ( UniqueId slot : expr->inferred.resnSlots() ) { 238 265 // fail if no matching assertions found 239 266 auto it = inferred.find( slot ); 240 267 if ( it == inferred.end() ) { 241 assert(!"missing assertion"); 268 // std::cerr << "missing assertion " << slot << std::endl; 269 missingSlots.push_back(slot); 270 continue; 242 271 } 243 272 … … 245 274 for ( auto & entry : it->second ) { 246 275 // recurse on inferParams of resolved expressions 247 entry.second.expr = post mutate( entry.second.expr );248 auto res = newInferred .emplace( entry );276 entry.second.expr = postvisit( entry.second.expr ); 277 auto res = newInferred->emplace( entry ); 249 278 assert( res.second && "all assertions newly placed" ); 250 279 } … … 252 281 253 282 ast::Expr * ret = mutate( expr ); 254 ret->inferred.set_inferParams( std::move( newInferred ) ); 283 ret->inferred.set_inferParams( newInferred ); 284 if (!missingSlots.empty()) ret->inferred.resnSlots() = missingSlots; 255 285 return ret; 256 286 } 257 287 }; 258 288 259 /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 289 /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 260 290 /// threshold. 261 void finalizeAssertions( 262 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 263 CandidateList & out 291 void finalizeAssertions( 292 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 293 CandidateList & out 264 294 ) { 265 295 // prune if cheaper alternative for same key has already been generated … … 278 308 } 279 309 280 /// Combo iterator that combines candidates into an output list, merging their environments. 281 /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 310 /// Combo iterator that combines candidates into an output list, merging their environments. 311 /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 282 312 /// for description of "combo iterator". 283 313 class CandidateEnvMerger { … … 299 329 Cost cost; 300 330 301 OutType( 302 const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 331 OutType( 332 const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 303 333 const std::vector< DeferRef > & as, const ast::SymbolTable & symtab ) 304 334 : env( e ), open( o ), assns( as ), cost( Cost::zero ) { … … 306 336 for ( const DeferRef & assn : assns ) { 307 337 // compute conversion cost from satisfying decl to assertion 308 cost += computeConversionCost( 309 assn.match.adjType, assn. decl->get_type(), symtab, env );310 338 cost += computeConversionCost( 339 assn.match.adjType, assn.expr->result, false, symtab, env ); 340 311 341 // mark vars+specialization on function-type assertions 312 const ast::FunctionType * func = 342 const ast::FunctionType * func = 313 343 GenPoly::getFunctionType( assn.match.cdata.id->get_type() ); 314 344 if ( ! func ) continue; 315 345 316 for ( const a st::DeclWithType *param : func->params ) {317 cost.decSpec( specCost( param ->get_type()) );346 for ( const auto & param : func->params ) { 347 cost.decSpec( specCost( param ) ); 318 348 } 319 349 320 350 cost.incVar( func->forall.size() ); 321 322 for ( const ast::TypeDecl * td : func->forall ) { 323 cost.decSpec( td->assertions.size() ); 324 } 351 352 cost.decSpec( func->assertions.size() ); 325 353 } 326 354 } … … 329 357 }; 330 358 331 CandidateEnvMerger( 332 const ast::TypeEnvironment & env, const ast::OpenVarSet & open, 359 CandidateEnvMerger( 360 const ast::TypeEnvironment & env, const ast::OpenVarSet & open, 333 361 const ast::SymbolTable & syms ) 334 362 : crnt(), envs{ env }, opens{ open }, symtab( syms ) {} … … 357 385 358 386 /// Limit to depth of recursion of assertion satisfaction 359 static const int recursionLimit = 4;387 static const int recursionLimit = 8; 360 388 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 361 389 static const int deferLimit = 10; 362 390 } // anonymous namespace 363 391 364 void satisfyAssertions( 365 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 392 void satisfyAssertions( 393 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 366 394 std::vector<std::string> & errors 367 395 ) { … … 389 417 if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat; 390 418 391 // make initial pass at matching assertions 392 for ( auto & assn : sat.need ) { 393 // fail early if any assertion is not satisfiable 394 if ( ! satisfyAssertion( assn, sat ) ) { 419 // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen. 420 for (unsigned resetCount = 0; ; ++resetCount) { 421 ast::AssertionList next; 422 resetTyVarRenaming(); 423 // make initial pass at matching assertions 424 for ( auto & assn : sat.need ) { 425 // fail early if any assertion is not satisfiable 426 if ( ! satisfyAssertion( assn, sat ) ) { 427 next.emplace_back(assn); 428 // goto nextSat; 429 } 430 } 431 // success 432 if (next.empty()) break; 433 // fail if nothing resolves 434 else if (next.size() == sat.need.size()) { 395 435 Indenter tabs{ 3 }; 396 436 std::ostringstream ss; … … 398 438 print( ss, *sat.cand, ++tabs ); 399 439 ss << (tabs-1) << "Could not satisfy assertion:\n"; 400 ast::print( ss, assn.first, tabs );440 ast::print( ss, next[0].first, tabs ); 401 441 402 442 errors.emplace_back( ss.str() ); 403 443 goto nextSat; 404 444 } 445 sat.need = std::move(next); 405 446 } 406 447 … … 408 449 // either add successful match or push back next state 409 450 if ( sat.newNeed.empty() ) { 410 finalizeAssertions( 451 finalizeAssertions( 411 452 sat.cand, sat.inferred, thresholds, std::move( sat.costs ), out ); 412 453 } else { … … 421 462 ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n"; 422 463 for ( const auto & d : sat.deferred ) { 423 ast::print( ss, d. decl, tabs );464 ast::print( ss, d.expr, tabs ); 424 465 } 425 466 … … 430 471 std::vector< CandidateEnvMerger::OutType > compatible = filterCombos( 431 472 sat.deferred, CandidateEnvMerger{ sat.cand->env, sat.cand->open, sat.symtab } ); 432 473 433 474 // fail early if no mutually-compatible assertion satisfaction 434 475 if ( compatible.empty() ) { … … 439 480 ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n"; 440 481 for ( const auto& d : sat.deferred ) { 441 ast::print( ss, d. decl, tabs );482 ast::print( ss, d.expr, tabs ); 442 483 } 443 484 … … 453 494 // set up next satisfaction state 454 495 CandidateRef nextCand = std::make_shared<Candidate>( 455 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 496 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 456 497 ast::AssertionSet{} /* need moved into satisfaction state */, 457 498 sat.cand->cost, sat.cand->cvtCost ); … … 459 500 ast::AssertionSet nextNewNeed{ sat.newNeed }; 460 501 InferCache nextInferred{ sat.inferred }; 461 502 462 503 CostVec nextCosts{ sat.costs }; 463 504 nextCosts.back() += compat.cost; 464 505 465 506 ast::SymbolTable nextSymtab{ sat.symtab }; 466 507 … … 471 512 nextNewNeed.insert( match.need.begin(), match.need.end() ); 472 513 473 bindAssertion( r. decl, r.info, nextCand, match, nextInferred );514 bindAssertion( r.expr, r.info, nextCand, match, nextInferred ); 474 515 } 475 516 476 517 // either add successful match or push back next state 477 518 if ( nextNewNeed.empty() ) { 478 finalizeAssertions( 519 finalizeAssertions( 479 520 nextCand, nextInferred, thresholds, std::move( nextCosts ), out ); 480 521 } else { 481 nextSats.emplace_back( 482 std::move( nextCand ), std::move( nextNewNeed ), 483 std::move( nextInferred ), std::move( nextCosts ), 522 nextSats.emplace_back( 523 std::move( nextCand ), std::move( nextNewNeed ), 524 std::move( nextInferred ), std::move( nextCosts ), 484 525 std::move( nextSymtab ) ); 485 526 } … … 493 534 nextSats.clear(); 494 535 } 495 536 496 537 // exceeded recursion limit if reaches here 497 538 if ( out.empty() ) { -
src/ResolvExpr/SatisfyAssertions.hpp
r3c64c668 r58fe85a 27 27 namespace ResolvExpr { 28 28 29 /// Recursively satisfies all assertions provided in a candidate; returns true if succeeds 30 void satisfyAssertions( 31 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 29 /// Recursively satisfies all assertions provided in a candidate 30 /// returns true if it has been run (candidate has any assertions) 31 void satisfyAssertions( 32 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 32 33 std::vector<std::string> & errors ); 33 34 -
src/ResolvExpr/SpecCost.cc
r3c64c668 r58fe85a 10 10 // Created On : Tue Oct 02 15:50:00 2018 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jun 19 10:43:00 2019 13 // Update Count : 2 14 // 15 12 // Last Modified On : Wed Jul 3 11:07:00 2019 13 // Update Count : 3 14 // 15 16 #include <cassert> 16 17 #include <limits> 17 18 #include <list> … … 129 130 typename std::add_pointer<ast::Type const *(typename T::value_type const &)>::type; 130 131 132 #warning Should use a standard maybe_accept 133 void maybe_accept( ast::Type const * type ) { 134 if ( type ) { 135 auto node = type->accept( *visitor ); 136 assert( node == nullptr || node == type ); 137 } 138 } 139 131 140 // Update the minimum to the new lowest non-none value. 132 141 template<typename T> … … 134 143 for ( const auto & node : list ) { 135 144 count = -1; 136 ma pper( node )->accept( *visitor);145 maybe_accept( mapper( node ) ); 137 146 if ( count != -1 && count < minimum ) minimum = count; 138 147 } … … 169 178 void previsit( const ast::FunctionType * fty ) { 170 179 int minCount = std::numeric_limits<int>::max(); 171 updateMinimumPresent( minCount, fty->params, decl_type);172 updateMinimumPresent( minCount, fty->returns, decl_type);180 updateMinimumPresent( minCount, fty->params, type_deref ); 181 updateMinimumPresent( minCount, fty->returns, type_deref ); 173 182 // Add another level to minCount if set. 174 183 count = toNoneOrInc( minCount ); … … 208 217 } 209 218 ast::Pass<SpecCounter> counter; 210 type->accept( *counter.pass.visitor );211 return counter. pass.get_count();219 type->accept( counter ); 220 return counter.core.get_count(); 212 221 } 213 222 -
src/ResolvExpr/TypeEnvironment.cc
r3c64c668 r58fe85a 20 20 #include <utility> // for pair, move 21 21 22 #include "CompilationState.h" // for deterministic_output 22 23 #include "Common/utility.h" // for maybeClone 23 24 #include "SynTree/Type.h" // for Type, FunctionType, Type::Fora... … … 106 107 107 108 void EqvClass::print( std::ostream &os, Indenter indent ) const { 108 os << "( "; 109 std::copy( vars.begin(), vars.end(), std::ostream_iterator< std::string >( os, " " ) ); 109 os << "("; 110 bool first = true; 111 for(const auto & var : vars) { 112 if(first) first = false; 113 else os << " "; 114 if( deterministic_output && isUnboundType(var) ) os << "[unbound]"; 115 else os << var; 116 } 110 117 os << ")"; 111 118 if ( type ) { … … 235 242 // check safely bindable 236 243 if ( r.type && occursIn( r.type, s.vars.begin(), s.vars.end(), *this ) ) return false; 237 244 238 245 // merge classes in 239 246 r.vars.insert( s.vars.begin(), s.vars.end() ); -
src/ResolvExpr/TypeEnvironment.h
r3c64c668 r58fe85a 149 149 iterator end() const { return env.end(); } 150 150 151 auto size() const { return env.size(); } 152 151 153 private: 152 154 ClassList env; -
src/ResolvExpr/Unify.cc
r3c64c668 r58fe85a 25 25 #include <vector> 26 26 27 #include "AST/Copy.hpp" 27 28 #include "AST/Decl.hpp" 28 29 #include "AST/Node.hpp" 29 30 #include "AST/Pass.hpp" 31 #include "AST/Print.hpp" 30 32 #include "AST/Type.hpp" 31 33 #include "AST/TypeEnvironment.hpp" … … 135 137 findOpenVars( newSecond, open, closed, need, have, FirstOpen ); 136 138 137 return unifyExact( 138 newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab ); 139 return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab ); 139 140 } 140 141 … … 148 149 newFirst->get_qualifiers() = Type::Qualifiers(); 149 150 newSecond->get_qualifiers() = Type::Qualifiers(); 150 /// std::cerr << "first is "; 151 /// first->print( std::cerr ); 152 /// std::cerr << std::endl << "second is "; 153 /// second->print( std::cerr ); 154 /// std::cerr << std::endl << "newFirst is "; 155 /// newFirst->print( std::cerr ); 156 /// std::cerr << std::endl << "newSecond is "; 157 /// newSecond->print( std::cerr ); 158 /// std::cerr << std::endl; 151 159 152 bool result = unifyExact( newFirst, newSecond, newEnv, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer ); 160 153 delete newFirst; … … 170 163 ast::AssertionSet need, have; 171 164 172 ast::ptr<ast::Type> newFirst{ first }, newSecond{ second }; 173 env.apply( newFirst ); 174 env.apply( newSecond ); 175 reset_qualifiers( newFirst ); 176 reset_qualifiers( newSecond ); 165 ast::Type * newFirst = shallowCopy( first ); 166 ast::Type * newSecond = shallowCopy( second ); 167 newFirst ->qualifiers = {}; 168 newSecond->qualifiers = {}; 169 ast::ptr< ast::Type > t1_(newFirst ); 170 ast::ptr< ast::Type > t2_(newSecond); 171 172 ast::ptr< ast::Type > subFirst = env.apply(newFirst).node; 173 ast::ptr< ast::Type > subSecond = env.apply(newSecond).node; 177 174 178 175 return unifyExact( 179 newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab ); 176 subFirst, 177 subSecond, 178 newEnv, need, have, open, noWiden(), symtab ); 180 179 } 181 180 … … 326 325 327 326 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;333 327 AssertionSet::iterator i = assertions.find( assert ); 334 328 if ( i != assertions.end() ) { 335 /// std::cerr << "found it!" << std::endl;336 329 i->second.isUsed = true; 337 330 } // if … … 402 395 403 396 template< typename Iterator1, typename Iterator2 > 404 bool unify DeclList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {397 bool unifyTypeList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) { 405 398 auto get_type = [](DeclarationWithType * dwt){ return dwt->get_type(); }; 406 399 for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) { … … 496 489 || flatOther->isTtype() 497 490 ) { 498 if ( unify DeclList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {499 if ( unify DeclList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {491 if ( unifyTypeList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) { 492 if ( unifyTypeList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) { 500 493 501 494 // the original types must be used in mark assertions, since pointer comparisons are used … … 709 702 const ast::SymbolTable & symtab; 710 703 public: 704 static size_t traceId; 711 705 bool result; 712 706 … … 773 767 /// If this isn't done when satifying ttype assertions, then argument lists can have 774 768 /// different size and structure when they should be compatible. 775 struct TtypeExpander_new : public ast::WithShortCircuiting {769 struct TtypeExpander_new : public ast::WithShortCircuiting, public ast::PureVisitor { 776 770 ast::TypeEnvironment & tenv; 777 771 … … 779 773 780 774 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 781 if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name) ) {775 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) { 782 776 // expand ttype parameter into its actual type 783 777 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) { … … 790 784 791 785 /// returns flattened version of `src` 792 static std::vector< ast::ptr< ast:: DeclWithType > > flattenList(793 const std::vector< ast::ptr< ast:: DeclWithType > > & src, ast::TypeEnvironment & env786 static std::vector< ast::ptr< ast::Type > > flattenList( 787 const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env 794 788 ) { 795 std::vector< ast::ptr< ast:: DeclWithType > > dst;789 std::vector< ast::ptr< ast::Type > > dst; 796 790 dst.reserve( src.size() ); 797 for ( const a st::DeclWithType *d : src ) {791 for ( const auto & d : src ) { 798 792 ast::Pass<TtypeExpander_new> expander{ env }; 799 d = d->accept( expander ); 800 auto types = flatten( d->get_type() ); 793 // TtypeExpander pass is impure (may mutate nodes in place) 794 // need to make nodes shared to prevent accidental mutation 795 ast::ptr<ast::Type> dc = d->accept(expander); 796 auto types = flatten( dc ); 801 797 for ( ast::ptr< ast::Type > & t : types ) { 802 798 // outermost const, volatile, _Atomic qualifiers in parameters should not play … … 807 803 // requirements than a non-mutex function 808 804 remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic ); 809 dst.emplace_back( new ast::ObjectDecl{ d->location, "", t });805 dst.emplace_back( t ); 810 806 } 811 807 } … … 815 811 /// Creates a tuple type based on a list of DeclWithType 816 812 template< typename Iter > 817 static ast::ptr< ast::Type > tupleFromDecls( Iter crnt, Iter end ) {813 static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) { 818 814 std::vector< ast::ptr< ast::Type > > types; 819 815 while ( crnt != end ) { 820 816 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 821 817 // that this results in a flat tuple 822 flatten( (*crnt)->get_type(), types );818 flatten( *crnt, types ); 823 819 824 820 ++crnt; 825 821 } 826 822 827 return { new ast::TupleType{ std::move(types) }};823 return new ast::TupleType{ std::move(types) }; 828 824 } 829 825 830 826 template< typename Iter > 831 static bool unify DeclList(827 static bool unifyTypeList( 832 828 Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env, 833 829 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, … … 835 831 ) { 836 832 while ( crnt1 != end1 && crnt2 != end2 ) { 837 const ast::Type * t1 = (*crnt1)->get_type();838 const ast::Type * t2 = (*crnt2)->get_type();833 const ast::Type * t1 = *crnt1; 834 const ast::Type * t2 = *crnt2; 839 835 bool isTuple1 = Tuples::isTtype( t1 ); 840 836 bool isTuple2 = Tuples::isTtype( t2 ); … … 844 840 // combine remainder of list2, then unify 845 841 return unifyExact( 846 t1, tupleFrom Decls( crnt2, end2 ), env, need, have, open,842 t1, tupleFromTypes( crnt2, end2 ), env, need, have, open, 847 843 noWiden(), symtab ); 848 844 } else if ( ! isTuple1 && isTuple2 ) { 849 845 // combine remainder of list1, then unify 850 846 return unifyExact( 851 tupleFrom Decls( crnt1, end1 ), t2, env, need, have, open,847 tupleFromTypes( crnt1, end1 ), t2, env, need, have, open, 852 848 noWiden(), symtab ); 853 849 } … … 864 860 if ( crnt1 != end1 ) { 865 861 // try unifying empty tuple with ttype 866 const ast::Type * t1 = (*crnt1)->get_type();862 const ast::Type * t1 = *crnt1; 867 863 if ( ! Tuples::isTtype( t1 ) ) return false; 868 864 return unifyExact( 869 t1, tupleFrom Decls( crnt2, end2 ), env, need, have, open,865 t1, tupleFromTypes( crnt2, end2 ), env, need, have, open, 870 866 noWiden(), symtab ); 871 867 } else if ( crnt2 != end2 ) { 872 868 // try unifying empty tuple with ttype 873 const ast::Type * t2 = (*crnt2)->get_type();869 const ast::Type * t2 = *crnt2; 874 870 if ( ! Tuples::isTtype( t2 ) ) return false; 875 871 return unifyExact( 876 tupleFrom Decls( crnt1, end1 ), t2, env, need, have, open,872 tupleFromTypes( crnt1, end1 ), t2, env, need, have, open, 877 873 noWiden(), symtab ); 878 874 } … … 881 877 } 882 878 883 static bool unify DeclList(884 const std::vector< ast::ptr< ast:: DeclWithType > > & list1,885 const std::vector< ast::ptr< ast:: DeclWithType > > & list2,879 static bool unifyTypeList( 880 const std::vector< ast::ptr< ast::Type > > & list1, 881 const std::vector< ast::ptr< ast::Type > > & list2, 886 882 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 887 883 const ast::OpenVarSet & open, const ast::SymbolTable & symtab 888 884 ) { 889 return unify DeclList(885 return unifyTypeList( 890 886 list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open, 891 887 symtab ); 892 888 } 893 889 894 static void markAssertionSet( ast::AssertionSet & assns, const ast:: DeclWithType* assn ) {890 static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) { 895 891 auto i = assns.find( assn ); 896 892 if ( i != assns.end() ) { … … 902 898 static void markAssertions( 903 899 ast::AssertionSet & assn1, ast::AssertionSet & assn2, 904 const ast:: ParameterizedType * type900 const ast::FunctionType * type 905 901 ) { 906 for ( const auto & tyvar : type->forall ) { 907 for ( const ast::DeclWithType * assert : tyvar->assertions ) { 908 markAssertionSet( assn1, assert ); 909 markAssertionSet( assn2, assert ); 910 } 902 for ( auto & assert : type->assertions ) { 903 markAssertionSet( assn1, assert ); 904 markAssertionSet( assn2, assert ); 911 905 } 912 906 } … … 932 926 ) return; 933 927 934 if ( ! unify DeclList( params, params2, tenv, need, have, open, symtab ) ) return;935 if ( ! unify DeclList(928 if ( ! unifyTypeList( params, params2, tenv, need, have, open, symtab ) ) return; 929 if ( ! unifyTypeList( 936 930 func->returns, func2->returns, tenv, need, have, open, symtab ) ) return; 937 931 … … 943 937 944 938 private: 945 template< typename RefType > 946 const RefType * handleRefType( const RefType * inst, const ast::Type * other ) { 939 // Returns: other, cast as XInstType 940 // Assigns this->result: whether types are compatible (up to generic parameters) 941 template< typename XInstType > 942 const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) { 947 943 // check that the other type is compatible and named the same 948 auto otherInst = dynamic_cast< const RefType * >( other );949 result = otherInst && inst->name == otherInst->name;944 auto otherInst = dynamic_cast< const XInstType * >( other ); 945 this->result = otherInst && inst->name == otherInst->name; 950 946 return otherInst; 951 947 } … … 968 964 } 969 965 970 template< typename RefType >971 void handleGenericRefType( const RefType * inst, const ast::Type * other ) {966 template< typename XInstType > 967 void handleGenericRefType( const XInstType * inst, const ast::Type * other ) { 972 968 // check that other type is compatible and named the same 973 const RefType * inst2= handleRefType( inst, other );974 if ( ! inst2) return;969 const XInstType * otherInst = handleRefType( inst, other ); 970 if ( ! this->result ) return; 975 971 976 972 // check that parameters of types unify, if any 977 973 const std::vector< ast::ptr< ast::Expr > > & params = inst->params; 978 const std::vector< ast::ptr< ast::Expr > > & params2 = inst2->params;974 const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params; 979 975 980 976 auto it = params.begin(); … … 1032 1028 1033 1029 void postvisit( const ast::TypeInstType * typeInst ) { 1034 assert( open.find( typeInst->name) == open.end() );1030 assert( open.find( *typeInst ) == open.end() ); 1035 1031 handleRefType( typeInst, type2 ); 1036 1032 } … … 1038 1034 private: 1039 1035 /// Creates a tuple type based on a list of Type 1040 static ast::ptr< ast::Type >tupleFromTypes(1036 static const ast::Type * tupleFromTypes( 1041 1037 const std::vector< ast::ptr< ast::Type > > & tys 1042 1038 ) { … … 1114 1110 1115 1111 ast::Pass<TtypeExpander_new> expander{ tenv }; 1112 1116 1113 const ast::Type * flat = tuple->accept( expander ); 1117 1114 const ast::Type * flat2 = tuple2->accept( expander ); … … 1140 1137 }; 1141 1138 1139 // size_t Unify_new::traceId = Stats::Heap::new_stacktrace_id("Unify_new"); 1142 1140 bool unify( 1143 1141 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, … … 1171 1169 auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 ); 1172 1170 ast::OpenVarSet::const_iterator 1173 entry1 = var1 ? open.find( var1->name) : open.end(),1174 entry2 = var2 ? open.find( var2->name) : open.end();1171 entry1 = var1 ? open.find( *var1 ) : open.end(), 1172 entry2 = var2 ? open.find( *var2 ) : open.end(); 1175 1173 bool isopen1 = entry1 != open.end(); 1176 1174 bool isopen2 = entry2 != open.end(); … … 1188 1186 ast::Pass<Unify_new> comparator{ type2, env, need, have, open, widen, symtab }; 1189 1187 type1->accept( comparator ); 1190 return comparator. pass.result;1188 return comparator.core.result; 1191 1189 } 1192 1190 } … … 1202 1200 // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and 1203 1201 // type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1 1204 ast::ptr<ast::Type> t1{ type1 }, t2{ type2 }; 1205 reset_qualifiers( t1 ); 1206 reset_qualifiers( t2 ); 1202 ast::Type * t1 = shallowCopy(type1.get()); 1203 ast::Type * t2 = shallowCopy(type2.get()); 1204 t1->qualifiers = {}; 1205 t2->qualifiers = {}; 1206 ast::ptr< ast::Type > t1_(t1); 1207 ast::ptr< ast::Type > t2_(t2); 1207 1208 1208 1209 if ( unifyExact( t1, t2, env, need, have, open, widen, symtab ) ) { 1209 t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones1210 1211 1210 // if exact unification on unqualified types, try to merge qualifiers 1212 1211 if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) { 1213 common = type1;1214 reset_qualifiers( common, q1 | q2 );1212 t1->qualifiers = q1 | q2; 1213 common = t1; 1215 1214 return true; 1216 1215 } else { … … 1219 1218 1220 1219 } else if (( common = commonType( t1, t2, widen, symtab, env, open ) )) { 1221 t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones1222 1223 1220 // no exact unification, but common type 1224 reset_qualifiers( common, q1 | q2 ); 1221 auto c = shallowCopy(common.get()); 1222 c->qualifiers = q1 | q2; 1223 common = c; 1225 1224 return true; 1226 1225 } else { … … 1231 1230 ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) { 1232 1231 if ( func->returns.empty() ) return new ast::VoidType{}; 1233 if ( func->returns.size() == 1 ) return func->returns[0] ->get_type();1232 if ( func->returns.size() == 1 ) return func->returns[0]; 1234 1233 1235 1234 std::vector<ast::ptr<ast::Type>> tys; 1236 for ( const a st::DeclWithType *decl : func->returns ) {1237 tys.emplace_back( decl ->get_type());1235 for ( const auto & decl : func->returns ) { 1236 tys.emplace_back( decl ); 1238 1237 } 1239 1238 return new ast::TupleType{ std::move(tys) }; -
src/ResolvExpr/module.mk
r3c64c668 r58fe85a 19 19 ResolvExpr/Alternative.cc \ 20 20 ResolvExpr/AlternativeFinder.cc \ 21 ResolvExpr/AlternativeFinder.h \ 22 ResolvExpr/Alternative.h \ 21 23 ResolvExpr/Candidate.cpp \ 22 24 ResolvExpr/CandidateFinder.cpp \ 25 ResolvExpr/CandidateFinder.hpp \ 26 ResolvExpr/Candidate.hpp \ 23 27 ResolvExpr/CastCost.cc \ 24 28 ResolvExpr/CommonType.cc \ 25 29 ResolvExpr/ConversionCost.cc \ 30 ResolvExpr/ConversionCost.h \ 31 ResolvExpr/Cost.h \ 26 32 ResolvExpr/CurrentObject.cc \ 33 ResolvExpr/CurrentObject.h \ 27 34 ResolvExpr/ExplodedActual.cc \ 35 ResolvExpr/ExplodedActual.h \ 28 36 ResolvExpr/ExplodedArg.cpp \ 37 ResolvExpr/ExplodedArg.hpp \ 29 38 ResolvExpr/FindOpenVars.cc \ 39 ResolvExpr/FindOpenVars.h \ 30 40 ResolvExpr/Occurs.cc \ 31 41 ResolvExpr/PolyCost.cc \ … … 33 43 ResolvExpr/PtrsCastable.cc \ 34 44 ResolvExpr/RenameVars.cc \ 45 ResolvExpr/RenameVars.h \ 35 46 ResolvExpr/ResolveAssertions.cc \ 47 ResolvExpr/ResolveAssertions.h \ 36 48 ResolvExpr/Resolver.cc \ 49 ResolvExpr/Resolver.h \ 37 50 ResolvExpr/ResolveTypeof.cc \ 51 ResolvExpr/ResolveTypeof.h \ 52 ResolvExpr/ResolvMode.h \ 38 53 ResolvExpr/SatisfyAssertions.cpp \ 54 ResolvExpr/SatisfyAssertions.hpp \ 39 55 ResolvExpr/SpecCost.cc \ 40 56 ResolvExpr/TypeEnvironment.cc \ 41 ResolvExpr/Unify.cc 57 ResolvExpr/TypeEnvironment.h \ 58 ResolvExpr/typeops.h \ 59 ResolvExpr/Unify.cc \ 60 ResolvExpr/Unify.h \ 61 ResolvExpr/WidenMode.h 42 62 43 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc 63 64 SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h 44 65 SRCDEMANGLE += $(SRC_RESOLVEXPR) -
src/ResolvExpr/typeops.h
r3c64c668 r58fe85a 10 10 // Created On : Sun May 17 07:28:22 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : T hu Aug 8 16:36:00 201913 // Update Count : 512 // Last Modified On : Tue Oct 1 09:45:00 2019 13 // Update Count : 6 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, const ast::SymbolTable & symtab,86 const ast:: TypeEnvironment & env );85 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 86 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ); 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, const ast::SymbolTable & symtab,93 const ast:: TypeEnvironment & env );92 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue, 93 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ); 94 94 95 95 // in AlternativeFinder.cc -
src/SymTab/Autogen.cc
r3c64c668 r58fe85a 38 38 #include "SynTree/Type.h" // for FunctionType, Type, TypeInstType 39 39 #include "SynTree/Visitor.h" // for maybeAccept, Visitor, acceptAll 40 #include "CompilationState.h" 40 41 41 42 class Attribute; … … 233 234 } 234 235 236 // shallow copy the pointer list for return 237 std::vector<ast::ptr<ast::TypeDecl>> getGenericParams (const ast::Type * t) { 238 if (auto structInst = dynamic_cast<const ast::StructInstType*>(t)) { 239 return structInst->base->params; 240 } 241 if (auto unionInst = dynamic_cast<const ast::UnionInstType*>(t)) { 242 return unionInst->base->params; 243 } 244 return {}; 245 } 246 235 247 /// given type T, generate type of default ctor/dtor, i.e. function type void (*) (T *) 236 248 FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic ) { … … 244 256 ftype->parameters.push_back( dstParam ); 245 257 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)); 246 266 } 247 267 … … 327 347 void FuncGenerator::resolve( FunctionDecl * dcl ) { 328 348 try { 329 ResolvExpr::resolveDecl( dcl, indexer ); 349 if (!useNewAST) // attempt to delay resolver call 350 ResolvExpr::resolveDecl( dcl, indexer ); 330 351 if ( functionNesting == 0 ) { 331 352 // forward declare if top-level struct, so that … … 339 360 } catch ( SemanticErrorException & ) { 340 361 // okay if decl does not resolve - that means the function should not be generated 341 delete dcl; 362 // delete dcl; 363 delete dcl->statements; 364 dcl->statements = nullptr; 365 dcl->isDeleted = true; 366 definitions.push_back( dcl ); 367 indexer.addId( dcl ); 342 368 } 343 369 } -
src/SymTab/Autogen.h
r3c64c668 r58fe85a 21 21 22 22 #include "AST/Decl.hpp" 23 #include "AST/Eval.hpp" 23 24 #include "AST/Expr.hpp" 24 25 #include "AST/Init.hpp" … … 54 55 /// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic 55 56 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); 56 59 57 60 /// generate the type of a copy constructor for paramType. … … 164 167 fExpr->args.emplace_back( dstParam ); 165 168 166 const ast::Stmt *listInit = srcParam.buildListInit( fExpr );169 ast::ptr<ast::Stmt> listInit = srcParam.buildListInit( fExpr ); 167 170 168 171 // fetch next set of arguments … … 265 268 } 266 269 267 ast::ptr< ast::Expr > begin, end, cmp, update; 270 ast::ptr< ast::Expr > begin, end; 271 std::string cmp, update; 268 272 269 273 if ( forward ) { … … 271 275 begin = ast::ConstantExpr::from_int( loc, 0 ); 272 276 end = array->dimension; 273 cmp = new ast::NameExpr{ loc, "?<?" };274 update = new ast::NameExpr{ loc, "++?" };277 cmp = "?<?"; 278 update = "++?"; 275 279 } else { 276 280 // generate: for ( int i = N-1; i >= 0; --i ) 277 begin = new ast::UntypedExpr{ 278 loc, new ast::NameExpr{ loc, "?-?" }, 279 { array->dimension, ast::ConstantExpr::from_int( loc, 1 ) } }; 281 begin = ast::call( 282 loc, "?-?", array->dimension, ast::ConstantExpr::from_int( loc, 1 ) ); 280 283 end = ast::ConstantExpr::from_int( loc, 0 ); 281 cmp = new ast::NameExpr{ loc, "?>=?" };282 update = new ast::NameExpr{ loc, "--?" };284 cmp = "?>=?"; 285 update = "--?"; 283 286 } 284 287 … … 286 289 loc, indexName.newName(), new ast::BasicType{ ast::BasicType::SignedInt }, 287 290 new ast::SingleInit{ loc, begin } }; 288 289 ast::ptr< ast::Expr > cond = new ast::UntypedExpr{ 290 loc, cmp, { new ast::VariableExpr{ loc, index }, end } }; 291 292 ast::ptr< ast::Expr > inc = new ast::UntypedExpr{ 293 loc, update, { new ast::VariableExpr{ loc, index } } }; 294 295 ast::ptr< ast::Expr > dstIndex = new ast::UntypedExpr{ 296 loc, new ast::NameExpr{ loc, "?[?]" }, 297 { dstParam, new ast::VariableExpr{ loc, index } } }; 291 ast::ptr< ast::Expr > indexVar = new ast::VariableExpr{ loc, index }; 292 293 ast::ptr< ast::Expr > cond = ast::call( loc, cmp, indexVar, end ); 294 295 ast::ptr< ast::Expr > inc = ast::call( loc, update, indexVar ); 296 297 ast::ptr< ast::Expr > dstIndex = ast::call( loc, "?[?]", dstParam, indexVar ); 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( new ast::VariableExpr{ loc, index }, array->dimension );301 srcParam.addArrayIndex( indexVar, 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 = nullptr;387 ast::ptr< ast::Type > addCast; 388 388 if ( (fname == "?{}" || fname == "^?{}") && ( ! obj || ( obj && ! obj->bitfieldWidth ) ) ) { 389 389 assert( dstParam->result ); -
src/SymTab/FixFunction.cc
r3c64c668 r58fe85a 106 106 bool isVoid = false; 107 107 108 void pre mutate( const ast::FunctionDecl * ) { visit_children = false; }108 void previsit( const ast::FunctionDecl * ) { visit_children = false; } 109 109 110 const ast::DeclWithType * post mutate( const ast::FunctionDecl * func ) {110 const ast::DeclWithType * postvisit( 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 mutate( const ast::ArrayType * ) { visit_children = false; }116 void previsit( const ast::ArrayType * ) { visit_children = false; } 117 117 118 const ast::Type * post mutate( const ast::ArrayType * array ) {118 const ast::Type * postvisit( 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 mutate( const ast::VoidType * ) { isVoid = true; }124 void previsit( const ast::VoidType * ) { isVoid = true; } 125 125 126 void pre mutate( const ast::BasicType * ) { visit_children = false; }127 void pre mutate( const ast::PointerType * ) { visit_children = false; }128 void pre mutate( const ast::StructInstType * ) { visit_children = false; }129 void pre mutate( const ast::UnionInstType * ) { visit_children = false; }130 void pre mutate( const ast::EnumInstType * ) { visit_children = false; }131 void pre mutate( const ast::TraitInstType * ) { visit_children = false; }132 void pre mutate( const ast::TypeInstType * ) { visit_children = false; }133 void pre mutate( const ast::TupleType * ) { visit_children = false; }134 void pre mutate( const ast::VarArgsType * ) { visit_children = false; }135 void pre mutate( const ast::ZeroType * ) { visit_children = false; }136 void pre mutate( const ast::OneType * ) { visit_children = false; }126 void previsit( const ast::BasicType * ) { visit_children = false; } 127 void previsit( const ast::PointerType * ) { visit_children = false; } 128 void previsit( const ast::StructInstType * ) { visit_children = false; } 129 void previsit( const ast::UnionInstType * ) { visit_children = false; } 130 void previsit( const ast::EnumInstType * ) { visit_children = false; } 131 void previsit( const ast::TraitInstType * ) { visit_children = false; } 132 void previsit( const ast::TypeInstType * ) { visit_children = false; } 133 void previsit( const ast::TupleType * ) { visit_children = false; } 134 void previsit( const ast::VarArgsType * ) { visit_children = false; } 135 void previsit( const ast::ZeroType * ) { visit_children = false; } 136 void previsit( const ast::OneType * ) { visit_children = false; } 137 137 }; 138 138 } // anonymous namespace … … 141 141 ast::Pass< FixFunction_new > fixer; 142 142 dwt = dwt->accept( fixer ); 143 isVoid |= fixer. pass.isVoid;143 isVoid |= fixer.core.isVoid; 144 144 return dwt; 145 145 } -
src/SymTab/Mangler.cc
r3c64c668 r58fe85a 10 10 // Created On : Sun May 17 21:40:29 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 15 13:55:12202013 // Update Count : 3312 // Last Modified On : Wed Nov 18 12:01:38 2020 13 // Update Count : 64 14 14 // 15 15 #include "Mangler.h" … … 65 65 void postvisit( const QualifiedType * qualType ); 66 66 67 std::string get_mangleName() { return mangleName .str(); }67 std::string get_mangleName() { return mangleName; } 68 68 private: 69 std:: ostringstream mangleName;///< Mangled name being constructed69 std::string 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;129 mangleName += Encoding::manglePrefix; 130 130 const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( declaration->get_name() ); 131 131 if ( opInfo ) { 132 mangleName << opInfo->outputName.size() <<opInfo->outputName;132 mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName; 133 133 } else { 134 mangleName << declaration->name.size() <<declaration->name;134 mangleName += std::to_string( 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 << refType->name.length() <<refType->name;224 mangleName += prefix + std::to_string( 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] << varNum->second.first;264 mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first ); 265 265 } // if 266 266 } … … 268 268 void Mangler_old::postvisit( const TraitInstType * inst ) { 269 269 printQualifiers( inst ); 270 mangleName << inst->name.size() <<inst->name;270 mangleName += std::to_string( 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 << tupleType->types.size();275 mangleName += Encoding::tuple + std::to_string( 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 << vargs.size() <<vargs;282 mangleName += Encoding::type + std::to_string( 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 ] << ( decl->name.length() ) <<decl->name;317 mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( 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 << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_"; 357 std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 358 mangleName << "_"; 356 mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_"; 357 for(const auto & a : assertionNames) mangleName += a; 358 // std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 359 mangleName += "_"; 359 360 } // if 360 361 if ( ! inFunctionType ) { 361 362 // these qualifiers do not distinguish the outermost type of a function parameter 362 363 if ( type->get_const() ) { 363 mangleName <<Encoding::qualifiers.at(Type::Const);364 mangleName += Encoding::qualifiers.at(Type::Const); 364 365 } // if 365 366 if ( type->get_volatile() ) { 366 mangleName <<Encoding::qualifiers.at(Type::Volatile);367 mangleName += Encoding::qualifiers.at(Type::Volatile); 367 368 } // if 368 369 // Removed due to restrict not affecting function compatibility in GCC 369 370 // if ( type->get_isRestrict() ) { 370 // mangleName <<"E";371 // mangleName += "E"; 371 372 // } // if 372 373 if ( type->get_atomic() ) { 373 mangleName <<Encoding::qualifiers.at(Type::Atomic);374 mangleName += Encoding::qualifiers.at(Type::Atomic); 374 375 } // if 375 376 } 376 377 if ( type->get_mutex() ) { 377 mangleName <<Encoding::qualifiers.at(Type::Mutex);378 mangleName += Encoding::qualifiers.at(Type::Mutex); 378 379 } // if 379 380 if ( inFunctionType ) { … … 383 384 } 384 385 } 385 } // namespace386 } // namespace 386 387 } // namespace Mangler 387 388 } // namespace SymTab … … 417 418 void postvisit( const ast::QualifiedType * qualType ); 418 419 419 std::string get_mangleName() { return mangleName .str(); }420 std::string get_mangleName() { return mangleName; } 420 421 private: 421 std:: ostringstream mangleName;///< Mangled name being constructed422 std::string mangleName; ///< Mangled name being constructed 422 423 typedef std::map< std::string, std::pair< int, int > > VarMapType; 423 424 VarMapType varNums; ///< Map of type variables to indices … … 437 438 private: 438 439 void mangleDecl( const ast::DeclWithType *declaration ); 439 void mangleRef( const ast:: ReferenceToType *refType, std::string prefix );440 void mangleRef( const ast::BaseInstType *refType, std::string prefix ); 440 441 441 442 void printQualifiers( const ast::Type *type ); … … 447 448 ast::Pass<Mangler_new> mangler( mode ); 448 449 maybeAccept( decl, mangler ); 449 return mangler. pass.get_mangleName();450 return mangler.core.get_mangleName(); 450 451 } 451 452 … … 470 471 isTopLevel = false; 471 472 } // if 472 mangleName <<Encoding::manglePrefix;473 mangleName += Encoding::manglePrefix; 473 474 const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( decl->name ); 474 475 if ( opInfo ) { 475 mangleName << opInfo->outputName.size() <<opInfo->outputName;476 mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName; 476 477 } else { 477 mangleName << decl->name.size() <<decl->name;478 mangleName += std::to_string( decl->name.size() ) + decl->name; 478 479 } // if 479 480 maybeAccept( decl->get_type(), *visitor ); … … 482 483 // so they need a different name mangling 483 484 if ( decl->linkage == ast::Linkage::AutoGen ) { 484 mangleName <<Encoding::autogen;485 mangleName += Encoding::autogen; 485 486 } else if ( decl->linkage == ast::Linkage::Intrinsic ) { 486 mangleName <<Encoding::intrinsic;487 mangleName += Encoding::intrinsic; 487 488 } else { 488 489 // if we add another kind of overridable function, this has to change … … 503 504 void Mangler_new::postvisit( const ast::VoidType * voidType ) { 504 505 printQualifiers( voidType ); 505 mangleName <<Encoding::void_t;506 mangleName += Encoding::void_t; 506 507 } 507 508 … … 509 510 printQualifiers( basicType ); 510 511 assertf( basicType->kind < ast::BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind ); 511 mangleName <<Encoding::basicTypes[ basicType->kind ];512 mangleName += Encoding::basicTypes[ basicType->kind ]; 512 513 } 513 514 … … 515 516 printQualifiers( pointerType ); 516 517 // mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers 517 if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName <<Encoding::pointer;518 if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName += Encoding::pointer; 518 519 maybe_accept( pointerType->base.get(), *visitor ); 519 520 } … … 522 523 // TODO: encode dimension 523 524 printQualifiers( arrayType ); 524 mangleName << Encoding::array <<"0";525 mangleName += Encoding::array + "0"; 525 526 maybeAccept( arrayType->base.get(), *visitor ); 526 527 } … … 545 546 void Mangler_new::postvisit( const ast::FunctionType * functionType ) { 546 547 printQualifiers( functionType ); 547 mangleName <<Encoding::function;548 mangleName += Encoding::function; 548 549 // turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters, 549 550 // since qualifiers on outermost parameter type do not differentiate function types, e.g., … … 551 552 GuardValue( inFunctionType ); 552 553 inFunctionType = true; 553 std::vector< ast::ptr< ast::Type > > returnTypes = getTypes( functionType->returns ); 554 if (returnTypes.empty()) mangleName << Encoding::void_t; 555 else accept_each( returnTypes, *visitor ); 556 mangleName << "_"; 557 std::vector< ast::ptr< ast::Type > > paramTypes = getTypes( functionType->params ); 558 accept_each( paramTypes, *visitor ); 559 mangleName << "_"; 560 } 561 562 void Mangler_new::mangleRef( const ast::ReferenceToType * refType, std::string prefix ) { 554 if (functionType->returns.empty()) mangleName += Encoding::void_t; 555 else accept_each( functionType->returns, *visitor ); 556 mangleName += "_"; 557 accept_each( functionType->params, *visitor ); 558 mangleName += "_"; 559 } 560 561 void Mangler_new::mangleRef( const ast::BaseInstType * refType, std::string prefix ) { 563 562 printQualifiers( refType ); 564 563 565 mangleName << prefix << refType->name.length() <<refType->name;564 mangleName += prefix + std::to_string( refType->name.length() ) + refType->name; 566 565 567 566 if ( mangleGenericParams ) { 568 567 if ( ! refType->params.empty() ) { 569 mangleName <<"_";568 mangleName += "_"; 570 569 for ( const ast::Expr * param : refType->params ) { 571 570 auto paramType = dynamic_cast< const ast::TypeExpr * >( param ); … … 573 572 maybeAccept( paramType->type.get(), *visitor ); 574 573 } 575 mangleName <<"_";574 mangleName += "_"; 576 575 } 577 576 } … … 602 601 // are first found and prefixing with the appropriate encoding for the type class. 603 602 assertf( varNum->second.second < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second ); 604 mangleName << Encoding::typeVariables[varNum->second.second] << varNum->second.first;603 mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first ); 605 604 } // if 606 605 } … … 608 607 void Mangler_new::postvisit( const ast::TraitInstType * inst ) { 609 608 printQualifiers( inst ); 610 mangleName << inst->name.size() <<inst->name;609 mangleName += std::to_string( inst->name.size() ) + inst->name; 611 610 } 612 611 613 612 void Mangler_new::postvisit( const ast::TupleType * tupleType ) { 614 613 printQualifiers( tupleType ); 615 mangleName << Encoding::tuple << tupleType->types.size();614 mangleName += Encoding::tuple + std::to_string( tupleType->types.size() ); 616 615 accept_each( tupleType->types, *visitor ); 617 616 } … … 620 619 printQualifiers( varArgsType ); 621 620 static const std::string vargs = "__builtin_va_list"; 622 mangleName << Encoding::type << vargs.size() <<vargs;621 mangleName += Encoding::type + std::to_string( vargs.size() ) + vargs; 623 622 } 624 623 625 624 void Mangler_new::postvisit( const ast::ZeroType * ) { 626 mangleName <<Encoding::zero;625 mangleName += Encoding::zero; 627 626 } 628 627 629 628 void Mangler_new::postvisit( const ast::OneType * ) { 630 mangleName <<Encoding::one;629 mangleName += Encoding::one; 631 630 } 632 631 … … 636 635 // N marks the start of a qualified type 637 636 inQualifiedType = true; 638 mangleName <<Encoding::qualifiedTypeStart;637 mangleName += Encoding::qualifiedTypeStart; 639 638 } 640 639 maybeAccept( qualType->parent.get(), *visitor ); … … 643 642 // E marks the end of a qualified type 644 643 inQualifiedType = false; 645 mangleName <<Encoding::qualifiedTypeEnd;644 mangleName += Encoding::qualifiedTypeEnd; 646 645 } 647 646 } … … 655 654 assertf(false, "Mangler_new should not visit typedecl: %s", toCString(decl)); 656 655 assertf( decl->kind < ast::TypeDecl::Kind::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind ); 657 mangleName << Encoding::typeVariables[ decl->kind ] << ( decl->name.length() ) <<decl->name;656 mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) + decl->name; 658 657 } 659 658 … … 667 666 // skip if not including qualifiers 668 667 if ( typeMode ) return; 669 if ( auto ptype = dynamic_cast< const ast:: ParameterizedType * >(type) ) {668 if ( auto ptype = dynamic_cast< const ast::FunctionType * >(type) ) { 670 669 if ( ! ptype->forall.empty() ) { 671 670 std::list< std::string > assertionNames; 672 671 int dcount = 0, fcount = 0, vcount = 0, acount = 0; 673 mangleName <<Encoding::forall;674 for ( const ast::TypeDecl *decl : ptype->forall ) {672 mangleName += Encoding::forall; 673 for ( auto & decl : ptype->forall ) { 675 674 switch ( decl->kind ) { 676 675 case ast::TypeDecl::Kind::Dtype: … … 687 686 } // switch 688 687 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 } // for696 688 } // for 697 mangleName << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_"; 698 std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 699 mangleName << "_"; 689 for ( auto & assert : ptype->assertions ) { 690 ast::Pass<Mangler_new> sub_mangler( 691 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ); 692 assert->var->accept( sub_mangler ); 693 assertionNames.push_back( sub_mangler.core.get_mangleName() ); 694 acount++; 695 } // for 696 mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_"; 697 for(const auto & a : assertionNames) mangleName += a; 698 // std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) ); 699 mangleName += "_"; 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
r3c64c668 r58fe85a 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 build 66 67 #include "Concurrency/Keywords.h" // for applyKeywords 67 68 #include "FixFunction.h" // for FixFunction … … 270 271 }; 271 272 272 struct ArrayLength : public WithIndexer{273 struct InitializerLength { 273 274 /// for array types without an explicit length, compute the length and store it so that it 274 275 /// is known to the rest of the phases. For example, … … 281 282 282 283 void previsit( ObjectDecl * objDecl ); 284 }; 285 286 struct ArrayLength : public WithIndexer { 287 static void computeLength( std::list< Declaration * > & translationUnit ); 288 283 289 void previsit( ArrayType * arrayType ); 284 290 }; … … 368 374 mutateAll( translationUnit, compoundliteral ); 369 375 }); 370 Stats::Time::TimeBlock("Resolve With Expressions", [&]() { 371 ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables 372 }); 376 if (!useNewAST) { 377 Stats::Time::TimeBlock("Resolve With Expressions", [&]() { 378 ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables 379 }); 380 } 373 381 } 374 382 { 375 383 Stats::Heap::newPass("validate-F"); 376 384 Stats::Time::BlockGuard guard("validate-F"); 377 Stats::Time::TimeCall("Fix Object Type", 378 FixObjectType::fix, translationUnit); 379 Stats::Time::TimeCall("Array Length", 380 ArrayLength::computeLength, translationUnit); 385 if (!useNewAST) { 386 Stats::Time::TimeCall("Fix Object Type", 387 FixObjectType::fix, translationUnit); 388 } 389 Stats::Time::TimeCall("Initializer Length", 390 InitializerLength::computeLength, translationUnit); 391 if (!useNewAST) { 392 Stats::Time::TimeCall("Array Length", 393 ArrayLength::computeLength, translationUnit); 394 } 381 395 Stats::Time::TimeCall("Find Special Declarations", 382 396 Validate::findSpecialDecls, translationUnit); 383 397 Stats::Time::TimeCall("Fix Label Address", 384 398 mutateAll<LabelAddressFixer>, translationUnit, labelAddrFixer); 385 Stats::Time::TimeCall("Handle Attributes", 386 Validate::handleAttributes, translationUnit); 399 if (!useNewAST) { 400 Stats::Time::TimeCall("Handle Attributes", 401 Validate::handleAttributes, translationUnit); 402 } 387 403 } 388 404 } … … 960 976 } 961 977 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 962 990 Type * ReplaceTypedef::postmutate( TypeInstType * typeInst ) { 963 991 // instances of typedef types will come here. If it is an instance … … 968 996 ret->location = typeInst->location; 969 997 ret->get_qualifiers() |= typeInst->get_qualifiers(); 970 // attributes are not carried over from typedef to function parameters/return values 971 if ( ! inFunctionType ) { 972 ret->attributes.splice( ret->attributes.end(), typeInst->attributes ); 973 } else { 974 deleteAll( ret->attributes ); 975 ret->attributes.clear(); 976 } 998 // GCC ignores certain attributes if they arrive by typedef, this mimics that. 999 if ( inFunctionType ) { 1000 ret->attributes.remove_if( isNonParameterAttribute ); 1001 } 1002 ret->attributes.splice( ret->attributes.end(), typeInst->attributes ); 977 1003 // place instance parameters on the typedef'd type 978 1004 if ( ! typeInst->parameters.empty() ) { … … 1315 1341 } 1316 1342 1343 void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) { 1344 PassVisitor<InitializerLength> len; 1345 acceptAll( translationUnit, len ); 1346 } 1347 1317 1348 void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) { 1318 1349 PassVisitor<ArrayLength> len; … … 1320 1351 } 1321 1352 1322 void ArrayLength::previsit( ObjectDecl * objDecl ) {1353 void InitializerLength::previsit( ObjectDecl * objDecl ) { 1323 1354 if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) { 1324 1355 if ( at->dimension ) return; … … 1374 1405 /// Replaces enum types by int, and function/array types in function parameter and return 1375 1406 /// lists by appropriate pointers 1407 /* 1376 1408 struct EnumAndPointerDecay_new { 1377 1409 const ast::EnumDecl * previsit( const ast::EnumDecl * enumDecl ) { … … 1424 1456 } 1425 1457 }; 1458 */ 1426 1459 1427 1460 /// expand assertions from a trait instance, performing appropriate type variable substitutions … … 1442 1475 } 1443 1476 1477 /* 1478 1444 1479 /// Associates forward declarations of aggregates with their definitions 1445 1480 class LinkReferenceToTypes_new final … … 1508 1543 } 1509 1544 1510 void checkGenericParameters( const ast:: ReferenceToType * inst ) {1545 void checkGenericParameters( const ast::BaseInstType * inst ) { 1511 1546 for ( const ast::Expr * param : inst->params ) { 1512 1547 if ( ! dynamic_cast< const ast::TypeExpr * >( param ) ) { … … 1772 1807 static const node_t * forallFixer( 1773 1808 const CodeLocation & loc, const node_t * node, 1774 ast:: ParameterizedType::ForallList parent_t::* forallField1809 ast::FunctionType::ForallList parent_t::* forallField 1775 1810 ) { 1776 1811 for ( unsigned i = 0; i < (node->* forallField).size(); ++i ) { … … 1823 1858 } 1824 1859 }; 1860 */ 1825 1861 } // anonymous namespace 1826 1862 1863 /* 1827 1864 const ast::Type * validateType( 1828 1865 const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) { 1829 ast::Pass< EnumAndPointerDecay_new > epc;1866 // ast::Pass< EnumAndPointerDecay_new > epc; 1830 1867 ast::Pass< LinkReferenceToTypes_new > lrt{ loc, symtab }; 1831 1868 ast::Pass< ForallPointerDecay_new > fpd{ loc }; 1832 1869 1833 return type->accept( epc )->accept(lrt )->accept( fpd );1870 return type->accept( lrt )->accept( fpd ); 1834 1871 } 1872 */ 1835 1873 1836 1874 } // namespace SymTab -
src/SymTab/module.mk
r3c64c668 r58fe85a 17 17 SRC_SYMTAB = \ 18 18 SymTab/Autogen.cc \ 19 SymTab/Autogen.h \ 19 20 SymTab/FixFunction.cc \ 21 SymTab/FixFunction.h \ 20 22 SymTab/Indexer.cc \ 23 SymTab/Indexer.h \ 21 24 SymTab/Mangler.cc \ 22 25 SymTab/ManglerCommon.cc \ 23 SymTab/Validate.cc 26 SymTab/Mangler.h \ 27 SymTab/Validate.cc \ 28 SymTab/Validate.h 24 29 25 30 SRC += $(SRC_SYMTAB) -
src/SynTree/AggregateDecl.cc
r3c64c668 r58fe85a 21 21 #include "Common/utility.h" // for printAll, cloneAll, deleteAll 22 22 #include "Declaration.h" // for AggregateDecl, TypeDecl, Declaration 23 #include "Expression.h" 23 24 #include "Initializer.h" 24 25 #include "LinkageSpec.h" // for Spec, linkageName, Cforall … … 88 89 const char * StructDecl::typeString() const { return aggrString( kind ); } 89 90 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 90 104 const char * UnionDecl::typeString() const { return aggrString( Union ); } 91 105 -
src/SynTree/ApplicationExpr.cc
r3c64c668 r58fe85a 34 34 35 35 ParamEntry::ParamEntry( const ParamEntry &other ) : 36 decl( other.decl ), declptr( maybeClone( other.declptr )), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) ) {36 decl( other.decl ), declptr( other.declptr ), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) ) { 37 37 } 38 38 39 39 ParamEntry::~ParamEntry() { 40 delete declptr;40 // delete declptr; 41 41 delete actualType; 42 42 delete formalType; -
src/SynTree/Declaration.h
r3c64c668 r58fe85a 181 181 public: 182 182 Type * base; 183 std::list< TypeDecl * > parameters;184 183 std::list< DeclarationWithType * > assertions; 185 184 … … 190 189 Type * get_base() const { return base; } 191 190 void set_base( Type * newValue ) { base = newValue; } 192 std::list< TypeDecl* > & get_parameters() { return parameters; }193 191 std::list< DeclarationWithType * >& get_assertions() { return assertions; } 194 192 … … 302 300 303 301 bool is_coroutine() { return kind == Coroutine; } 304 bool is_monitor() { return kind == Monitor; } 305 bool is_thread() { return kind == Thread; } 302 bool is_generator() { return kind == Generator; } 303 bool is_monitor () { return kind == Monitor ; } 304 bool is_thread () { return kind == Thread ; } 305 306 // Make a type instance of this declaration. 307 StructInstType * makeInst( std::list< Expression * > const & parameters ); 308 StructInstType * makeInst( std::list< Expression * > && parameters ); 306 309 307 310 virtual StructDecl * clone() const override { return new StructDecl( *this ); } -
src/SynTree/Expression.cc
r3c64c668 r58fe85a 30 30 #include "Type.h" // for Type, BasicType, Type::Qualifiers 31 31 #include "TypeSubstitution.h" // for TypeSubstitution 32 #include "CompilationState.h" // for deterministic_output 32 33 33 34 #include "GenPoly/Lvalue.h" … … 70 71 printInferParams( inferParams, os, indent+1, 0 ); 71 72 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 72 79 if ( env ) { 73 80 os << std::endl << indent << "... with environment:" << std::endl; … … 293 300 } 294 301 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 } 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 ) {} 300 306 301 307 KeywordCastExpr::~KeywordCastExpr() { -
src/SynTree/Expression.h
r3c64c668 r58fe85a 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 165 190 // The following classes are used to represent expression types that cannot be converted into 166 191 // function-call format. … … 206 231 public: 207 232 Expression * arg; 208 bool isGenerated = true; // cast generated implicitly by code generation or explicit in program 233 234 // Inidicates cast is introduced by the CFA type system. 235 // true for casts that the resolver introduces to force a return type 236 // false for casts from user code 237 // false for casts from desugaring advanced CFA features into simpler CFA 238 // example 239 // int * p; // declaration 240 // (float *) p; // use, with subject cast 241 // subject cast isGenerated means we are considering an interpretation with a type mismatch 242 // subject cast not isGenerated means someone in charge wants it that way 243 bool isGenerated = true; 209 244 210 245 CastExpr( Expression * arg, bool isGenerated = true ); … … 238 273 239 274 KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target ); 275 KeywordCastExpr( Expression * arg, AggregateDecl::Aggregate target, const Concrete & concrete_target ); 240 276 KeywordCastExpr( const KeywordCastExpr & other ); 241 277 virtual ~KeywordCastExpr(); … … 312 348 313 349 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 ); }339 350 virtual void accept( Visitor & v ) override { v.visit( this ); } 340 351 virtual void accept( Visitor & v ) const override { v.visit( this ); } -
src/SynTree/LinkageSpec.cc
r3c64c668 r58fe85a 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Sat May 16 13:22:09 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Dec 16 15:02:29 201913 // Update Count : 2 811 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Mar 2 16:13:00 2020 13 // Update Count : 29 14 14 // 15 15 … … 20 20 21 21 #include "LinkageSpec.h" 22 #include "Common/CodeLocation.h" 22 23 #include "Common/SemanticError.h" 23 24 -
src/SynTree/LinkageSpec.h
r3c64c668 r58fe85a 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Sat May 16 13:24:28 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Dec 16 15:03:43 201913 // Update Count : 2 011 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Mar 2 16:13:00 2020 13 // Update Count : 21 14 14 // 15 15 … … 18 18 #include <string> 19 19 20 #include "Common/CodeLocation.h" 20 struct CodeLocation; 21 21 22 22 namespace LinkageSpec { -
src/SynTree/Mutator.h
r3c64c668 r58fe85a 51 51 virtual Statement * mutate( CatchStmt * catchStmt ) = 0; 52 52 virtual Statement * mutate( FinallyStmt * catchStmt ) = 0; 53 virtual Statement * mutate( SuspendStmt * suspendStmt ) = 0; 53 54 virtual Statement * mutate( WaitForStmt * waitforStmt ) = 0; 54 55 virtual Declaration * mutate( WithStmt * withStmt ) = 0; -
src/SynTree/NamedTypeDecl.cc
r3c64c668 r58fe85a 22 22 #include "LinkageSpec.h" // for Spec, Cforall, linkageName 23 23 #include "Type.h" // for Type, Type::StorageClasses 24 #include "CompilationState.h" 24 25 25 26 NamedTypeDecl::NamedTypeDecl( const std::string &name, Type::StorageClasses scs, Type *base ) … … 28 29 NamedTypeDecl::NamedTypeDecl( const NamedTypeDecl &other ) 29 30 : 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 );37 36 deleteAll( assertions ); 38 37 } … … 41 40 using namespace std; 42 41 43 if ( name != "" ) os << name << ": "; 42 if ( ! name.empty() ) { 43 if( deterministic_output && isUnboundType(name) ) os << "[unbound]:"; 44 else os << name << ": "; 45 } 44 46 45 47 if ( linkage != LinkageSpec::Cforall ) { … … 51 53 os << " for "; 52 54 base->print( os, indent+1 ); 53 } // if54 if ( ! parameters.empty() ) {55 os << endl << indent << "... with parameters" << endl;56 printAll( parameters, os, indent+1 );57 55 } // if 58 56 if ( ! assertions.empty() ) { … … 72 70 base->print( os, indent+1 ); 73 71 } // if 74 if ( ! parameters.empty() ) {75 os << endl << indent << "... with parameters" << endl;76 printAll( parameters, os, indent+1 );77 } // if78 72 } 79 73 -
src/SynTree/ReferenceToType.cc
r3c64c668 r58fe85a 24 24 #include "Type.h" // for TypeInstType, StructInstType, UnionInstType 25 25 #include "TypeSubstitution.h" // for TypeSubstitution 26 #include "CompilationState.h" 26 27 27 28 class Attribute; … … 205 206 206 207 Type::print( os, indent ); 207 os << "instance of " << typeString() << " " << get_name() << " (" << ( isFtype ? "" : "not" ) << " function type)"; 208 os << "instance of " << typeString() << " "; 209 const auto & name_ = get_name(); 210 if( deterministic_output && isUnboundType(name) ) os << "[unbound]"; 211 else os << name; 212 os << " (" << ( isFtype ? "" : "not" ) << " function type)"; 208 213 if ( ! parameters.empty() ) { 209 214 os << endl << indent << "... with parameters" << endl; -
src/SynTree/Statement.cc
r3c64c668 r58fe85a 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 422 447 WaitForStmt::WaitForStmt() : Statement() { 423 448 timeout.time = nullptr; -
src/SynTree/Statement.h
r3c64c668 r58fe85a 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 424 440 class WaitForStmt : public Statement { 425 441 public: … … 502 518 class ImplicitCtorDtorStmt : public Statement { 503 519 public: 504 // Non-owned pointer to the constructor/destructor statement520 // the constructor/destructor call statement; owned here for a while, eventually transferred elsewhere 505 521 Statement * callStmt; 506 522 -
src/SynTree/SynTree.h
r3c64c668 r58fe85a 54 54 class CatchStmt; 55 55 class FinallyStmt; 56 class SuspendStmt; 56 57 class WaitForStmt; 57 58 class WithStmt; -
src/SynTree/Type.cc
r3c64c668 r58fe85a 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 check 163 // if the context id is filled. this is a temporary hack for now 164 return isUnboundType(typeInst->name); 165 } 166 return false; 167 } 168 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 check 173 // if the context id is filled. this is a temporary hack for now 174 if (std::count(tname.begin(), tname.end(), '_') >= 3) { 175 return true; 176 } 177 return false; 178 } 179 158 180 // Local Variables: // 159 181 // tab-width: 4 // -
src/SynTree/Type.h
r3c64c668 r58fe85a 733 733 }; 734 734 735 736 bool isUnboundType(const Type * type); 737 bool isUnboundType(const std::string & tname); 738 735 739 // Local Variables: // 736 740 // tab-width: 4 // -
src/SynTree/TypeDecl.cc
r3c64c668 r58fe85a 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Dec 13 15:26:14 201913 // Update Count : 2 112 // Last Modified On : Thu Oct 8 18:18:55 2020 13 // Update Count : 22 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 ) : 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 ) : 24 Parent( name, scs, type ), kind( kind ), sized( kind == Ttype || sized ), init( init ) { 24 25 } 25 26 -
src/SynTree/Visitor.h
r3c64c668 r58fe85a 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; 80 82 virtual void visit( WaitForStmt * node ) { visit( const_cast<const WaitForStmt *>(node) ); } 81 83 virtual void visit( const WaitForStmt * waitforStmt ) = 0; -
src/SynTree/module.mk
r3c64c668 r58fe85a 20 20 SynTree/ApplicationExpr.cc \ 21 21 SynTree/ArrayType.cc \ 22 SynTree/Attribute.cc \ 23 SynTree/Attribute.h \ 22 24 SynTree/AttrType.cc \ 23 SynTree/ Attribute.cc\25 SynTree/BaseSyntaxNode.h \ 24 26 SynTree/BasicType.cc \ 25 27 SynTree/CommaExpr.cc \ 26 28 SynTree/CompoundStmt.cc \ 27 29 SynTree/Constant.cc \ 30 SynTree/Constant.h \ 31 SynTree/Declaration.cc \ 32 SynTree/Declaration.h \ 33 SynTree/DeclarationWithType.cc \ 28 34 SynTree/DeclReplacer.cc \ 35 SynTree/DeclReplacer.h \ 29 36 SynTree/DeclStmt.cc \ 30 SynTree/Declaration.cc \31 SynTree/DeclarationWithType.cc \32 37 SynTree/Expression.cc \ 38 SynTree/Expression.h \ 33 39 SynTree/FunctionDecl.cc \ 34 40 SynTree/FunctionType.cc \ 35 41 SynTree/Initializer.cc \ 42 SynTree/Initializer.h \ 43 SynTree/Label.h \ 36 44 SynTree/LinkageSpec.cc \ 45 SynTree/LinkageSpec.h \ 46 SynTree/Mutator.h \ 37 47 SynTree/NamedTypeDecl.cc \ 38 48 SynTree/ObjectDecl.cc \ … … 41 51 SynTree/ReferenceType.cc \ 42 52 SynTree/Statement.cc \ 53 SynTree/Statement.h \ 54 SynTree/SynTree.h \ 43 55 SynTree/TupleExpr.cc \ 44 56 SynTree/TupleType.cc \ … … 46 58 SynTree/TypeDecl.cc \ 47 59 SynTree/TypeExpr.cc \ 60 SynTree/Type.h \ 61 SynTree/TypeofType.cc \ 48 62 SynTree/TypeSubstitution.cc \ 49 SynTree/Type ofType.cc\63 SynTree/TypeSubstitution.h \ 50 64 SynTree/VarArgsType.cc \ 65 SynTree/Visitor.h \ 51 66 SynTree/VoidType.cc \ 52 67 SynTree/ZeroOneType.cc -
src/Tuples/Explode.cc
r3c64c668 r58fe85a 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 ) ) );132 131 } 133 132 if ( first ) { … … 148 147 } 149 148 150 const ast::Expr * post mutate( const ast::UniqueExpr * node ) {149 const ast::Expr * postvisit( const ast::UniqueExpr * node ) { 151 150 // move cast into unique expr so that the unique expr has type T& rather than 152 151 // type T. In particular, this transformation helps with generating the … … 162 161 castAdded = false; 163 162 const ast::Type * newType = getReferenceBase( newNode->result ); 164 return new ast::CastExpr{ newNode->location, n ode, newType };163 return new ast::CastExpr{ newNode->location, newNode, newType }; 165 164 } 166 165 return newNode; 167 166 } 168 167 169 const ast::Expr * post mutate( const ast::TupleIndexExpr * tupleExpr ) {168 const ast::Expr * postvisit( const ast::TupleIndexExpr * tupleExpr ) { 170 169 // tuple index expr needs to be rebuilt to ensure that the type of the 171 170 // field is consistent with the type of the tuple expr, since the field … … 180 179 ast::Pass<CastExploderCore> exploder; 181 180 expr = expr->accept( exploder ); 182 if ( ! exploder. pass.foundUniqueExpr ) {181 if ( ! exploder.core.foundUniqueExpr ) { 183 182 expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } }; 184 183 } -
src/Tuples/Explode.h
r3c64c668 r58fe85a 210 210 } 211 211 // Cast a reference away to a value-type to allow further explosion. 212 if ( dynamic_cast< const ast::ReferenceType *>( local->result.get()) ) {212 if ( local->result.as< ast::ReferenceType >() ) { 213 213 local = new ast::CastExpr{ local, tupleType }; 214 214 } … … 220 220 // delete idx; 221 221 } 222 // delete local;223 222 } 224 223 } else { -
src/Tuples/TupleAssignment.cc
r3c64c668 r58fe85a 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. symtab);467 InitTweak::genCtorInit( location, ret ), spotter.crntFinder.localSyms ); 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. symtab, env };562 ResolvExpr::CandidateFinder finder{ spotter.crntFinder.localSyms, 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. symtab, back_inserter(lhs), true );611 explode( *lhsCand, crntFinder.localSyms, 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. symtab, back_inserter(rhs), true );631 explode( *rhsCand, crntFinder.localSyms, 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. symtab, back_inserter(rhs), true );650 explode( rhsCand, crntFinder.localSyms, 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. symtab, matcher->env };680 ResolvExpr::CandidateFinder finder{ crntFinder.localSyms, matcher->env }; 681 681 682 682 try { -
src/Tuples/TupleExpansion.cc
r3c64c668 r58fe85a 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 | ast::CV::Lvalue |325 ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict | 326 326 ast::CV::Atomic | ast::CV::Mutex }; 327 327 -
src/Tuples/Tuples.cc
r3c64c668 r58fe85a 43 43 }; 44 44 struct ImpurityDetectorIgnoreUnique : public ImpurityDetector { 45 using ImpurityDetector::previsit; 45 46 void previsit( ast::UniqueExpr const * ) { 46 47 visit_children = false; … … 52 53 ast::Pass<Detector> detector; 53 54 expr->accept( detector ); 54 return detector. pass.maybeImpure;55 return detector.core.maybeImpure; 55 56 } 56 57 } // namespace -
src/Tuples/module.mk
r3c64c668 r58fe85a 15 15 ############################################################################### 16 16 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 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) -
src/Validate/module.mk
r3c64c668 r58fe85a 15 15 ############################################################################### 16 16 17 SRC += Validate/HandleAttributes.cc Validate/ FindSpecialDecls.cc18 SRCDEMANGLE += Validate/HandleAttributes.cc Validate/ FindSpecialDecls.cc17 SRC += Validate/HandleAttributes.cc Validate/HandleAttributes.h Validate/FindSpecialDecls.cc Validate/FindSpecialDecls.h 18 SRCDEMANGLE += Validate/HandleAttributes.cc Validate/HandleAttributes.h Validate/FindSpecialDecls.cc Validate/FindSpecialDecls.h -
src/Virtual/ExpandCasts.cc
r3c64c668 r58fe85a 10 10 // Created On : Mon Jul 24 13:59:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Tus Aug 2 14:59:00 201713 // Update Count : 112 // Last Modified On : Fri Jul 31 10:29:00 2020 13 // Update Count : 4 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...21 20 #include <string> // for string, allocator, operator==, ope... 22 #include <utility> // for pair23 21 24 22 #include "Common/PassVisitor.h" // for PassVisitor 23 #include "Common/ScopedMap.h" // for ScopedMap 25 24 #include "Common/SemanticError.h" // for SemanticError 25 #include "SymTab/Mangler.h" // for mangleType 26 26 #include "SynTree/Declaration.h" // for ObjectDecl, StructDecl, FunctionDecl 27 27 #include "SynTree/Expression.h" // for VirtualCastExpr, CastExpr, Address... … … 31 31 32 32 namespace Virtual { 33 34 // Indented until the new ast code gets added. 35 36 /// Maps virtual table types the instance for that type. 37 class VirtualTableMap final { 38 ScopedMap<std::string, ObjectDecl *> vtable_instances; 39 public: 40 void enterScope() { 41 vtable_instances.beginScope(); 42 } 43 void leaveScope() { 44 vtable_instances.endScope(); 45 } 46 47 ObjectDecl * insert( ObjectDecl * vtableDecl ) { 48 std::string const & mangledName = SymTab::Mangler::mangleType( vtableDecl->type ); 49 ObjectDecl *& value = vtable_instances[ mangledName ]; 50 if ( value ) { 51 if ( vtableDecl->storageClasses.is_extern ) { 52 return nullptr; 53 } else if ( ! value->storageClasses.is_extern ) { 54 return value; 55 } 56 } 57 value = vtableDecl; 58 return nullptr; 59 } 60 61 ObjectDecl * lookup( const Type * vtableType ) { 62 std::string const & mangledName = SymTab::Mangler::mangleType( vtableType ); 63 const auto it = vtable_instances.find( mangledName ); 64 return ( vtable_instances.end() == it ) ? nullptr : it->second; 65 } 66 }; 33 67 34 68 /* Currently virtual depends on the rather brittle name matching between … … 39 73 */ 40 74 75 namespace { 76 41 77 std::string get_vtable_name( std::string const & name ) { 42 78 return name + "_vtable"; … … 53 89 std::string get_vtable_inst_name_root( std::string const & name ) { 54 90 return get_vtable_name_root( name.substr(1, name.size() - 10 ) ); 55 }56 57 bool is_vtable_name( std::string const & name ) {58 return (name.substr( name.size() - 7 ) == "_vtable" );59 91 } 60 92 … … 64 96 } 65 97 98 } // namespace 99 66 100 class VirtualCastCore { 67 std::map<std::string, ObjectDecl *> vtable_instances;68 FunctionDecl *vcast_decl;69 StructDecl *pvt_decl;70 71 101 Type * pointer_to_pvt(int level_of_indirection) { 72 102 Type * type = new StructInstType( … … 80 110 public: 81 111 VirtualCastCore() : 82 vtable_instances(), vcast_decl( nullptr ), pvt_decl( nullptr )112 indexer(), vcast_decl( nullptr ), pvt_decl( nullptr ) 83 113 {} 84 114 … … 88 118 89 119 Expression * postmutate( VirtualCastExpr * castExpr ); 120 121 VirtualTableMap indexer; 122 private: 123 FunctionDecl *vcast_decl; 124 StructDecl *pvt_decl; 90 125 }; 91 126 … … 107 142 void VirtualCastCore::premutate( ObjectDecl * objectDecl ) { 108 143 if ( is_vtable_inst_name( objectDecl->get_name() ) ) { 109 vtable_instances[objectDecl->get_name()] = objectDecl; 110 } 111 } 144 if ( ObjectDecl * existing = indexer.insert( objectDecl ) ) { 145 std::string msg = "Repeated instance of virtual table, original found at: "; 146 msg += existing->location.filename; 147 msg += ":" + toString( existing->location.first_line ); 148 SemanticError( objectDecl->location, msg ); 149 } 150 } 151 } 152 153 namespace { 154 155 /// Better error locations for generated casts. 156 CodeLocation castLocation( const VirtualCastExpr * castExpr ) { 157 if ( castExpr->location.isSet() ) { 158 return castExpr->location; 159 } else if ( castExpr->arg->location.isSet() ) { 160 return castExpr->arg->location; 161 } else if ( castExpr->result->location.isSet() ) { 162 return castExpr->result->location; 163 } else { 164 return CodeLocation(); 165 } 166 } 167 168 [[noreturn]] void castError( const VirtualCastExpr * castExpr, std::string const & message ) { 169 SemanticError( castLocation( castExpr ), message ); 170 } 171 172 /// Get the virtual table type used in a virtual cast. 173 Type * getVirtualTableType( const VirtualCastExpr * castExpr ) { 174 const Type * objectType; 175 if ( auto target = dynamic_cast<const PointerType *>( castExpr->result ) ) { 176 objectType = target->base; 177 } else if ( auto target = dynamic_cast<const ReferenceType *>( castExpr->result ) ) { 178 objectType = target->base; 179 } else { 180 castError( castExpr, "Virtual cast type must be a pointer or reference type." ); 181 } 182 assert( objectType ); 183 184 const StructInstType * structType = dynamic_cast<const StructInstType *>( objectType ); 185 if ( nullptr == structType ) { 186 castError( castExpr, "Virtual cast type must refer to a structure type." ); 187 } 188 const StructDecl * structDecl = structType->baseStruct; 189 assert( structDecl ); 190 191 const ObjectDecl * fieldDecl = nullptr; 192 if ( 0 < structDecl->members.size() ) { 193 const Declaration * memberDecl = structDecl->members.front(); 194 assert( memberDecl ); 195 fieldDecl = dynamic_cast<const ObjectDecl *>( memberDecl ); 196 if ( fieldDecl && fieldDecl->name != "virtual_table" ) { 197 fieldDecl = nullptr; 198 } 199 } 200 if ( nullptr == fieldDecl ) { 201 castError( castExpr, "Virtual cast type must have a leading virtual_table field." ); 202 } 203 const PointerType * fieldType = dynamic_cast<const PointerType *>( fieldDecl->type ); 204 if ( nullptr == fieldType ) { 205 castError( castExpr, "Virtual cast type virtual_table field is not a pointer." ); 206 } 207 assert( fieldType->base ); 208 auto virtualStructType = dynamic_cast<const StructInstType *>( fieldType->base ); 209 assert( virtualStructType ); 210 211 // Here is the type, but if it is polymorphic it will have lost information. 212 // (Always a clone so that it may always be deleted.) 213 StructInstType * virtualType = virtualStructType->clone(); 214 if ( ! structType->parameters.empty() ) { 215 deleteAll( virtualType->parameters ); 216 virtualType->parameters.clear(); 217 cloneAll( structType->parameters, virtualType->parameters ); 218 } 219 return virtualType; 220 } 221 222 } // namespace 112 223 113 224 Expression * VirtualCastCore::postmutate( VirtualCastExpr * castExpr ) { 114 assertf( castExpr-> get_result(), "Virtual Cast target not found before expansion." );225 assertf( castExpr->result, "Virtual Cast target not found before expansion." ); 115 226 116 227 assert( vcast_decl ); 117 228 assert( pvt_decl ); 118 229 119 // May only cast to a pointer or reference type. 120 // A earlier validation should give a syntax error, this is 121 // just to make sure errors don't creep during translation. 122 // Move to helper with more detailed error messages. 123 PointerType * target_type = 124 dynamic_cast<PointerType *>( castExpr->get_result() ); 125 assert( target_type ); 126 127 StructInstType * target_struct = 128 dynamic_cast<StructInstType *>( target_type->get_base() ); 129 assert( target_struct ); 130 131 StructDecl * target_decl = target_struct->get_baseStruct(); 132 133 std::map<std::string, ObjectDecl *>::iterator found = 134 vtable_instances.find( 135 get_vtable_inst_name( target_decl->get_name() ) ); 136 if ( vtable_instances.end() == found ) { 137 assertf( false, "virtual table instance not found." ); 138 } 139 ObjectDecl * table = found->second; 230 const Type * vtable_type = getVirtualTableType( castExpr ); 231 ObjectDecl * table = indexer.lookup( vtable_type ); 232 if ( nullptr == table ) { 233 SemanticError( castLocation( castExpr ), 234 "Could not find virtual table instance." ); 235 } 140 236 141 237 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 // ),149 238 new ApplicationExpr( VariableExpr::functionPointer( vcast_decl ), { 150 239 new CastExpr( … … 163 252 castExpr->set_result( nullptr ); 164 253 delete castExpr; 254 delete vtable_type; 165 255 return result; 166 256 } -
src/Virtual/module.mk
r3c64c668 r58fe85a 15 15 ############################################################################### 16 16 17 SRC += Virtual/ExpandCasts.cc 17 SRC += Virtual/ExpandCasts.cc Virtual/ExpandCasts.h \ 18 Virtual/Tables.cc Virtual/Tables.h 19 20 SRCDEMANGLE += Virtual/Tables.cc -
src/config.h.in
r3c64c668 r58fe85a 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 be 31 overrided by --old-ast and --new-ast */ 32 #undef CFA_USE_NEW_AST 29 33 30 34 /* Major.Minor */ -
src/main.cc
r3c64c668 r58fe85a 9 9 // Author : Peter Buhr and Rob Schluntz 10 10 // Created On : Fri May 15 23:12:02 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Feb 8 08:33:50 202013 // Update Count : 63 311 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Dec 7 15:29:00 2020 13 // Update Count : 639 14 14 // 15 15 … … 31 31 using namespace std; 32 32 33 33 #include "AST/Convert.hpp" 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 forceFillCodeLocations 42 43 #include "Common/CompilerError.h" // for CompilerError 43 44 #include "Common/Stats.h" … … 312 313 } // if 313 314 315 PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) ); 314 316 PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) ); 315 317 PASS( "Fix Names", CodeGen::fixNames( translationUnit ) ); … … 339 341 } // if 340 342 341 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) ); 342 if ( exprp ) { 343 dump( translationUnit ); 344 return EXIT_SUCCESS; 345 } // if 343 if( useNewAST ) { 344 if (Stats::Counters::enabled) { 345 ast::pass_visitor_stats.avg = Stats::Counters::build<Stats::Counters::AverageCounter<double>>("Average Depth - New"); 346 ast::pass_visitor_stats.max = Stats::Counters::build<Stats::Counters::MaxCounter<double>>("Max depth - New"); 347 } 348 auto transUnit = convert( move( translationUnit ) ); 349 PASS( "Resolve", ResolvExpr::resolve( transUnit ) ); 350 if ( exprp ) { 351 translationUnit = convert( move( transUnit ) ); 352 dump( translationUnit ); 353 return EXIT_SUCCESS; 354 } // if 355 356 forceFillCodeLocations( transUnit ); 357 358 PASS( "Fix Init", InitTweak::fix(transUnit, buildingLibrary())); 359 translationUnit = convert( move( transUnit ) ); 360 } else { 361 PASS( "Resolve", ResolvExpr::resolve( translationUnit ) ); 362 if ( exprp ) { 363 dump( translationUnit ); 364 return EXIT_SUCCESS; 365 } 366 367 PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) ); 368 } 346 369 347 370 // fix ObjectDecl - replaces ConstructorInit nodes 348 PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) );349 371 if ( ctorinitp ) { 350 372 dump ( translationUnit ); … … 354 376 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 355 377 356 PASS( "Translate EHM" , ControlStruct::translateEHM( translationUnit ) );378 PASS( "Translate Tries" , ControlStruct::translateTries( translationUnit ) ); 357 379 358 380 PASS( "Gen Waitfor" , Concurrency::generateWaitFor( translationUnit ) ); … … 443 465 444 466 445 static const char optstring[] = ":c:ghlLmNnp P:S:twW:D:";467 static const char optstring[] = ":c:ghlLmNnpdOAP:S:twW:D:"; 446 468 447 469 enum { PreludeDir = 128 }; … … 456 478 { "no-prelude", no_argument, nullptr, 'n' }, 457 479 { "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'}, 458 483 { "print", required_argument, nullptr, 'P' }, 459 484 { "prelude-dir", required_argument, nullptr, PreludeDir }, … … 467 492 468 493 static const char * description[] = { 469 "diagnostic color: never, always, or auto.", // -c470 "wait for gdb to attach", // -g471 "print help message", // -h472 "generate libcfa.c", // -l473 "generate line marks", // -L474 "do not replace main", // -m475 "do not generate line marks", // -N476 "do not read prelude", // -n494 "diagnostic color: never, always, or auto.", // -c 495 "wait for gdb to attach", // -g 496 "print help message", // -h 497 "generate libcfa.c", // -l 498 "generate line marks", // -L 499 "do not replace main", // -m 500 "do not generate line marks", // -N 501 "do not read prelude", // -n 477 502 "generate prototypes for prelude functions", // -p 478 "print", // -P 503 "only print deterministic output", // -d 504 "Use the old-ast", // -O 505 "Use the new-ast", // -A 506 "print", // -P 479 507 "<directory> prelude directory for debug/nodebug", // no flag 480 508 "<option-list> enable profiling information:\n counters,heap,time,all,none", // -S 481 "building cfa standard lib", // -t482 "", // -w483 "", // -W484 "", // -D509 "building cfa standard lib", // -t 510 "", // -w 511 "", // -W 512 "", // -D 485 513 }; // description 486 514 … … 580 608 genproto = true; 581 609 break; 610 case 'd': // don't print non-deterministic output 611 deterministic_output = true; 612 break; 613 case 'O': // don't print non-deterministic output 614 useNewAST = false; 615 break; 616 case 'A': // don't print non-deterministic output 617 useNewAST = true; 618 break; 582 619 case 'P': // print options 583 620 for ( int i = 0;; i += 1 ) { -
tests/.expect/alloc.txt
r3c64c668 r58fe85a 14 14 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 15 15 CFA array alloc, fill 0xef 16 0x efefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef16 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 17 17 CFA array alloc, fill from array 18 0x efefefef 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,18 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 0xdeadbeef, 19 19 20 20 C realloc 21 0x efefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef21 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 22 22 CFA realloc 23 0x efefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0xefefefef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x101010123 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 24 24 25 CFA re sizearray alloc25 CFA realloc array alloc 26 26 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 27 CFA re sizearray alloc27 CFA realloc array alloc 28 28 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 29 CFA re sizearray alloc29 CFA realloc array alloc 30 30 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 31 CFA re size array alloc32 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x 1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x10101010xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede33 CFA re size array alloc31 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 34 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 35 CFA re sizearray alloc, fill36 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0x 1010101 0x1010101 0x1010101 0x1010101 0x1010101 0x10101010xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede 0xdededede35 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 37 37 38 38 C memalign 42 42.5 -
tests/.expect/array.txt
r3c64c668 r58fe85a 1 array.cfa: In function '_X4mainFi___1': 2 array.cfa:55:9: note: #pragma message: Compiled -
tests/.expect/cast.txt
r3c64c668 r58fe85a 1 cast.cfa: In function '_X4mainFi_iPPKc__1': 2 cast.cfa:18:9: note: #pragma message: Compiled -
tests/.expect/copyfile.txt
r3c64c668 r58fe85a 8 8 // 9 9 // Author : Peter A. Buhr 10 // Created On : Tue Jul 16 16:47:22 201910 // Created On : Fri Jun 19 13:44:05 2020 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jul 17 18:04:44 201913 // Update Count : 2612 // Last Modified On : Fri Jun 19 17:58:03 2020 13 // Update Count : 4 14 14 // 15 15 16 16 #include <fstream.hfa> 17 #include < stdlib.hfa> // new/delete17 #include <exception.hfa> 18 18 19 19 int main( int argc, char * argv[] ) { 20 ifstream * in = &stdin; // default files 21 ofstream * out = &stdout; 20 ifstream in = stdin; // copy default files 21 ofstream out = stdout; 22 22 23 try { 23 24 choose ( argc ) { 24 25 case 2, 3: 25 in = new( (const char *)argv[1] );// open input file first as output creates file26 if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent27 case 1: ; // use default files26 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 28 29 default: 29 exit | "Usage[ input-file (default stdin) [ output-file (default stdout) ] ]";30 exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]"; 30 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]; 37 } // try 31 38 32 char ch; 33 *out | nlOff; // turn off auto newline 34 *in | nlOn; // turn on reading newline 39 out | nlOff; // turn off auto newline 40 in | nlOn; // turn on reading newline 35 41 36 for () { // read all characters 37 *in | ch; 38 if ( eof( *in ) ) break; // eof ? 39 *out | ch; 40 } // for 41 } finally { 42 if ( in != &stdin ) delete( in ); // close file, do not delete stdin! 43 if ( out != &stdout ) delete( out ); // close file, do not delete stdout! 44 } // try 42 char ch; 43 for () { // read all characters 44 in | ch; 45 if ( eof( in ) ) break; // eof ? 46 out | ch; 47 } //for 45 48 } // main 46 49 -
tests/.expect/declarationSpecifier.x64.txt
r3c64c668 r58fe85a 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; 1131 1134 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 1132 1135 __attribute__ ((unused)) signed int _X12_retval_maini_1; 1133 1136 { 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 { 1134 1149 signed int _tmp_cp_ret4; 1135 1150 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/declarationSpecifier.x86.txt
r3c64c668 r58fe85a 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; 1131 1134 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 1132 1135 __attribute__ ((unused)) signed int _X12_retval_maini_1; 1133 1136 { 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 { 1134 1149 signed int _tmp_cp_ret4; 1135 1150 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/enum.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/expression.txt
r3c64c668 r58fe85a 1 expression.cfa: In function '_X4mainFi___1': 2 expression.cfa:89:9: note: #pragma message: Compiled -
tests/.expect/forall.txt
r3c64c668 r58fe85a 1 forall.cfa: In function '_X4mainFi___1': 2 forall.cfa:218:9: note: #pragma message: Compiled -
tests/.expect/gccExtensions.x64.txt
r3c64c668 r58fe85a 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; 323 326 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 324 327 __attribute__ ((unused)) signed int _X12_retval_maini_1; 325 328 { 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 { 326 341 signed int _tmp_cp_ret4; 327 342 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/gccExtensions.x86.txt
r3c64c668 r58fe85a 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; 301 304 signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){ 302 305 __attribute__ ((unused)) signed int _X12_retval_maini_1; 303 306 { 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 { 304 319 signed int _tmp_cp_ret4; 305 320 ((void)(_X12_retval_maini_1=(((void)(_tmp_cp_ret4=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret4)) /* ?{} */); -
tests/.expect/heap.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/identFuncDeclarator.txt
r3c64c668 r58fe85a 1 identFuncDeclarator.cfa: In function '_X4mainFi___1': 2 identFuncDeclarator.cfa:116:9: note: #pragma message: Compiled -
tests/.expect/identParamDeclarator.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/labelledExit.txt
r3c64c668 r58fe85a 1 labelledExit.cfa: In function '_X4mainFi_iPPKc__1': 2 labelledExit.cfa:183:9: note: #pragma message: Compiled -
tests/.expect/limits.txt
r3c64c668 r58fe85a 1 limits.cfa: In function '_X4mainFi_iPPKc__1': 2 limits.cfa:154:9: note: #pragma message: Compiled -
tests/.expect/maybe.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/minmax.txt
r3c64c668 r58fe85a 1 1 char z a min a 2 signed int 4 3 min32 signed int 4 -3 min -3 3 3 unsigned int 4 3 min 3 4 signed long int 4 3 min34 signed long int 4 -3 min -3 5 5 unsigned long int 4 3 min 3 6 signed long long int 4 3 min36 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.txt
r3c64c668 r58fe85a 1 nested-types.cfa: In function '_X4mainFi___1': 2 nested-types.cfa:102:9: note: #pragma message: Compiled -
tests/.expect/numericConstants.txt
r3c64c668 r58fe85a 1 numericConstants.cfa: In function '_X4mainFi___1': 2 numericConstants.cfa:68:9: note: #pragma message: Compiled -
tests/.expect/operators.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/result.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/stdincludes.txt
r3c64c668 r58fe85a 1 stdincludes.cfa: In function '_X4mainFi___1': 2 stdincludes.cfa:52:9: note: #pragma message: Compiled -
tests/.expect/switch.txt
r3c64c668 r58fe85a 1 switch.cfa: In function '_X4mainFi___1': 2 switch.cfa:105:9: note: #pragma message: Compiled -
tests/.expect/time.txt
r3c64c668 r58fe85a 1 1 10800 2 3.375 12 1.00001 2 0.125 0.0333333333333333 3.375 12000. 1000010. 2 3 0 2 3.375 3 4 7 7 7 -
tests/.expect/typedefRedef-ERR1.txt
r3c64c668 r58fe85a 1 1 typedefRedef.cfa:4:1 error: Cannot redefine typedef: Foo 2 typedefRedef.cfa: 60:1 error: Cannot redefine typedef: ARR2 typedefRedef.cfa:59:1 error: Cannot redefine typedef: ARR -
tests/.expect/typedefRedef.txt
r3c64c668 r58fe85a 1 typedefRedef.cfa: In function '_X4mainFi___1': 2 typedefRedef.cfa:71:9: note: #pragma message: Compiled -
tests/.expect/typeof.txt
r3c64c668 r58fe85a 1 done -
tests/.expect/variableDeclarator.txt
r3c64c668 r58fe85a 1 variableDeclarator.cfa: In function '_X4mainFi_iPPKc__1': 2 variableDeclarator.cfa:182:9: note: #pragma message: Compiled -
tests/.expect/voidPtr.txt
r3c64c668 r58fe85a 1 done -
tests/.in/copyfile.txt
r3c64c668 r58fe85a 8 8 // 9 9 // Author : Peter A. Buhr 10 // Created On : Tue Jul 16 16:47:22 201910 // Created On : Fri Jun 19 13:44:05 2020 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jul 17 18:04:44 201913 // Update Count : 2612 // Last Modified On : Fri Jun 19 17:58:03 2020 13 // Update Count : 4 14 14 // 15 15 16 16 #include <fstream.hfa> 17 #include < stdlib.hfa> // new/delete17 #include <exception.hfa> 18 18 19 19 int main( int argc, char * argv[] ) { 20 ifstream * in = &stdin; // default files 21 ofstream * out = &stdout; 20 ifstream in = stdin; // copy default files 21 ofstream out = stdout; 22 22 23 try { 23 24 choose ( argc ) { 24 25 case 2, 3: 25 in = new( (const char *)argv[1] );// open input file first as output creates file26 if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent27 case 1: ; // use default files26 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 28 29 default: 29 exit | "Usage[ input-file (default stdin) [ output-file (default stdout) ] ]";30 exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]"; 30 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]; 37 } // try 31 38 32 char ch; 33 *out | nlOff; // turn off auto newline 34 *in | nlOn; // turn on reading newline 39 out | nlOff; // turn off auto newline 40 in | nlOn; // turn on reading newline 35 41 36 for () { // read all characters 37 *in | ch; 38 if ( eof( *in ) ) break; // eof ? 39 *out | ch; 40 } // for 41 } finally { 42 if ( in != &stdin ) delete( in ); // close file, do not delete stdin! 43 if ( out != &stdout ) delete( out ); // close file, do not delete stdout! 44 } // try 42 char ch; 43 for () { // read all characters 44 in | ch; 45 if ( eof( in ) ) break; // eof ? 46 out | ch; 47 } //for 45 48 } // main 46 49 -
tests/.in/manipulatorsInput.txt
r3c64c668 r58fe85a 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 42798 30 1402432282 1505850196993244515 31 394749758663249135511342 32 12935154696204706112391834394 33 34 423859149128410414395372834994551 35 36 37 13889016598639747063234935497057631587 38 170141183460469231731687303715884105727 39 340282366920938463463374607431768211455 40 -340282366920938463463374607431768211455 41 340282366920938463463374607431768211455999 42 1234567890123456789 -1234567890123456789 -
tests/Makefile.am
r3c64c668 r58fe85a 11 11 ## Created On : Sun May 31 09:08:15 2015 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Tue Nov 20 11:18:51 201814 ## Update Count : 6813 ## Last Modified On : Fri Oct 9 23:13:07 2020 14 ## Update Count : 86 15 15 ############################################################################### 16 16 … … 18 18 ACLOCAL_AMFLAGS = -I automake 19 19 20 include $(top_srcdir)/src/cfa.make 20 include $(top_srcdir)/tools/build/cfa.make 21 22 DEFAULT_INCLUDES = -I${abs_srcdir} 21 23 22 24 debug=yes … … 36 38 # since automake doesn't have support for CFA we have to 37 39 AM_CFLAGS = $(if $(test), 2> $(test), ) \ 40 -fdebug-prefix-map=$(abspath ${abs_srcdir})= \ 41 -fdebug-prefix-map=/tmp= \ 42 -fno-diagnostics-show-caret \ 38 43 -g \ 39 44 -Wall \ … … 42 47 -DIN_DIR="${abs_srcdir}/.in/" 43 48 49 AM_CFAFLAGS = -XCFA --deterministic-out 50 44 51 # get the desired cfa to test 45 52 TARGET_CFA = $(if $(filter $(installed),yes), @CFACC_INSTALL@, @CFACC@) 46 53 47 54 # adjust CC to current flags 48 CC = $(if $(DISTCC_CFA_PATH),distcc $(DISTCC_CFA_PATH) ${ARCH_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_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}) 49 56 CFACC = $(CC) 50 57 … … 53 60 54 61 # adjusted CC but without the actual distcc call 55 CFACCLOCAL = $(if $(DISTCC_CFA_PATH),$(DISTCC_CFA_PATH) ${ARCH_FLAGS},$(TARGET_CFA) ${DEBUG_FLAGS} ${ARCH_FLAGS}) 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')) 56 64 57 65 PRETTY_PATH=mkdir -p $(dir $(abspath ${@})) && cd ${srcdir} && … … 60 68 .INTERMEDIATE: .validate .validate.cfa 61 69 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.hfa 83 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 done 62 92 63 93 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 64 94 # automake doesn't know we still need C/CPP rules so pretend like we have a C program 65 _dummy_hack_SOURCES = .dummy_hack.c .dummy_hackxx.cpp95 nodist__dummy_hack_SOURCES = .dummy_hack.c .dummy_hackxx.cpp 66 96 67 97 #---------------------------------------------------------------------------------------------------------------- … … 72 102 @+${TEST_PY} --debug=${debug} --install=${installed} --archive-errors=${archiveerrors} ${concurrent} ${timeouts} --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program 73 103 74 clean-local : 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 75 108 rm -f ${EXTRA_PROGRAMS} 109 rm -rf __pycache__ 110 111 distclean-local : 112 find ${builddir} -path '*.Po' -delete 76 113 77 114 list : … … 106 143 % : %.cfa $(CFACCBIN) 107 144 $(CFACOMPILETEST) -c -o $(abspath ${@}).o 108 $(CFACCLOCAL) $($(shell echo "${@}_FLAGSLD" | sed 's/-\|\//_/g')) $(abspath ${@}).o -o $(abspath ${@}) 145 $(CFACCLINK) ${@}.o -o $(abspath ${@}) 146 rm $(abspath ${@}).o 109 147 110 148 # implicit rule for c++ test … … 125 163 $(CFACOMPILETEST) -CFA -XCFA -p -c -fsyntax-only -o $(abspath ${@}) 126 164 127 # Use for tests where the make command is expected to succeed but the expected.txt should be compared to stderr128 EXPECT_STDERR = builtins/sync warnings/self-assignment129 $(EXPECT_STDERR): % : %.cfa $(CFACCBIN)130 $(CFACOMPILETEST) -c -fsyntax-only 2> $(abspath ${@})131 132 165 #------------------------------------------------------------------------------ 133 166 # CUSTOM TARGET 134 167 #------------------------------------------------------------------------------ 168 # tests that just validate syntax and compiler output should be compared to stderr 169 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-assignment 173 $(SYNTAX_ONLY_CODE): % : %.cfa $(CFACCBIN) 174 $(CFACOMPILE_SYNTAX) 175 $(if $(test), cp $(test) $(abspath ${@}), ) 176 135 177 # expected failures 136 # use custom target since they require a custom define and custom dependencies178 # use custom target since they require a custom define *and* have a name that doesn't match the file 137 179 alloc-ERROR : alloc.cfa $(CFACCBIN) 138 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 180 $(CFACOMPILE_SYNTAX) -DERR1 181 -cp $(test) $(abspath ${@}) 182 183 init1-ERROR : init1.cfa $(CFACCBIN) 184 $(CFACOMPILE_SYNTAX) -DERR1 185 -cp $(test) $(abspath ${@}) 139 186 140 187 typedefRedef-ERR1 : typedefRedef.cfa $(CFACCBIN) 141 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 188 $(CFACOMPILE_SYNTAX) -DERR1 189 -cp $(test) $(abspath ${@}) 142 190 143 191 nested-types-ERR1 : nested-types.cfa $(CFACCBIN) 144 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 192 $(CFACOMPILE_SYNTAX) -DERR1 193 -cp $(test) $(abspath ${@}) 145 194 146 195 nested-types-ERR2 : nested-types.cfa $(CFACCBIN) 147 $(CFACOMPILETEST) -DERR2 -c -fsyntax-only -o $(abspath ${@}) 196 $(CFACOMPILE_SYNTAX) -DERR2 197 -cp $(test) $(abspath ${@}) 148 198 149 199 raii/memberCtors-ERR1 : raii/memberCtors.cfa $(CFACCBIN) 150 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 200 $(CFACOMPILE_SYNTAX) -DERR1 201 -cp $(test) $(abspath ${@}) 151 202 152 203 raii/ctor-autogen-ERR1 : raii/ctor-autogen.cfa $(CFACCBIN) 153 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 204 $(CFACOMPILE_SYNTAX) -DERR1 205 -cp $(test) $(abspath ${@}) 154 206 155 207 raii/dtor-early-exit-ERR1 : raii/dtor-early-exit.cfa $(CFACCBIN) 156 $(CFACOMPILETEST) -DERR1 -c -fsyntax-only -o $(abspath ${@}) 208 $(CFACOMPILE_SYNTAX) -DERR1 209 -cp $(test) $(abspath ${@}) 157 210 158 211 raii/dtor-early-exit-ERR2 : raii/dtor-early-exit.cfa $(CFACCBIN) 159 $(CFACOMPILETEST) -DERR2 -c -fsyntax-only -o $(abspath ${@}) 212 $(CFACOMPILE_SYNTAX) -DERR2 213 -cp $(test) $(abspath ${@}) 214 215 # Exception Tests 216 # Test with libcfathread; it changes how storage works. 217 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 160 228 161 229 #------------------------------------------------------------------------------ -
tests/alloc.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Feb 3 07:56:22 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 16 09:21:13202013 // Update Count : 4 0512 // Last Modified On : Thu Nov 12 10:02:18 2020 13 // Update Count : 432 14 14 // 15 15 … … 28 28 size_t dim = 10; 29 29 char fill = '\xde'; 30 int * p, *p1;30 int * ip, * ip1; 31 31 32 32 // allocation, non-array types 33 33 34 p = (int *)malloc( sizeof(*p) ); // C malloc, type unsafe35 * p = 0xdeadbeef;36 printf( "C malloc %#x\n", * p );37 free( p );38 39 p = malloc();// CFA malloc, type safe40 * p = 0xdeadbeef;41 printf( "CFA malloc %#x\n", * p );42 free( p );43 44 p = alloc();// CFA alloc, type safe45 * p = 0xdeadbeef;46 printf( "CFA alloc %#x\n", * p );47 free( p );48 49 p = alloc_set(fill ); // CFA alloc, fill50 printf( "CFA alloc, fill %08x\n", * p );51 free( p );52 53 p = alloc_set( 3 );// CFA alloc, fill54 printf( "CFA alloc, fill %d\n", * p );55 free( p );34 ip = (int *)malloc( sizeof(*ip) ); // C malloc, type unsafe 35 *ip = 0xdeadbeef; 36 printf( "C malloc %#x\n", *ip ); 37 free( ip ); 38 39 ip = malloc(); // CFA malloc, type safe 40 *ip = 0xdeadbeef; 41 printf( "CFA malloc %#x\n", *ip ); 42 free( ip ); 43 44 ip = alloc(); // CFA alloc, type safe 45 *ip = 0xdeadbeef; 46 printf( "CFA alloc %#x\n", *ip ); 47 free( ip ); 48 49 ip = alloc( fill`fill ); // CFA alloc, fill 50 printf( "CFA alloc, fill %08x\n", *ip ); 51 free( ip ); 52 53 ip = alloc( 3`fill ); // CFA alloc, fill 54 printf( "CFA alloc, fill %d\n", *ip ); 55 free( ip ); 56 56 57 57 … … 59 59 printf( "\n" ); 60 60 61 p = (int *)calloc( dim, sizeof( *p ) );// C array calloc, type unsafe61 ip = (int *)calloc( dim, sizeof( *ip ) ); // C array calloc, type unsafe 62 62 printf( "C array calloc, fill 0\n" ); 63 for ( i; dim ) { printf( "%#x ", p[i] ); }64 printf( "\n" ); 65 free( p );66 67 p = calloc( dim );// CFA array calloc, type safe63 for ( i; dim ) { printf( "%#x ", ip[i] ); } 64 printf( "\n" ); 65 free( ip ); 66 67 ip = calloc( dim ); // CFA array calloc, type safe 68 68 printf( "CFA array calloc, fill 0\n" ); 69 for ( i; dim ) { printf( "%#x ", p[i] ); }70 printf( "\n" ); 71 free( p );72 73 p = alloc( dim );// CFA array alloc, type safe74 for ( i; dim ) { p[i] = 0xdeadbeef; }69 for ( i; dim ) { printf( "%#x ", ip[i] ); } 70 printf( "\n" ); 71 free( ip ); 72 73 ip = alloc( dim ); // CFA array alloc, type safe 74 for ( i; dim ) { ip[i] = 0xdeadbeef; } 75 75 printf( "CFA array alloc, no fill\n" ); 76 for ( i; dim ) { printf( "%#x ", p[i] ); }77 printf( "\n" ); 78 free( p );79 80 p = alloc_set( 2 * dim, fill );// CFA array alloc, fill76 for ( i; dim ) { printf( "%#x ", ip[i] ); } 77 printf( "\n" ); 78 free( ip ); 79 80 ip = alloc( 2 * dim, fill`fill ); // CFA array alloc, fill 81 81 printf( "CFA array alloc, fill %#hhx\n", fill ); 82 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); }83 printf( "\n" ); 84 free( p );85 86 p = alloc_set( 2 * dim, 0xdeadbeef); // CFA array alloc, fill82 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); } 83 printf( "\n" ); 84 free( ip ); 85 86 ip = alloc( 2 * dim, ((int)0xdeadbeef)`fill ); // CFA array alloc, fill 87 87 printf( "CFA array alloc, fill %#hhx\n", 0xdeadbeef ); 88 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); }89 printf( "\n" ); 90 // do not free 91 92 p1 = alloc_set( 2 * dim, p );// CFA array alloc, fill88 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); } 89 printf( "\n" ); 90 // do not free 91 92 ip1 = alloc( 2 * dim, [ip, 2 * dim]`fill ); // CFA array alloc, fill 93 93 printf( "CFA array alloc, fill from array\n" ); 94 for ( i; 2 * dim ) { printf( "%#x %#x, ", p[i], p1[i] ); } 95 free( p1 ); 96 printf( "\n" ); 97 94 for ( i; 2 * dim ) { printf( "%#x %#x, ", ip[i], ip1[i] ); } 95 free( ip1 ); 96 printf( "\n" ); 97 98 99 // realloc, non-array types 100 printf( "\n" ); 101 102 ip = (int *)realloc( ip, dim * sizeof(*ip) ); // C realloc 103 printf( "C realloc\n" ); 104 for ( i; dim ) { printf( "%#x ", ip[i] ); } 105 printf( "\n" ); 106 // do not free 107 108 ip = realloc( ip, 2 * dim * sizeof(*ip) ); // CFA realloc 109 for ( i; dim ~ 2 * dim ) { ip[i] = 0x1010101; } 110 printf( "CFA realloc\n" ); 111 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); } 112 printf( "\n" ); 113 // do not free 114 115 116 // realloc, array types 117 printf( "\n" ); 118 119 ip = alloc( dim, ip`realloc ); // CFA realloc array alloc 120 for ( i; dim ) { ip[i] = 0xdeadbeef; } 121 printf( "CFA realloc array alloc\n" ); 122 for ( i; dim ) { printf( "%#x ", ip[i] ); } 123 printf( "\n" ); 124 // do not free 125 126 ip = alloc( 2 * dim, ip`realloc ); // CFA realloc array alloc 127 for ( i; dim ~ 2 * dim ) { ip[i] = 0x1010101; } // fill upper part 128 printf( "CFA realloc array alloc\n" ); 129 for ( i; 2 * dim ) { printf( "%#x ", ip[i] ); } 130 printf( "\n" ); 131 // do not free 132 133 ip = alloc( dim, ip`realloc ); // CFA realloc array alloc 134 printf( "CFA realloc array alloc\n" ); 135 for ( i; dim ) { printf( "%#x ", ip[i] ); } 136 printf( "\n" ); 137 // do not free 138 139 ip = alloc( 3 * dim, ip`realloc, fill`fill ); // CFA realloc array alloc, fill 140 printf( "CFA realloc array alloc, fill\n" ); 141 for ( i; 3 * dim ) { printf( "%#x ", ip[i] ); } 142 printf( "\n" ); 143 // do not free 144 145 ip = alloc( dim, ip`realloc, fill`fill ); // CFA realloc array alloc, fill 146 printf( "CFA realloc array alloc, fill\n" ); 147 for ( i; dim ) { printf( "%#x ", ip[i] ); } 148 printf( "\n" ); 149 // do not free 150 151 ip = alloc( 3 * dim, ip`realloc, fill`fill ); // CFA realloc array alloc, fill 152 printf( "CFA realloc array alloc, fill\n" ); 153 for ( i; 3 * dim ) { printf( "%#x ", ip[i] ); } 154 printf( "\n" ); 155 // do not free 156 #if 0 // FIX ME 157 ip = alloc( 5 * dim, ip`realloc, 5`fill ); // CFA realloc array alloc, 5 158 printf( "CFA realloc array alloc, 5\n" ); 159 for ( i; 5 * dim ) { printf( "%#x ", ip[i] ); } 160 printf( "\n" ); 161 // do not free 162 163 ip = alloc( dim, ip`realloc, 5`fill ); // CFA realloc array alloc, 5 164 printf( "CFA realloc array alloc, 5\n" ); 165 for ( i; dim ) { printf( "%#x ", ip[i] ); } 166 printf( "\n" ); 167 // do not free 168 169 ip = alloc( 5 * dim, ip`realloc, 5`fill ); // CFA realloc array alloc, 5 170 printf( "CFA realloc array alloc, 5\n" ); 171 for ( i; 5 * dim ) { printf( "%#x ", ip[i] ); } 172 printf( "\n" ); 173 #endif // 0 174 free( ip ); 98 175 99 176 // resize, non-array types 100 printf( "\n" ); 101 102 p = (int *)realloc( p, dim * sizeof(*p) ); // C realloc103 printf( "C realloc\n" );104 for ( i; dim ) { printf( "%#x ", p[i] ); } 105 printf( "\n");106 // do not free107 108 p = realloc( p, 2 * dim * sizeof(*p) ); // CFA realloc109 for ( i; dim ~ 2 * dim ) { p[i] = 0x1010101; } 110 printf( "CFA realloc\n" );111 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); } 112 printf( "\n" );113 // do not free 177 178 struct S { 179 int a[5]; 180 }; 181 182 ip = alloc(); 183 *ip = 5; 184 double * dp = alloc( ip`resize ); 185 *dp = 5.5; 186 S * sp = alloc( dp`resize ); 187 *sp = (S){ {0, 1, 2, 3, 4} }; 188 ip = alloc( sp`resize ); 189 *ip = 3; 190 free( ip ); 114 191 115 192 116 193 // resize, array types 117 printf( "\n" ); 118 119 p = alloc( p, dim ); // CFA resize array alloc 120 for ( i; dim ) { p[i] = 0xdeadbeef; } 121 printf( "CFA resize array alloc\n" ); 122 for ( i; dim ) { printf( "%#x ", p[i] ); } 123 printf( "\n" ); 124 // do not free 125 126 p = alloc( p, 2 * dim ); // CFA resize array alloc 127 for ( i; dim ~ 2 * dim ) { p[i] = 0x1010101; } // fill upper part 128 printf( "CFA resize array alloc\n" ); 129 for ( i; 2 * dim ) { printf( "%#x ", p[i] ); } 130 printf( "\n" ); 131 // do not free 132 133 p = alloc( p, dim ); // CFA resize array alloc 134 printf( "CFA resize array alloc\n" ); 135 for ( i; dim ) { printf( "%#x ", p[i] ); } 136 printf( "\n" ); 137 // do not free 138 139 p = alloc_set( p, 3 * dim, fill ); // CFA resize array alloc, fill 140 printf( "CFA resize array alloc\n" ); 141 for ( i; 3 * dim ) { printf( "%#x ", p[i] ); } 142 printf( "\n" ); 143 // do not free 144 145 p = alloc_set( p, dim, fill ); // CFA resize array alloc, fill 146 printf( "CFA resize array alloc\n" ); 147 for ( i; dim ) { printf( "%#x ", p[i] ); } 148 printf( "\n" ); 149 // do not free 150 151 p = alloc_set( p, 3 * dim, fill ); // CFA resize array alloc, fill 152 printf( "CFA resize array alloc, fill\n" ); 153 for ( i; 3 * dim ) { printf( "%#x ", p[i] );; } 154 printf( "\n" ); 155 free( p ); 156 157 194 195 ip = alloc( 5 ); 196 for ( i; 5 ) { ip[i] = 5; } 197 dp = alloc( 5, ip`resize ); 198 for ( i; 5 ) { dp[i] = 5.5; } 199 sp = alloc( 5, dp`resize ); 200 for ( i; 5 ) { sp[i] = (S){ {0, 1, 2, 3, 4} }; } 201 ip = alloc( 3, sp`resize ); 202 for ( i; 3 ) { ip[i] = 3; } 203 ip = alloc( 7, ip`realloc ); 204 for ( i; 7 ) { ip[i] = 7; } 205 ip = alloc( 7, ip`resize ); 206 for ( i; 7 ) { ip[i] = 7; } 207 free( ip ); 208 209 210 int const_count, dest_count; 158 211 struct Struct { int x; double y; }; 212 void ?{}( Struct & a ) { // construct 213 a.[ x, y ] = [ -1, -1.0 ]; 214 } 215 void ?{}( Struct & a, int x, double y ) { // initialize 216 a.[ x, y ] = [ x, y ]; 217 const_count++; 218 } 219 void ^?{}( Struct & a ) { dest_count++; } // destruct 159 220 Struct st, st1, sta[dim], sta1[dim], * stp, * stp1; 160 221 … … 168 229 free( stp ); 169 230 170 stp = &(*memalign( Alignment )){ 42, 42.5 }; // CFA memalign231 stp = &(*memalign( Alignment )){ 42, 42.5 }; // CFA memalign 171 232 assert( (uintptr_t)stp % Alignment == 0 ); 172 233 printf( "CFA memalign %d %g\n", stp->x, stp->y ); … … 185 246 free( stp ); 186 247 187 stp = &(*alloc _align( Alignment)){ 42, 42.5 }; // CFA alloc_align248 stp = &(*alloc( Alignment`align)){ 42, 42.5 }; // CFA alloc_align 188 249 assert( (uintptr_t)stp % Alignment == 0 ); 189 250 printf( "CFA alloc_align %d %g\n", stp->x, stp->y ); 190 251 free( stp ); 191 252 192 stp = &(*alloc _align( Alignment)){ 42, 42.5 }; // CFA alloc_align253 stp = &(*alloc( Alignment`align )){ 42, 42.5 }; // CFA alloc_align 193 254 assert( (uintptr_t)stp % Alignment == 0 ); 194 255 printf( "CFA alloc_align %d %g\n", stp->x, stp->y ); 195 256 free( stp ); 196 257 197 stp = alloc _align_set( Alignment,fill ); // CFA memalign, fill258 stp = alloc( Alignment`align, fill`fill ); // CFA memalign, fill 198 259 assert( (uintptr_t)stp % Alignment == 0 ); 199 260 printf( "CFA alloc_align fill %#x %a\n", stp->x, stp->y ); 200 261 free( stp ); 201 262 202 stp = alloc _align_set( Alignment, (Struct){ 42, 42.5 }); // CFA memalign, fill263 stp = alloc( Alignment`align, (Struct){ 42, 42.5 }`fill ); // CFA memalign, fill 203 264 assert( (uintptr_t)stp % Alignment == 0 ); 204 265 printf( "CFA alloc_align fill %d %g\n", stp->x, stp->y ); 205 266 // do not free 206 267 207 stp = &(*alloc _align( stp, 4096)){ 42, 42.5 }; // CFA realign268 stp = &(*alloc( stp`realloc, 4096`align )){ 42, 42.5 }; // CFA realign 208 269 assert( (uintptr_t)stp % 4096 == 0 ); 209 270 printf( "CFA alloc_align %d %g\n", stp->x, stp->y ); … … 214 275 printf( "\n" ); 215 276 216 stp = alloc _align( Alignment, dim); // CFA array memalign277 stp = alloc( dim, Alignment`align ); // CFA array memalign 217 278 assert( (uintptr_t)stp % Alignment == 0 ); 218 279 for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; } … … 222 283 free( stp ); 223 284 224 stp = alloc _align_set( Alignment, dim,fill ); // CFA array memalign, fill285 stp = alloc( dim, Alignment`align, fill`fill ); // CFA array memalign, fill 225 286 assert( (uintptr_t)stp % Alignment == 0 ); 226 287 printf( "CFA array alloc_align, fill\n" ); … … 229 290 free( stp ); 230 291 231 stp = alloc _align_set( Alignment, dim, (Struct){ 42, 42.5 }); // CFA array memalign, fill292 stp = alloc( dim, Alignment`align, ((Struct){ 42, 42.5 })`fill ); // CFA array memalign, fill 232 293 assert( (uintptr_t)stp % Alignment == 0 ); 233 294 printf( "CFA array alloc_align, fill\n" ); … … 236 297 // do not free 237 298 238 stp1 = alloc _align_set( Alignment, dim, stp );// CFA array memalign, fill299 stp1 = alloc( dim, Alignment`align, [stp, dim]`fill ); // CFA array memalign, fill 239 300 assert( (uintptr_t)stp % Alignment == 0 ); 240 301 printf( "CFA array alloc_align, fill array\n" ); … … 243 304 free( stp1 ); 244 305 245 stp = alloc _align( stp, 4096, dim); // CFA aligned realloc array306 stp = alloc( dim, stp`realloc, 4096`align ); // CFA aligned realloc array 246 307 assert( (uintptr_t)stp % 4096 == 0 ); 247 308 for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; } … … 274 335 printf( "\n" ); 275 336 276 277 337 // new, non-array types 278 338 printf( "\n" ); 279 339 340 const_count = dest_count = 0; 280 341 stp = new( 42, 42.5 ); 342 assert( const_count == 1 && dest_count == 0 ); // assertion for testing 281 343 stp1 = new( 42, 42.5 ); 344 assert( const_count == 2 && dest_count == 0 ); // assertion for testing 345 282 346 printf( "CFA new initialize\n%d %g %d %g\n", stp->x, stp->y, stp1->x, stp1->y ); 283 347 delete( stp, stp1 ); 348 assert( const_count == 2 && dest_count == 2 ); // assertion for testing 284 349 285 350 // new, array types 286 351 stp = anew( dim, 42, 42.5 ); 352 assert( const_count == 2 + dim && dest_count == 2 ); // assertion for testing 287 353 printf( "CFA array new initialize\n" ); 288 354 for ( i; dim ) { printf( "%d %g, ", stp[i].x, stp[i].y ); } 289 355 printf( "\n" ); 356 290 357 stp1 = anew( dim, 42, 42.5 ); 358 assert( const_count == 2 + 2 * dim && dest_count == 2 ); // assertion for testing 291 359 for ( i; dim ) { printf( "%d %g, ", stp1[i].x, stp1[i].y ); } 292 360 printf( "\n" ); 293 adelete( dim, stp, dim, stp1 ); 361 adelete( stp, stp1 ); 362 assert( const_count == 2 + 2 * dim && dest_count == 2 + 2 * dim); // assertion for testing 294 363 295 364 // extras … … 300 369 free( fp - 1 ); 301 370 302 p = foo( bar( baz( malloc(), 0 ), 0 ), 0 ); 303 *p = 0xdeadbeef; 304 printf( "CFA deep malloc %#x\n", *p ); 305 free( p ); 371 ip = foo( bar( baz( malloc(), 0 ), 0 ), 0 ); 372 *ip = 0xdeadbeef; 373 printf( "CFA deep malloc %#x\n", *ip ); 374 375 dp = alloc(5.0`fill); // just for testing multiple free 376 assert(*dp == 5.0); 377 free( ip, dp, 0p ); 306 378 307 379 #ifdef ERR1 308 380 stp = malloc(); 309 381 printf( "\nSHOULD FAIL\n" ); 310 p = realloc( stp, dim * sizeof( *stp ) ); 311 p = alloc( stp, dim * sizeof( *stp ) ); 312 p = memset( stp, 10 ); 313 p = memcpy( &st1, &st ); 314 #endif 382 ip = realloc( stp, dim * sizeof( *stp ) ); 383 ip = memset( stp, 10 ); 384 ip = memcpy( &st1, &st ); 385 #endif // ERR1 315 386 } // main 316 387 -
tests/array.cfa
r3c64c668 r58fe85a 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 : Tue Feb 19 21:18:46 201914 // Update Count : 115 // 13 // Last Modified On : Sun Sep 27 09:05:40 2020 14 // Update Count : 4 15 // 16 16 17 int a1[ ];17 int a1[0]; 18 18 //int a2[*]; 19 19 //double a4[3.0]; 20 20 21 int m1[ ][3];21 int m1[0][3]; 22 22 //int m2[*][*]; 23 23 int m4[3][3]; … … 49 49 } 50 50 51 int main() {} 51 int main() { 52 #if !defined(NO_COMPILED_PRAGMA) 53 #pragma message( "Compiled" ) // force non-empty .expect file 54 #endif 55 } 52 56 53 57 // Local Variables: // -
tests/avltree/avl1.cfa
r3c64c668 r58fe85a 24 24 tree(K, V) * create(K key, V value) { 25 25 // infinite loop trying to resolve ... t = malloc(); 26 tree(K, V) * t = malloc(sizeof(tree(K,V)));26 tree(K, V) * t = ( tree(K, V) * ) malloc(sizeof(tree(K,V))); 27 27 (*t){ key, value }; 28 28 return t; -
tests/builtins/.expect/sync.txt
r3c64c668 r58fe85a 1 builtins/sync.cfa: In function '_X4mainFi___1': 2 builtins/sync.cfa:358:9: note: #pragma message: Compiled -
tests/builtins/sync.cfa
r3c64c668 r58fe85a 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); }69 68 #endif 70 69 … … 355 354 356 355 int main() { 357 return 0;356 #pragma message( "Compiled" ) // force non-empty .expect file 358 357 } -
tests/cast.cfa
r3c64c668 r58fe85a 13 13 14 14 //Dummy main 15 int main(int argc, char const *argv[]) 16 { 17 return 0; 15 int main( int argc, char const * argv[] ) { 16 #pragma message( "Compiled" ) // force non-empty .expect file 18 17 } -
tests/castError.cfa
r3c64c668 r58fe85a 14 14 // 15 15 16 forall(otype T) struct S { T p; }; 16 17 int f; 18 S(int) sint; 17 19 18 20 void f() { … … 25 27 short int v; 26 28 3, v; // implicit void cast 29 30 (S(char)) sint; 27 31 } 28 32 -
tests/complex.cfa
r3c64c668 r58fe85a 14 14 // 15 15 16 #include <stdio.h>17 16 #include <complex.h> 18 17 #ifdef __CFA__ -
tests/concurrent/.expect/monitor.txt
r3c64c668 r58fe85a 1 40000001 3000000 -
tests/concurrent/coroutineYield.cfa
r3c64c668 r58fe85a 33 33 sout | "Coroutine 2"; 34 34 #endif 35 suspend ();35 suspend; 36 36 } 37 37 } -
tests/concurrent/examples/.expect/datingService.txt
r3c64c668 r58fe85a 1 done -
tests/concurrent/examples/boundedBufferEXT.cfa
r3c64c668 r58fe85a 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 89 93 int main() { 90 94 Buffer(int) buffer; 91 enum { Prods = 4, Cons = 5 };92 Producer * prods[Prods];93 Consumer * cons[Cons];94 95 int sums[Cons]; 95 96 int i; -
tests/concurrent/examples/datingService.cfa
r3c64c668 r58fe85a 10 10 // Created On : Mon Oct 30 12:56:20 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 21 11:32:34 201913 // Update Count : 3812 // Last Modified On : Sun Sep 27 15:42:25 2020 13 // Update Count : 40 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 file 110 112 } // main 111 113 -
tests/concurrent/monitor.cfa
r3c64c668 r58fe85a 29 29 30 30 void main( MyThread & this ) { 31 for(int i = 0; i < 1_000_000; i++) {31 for(int i = 0; i < 750_000; i++) { 32 32 increment( global ); 33 33 } -
tests/concurrent/park/.expect/force_preempt.txt
r3c64c668 r58fe85a 1 0 Calling unpark 0 2 1 Calling unpark 0 3 2 Calling unpark 0 4 3 Calling unpark 0 5 4 Calling unpark 0 6 0 Parking 0 7 0 Unparked 0 8 0 Calling unpark 1 9 1 Parking 0 10 1 Unparked 0 11 1 Calling unpark 1 12 2 Parking 0 13 2 Unparked 0 14 2 Calling unpark 1 15 3 Parking 0 16 3 Unparked 0 17 3 Calling unpark 1 18 4 Parking 0 19 4 Unparked 0 20 4 Calling unpark 1 21 0 Parking 1 22 0 Unparked 1 23 0 Calling unpark 2 24 1 Parking 1 25 1 Unparked 1 26 1 Calling unpark 2 27 2 Parking 1 28 2 Unparked 1 29 2 Calling unpark 2 30 3 Parking 1 31 3 Unparked 1 32 3 Calling unpark 2 33 4 Parking 1 34 4 Unparked 1 35 4 Calling unpark 2 36 0 Parking 2 37 0 Unparked 2 38 0 Calling unpark 3 39 1 Parking 2 40 1 Unparked 2 41 1 Calling unpark 3 42 2 Parking 2 43 2 Unparked 2 44 2 Calling unpark 3 45 3 Parking 2 46 3 Unparked 2 47 3 Calling unpark 3 48 4 Parking 2 49 4 Unparked 2 50 4 Calling unpark 3 51 0 Parking 3 52 0 Unparked 3 53 0 Calling unpark 4 54 1 Parking 3 55 1 Unparked 3 56 1 Calling unpark 4 57 2 Parking 3 58 2 Unparked 3 59 2 Calling unpark 4 60 3 Parking 3 61 3 Unparked 3 62 3 Calling unpark 4 63 4 Parking 3 64 4 Unparked 3 65 4 Calling unpark 4 66 0 Parking 4 67 0 Unparked 4 68 1 Parking 4 69 1 Unparked 4 70 2 Parking 4 71 2 Unparked 4 72 3 Parking 4 73 3 Unparked 4 74 4 Parking 4 75 4 Unparked 4 1 done -
tests/concurrent/park/contention.cfa
r3c64c668 r58fe85a 24 24 } else { 25 25 Thread * thrd = __atomic_exchange_n(&blocked[idx], &this, __ATOMIC_SEQ_CST); 26 unpark( *thrd );26 unpark( *thrd ); 27 27 park(); 28 28 } -
tests/concurrent/park/force_preempt.cfa
r3c64c668 r58fe85a 16 16 } 17 17 18 thread Waiter;19 18 thread Waiter {}; 20 21 19 22 20 volatile int count = 0; … … 25 23 // Get a unique id 26 24 int id = __atomic_fetch_add(&count, 1, __ATOMIC_SEQ_CST); 25 int id_hash = id | (id << 8) | (id << 16) | (id << 24); 26 int mask = 0xCAFEBABA; 27 27 28 28 for(int i = 0; i < 5; i++) { 29 assert(mask == 0xCAFEBABA); 30 29 31 // Unpark this thread, don't force a yield 30 sout | id | "Calling unpark" | i; 31 unpark(this); 32 unpark( this ); 33 assert(mask == 0xCAFEBABA); 34 35 // Hash the mask to make sure no one else messes with them 36 mask ^= id_hash; 37 assert(mask == (id_hash ^ 0xCAFEBABA)); 32 38 33 39 // Force a preemption before the call to park … … 36 42 37 43 // Park this thread, 38 sout | id | "Parking" | i;44 assert(mask == (id_hash ^ 0xCAFEBABA)); 39 45 park(); 40 sout | id | "Unparked" | i; 46 assert(mask == (id_hash ^ 0xCAFEBABA)); 47 48 // Reset the hash and recheck it 49 mask ^= id_hash; 50 assert(mask == 0xCAFEBABA); 41 51 } 42 52 } … … 47 57 Waiter waiters[5]; 48 58 } 59 printf( "done\n" ); // non-empty .expect file 49 60 } -
tests/concurrent/signal/block.cfa
r3c64c668 r58fe85a 82 82 if( !is_empty( cond ) ) { 83 83 84 $thread * next = front( cond );84 $thread * next = ( $thread * ) front( cond ); 85 85 86 86 if( ! signal_block( cond ) ) { -
tests/concurrent/signal/disjoint.cfa
r3c64c668 r58fe85a 21 21 #endif 22 22 23 // This tests checks what happens when someone barges in the midle of the release 24 // of a bulk of monitors. 25 23 26 enum state_t { WAIT, SIGNAL, BARGE }; 24 27 25 28 monitor global_t {}; 26 global_t mut;27 29 28 30 monitor global_data_t; … … 33 35 int counter; 34 36 state_t state; 35 } data; 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; 36 44 37 45 condition cond; … … 40 48 41 49 void ?{}( global_data_t & this ) { 42 this.counter = =0;50 this.counter = 0; 43 51 this.state = BARGE; 44 52 } … … 53 61 54 62 thread Barger {}; 63 void ?{}( Barger & this ) { 64 ((thread&)this){ "Barger Thread" }; 65 } 55 66 56 67 void main( Barger & this ) { 57 68 while( !all_done ) { 58 barge( data );69 barge( globals.data ); 59 70 yield(); 60 71 } … … 78 89 79 90 thread Waiter {}; 91 void ?{}( Waiter & this ) { 92 ((thread&)this){ "Waiter Thread" }; 93 } 80 94 81 95 void main( Waiter & this ) { 82 while( wait( mut,data ) ) { KICK_WATCHDOG; yield(); }96 while( wait( globals.mut, globals.data ) ) { KICK_WATCHDOG; yield(); } 83 97 } 84 98 … … 92 106 93 107 void logic( global_t & mutex a ) { 94 signal( cond, a, data );108 signal( cond, a, globals.data ); 95 109 96 110 yield( random( 10 ) ); 97 111 98 112 //This is technically a mutual exclusion violation but the mutex monitor protects us 99 bool running = TEST( data.counter < N) &&data.counter > 0;100 if( data.state != SIGNAL && running ) {101 sout | "ERROR Eager signal" | data.state;113 bool running = TEST(globals.data.counter < N) && globals.data.counter > 0; 114 if( globals.data.state != SIGNAL && running ) { 115 sout | "ERROR Eager signal" | globals.data.state; 102 116 } 103 117 } 104 118 105 119 thread Signaller {}; 120 void ?{}( Signaller & this ) { 121 ((thread&)this){ "Signaller Thread" }; 122 } 106 123 107 124 void main( Signaller & this ) { 108 125 while( !all_done ) { 109 logic( mut );126 logic( globals.mut ); 110 127 yield(); 111 128 } -
tests/concurrent/waitfor/when.cfa
r3c64c668 r58fe85a 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 on 61 this.last_call = 6; 62 59 63 for( int i = 0; i < N; i++ ) { 60 64 when( this.last_call == 6 ) waitfor( call1 : this ) { if( this.last_call != 1) { serr | "Expected last_call to be 1 got" | this.last_call; } } -
tests/config.py.in
r3c64c668 r58fe85a 9 9 HOSTARCH = "@host_cpu@" 10 10 DISTRIBUTE = @HAS_DISTCC@ 11 NEWAST = @DEFAULT_NEW_AST@ -
tests/copyfile.cfa
r3c64c668 r58fe85a 8 8 // 9 9 // Author : Peter A. Buhr 10 // Created On : Tue Jul 16 16:47:22 201910 // Created On : Fri Jun 19 13:44:05 2020 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jul 17 18:04:44 201913 // Update Count : 2612 // Last Modified On : Sat Aug 15 15:00:48 2020 13 // Update Count : 6 14 14 // 15 15 16 16 #include <fstream.hfa> 17 #include < stdlib.hfa> // new/delete17 #include <exception.hfa> 18 18 19 19 int main( int argc, char * argv[] ) { 20 ifstream * in = &stdin; // default files 21 ofstream * out = &stdout; 20 ifstream in = stdin; // copy default files 21 ofstream out = stdout; 22 22 23 try { 23 choose ( argc ) { 24 choose ( argc ) { // terminate if command-line errors 24 25 case 2, 3: 25 in = new( (const char *)argv[1] );// open input file first as output creates file26 if ( argc == 3 ) out = new( (const char *)argv[2] ); // only open output if input opens as output created if nonexistent27 case 1: ; // use default files28 default: 29 exit | "Usage[ input-file (default stdin) [ output-file (default stdout) ] ]";26 open( in, argv[1] ); // open input file first as output creates file 27 if ( argc == 3 ) open( out, argv[2] ); // do not create output unless input opens 28 case 1: ; // use default files 29 default: // wrong number of options 30 exit | "Usage" | argv[0] | "[ input-file (default stdin) [ output-file (default stdout) ] ]"; 30 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]; 37 } // try 31 38 32 char ch; 33 *out | nlOff; // turn off auto newline 34 *in | nlOn; // turn on reading newline 39 out | nlOff; // turn off auto newline 40 in | nlOn; // turn on reading newline 35 41 36 for () { // read all characters 37 *in | ch; 38 if ( eof( *in ) ) break; // eof ? 39 *out | ch; 40 } // for 41 } finally { 42 if ( in != &stdin ) delete( in ); // close file, do not delete stdin! 43 if ( out != &stdout ) delete( out ); // close file, do not delete stdout! 44 } // try 42 char ch; 43 for () { // read all characters 44 in | ch; 45 if ( eof( in ) ) break; // eof ? 46 out | ch; 47 } // for 45 48 } // main 46 49 -
tests/coroutine/.expect/fmtLines.txt
r3c64c668 r58fe85a 48 48 { // f or n ewli 49 49 ne c hara cter s su 50 spen d (); if ( fm51 t.ch != '\n' ) b reak52 ; / / ig nore new line53 } // for sout54 | f mt.c h; //55 prin t ch arac ter }56 // f or sou t | " "57 ; // prin t bl58 ock sepa rato r } //59 for sou t | nl;60 // p rint gro up s61 epar ator } / / fo r} /62 / ma invo id p rt( Form63 at & fmt , ch ar c h )64 { fmt .ch = ch ;65 res ume( fmt );} //66 prti nt m ain( ) { Form67 at f mt; char ch; for68 ( ; ; ) { s in | ch;69 // rea d on70 e ch arac ter if ( e71 of( sin ) ) brea k;72 // eof ? p rt(73 fmt, ch ); } // for}74 // main // L ocal Var75 iabl es: //// tab -wid76 th: 4 // // c ompi le-c77 omma nd: "cfa fmt Line78 s.cf a" / /// End: //50 spen d; i f ( fmt. 51 ch ! = '\ n' ) bre ak; 52 // igno re n ewli ne 53 } // f or so ut | 54 fmt .ch; / / pr 55 int char acte r } // 56 for s out | " "; 57 / / pr int bloc 58 k se para tor } / / fo 59 r s out | nl ; 60 // pri nt g roup sep 61 arat or } // for} // 62 main void prt ( Fo rmat 63 & f mt, char ch ) { 64 f mt.c h = ch; r 65 esum e( f mt ) ;} / / pr 66 tint mai n() { Fo rmat 67 fmt ; ch ar c h; f or ( 68 ;; ) { sin | c h; 69 // r ead one 70 char acte r if ( eof 71 ( si n ) ) br eak; 72 / / eo f ? prt ( fm 73 t, c h ); } / / fo r} / 74 / ma in// Loc al V aria 75 bles : // // t ab-w idth 76 : 4 //// com pile -com 77 mand : "c fa f mtLi nes. 78 cfa" /// / En d: / / -
tests/coroutine/.in/fmtLines.txt
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 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
r3c64c668 r58fe85a 15 15 16 16 #include <fstream.hfa> 17 #include <coroutine.hfa>18 17 19 void then() { 20 sout | "Then!"; 21 } 22 23 coroutine Fibonacci { int fn; }; // used for communication 18 generator Fibonacci { 19 int fn; // used for communication 20 int fn1, fn2; // retained between resumes 21 }; 24 22 25 23 void main( Fibonacci & fib ) with( fib ) { // called on first resume 26 int fn1, fn2; // retained between resumes27 24 fn = 0; fn1 = fn; // 1st case 28 suspend _then(then);// restart last resume25 suspend { sout | "Then!"; } // restart last resume 29 26 fn = 1; fn2 = fn1; fn1 = fn; // 2nd case 30 suspend _then(then);// restart last resume27 suspend { sout | "Then!"; } // restart last resume 31 28 for () { 32 29 fn = fn1 + fn2; fn2 = fn1; fn1 = fn; // general case 33 suspend _then(then);// restart last resume30 suspend { sout | "Then!"; } // restart last resume 34 31 } // for 35 32 } -
tests/enum.cfa
r3c64c668 r58fe85a 26 26 //Dummy main 27 27 int main(int argc, char const *argv[]) { 28 printf( "done\n" ); // non-empty .expect file 28 29 } -
tests/expression.cfa
r3c64c668 r58fe85a 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; 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 85 89 } // main -
tests/forall.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed May 9 08:48:15 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Mar 19 08:29:38 201913 // Update Count : 3 212 // Last Modified On : Sun Sep 27 08:43:20 2020 13 // Update Count : 35 14 14 // 15 15 … … 158 158 } 159 159 forall( otype T ) inline static { 160 int RT9( T ) { T t; }160 int RT9( T ) { T t; return 3; } 161 161 } 162 162 … … 213 213 // w3 g3; 214 214 215 int main( void ) {} 215 int main( void ) { 216 #pragma message( "Compiled" ) // force non-empty .expect file 217 } 216 218 217 219 // Local Variables: // -
tests/heap.cfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Nov 6 17:54:56 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Nov 24 12:34:51 201913 // Update Count : 2812 // Last Modified On : Tue Dec 15 12:11:51 2020 13 // Update Count : 79 14 14 // 15 15 … … 27 27 // } 28 28 29 #define __U_DEFAULT_MMAP_START__ (512 * 1024 + 1) 30 size_t default_mmap_start() __attribute__(( weak )) { 31 return __U_DEFAULT_MMAP_START__; 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; 32 35 } // default_mmap_start 33 36 … … 75 78 size_t s = (i + 1) * 20; 76 79 char * area = (char *)malloc( s ); 77 if ( area == 0p ) abort( "malloc/free out of memory" );78 80 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last 79 81 area[malloc_usable_size( area ) - 1] = '\345'; // fill ultimate byte … … 84 86 size_t s = i + 1; // +1 to make initialization simpler 85 87 locns[i] = (char *)malloc( s ); 86 if ( locns[i] == 0p ) abort( "malloc/free out of memory" );87 88 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last 88 89 locns[i][malloc_usable_size( locns[i] ) - 1] = '\345'; // fill ultimate byte … … 100 101 size_t s = i + default_mmap_start(); // cross over point 101 102 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" );112 111 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last 113 112 locns[i][malloc_usable_size( locns[i] ) - 1] = '\345'; // fill ultimate byte … … 125 124 size_t s = (i + 1) * 20; 126 125 char * area = (char *)calloc( 5, s ); 127 if ( area == 0p ) abort( "calloc/free out of memory" );128 126 if ( area[0] != '\0' || area[s - 1] != '\0' || 129 area[malloc_ usable_size( area ) - 1] != '\0' ||127 area[malloc_size( area ) - 1] != '\0' || 130 128 ! malloc_zero_fill( area ) ) abort( "calloc/free corrupt storage1" ); 131 129 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last … … 137 135 size_t s = i + 1; 138 136 locns[i] = (char *)calloc( 5, s ); 139 if ( locns[i] == 0p ) abort( "calloc/free out of memory" );140 137 if ( locns[i][0] != '\0' || locns[i][s - 1] != '\0' || 141 locns[i][malloc_ usable_size( locns[i] ) - 1] != '\0' ||138 locns[i][malloc_size( locns[i] ) - 1] != '\0' || 142 139 ! malloc_zero_fill( locns[i] ) ) abort( "calloc/free corrupt storage2" ); 143 140 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last … … 156 153 size_t s = i + default_mmap_start(); // cross over point 157 154 char * area = (char *)calloc( 1, s ); 158 if ( area == 0p ) abort( "calloc/free out of memory" );159 155 if ( area[0] != '\0' || area[s - 1] != '\0' ) abort( "calloc/free corrupt storage4.1" ); 160 if ( area[malloc_ usable_size( area ) - 1] != '\0' ) abort( "calloc/free corrupt storage4.2" );156 if ( area[malloc_size( area ) - 1] != '\0' ) abort( "calloc/free corrupt storage4.2" ); 161 157 if ( ! malloc_zero_fill( area ) ) abort( "calloc/free corrupt storage4.3" ); 162 158 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last … … 168 164 size_t s = i + default_mmap_start(); // cross over point 169 165 locns[i] = (char *)calloc( 1, s ); 170 if ( locns[i] == 0p ) abort( "calloc/free out of memory" );171 166 if ( locns[i][0] != '\0' || locns[i][s - 1] != '\0' || 172 locns[i][malloc_ usable_size( locns[i] ) - 1] != '\0' ||167 locns[i][malloc_size( locns[i] ) - 1] != '\0' || 173 168 ! malloc_zero_fill( locns[i] ) ) abort( "calloc/free corrupt storage5" ); 174 169 locns[i][0] = '\345'; locns[i][s - 1] = '\345'; // fill first/last … … 188 183 for ( s; 1 ~ NoOfAllocs ) { // allocation of size 0 can return null 189 184 char * area = (char *)memalign( a, s ); 190 if ( area == 0p ) abort( "memalign/free out of memory" );191 185 //sout | i | area; 192 186 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 206 200 size_t s = i + default_mmap_start(); // cross over point 207 201 char * area = (char *)memalign( a, s ); 208 if ( area == 0p ) abort( "memalign/free out of memory" );209 202 //sout | i | area; 210 203 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 217 210 } // for 218 211 212 // check malloc/resize/free (sbrk) 213 214 for ( i; 2 ~ NoOfAllocs ~ 12 ) { 215 // initial N byte allocation 216 char * area = (char *)malloc( i ); 217 area[0] = '\345'; area[i - 1] = '\345'; // fill first/penultimate byte 218 219 // Do not start this loop index at 0 because resize of 0 bytes frees the storage. 220 int prev = i; 221 for ( s; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request 222 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/resize/free corrupt storage" ); 223 area = (char *)resize( area, s ); // attempt to reuse storage 224 area[0] = area[s - 1] = '\345'; // fill last byte 225 prev = s; 226 } // for 227 free( area ); 228 } // for 229 230 // check malloc/resize/free (mmap) 231 232 for ( i; 2 ~ NoOfAllocs ~ 12 ) { 233 // initial N byte allocation 234 size_t s = i + default_mmap_start(); // cross over point 235 char * area = (char *)malloc( s ); 236 area[0] = '\345'; area[s - 1] = '\345'; // fill first/penultimate byte 237 238 // Do not start this loop index at 0 because resize of 0 bytes frees the storage. 239 int prev = s; 240 for ( r; s ~ 256 * 1024 ~ 26 ) { // start at initial memory request 241 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/resize/free corrupt storage" ); 242 area = (char *)resize( area, s ); // attempt to reuse storage 243 area[0] = area[r - 1] = '\345'; // fill last byte 244 prev = r; 245 } // for 246 free( area ); 247 } // for 248 249 // check malloc/realloc/free (sbrk) 250 251 for ( i; 2 ~ NoOfAllocs ~ 12 ) { 252 // initial N byte allocation 253 char * area = (char *)malloc( i ); 254 area[0] = '\345'; area[i - 1] = '\345'; // fill first/penultimate byte 255 256 // Do not start this loop index at 0 because realloc of 0 bytes frees the storage. 257 int prev = i; 258 for ( s; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request 259 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/realloc/free corrupt storage" ); 260 area = (char *)realloc( area, s ); // attempt to reuse storage 261 area[s - 1] = '\345'; // fill last byte 262 prev = s; 263 } // for 264 free( area ); 265 } // for 266 267 // check malloc/realloc/free (mmap) 268 269 for ( i; 2 ~ NoOfAllocs ~ 12 ) { 270 // initial N byte allocation 271 size_t s = i + default_mmap_start(); // cross over point 272 char * area = (char *)malloc( s ); 273 area[0] = '\345'; area[s - 1] = '\345'; // fill first/penultimate byte 274 275 // Do not start this loop index at 0 because realloc of 0 bytes frees the storage. 276 int prev = s; 277 for ( r; s ~ 256 * 1024 ~ 26 ) { // start at initial memory request 278 if ( area[0] != '\345' || area[prev - 1] != '\345' ) abort( "malloc/realloc/free corrupt storage" ); 279 area = (char *)realloc( area, s ); // attempt to reuse storage 280 area[r - 1] = '\345'; // fill last byte 281 prev = r; 282 } // for 283 free( area ); 284 } // for 285 219 286 // check calloc/realloc/free (sbrk) 220 287 … … 222 289 // initial N byte allocation 223 290 char * area = (char *)calloc( 5, i ); 224 if ( area == 0p ) abort( "calloc/realloc/free out of memory" );225 291 if ( area[0] != '\0' || area[i - 1] != '\0' || 226 area[malloc_ usable_size( area ) - 1] != '\0' ||292 area[malloc_size( area ) - 1] != '\0' || 227 293 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage1" ); 228 294 … … 230 296 for ( s; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request 231 297 area = (char *)realloc( area, s ); // attempt to reuse storage 232 if ( area == 0p ) abort( "calloc/realloc/free out of memory" );233 298 if ( area[0] != '\0' || area[s - 1] != '\0' || 234 area[malloc_ usable_size( area ) - 1] != '\0' ||299 area[malloc_size( area ) - 1] != '\0' || 235 300 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage2" ); 236 301 } // for … … 244 309 size_t s = i + default_mmap_start(); // cross over point 245 310 char * area = (char *)calloc( 1, s ); 246 if ( area == 0p ) abort( "calloc/realloc/free out of memory" );247 311 if ( area[0] != '\0' || area[s - 1] != '\0' || 248 area[malloc_ usable_size( area ) - 1] != '\0' ||249 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage 1" );312 area[malloc_size( area ) - 1] != '\0' || 313 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage3" ); 250 314 251 315 // Do not start this loop index at 0 because realloc of 0 bytes frees the storage. 252 316 for ( r; i ~ 256 * 1024 ~ 26 ) { // start at initial memory request 253 317 area = (char *)realloc( area, r ); // attempt to reuse storage 254 if ( area == 0p ) abort( "calloc/realloc/free out of memory" );255 318 if ( area[0] != '\0' || area[r - 1] != '\0' || 256 area[malloc_ usable_size( area ) - 1] != '\0' ||257 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage 2" );319 area[malloc_size( area ) - 1] != '\0' || 320 ! malloc_zero_fill( area ) ) abort( "calloc/realloc/free corrupt storage4" ); 258 321 } // for 259 322 free( area ); … … 266 329 // initial N byte allocation 267 330 char * area = (char *)memalign( a, amount ); // aligned N-byte allocation 268 if ( area == 0p ) abort( "memalign/realloc/free out of memory" ); // no storage ?269 331 //sout | alignments[a] | area; 270 332 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 277 339 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "memalign/realloc/free corrupt storage" ); 278 340 area = (char *)realloc( area, s ); // attempt to reuse storage 279 if ( area == 0p ) abort( "memalign/realloc/free out of memory" ); // no storage ?280 341 //sout | i | area; 281 342 if ( (size_t)area % a != 0 ) { // check for initial alignment … … 293 354 for ( s; 1 ~ limit ) { // allocation of size 0 can return null 294 355 char * area = (char *)cmemalign( a, 1, s ); 295 if ( area == 0p ) abort( "cmemalign/free out of memory" );296 356 //sout | i | area; 297 357 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 299 359 } // if 300 360 if ( area[0] != '\0' || area[s - 1] != '\0' || 301 area[malloc_ usable_size( area ) - 1] != '\0' ||361 area[malloc_size( area ) - 1] != '\0' || 302 362 ! malloc_zero_fill( area ) ) abort( "cmemalign/free corrupt storage" ); 303 363 area[0] = '\345'; area[s - 1] = '\345'; // fill first/last byte … … 312 372 // initial N byte allocation 313 373 char * area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation 314 if ( area == 0p ) abort( "cmemalign/realloc/free out of memory" ); // no storage ?315 374 //sout | alignments[a] | area; 316 375 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 318 377 } // if 319 378 if ( area[0] != '\0' || area[amount - 1] != '\0' || 320 area[malloc_ usable_size( area ) - 1] != '\0' ||379 area[malloc_size( area ) - 1] != '\0' || 321 380 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage1" ); 322 381 area[0] = '\345'; area[amount - 2] = '\345'; // fill first/penultimate byte … … 326 385 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "cmemalign/realloc/free corrupt storage2" ); 327 386 area = (char *)realloc( area, s ); // attempt to reuse storage 328 if ( area == 0p ) abort( "cmemalign/realloc/free out of memory" ); // no storage ?329 387 //sout | i | area; 330 388 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment 331 389 abort( "cmemalign/realloc/free bad alignment %p", area ); 332 390 } // if 333 if ( area[ s - 1] != '\0' || area[s - 1] != '\0' ||334 area[malloc_ usable_size( area ) - 1] != '\0' ||391 if ( area[0] != '\345' || area[s - 1] != '\0' || 392 area[malloc_size( area ) - 1] != '\0' || 335 393 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage3" ); 336 394 area[s - 1] = '\345'; // fill last byte … … 339 397 } // for 340 398 399 // check memalign/resize with align/free 400 401 amount = 2; 402 for ( a; libAlign() ~= limit ~ a ) { // generate powers of 2 403 // initial N byte allocation 404 char * area = (char *)memalign( a, amount ); // aligned N-byte allocation 405 //sout | alignments[a] | area | endl; 406 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment 407 abort( "memalign/resize with align/free bad alignment : memalign(%d,%d) = %p", (int)a, (int)amount, area ); 408 } // if 409 area[0] = '\345'; area[amount - 2] = '\345'; // fill first/penultimate byte 410 411 // Do not start this loop index at 0 because resize of 0 bytes frees the storage. 412 for ( s; amount ~ 256 * 1024 ) { // start at initial memory request 413 area = (char *)resize( area, a * 2, s ); // attempt to reuse storage 414 //sout | i | area | endl; 415 if ( (size_t)area % a * 2 != 0 ) { // check for initial alignment 416 abort( "memalign/resize with align/free bad alignment %p", area ); 417 } // if 418 area[s - 1] = '\345'; // fill last byte 419 } // for 420 free( area ); 421 } // for 422 341 423 // check memalign/realloc with align/free 342 424 … … 345 427 // initial N byte allocation 346 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 ?348 429 //sout | alignments[a] | area | endl; 349 430 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 356 437 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "memalign/realloc/free corrupt storage" ); 357 438 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 ?359 439 //sout | i | area | endl; 360 440 if ( (size_t)area % a * 2 != 0 ) { // check for initial alignment … … 371 451 for ( size_t a = libAlign() + libAlign(); a <= limit; a += a ) { // generate powers of 2 372 452 // initial N byte allocation 373 char *area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation 374 if ( area == 0p ) abort( "cmemalign/realloc with align/free out of memory" ); // no storage ? 453 char * area = (char *)cmemalign( a, 1, amount ); // aligned N-byte allocation 375 454 //sout | alignments[a] | area | endl; 376 455 if ( (size_t)area % a != 0 || malloc_alignment( area ) != a ) { // check for initial alignment … … 378 457 } // if 379 458 if ( area[0] != '\0' || area[amount - 1] != '\0' || 380 area[malloc_ usable_size( area ) - 1] != '\0' ||459 area[malloc_size( area ) - 1] != '\0' || 381 460 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc with align/free corrupt storage1" ); 382 461 area[0] = '\345'; area[amount - 2] = '\345'; // fill first/penultimate byte … … 386 465 if ( area[0] != '\345' || area[s - 2] != '\345' ) abort( "cmemalign/realloc with align/free corrupt storage2" ); 387 466 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 ?389 467 //sout | i | area | endl; 390 468 if ( (size_t)area % a * 2 != 0 || malloc_alignment( area ) != a * 2 ) { // check for initial alignment 391 abort( "cmemalign/realloc with align/free bad alignment %p % jd %jd", area, malloc_alignment( area ), a * 2 );469 abort( "cmemalign/realloc with align/free bad alignment %p %zd %zd", area, malloc_alignment( area ), a * 2 ); 392 470 } // if 393 471 if ( area[s - 1] != '\0' || area[s - 1] != '\0' || 394 area[malloc_ usable_size( area ) - 1] != '\0' ||472 area[malloc_size( area ) - 1] != '\0' || 395 473 ! malloc_zero_fill( area ) ) abort( "cmemalign/realloc/free corrupt storage3" ); 396 474 area[s - 1] = '\345'; // fill last byte … … 410 488 // checkFreeOn(); 411 489 // malloc_stats(); 490 printf( "done\n" ); // non-empty .expect file 412 491 } 413 492 -
tests/identFuncDeclarator.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Aug 17 08:36:34 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 17:56:33 201813 // Update Count : 312 // Last Modified On : Sun Sep 27 08:20:46 2020 13 // Update Count : 5 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 file 113 115 } 114 116 -
tests/identParamDeclarator.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Aug 17 08:37:56 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 17:56:44 201813 // Update Count : 312 // Last Modified On : Fri Sep 25 14:31:08 2020 13 // Update Count : 4 14 14 // 15 15 … … 158 158 159 159 int main( int argc, char const *argv[] ) { // dummy main 160 return 0;160 printf( "done\n" ); // non-empty .expect file 161 161 } 162 162 -
tests/io2.cfa
r3c64c668 r58fe85a 121 121 122 122 [int, int, const char *, double] t3 = { 3, 4, "a", 7.2 }; 123 sout | [ 3, 4, "a", 7.2 ];123 sout | [ 3, 4, (const char*)"a", 7.2 ]; // workaround trac#207: the const cast should not be needed 124 124 sout | t3; 125 125 sepSetTuple( sout, " " ); -
tests/labelledExit.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Aug 10 07:29:39 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 5 16:49:48202013 // Update Count : 912 // Last Modified On : Sun Sep 27 09:01:34 2020 13 // Update Count : 12 14 14 // 15 15 … … 179 179 180 180 int main( int argc, char const *argv[] ) { 181 /* code */181 #pragma message( "Compiled" ) // force non-empty .expect file 182 182 } 183 183 -
tests/limits.cfa
r3c64c668 r58fe85a 10 10 // Created On : Tue May 10 20:44:20 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 17:57:55 201813 // Update Count : 812 // Last Modified On : Sun Sep 27 08:45:43 2020 13 // Update Count : 10 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. 15 18 16 19 #include <limits.hfa> … … 147 150 148 151 int main(int argc, char const *argv[]) { 149 //DUMMY 150 return 0; 152 #pragma message( "Compiled" ) // force non-empty .expect file 151 153 } 152 154 -
tests/linking/withthreads.cfa
r3c64c668 r58fe85a 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // nothreads.cfa --7 // withthreads.cfa -- 8 8 // 9 9 // Author : Thierry Delisle -
tests/literals.cfa
r3c64c668 r58fe85a 10 10 // Created On : Sat Sep 9 16:34:38 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 12 08:07:39 201913 // Update Count : 22 412 // Last Modified On : Sat Aug 29 10:57:56 2020 13 // Update Count : 226 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 ) 153 154 #if defined(__GNUC__) && __GNUC_PREREQ(7,0) // gcc version >= 7 154 155 // floating with length, gcc f16/f128x unsupported and no prelude code for any _FloatXXx, so they work by conversion to long double … … 194 195 /* -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; */ 195 196 #endif // __GNUC_PREREQ(7,0) 197 #endif // __i386 ) || __x86_64 196 198 197 199 #ifdef __CFA__ … … 214 216 -01234567_l8; -01234567_l16; -01234567_l32; -01234567_l64; -01234567_l8u; -01234567_ul16; -01234567_l32u; -01234567_ul64; 215 217 216 #if def __LP64__ // 64-bit processor218 #if defined( __SIZEOF_INT128__ ) 217 219 01234567_l128; 01234567_ul128; 218 220 +01234567_l128; +01234567_ul128; 219 221 -01234567_l128; -01234567_ul128; 220 #endif // __ LP64__222 #endif // __SIZEOF_INT128__ 221 223 222 224 // decimal … … 225 227 -1234567890L8; -1234567890L16; -1234567890l32; -1234567890l64; -1234567890UL8; -1234567890L16U; -1234567890Ul32; -1234567890l64u; 226 228 227 #if def __LP64__ // 64-bit processor229 #if defined( __SIZEOF_INT128__ ) 228 230 1234567890l128; 1234567890l128u; 229 231 +1234567890l128; +1234567890l128u; 230 232 -1234567890l128; -1234567890l128u; 231 #endif // __LP64__ 233 1234567890123456789_L128u; 1234567890123456789_L128u; 234 18446708753438544741_l64u; 18446708753438544741_Ul64; 235 #endif // __SIZEOF_INT128__ 232 236 233 237 // hexadecimal -
tests/manipulatorsInput.cfa
r3c64c668 r58fe85a 7 7 // Created On : Sat Jun 8 17:58:54 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Thu Jun 13 17:41:43 201910 // Update Count : 379 // Last Modified On : Wed Jul 15 15:56:03 2020 10 // Update Count : 47 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__ 154 163 } // main 155 164 -
tests/manipulatorsOutput1.cfa
r3c64c668 r58fe85a 7 7 // Created On : Sat Jun 8 18:04:11 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Mon Jun 10 12:37:28 201910 // Update Count : 89 // Last Modified On : Fri May 1 11:51:44 2020 10 // Update Count : 9 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) | nobase(hex(sc)) | hex(sc) | wd(8,hex(sc)) | wd(8,10,hex(sc)) | upcase(wd(8,3,hex(sc))) | left(sign(upcase(wd(8,3,sc)))) | pad0(wd(8,sc)); 19 sout | sc | wd(2,sc) | wd(5,2,sc) | left(wd(5,2,sc)) | nobase(oct(sc)) | oct(sc) | nonl; 20 sout | nobase(hex(sc)) | hex(sc) | wd(8,hex(sc)) | wd(8,10,hex(sc)) | upcase(wd(8,3,hex(sc))) | nonl; 21 sout | left(sign(upcase(wd(8,3,sc)))) | pad0(wd(8,sc)); 20 22 21 23 sout | "unsigned char"; 22 24 unsigned char usc = 12; 23 25 printf( "%hhu %2hhu %5.2hhu %-5.2hhu %hho %#hho %hhx %#hhx %#8hhx %#8.10hhx %#8.3hhX %-8.3hhu %08hhu\n", usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc, usc ); 24 sout | usc | wd(2,usc) | wd(5,2,usc) | left(wd(5,2,usc)) | nobase(oct(usc)) | oct(usc) | nobase(hex(usc)) | hex(usc) | wd(8,hex(usc)) | wd(8,10,hex(usc)) | upcase(wd(8,3,hex(usc))) | left(upcase(wd(8,3,usc))) | pad0(wd(8,usc)); 26 sout | usc | wd(2,usc) | wd(5,2,usc) | left(wd(5,2,usc)) | nobase(oct(usc)) | oct(usc) | nonl; 27 sout | nobase(hex(usc)) | hex(usc) | wd(8,hex(usc)) | wd(8,10,hex(usc)) | upcase(wd(8,3,hex(usc))) | nonl; 28 sout | left(upcase(wd(8,3,usc))) | pad0(wd(8,usc)); 25 29 26 30 sout | "signed short int"; 27 31 signed short int si = -12; 28 32 printf( "%hd %2hd %5.2hd %-5.2hd %ho %#ho %hx %#hx %#8hx %#8.10hx %#8.3hX %+-8.3hd %08hd\n", si, si, si, si, si, si, si, si, si, si, si, si, si ); 29 sout | si | wd(2,si) | wd(5,2,si) | left(wd(5,2,si)) | nobase(oct(si)) | oct(si) | nobase(hex(si)) | hex(si) | wd(8,hex(si)) | wd(8,10,hex(si)) | upcase(wd(8,3,hex(si))) | left(sign(upcase(wd(8,3,si)))) | pad0(wd(8,si)); 33 sout | si | wd(2,si) | wd(5,2,si) | left(wd(5,2,si)) | nobase(oct(si)) | oct(si) | nonl; 34 sout | nobase(hex(si)) | hex(si) | wd(8,hex(si)) | wd(8,10,hex(si)) | upcase(wd(8,3,hex(si))) | nonl; 35 sout | left(sign(upcase(wd(8,3,si)))) | pad0(wd(8,si)); 30 36 31 37 sout | "unsigned short int"; 32 38 unsigned short int usi = 12; 33 39 printf( "%hu %2hu %5.2hu %-5.2hu %ho %#ho %hx %#hx %#8hx %#8.10hx %#8.3hX %-8.3hu %08hu\n", usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi, usi ); 34 sout | usi | wd(2,usi) | wd(5,2,usi) | left(wd(5,2,usi)) | nobase(oct(usi)) | oct(usi) | nobase(hex(usi)) | hex(usi) | wd(8,hex(usi)) | wd(8,10,hex(usi)) | upcase(wd(8,3,hex(usi))) | left(upcase(wd(8,3,usi))) | pad0(wd(8,usi)); 40 sout | usi | wd(2,usi) | wd(5,2,usi) | left(wd(5,2,usi)) | nobase(oct(usi)) | oct(usi) | nonl; 41 sout | nobase(hex(usi)) | hex(usi) | wd(8,hex(usi)) | wd(8,10,hex(usi)) | upcase(wd(8,3,hex(usi))) | nonl; 42 sout | left(upcase(wd(8,3,usi))) | pad0(wd(8,usi)); 35 43 36 44 sout | "signed int"; 37 45 signed int i = -12; 38 46 printf( "%d %2d %5.2d %-5.2d %o %#o %x %#x %#8x %#8.10x %#8.3X %+-8.3d %08d\n", i, i, i, i, i, i, i, i, i, i, i, i, i ); 39 sout | i | wd(2,i) | wd(5,2,i) | left(wd(5,2,i)) | nobase(oct(i)) | oct(i) | nobase(hex(i)) | hex(i) | wd(8,hex(i)) | wd(8,10,hex(i)) | upcase(wd(8,3,hex(i))) | left(sign(upcase(wd(8,3,i)))) | pad0(wd(8,i)); 47 sout | i | wd(2,i) | wd(5,2,i) | left(wd(5,2,i)) | nobase(oct(i)) | oct(i) | nonl; 48 sout | nobase(hex(i)) | hex(i) | wd(8,hex(i)) | wd(8,10,hex(i)) | upcase(wd(8,3,hex(i))) | nonl; 49 sout | left(sign(upcase(wd(8,3,i)))) | pad0(wd(8,i)); 40 50 41 51 sout | "unsigned int"; 42 52 unsigned int ui = 12; 43 53 printf( "%u %2u %5.2u %-5.2u %o %#o %x %#x %#8x %#8.10x %#8.3X %-8.3u %08u\n", ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui, ui ); 44 sout | ui | wd(2,ui) | wd(5,2,ui) | left(wd(5,2,ui)) | nobase(oct(ui)) | oct(ui) | nobase(hex(ui)) | hex(ui) | wd(8,hex(ui)) | wd(8,10,hex(ui)) | upcase(wd(8,3,hex(ui))) | left(upcase(wd(8,3,ui))) | pad0(wd(8,ui)); 54 sout | ui | wd(2,ui) | wd(5,2,ui) | left(wd(5,2,ui)) | nobase(oct(ui)) | oct(ui) | nonl; 55 sout | nobase(hex(ui)) | hex(ui) | wd(8,hex(ui)) | wd(8,10,hex(ui)) | upcase(wd(8,3,hex(ui))) | nonl; 56 sout | left(upcase(wd(8,3,ui))) | pad0(wd(8,ui)); 45 57 46 58 sout | "signed long long int"; 47 59 signed long long int lli = -12; 48 60 printf( "%lld %2lld %5.2lld %-5.2lld %llo %#llo %llx %#llx %#8llx %#8.10llx %#8.3llX %+-8.3lld %08lld\n", lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli, lli ); 49 sout | lli | wd(2,lli) | wd(5,2,lli) | left(wd(5,2,lli)) | nobase(oct(lli)) | oct(lli) | nobase(hex(lli)) | hex(lli) | wd(8,hex(lli)) | wd(8,10,hex(lli)) | upcase(wd(8,3,hex(lli))) | left(sign(upcase(wd(8,3,lli)))) | pad0(wd(8,lli)); 61 sout | lli | wd(2,lli) | wd(5,2,lli) | left(wd(5,2,lli)) | nobase(oct(lli)) | oct(lli) | nonl; 62 sout | nobase(hex(lli)) | hex(lli) | wd(8,hex(lli)) | wd(8,10,hex(lli)) | upcase(wd(8,3,hex(lli))) | nonl; 63 sout | left(sign(upcase(wd(8,3,lli)))) | pad0(wd(8,lli)); 50 64 51 65 sout | "unsigned long long int"; 52 66 unsigned long long int ulli = 12; 53 67 printf( "%llu %2llu %5.2llu %-5.2llu %llo %#llo %llx %#llx %#8llx %#8.10llx %#8.3llX %-8.3llu %08llu\n", ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli, ulli ); 54 sout | ulli | wd(2,ulli) | wd(5,2,ulli) | left(wd(5,2,ulli)) | nobase(oct(ulli)) | oct(ulli) | nobase(hex(ulli)) | hex(ulli) | wd(8,hex(ulli)) | wd(8,10,hex(ulli)) | upcase(wd(8,3,hex(ulli))) | left(upcase(wd(8,3,ulli))) | pad0(wd(8,ulli)); 68 sout | ulli | wd(2,ulli) | wd(5,2,ulli) | left(wd(5,2,ulli)) | nobase(oct(ulli)) | oct(ulli) | nonl; 69 sout | nobase(hex(ulli)) | hex(ulli) | wd(8,hex(ulli)) | wd(8,10,hex(ulli)) | upcase(wd(8,3,hex(ulli))) | nonl; 70 sout | left(upcase(wd(8,3,ulli))) | pad0(wd(8,ulli)); 55 71 56 72 sout | nl | "binary integral"; 57 sout | bin(0) | bin(13) | upcase(bin(13)) | nobase(bin(13)) | left(wd(8,bin(13))) | wd(8,bin(13)) | pad0(left(wd(8,bin(13)))) | pad0(wd(8,bin(13))) | pad0(wd(8,10,bin(13))) | pad0(wd(8,6,bin(13))); 73 sout | bin(0) | bin(13) | upcase(bin(13)) | nobase(bin(13)) | left(wd(8,bin(13))) | wd(8,bin(13)) | nonl; 74 sout | pad0(left(wd(8,bin(13)))) | pad0(wd(8,bin(13))) | pad0(wd(8,10,bin(13))) | pad0(wd(8,6,bin(13))); 58 75 59 76 … … 62 79 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", 63 80 0.0,3.0F,3.0F, f, f, f, f, f, f, 3.0F, f, f, f, f, f, f, f, f, f ); 64 sout | 0.0 | wd(8, 3.0F) | nodp(wd(8, 3.0F)) | f | wd(8, f) | ws(8,0, f) | nodp(ws(8,0, f)) | ws(8,2, f) | nodp(ws(8,2, f)) | left(ws(8,2, 3.0F)) | left(ws(8,2, f)) | left(nodp(ws(8,2, f))) | left(sign(ws(8,2, f))) | left(sign(nodp(ws(8,2, f)))) | pad0(ws(8,2, f)) | upcase(wd(8,2, sci(f))) | wd(8,2, hex(f)) | upcase(wd(8,2, hex(f))) | nodp(wd(8,2, sci(f))); 81 sout | 0.0 | wd(8, 3.0F) | nodp(wd(8, 3.0F)) | f | wd(8, f) | ws(8,0, f) | nodp(ws(8,0, f)) | ws(8,2, f) | nodp(ws(8,2, f)) | nonl; 82 sout | left(ws(8,2, 3.0F)) | left(ws(8,2, f)) | left(nodp(ws(8,2, f))) | left(sign(ws(8,2, f))) | left(sign(nodp(ws(8,2, f)))) | nonl; 83 sout | pad0(ws(8,2, f)) | upcase(wd(8,2, sci(f))) | wd(8,2, hex(f)) | upcase(wd(8,2, hex(f))) | nodp(wd(8,2, sci(f))); 65 84 66 85 sout | "double"; … … 68 87 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", 69 88 0.0, 3.0, d, d, d, d, d, d, d, d, d, d, d, d ); 70 sout | 0.0 | wd(8, 3.0) | d | wd(8, d) | nodp(wd(8,0, d)) | wd(8,0, d) | wd(8,2, d) | left(wd(8,2, d)) | left(sign(wd(8,2, d))) | pad0(upcase(wd(8,2, d))) | upcase(wd(8,2, sci(d))) | wd(8,2, hex(d)) | upcase(wd(8,2, hex(d))) | wd(8,2, sci(d)); 89 sout | 0.0 | wd(8, 3.0) | d | wd(8, d) | nodp(wd(8,0, d)) | wd(8,0, d) | wd(8,2, d) | nonl; 90 sout | left(wd(8,2, d)) | left(sign(wd(8,2, d))) | pad0(upcase(wd(8,2, d))) | upcase(wd(8,2, sci(d))) | wd(8,2, hex(d)) | upcase(wd(8,2, hex(d))) | wd(8,2, sci(d)); 71 91 72 92 sout | "long double"; … … 74 94 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", 75 95 0.0L, 3.0L, ld, ld, ld, ld, ld, ld, ld, ld, ld, ld, ld, ld ); 76 sout | 0.0L | wd(8, 3.0L) | ld | wd(8, ld) | nodp(wd(8,0, ld)) | wd(8,0, ld) | wd(8,2, ld) | left(wd(8,2, ld)) | left(sign(wd(8,2, ld))) | pad0(upcase(wd(8,2, ld))) | upcase(wd(8,2, sci(ld))) | wd(8,2, hex(ld)) | upcase(wd(8,2, hex(ld))) | wd(8,2, sci(ld)); 96 sout | 0.0L | wd(8, 3.0L) | ld | wd(8, ld) | nodp(wd(8,0, ld)) | wd(8,0, ld) | wd(8,2, ld) | nonl; 97 sout | left(wd(8,2, ld)) | left(sign(wd(8,2, ld))) | pad0(upcase(wd(8,2, ld))) | upcase(wd(8,2, sci(ld))) | wd(8,2, hex(ld)) | upcase(wd(8,2, hex(ld))) | wd(8,2, sci(ld)); 77 98 78 99 … … 80 101 char c = 'a'; 81 102 printf( "%c %2c %5c %-5c %hho %#hho %hhx %#hhx %#8hhx %#8hhX %-8c %8c\n", c, c, c, c, c, c, c, c, c, c, c, c ); 82 sout | c | ' ' | wd(2,c) | wd(5,c) | left(wd(5,c)) | nobase(oct(c)) | oct(c) | nobase(hex(c)) | hex(c) | wd(8,hex(c)) | upcase(wd(8,hex(c))) | left(wd(8,c)) | wd(8,c); 103 sout | c | ' ' | wd(2,c) | wd(5,c) | left(wd(5,c)) | nobase(oct(c)) | oct(c) | nonl; 104 sout | nobase(hex(c)) | hex(c) | wd(8,hex(c)) | upcase(wd(8,hex(c))) | left(wd(8,c)) | wd(8,c); 83 105 84 106 sout | nl | "string"; -
tests/manipulatorsOutput2.cfa
r3c64c668 r58fe85a 7 7 // Created On : Sat Jun 8 18:04:11 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Mon Jun 10 12:37:57 201910 // Update Count : 89 // Last Modified On : Sun Nov 15 08:11:53 2020 10 // Update Count : 9 11 11 // 12 12 … … 52 52 // Local Variables: // 53 53 // tab-width: 4 // 54 // compile-command: "cfa -Wall -Wextra amanipulatorsOutput2.cfa" //54 // compile-command: "cfa -Wall -Wextra manipulatorsOutput2.cfa" // 55 55 // End: // -
tests/math4.cfa
r3c64c668 r58fe85a 10 10 // Created On : Thu May 24 20:56:54 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Dec 4 18:15:01 201813 // Update Count : 412 // Last Modified On : Tue Aug 25 17:56:45 2020 13 // Update Count : 7 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 20 31 float f; 21 32 double d; … … 23 34 24 35 //---------------------- Nearest Integer ---------------------- 36 37 //============================================================ 38 #if 1 39 sout | nl | "floor" | nl | nl; 40 41 printf( "signed char\n" ); 42 for ( sc = 1; sc != 0; sc <<= 1 ) { 43 scr1 = floor( sc, sc ); scr2 = floor( sc + 2hh, sc ); scr3 = floor( -sc - 2hh, sc ); 44 printf( "floor(%hhd, %hhd) = %hhd, floor(%hhd, %hhd) = %hhd, floor(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 ); 45 } // for 46 printf( "\n" ); 47 48 printf( "unsigned char\n" ); 49 for ( uc = 1; uc != 0; uc <<= 1 ) { 50 ucr1 = floor( uc, uc ); ucr2 = floor( uc + 2hh, uc ); ucr3 = floor( -uc - 2hh, uc ); 51 printf( "floor(%hhu, %hhu) = %hhu, floor(%hhu, %hhu) = %hhu, floor(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 ); 52 } // for 53 printf( "\n" ); 54 55 printf( "short int\n" ); 56 for ( si = 1; si != 0; si <<= 1 ) { 57 sir1 = floor( si, si ); sir2 = floor( si + 2hh, si ); sir3 = floor( -si - 2hh, si ); 58 printf( "floor(%hd, %hd) = %hd, floor(%hd, %hd) = %hd, floor(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 ); 59 } // for 60 printf( "\n" ); 61 62 printf( "unsigned short int\n" ); 63 for ( usi = 1; usi != 0; usi <<= 1 ) { 64 usir1 = floor( usi, usi ); usir2 = floor( usi + 2hh, usi ); usir3 = floor( -usi - 2hh, usi ); 65 printf( "floor(%hu, %hu) = %hu, floor(%hu, %hu) = %hu, floor(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 ); 66 } // for 67 printf( "\n" ); 68 69 printf( "int\n" ); 70 for ( i = 1; i != 0; i <<= 1 ) { 71 ir1 = floor( i, i ); ir2 = floor( i + 2hh, i ); ir3 = floor( -i - 2hh, i ); 72 printf( "floor(%d, %d) = %d, floor(%d, %d) = %d, floor(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 ); 73 } // for 74 printf( "\n" ); 75 76 printf( "unsigned int\n" ); 77 for ( ui = 1; ui != 0; ui <<= 1 ) { 78 uir1 = floor( ui, ui ); uir2 = floor( ui + 2hh, ui ); uir3 = floor( -ui - 2hh, ui ); 79 printf( "floor(%u, %u) = %u, floor(%u, %u) = %u, floor(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 ); 80 } // for 81 printf( "\n" ); 82 83 printf( "long int\n" ); 84 for ( li = 1; li != 0; li <<= 1 ) { 85 lir1 = floor( li, li ); lir2 = floor( li + 2hh, li ); lir3 = floor( -li - 2hh, li ); 86 printf( "floor(%ld, %ld) = %ld, floor(%ld, %ld) = %ld, floor(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 ); 87 } // for 88 printf( "\n" ); 89 90 printf( "unsigned long int\n" ); 91 for ( uli = 1; uli != 0; uli <<= 1 ) { 92 ulir1 = floor( uli, uli ); ulir2 = floor( uli + 2hh, uli ); ulir3 = floor( -uli - 2hh, uli ); 93 printf( "floor(%lu, %lu) = %lu, floor(%lu, %lu) = %lu, floor(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 ); 94 } // for 95 printf( "\n" ); 96 97 printf( "long long int\n" ); 98 for ( lli = 1; lli != 0; lli <<= 1 ) { 99 llir1 = floor( lli, lli ); llir2 = floor( lli + 2hh, lli ); llir3 = floor( -lli - 2hh, lli ); 100 printf( "floor(%lld, %lld) = %lld, floor(%lld, %lld) = %lld, floor(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 ); 101 } // for 102 printf( "\n" ); 103 104 printf( "unsigned long long int\n" ); 105 for ( ulli = 1; ulli != 0; ulli <<= 1 ) { 106 ullir1 = floor( ulli, ulli ); ullir2 = floor( ulli + 2hh, ulli ); ullir3 = floor( -ulli - 2hh, ulli ); 107 printf( "floor(%llu, %llu) = %llu, floor(%llu, %llu) = %llu, floor(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 ); 108 } // for 109 printf( "\n" ); 110 #endif // 0 111 //============================================================ 112 #if 1 113 sout | nl | "ceiling_div" | nl | nl; 114 115 printf( "signed char\n" ); 116 for ( sc = 1; sc != 0; sc <<= 1 ) { 117 scr1 = ceiling_div( sc, sc ); scr2 = ceiling_div( sc + 2hh, sc ); scr3 = ceiling_div( -sc - 2hh, sc ); 118 printf( "ceiling_div(%hhd, %hhd) = %hhd, ceiling_div(%hhd, %hhd) = %hhd, ceiling_div(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 ); 119 } // for 120 printf( "\n" ); 121 122 printf( "unsigned char\n" ); 123 for ( uc = 1; uc != 0; uc <<= 1 ) { 124 ucr1 = ceiling_div( uc, uc ); ucr2 = ceiling_div( uc + 2hh, uc ); ucr3 = ceiling_div( -uc - 2hh, uc ); 125 printf( "ceiling_div(%hhu, %hhu) = %hhu, ceiling_div(%hhu, %hhu) = %hhu, ceiling_div(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 ); 126 } // for 127 printf( "\n" ); 128 129 printf( "short int\n" ); 130 for ( si = 1; si != 0; si <<= 1 ) { 131 sir1 = ceiling_div( si, si ); sir2 = ceiling_div( si + 2hh, si ); sir3 = ceiling_div( -si - 2hh, si ); 132 printf( "ceiling_div(%hd, %hd) = %hd, ceiling_div(%hd, %hd) = %hd, ceiling_div(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 ); 133 } // for 134 printf( "\n" ); 135 136 printf( "unsigned short int\n" ); 137 for ( usi = 1; usi != 0; usi <<= 1 ) { 138 usir1 = ceiling_div( usi, usi ); usir2 = ceiling_div( usi + 2hh, usi ); usir3 = ceiling_div( -usi - 2hh, usi ); 139 printf( "ceiling_div(%hu, %hu) = %hu, ceiling_div(%hu, %hu) = %hu, ceiling_div(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 ); 140 } // for 141 printf( "\n" ); 142 143 printf( "int\n" ); 144 for ( i = 1; i != 0; i <<= 1 ) { 145 ir1 = ceiling_div( i, i ); ir2 = ceiling_div( i + 2hh, i ); ir3 = ceiling_div( -i - 2hh, i ); 146 printf( "ceiling_div(%d, %d) = %d, ceiling_div(%d, %d) = %d, ceiling_div(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 ); 147 } // for 148 printf( "\n" ); 149 150 printf( "unsigned int\n" ); 151 for ( ui = 1; ui != 0; ui <<= 1 ) { 152 uir1 = ceiling_div( ui, ui ); uir2 = ceiling_div( ui + 2hh, ui ); uir3 = ceiling_div( -ui - 2hh, ui ); 153 printf( "ceiling_div(%u, %u) = %u, ceiling_div(%u, %u) = %u, ceiling_div(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 ); 154 } // for 155 printf( "\n" ); 156 157 printf( "long int\n" ); 158 for ( li = 1; li != 0; li <<= 1 ) { 159 lir1 = ceiling_div( li, li ); lir2 = ceiling_div( li + 2hh, li ); lir3 = ceiling_div( -li - 2hh, li ); 160 printf( "ceiling_div(%ld, %ld) = %ld, ceiling_div(%ld, %ld) = %ld, ceiling_div(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 ); 161 } // for 162 printf( "\n" ); 163 164 printf( "unsigned long int\n" ); 165 for ( uli = 1; uli != 0; uli <<= 1 ) { 166 ulir1 = ceiling_div( uli, uli ); ulir2 = ceiling_div( uli + 2hh, uli ); ulir3 = ceiling_div( -uli - 2hh, uli ); 167 printf( "ceiling_div(%lu, %lu) = %lu, ceiling_div(%lu, %lu) = %lu, ceiling_div(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 ); 168 } // for 169 printf( "\n" ); 170 171 printf( "long long int\n" ); 172 for ( lli = 1; lli != 0; lli <<= 1 ) { 173 llir1 = ceiling_div( lli, lli ); llir2 = ceiling_div( lli + 2hh, lli ); llir3 = ceiling_div( -lli - 2hh, lli ); 174 printf( "ceiling_div(%lld, %lld) = %lld, ceiling_div(%lld, %lld) = %lld, ceiling_div(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 ); 175 } // for 176 printf( "\n" ); 177 178 printf( "unsigned long long int\n" ); 179 for ( ulli = 1; ulli != 0; ulli <<= 1 ) { 180 ullir1 = ceiling_div( ulli, ulli ); ullir2 = ceiling_div( ulli + 2hh, ulli ); ullir3 = ceiling_div( -ulli - 2hh, ulli ); 181 printf( "ceiling_div(%llu, %llu) = %llu, ceiling_div(%llu, %llu) = %llu, ceiling_div(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 ); 182 } // for 183 printf( "\n" ); 184 #endif // 0 185 //============================================================ 186 #if 1 187 sout | nl | "ceiling" | nl | nl; 188 189 printf( "signed char\n" ); 190 for ( sc = 1; sc != 0; sc <<= 1 ) { 191 scr1 = ceiling( sc, sc ); scr2 = ceiling( sc + 2hh, sc ); scr3 = ceiling( -sc - 2hh, sc ); 192 printf( "ceiling(%hhd, %hhd) = %hhd, ceiling(%hhd, %hhd) = %hhd, ceiling(%hhd, %hhd) = %hhd\n", sc, sc, scr1, sc + 2hh, sc, scr2, -sc - 2hh, sc, scr3 ); 193 } // for 194 printf( "\n" ); 195 196 printf( "unsigned char\n" ); 197 for ( uc = 1; uc != 0; uc <<= 1 ) { 198 ucr1 = ceiling( uc, uc ); ucr2 = ceiling( uc + 2hh, uc ); ucr3 = ceiling( -uc - 2hh, uc ); 199 printf( "ceiling(%hhu, %hhu) = %hhu, ceiling(%hhu, %hhu) = %hhu, ceiling(%hhu, %hhu) = %hhu\n", uc, uc, ucr1, uc + 2uhh, uc, ucr2, -uc - 2uhh, uc, ucr3 ); 200 } // for 201 printf( "\n" ); 202 203 printf( "short int\n" ); 204 for ( si = 1; si != 0; si <<= 1 ) { 205 sir1 = ceiling( si, si ); sir2 = ceiling( si + 2hh, si ); sir3 = ceiling( -si - 2hh, si ); 206 printf( "ceiling(%hd, %hd) = %hd, ceiling(%hd, %hd) = %hd, ceiling(%hd, %hd) = %hd\n", si, si, sir1, si + 2h, si, sir2, -si - 2h, si, sir3 ); 207 } // for 208 printf( "\n" ); 209 210 printf( "unsigned short int\n" ); 211 for ( usi = 1; usi != 0; usi <<= 1 ) { 212 usir1 = ceiling( usi, usi ); usir2 = ceiling( usi + 2hh, usi ); usir3 = ceiling( -usi - 2hh, usi ); 213 printf( "ceiling(%hu, %hu) = %hu, ceiling(%hu, %hu) = %hu, ceiling(%hu, %hu) = %hu\n", usi, usi, usir1, usi + 2uh, usi, usir2, -usi - 2uh, usi, usir3 ); 214 } // for 215 printf( "\n" ); 216 217 printf( "int\n" ); 218 for ( i = 1; i != 0; i <<= 1 ) { 219 ir1 = ceiling( i, i ); ir2 = ceiling( i + 2hh, i ); ir3 = ceiling( -i - 2hh, i ); 220 printf( "ceiling(%d, %d) = %d, ceiling(%d, %d) = %d, ceiling(%d, %d) = %d\n", i, i, ir1, i + 2h, i, ir2, -i - 2h, i, ir3 ); 221 } // for 222 printf( "\n" ); 223 224 printf( "unsigned int\n" ); 225 for ( ui = 1; ui != 0; ui <<= 1 ) { 226 uir1 = ceiling( ui, ui ); uir2 = ceiling( ui + 2hh, ui ); uir3 = ceiling( -ui - 2hh, ui ); 227 printf( "ceiling(%u, %u) = %u, ceiling(%u, %u) = %u, ceiling(%u, %u) = %u\n", ui, ui, uir1, ui + 2h, ui, uir2, -ui - 2h, ui, uir3 ); 228 } // for 229 printf( "\n" ); 230 231 printf( "long int\n" ); 232 for ( li = 1; li != 0; li <<= 1 ) { 233 lir1 = ceiling( li, li ); lir2 = ceiling( li + 2hh, li ); lir3 = ceiling( -li - 2hh, li ); 234 printf( "ceiling(%ld, %ld) = %ld, ceiling(%ld, %ld) = %ld, ceiling(%ld, %ld) = %ld\n", li, li, lir1, li + 2h, li, lir2, -li - 2h, li, lir3 ); 235 } // for 236 printf( "\n" ); 237 238 printf( "unsigned long int\n" ); 239 for ( uli = 1; uli != 0; uli <<= 1 ) { 240 ulir1 = ceiling( uli, uli ); ulir2 = ceiling( uli + 2hh, uli ); ulir3 = ceiling( -uli - 2hh, uli ); 241 printf( "ceiling(%lu, %lu) = %lu, ceiling(%lu, %lu) = %lu, ceiling(%lu, %lu) = %lu\n", uli, uli, ulir1, uli + 2h, uli, ulir2, -uli - 2h, uli, ulir3 ); 242 } // for 243 printf( "\n" ); 244 245 printf( "long long int\n" ); 246 for ( lli = 1; lli != 0; lli <<= 1 ) { 247 llir1 = ceiling( lli, lli ); llir2 = ceiling( lli + 2hh, lli ); llir3 = ceiling( -lli - 2hh, lli ); 248 printf( "ceiling(%lld, %lld) = %lld, ceiling(%lld, %lld) = %lld, ceiling(%lld, %lld) = %lld\n", lli, lli, llir1, lli + 2h, lli, llir2, -lli - 2h, lli, llir3 ); 249 } // for 250 printf( "\n" ); 251 252 printf( "unsigned long long int\n" ); 253 for ( ulli = 1; ulli != 0; ulli <<= 1 ) { 254 ullir1 = ceiling( ulli, ulli ); ullir2 = ceiling( ulli + 2hh, ulli ); ullir3 = ceiling( -ulli - 2hh, ulli ); 255 printf( "ceiling(%llu, %llu) = %llu, ceiling(%llu, %llu) = %llu, ceiling(%llu, %llu) = %llu\n", ulli, ulli, ullir1, ulli + 2h, ulli, ullir2, -ulli - 2h, ulli, ullir3 ); 256 } // for 257 printf( "\n" ); 258 #endif // 0 25 259 26 260 sout | "floor:" | floor( 1.2F ) | floor( 1.2D ) | floor( 1.2L ); … … 69 303 // Local Variables: // 70 304 // tab-width: 4 // 71 // compile-command: "cfa math 3.cfa" //305 // compile-command: "cfa math4.cfa" // 72 306 // End: // -
tests/maybe.cfa
r3c64c668 r58fe85a 10 10 // Created On : Thr May 25 16:02:00 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jul 20 15:24:07 201713 // Update Count : 112 // Last Modified On : Fri Sep 25 15:13:28 2020 13 // Update Count : 2 14 14 // 15 15 … … 65 65 //checkNamedConstructors(); 66 66 checkSetters(); 67 printf( "done\n" ); // non-empty .expect file 67 68 } -
tests/minmax.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Dec 4 21:45:31 201813 // Update Count : 5 212 // Last Modified On : Sat Aug 15 08:28:01 2020 13 // Update Count : 54 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
r3c64c668 r58fe85a 10 10 // Created On : Mon Jul 9 10:20:03 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 12 18:21:15202013 // Update Count : 312 // Last Modified On : Sun Sep 27 08:48:59 2020 13 // Update Count : 6 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 … … 65 65 66 66 int main() { 67 // access nested struct68 S.T x;67 // access nested struct 68 S.T x; 69 69 70 {71 struct S {72 int i;73 struct Z {74 double d;75 };76 };70 { 71 struct S { 72 int i; 73 struct Z { 74 double d; 75 }; 76 }; 77 77 78 S.Z z;// gets local S79 .S.T y;// lookup at global scope only78 S.Z z; // gets local S 79 .S.T y; // lookup at global scope only 80 80 81 const volatile .S.T q;81 const volatile .S.T q; 82 82 #if ERR1 83 T err1;// error: no T in scope83 T err1; // error: no T in scope 84 84 #endif 85 85 #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 S86 .Z err2; // error: no Z in global scope 87 .S.Baz.Bar err3; // error: .S.Baz => int, int is not aggregate and should not appear left of the dot 88 .S.Z err4; // error: no Z in global S 89 89 #endif 90 }90 } 91 91 92 // U.S un;92 // U.S un; 93 93 94 S.Bar y;95 S.Baz x;96 S.T.Bar z;94 S.Bar y; 95 S.Baz x; 96 S.T.Bar z; 97 97 98 // A.N(int) x; // xxx - should not be an error, but currently is. 98 // A.N(int) x; // xxx - should not be an error, but currently is. 99 100 #pragma message( "Compiled" ) // force non-empty .expect file 99 101 } 100 102 -
tests/numericConstants.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed May 24 22:10:36 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 5 08:58:16 201913 // Update Count : 512 // Last Modified On : Sun Sep 27 07:55:22 2020 13 // Update Count : 7 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 file 65 67 } // main 66 68 -
tests/operators.cfa
r3c64c668 r58fe85a 31 31 int main(int argc, char const *argv[]) { 32 32 /* code */ 33 return 0;33 printf( "done\n" ); // non-empty .expect file 34 34 } 35 35 -
tests/pybin/settings.py
r3c64c668 r58fe85a 23 23 class Architecture: 24 24 KnownArchitectures = { 25 'x64' : 'x64', 26 'x86-64' : 'x64', 27 'x86_64' : 'x64', 28 'x86' : 'x86', 29 'aarch64' : 'arm', 30 'i386' : 'x86', 31 'i486' : 'x86', 32 'i686' : 'x86', 33 'Intel 80386' : 'x86', 34 'arm' : 'arm', 35 'ARM' : 'arm', 25 'x64' : 'x64', 26 'x86-64' : 'x64', 27 'x86_64' : 'x64', 28 'x86' : 'x86', 29 'aarch64' : 'arm64', 30 'arm64' : 'arm64', 31 'ARM64' : 'arm64', 32 'i386' : 'x86', 33 'i486' : 'x86', 34 'i686' : 'x86', 35 'Intel 80386' : 'x86', 36 'arm' : 'arm32', 37 'ARM' : 'arm32', 38 'arm32' : 'arm32', 39 'ARM32' : 'arm32', 36 40 } 37 41 38 42 CrossCompileFlags = { 39 'x64' : 'ARCH_FLAGS=-m64', 40 'x86' : 'ARCH_FLAGS=-m32', 43 'x64' : 'ARCH_FLAGS=-m64', 44 'x86' : 'ARCH_FLAGS=-m32', 45 'arm64': 'ARCH_FLAGS=', 46 'arm32': 'ARCH_FLAGS=', 41 47 } 42 48 … … 77 83 print("updated to %s" % self.target) 78 84 79 def match(self, arch):80 return True if not arch else self.target == arch81 82 @ classmethod83 def make_canonical( _,arch):85 def filter(self, tests): 86 return [test for test in tests if not test.arch or self.target == test.arch] 87 88 @staticmethod 89 def make_canonical(arch): 84 90 return Architecture.KnownArchitectures[arch] 85 91 … … 91 97 self.path = "debug" if value else "nodebug" 92 98 99 class AST: 100 def __init__(self, ast): 101 if ast == "new": 102 self.target = ast 103 self.string = "New AST" 104 self.flags = """AST_FLAGS=-XCFA,--new-ast""" 105 elif ast == "old": 106 self.target = ast 107 self.string = "Old AST" 108 self.flags = """AST_FLAGS=-XCFA,--old-ast""" 109 elif ast == None: 110 self.target = "new" if config.NEWAST else "old" 111 self.string = "Default AST (%s)" % self.target 112 self.flags = """AST_FLAGS=""" 113 else: 114 print("""ERROR: Invalid ast configuration, must be "old", "new" or left unspecified, was %s""" % (value), file=sys.stderr) 115 sys.exit(1) 116 117 def filter(self, tests): 118 119 return [test for test in tests if not test.astv or self.target == test.astv] 120 93 121 class Install: 94 122 def __init__(self, value): … … 104 132 self.total = Timeouts.check(tg) 105 133 106 @ classmethod107 def check( _,value):134 @staticmethod 135 def check(value): 108 136 if value < 1: 109 137 print("Timeouts must be at least 1 second", file=sys.stderr) … … 113 141 114 142 def init( options ): 143 global all_ast 144 global all_arch 145 global all_debug 146 global all_install 147 global ast 115 148 global arch 149 global debug 116 150 global archive 117 global debug 118 global distcc 151 global install 152 153 global continue_ 119 154 global dry_run 120 155 global generating 121 global install122 156 global make 123 157 global output_width 124 158 global timeout 125 126 arch = Architecture(options.arch) 159 global timeout2gdb 160 161 all_ast = [AST(o) for o in list(dict.fromkeys(options.ast ))] if options.ast else [AST(None)] 162 all_arch = [Architecture(o) for o in list(dict.fromkeys(options.arch ))] if options.arch else [Architecture(None)] 163 all_debug = [Debug(o) for o in list(dict.fromkeys(options.debug ))] 164 all_install = [Install(o) for o in list(dict.fromkeys(options.install))] 127 165 archive = os.path.abspath(os.path.join(original_path, options.archive_errors)) if options.archive_errors else None 128 debug = Debug(options.debug)166 continue_ = options.continue_ 129 167 dry_run = options.dry_run # must be called before tools.config_hash() 130 distcc = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash()131 168 generating = options.regenerate_expected 132 install = Install(options.install)133 169 make = ['make'] 134 170 output_width = 24 135 171 timeout = Timeouts(options.timeout, options.global_timeout) 172 timeout2gdb = options.timeout_with_gdb 136 173 137 174 # if we distribute, distcc errors will fail tests, use log file for distcc … … 146 183 147 184 def validate(): 185 """Validate the current configuration and update globals""" 186 187 global distcc 188 distcc = "DISTCC_CFA_PATH=~/.cfadistcc/%s/cfa" % tools.config_hash() 148 189 errf = os.path.join(BUILDDIR, ".validate.err") 149 190 make_ret, out = tools.make( ".validate", error_file = errf, output_file=subprocess.DEVNULL, error=subprocess.DEVNULL ) -
tests/pybin/test_run.py
r3c64c668 r58fe85a 11 11 self.path = '' 12 12 self.arch = '' 13 self.astv = '' 13 14 14 15 def toString(self): 15 return "{:25s} ({:5s} {:s})".format( self.name, self.arch if self.archelse "Any", self.target() )16 return "{:25s} ({:5s} arch, {:s} ast: {:s})".format( self.name, self.arch if self.arch else "Any", self.astv if self.astv else "Any", self.target() ) 16 17 17 18 def prepare(self): … … 20 21 21 22 def expect(self): 22 return os.path.normpath( os.path.join(settings.SRCDIR , self.path, ".expect", "%s%s.txt" % (self.name,'' if not self.arch else ".%s" % self.arch)) ) 23 arch = '' if not self.arch else ".%s" % self.arch 24 astv = '' if not self.astv else ".nast" if self.astv == "new" else ".oast" 25 return os.path.normpath( os.path.join(settings.SRCDIR , self.path, ".expect", "%s%s%s.txt" % (self.name,astv,arch)) ) 23 26 24 27 def error_log(self): … … 40 43 return os.path.normpath( os.path.join(settings.BUILDDIR, self.path, self.name) ) 41 44 42 @ classmethod43 def valid_name( _,name):45 @staticmethod 46 def valid_name(name): 44 47 return not name.endswith( ('.c', '.cc', '.cpp', '.cfa') ) 45 48 46 @ classmethod47 def from_target(_, target):49 @staticmethod 50 def new_target(target, arch, astv): 48 51 test = Test() 49 52 test.name = os.path.basename(target) 50 53 test.path = os.path.relpath (os.path.dirname(target), settings.SRCDIR) 51 test.arch = settings.arch.target if settings.arch.cross_compile else '' 54 test.arch = arch.target if arch else '' 55 test.astv = astv.target if astv else '' 52 56 return test 53 57 … … 69 73 else : text = "FAILED with code %d" % retcode 70 74 71 text += " C%s - R%s" % ( cls.fmtDur(duration[0]), cls.fmtDur(duration[1]))75 text += " C%s - R%s" % (fmtDur(duration[0]), fmtDur(duration[1])) 72 76 return text 73 74 @classmethod75 def fmtDur( cls, duration ):76 if duration :77 hours, rem = divmod(duration, 3600)78 minutes, rem = divmod(rem, 60)79 seconds, millis = divmod(rem, 1)80 return "%2d:%02d.%03d" % (minutes, seconds, millis * 1000)81 return " n/a" -
tests/pybin/tools.py
r3c64c668 r58fe85a 73 73 ) 74 74 75 return proc.returncode, out.decode(" utf-8") if out else None75 return proc.returncode, out.decode("latin-1") if out else None 76 76 except subprocess.TimeoutExpired: 77 proc.send_signal(signal.SIGABRT) 78 proc.communicate() 79 return 124, str(None) 77 if settings.timeout2gdb: 78 print("Process {} timeout".format(proc.pid)) 79 proc.communicate() 80 return 124, str(None) 81 else: 82 proc.send_signal(signal.SIGABRT) 83 proc.communicate() 84 return 124, str(None) 80 85 81 86 except Exception as ex: … … 83 88 raise 84 89 90 def is_empty(fname): 91 if not os.path.isfile(fname): 92 return True 93 94 if os.stat(fname).st_size == 0: 95 return True 96 97 return False 98 85 99 def is_ascii(fname): 86 100 if settings.dry_run: 87 101 print("is_ascii: %s" % fname) 88 return True102 return (True, "") 89 103 90 104 if not os.path.isfile(fname): 91 return False92 93 code, out = sh("file %s" %fname, output_file=subprocess.PIPE)105 return (False, "No file") 106 107 code, out = sh("file", fname, output_file=subprocess.PIPE) 94 108 if code != 0: 95 return False109 return (False, "'file EXPECT' failed with code {}".format(code)) 96 110 97 111 match = re.search(".*: (.*)", out) 98 112 99 113 if not match: 100 return False 101 102 return match.group(1).startswith("ASCII text") 114 return (False, "Unreadable file type: '{}'".format(out)) 115 116 if "ASCII text" in match.group(1): 117 return (True, "") 118 119 return (False, "File type should be 'ASCII text', was '{}'".format(match.group(1))) 103 120 104 121 def is_exe(fname): … … 115 132 return None 116 133 117 file = open(file, mode )134 file = open(file, mode, encoding="latin-1") # use latin-1 so all chars mean something. 118 135 exitstack.push(file) 119 136 return file … … 164 181 '-s' if silent else None, 165 182 test_param, 183 settings.ast.flags, 166 184 settings.arch.flags, 167 185 settings.debug.flags, … … 173 191 cmd = [s for s in cmd if s] 174 192 return sh(*cmd, output_file=output_file, error=error) 193 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 target 201 ] 202 cmd = [s for s in cmd if s] 203 return sh(*cmd, output_file=subprocess.PIPE) 175 204 176 205 def which(program): … … 233 262 # helper function to check if a files contains only a specific string 234 263 def file_contains_only(file, text) : 235 with open(file ) as f:264 with open(file, encoding="latin-1") as f: # use latin-1 so all chars mean something. 236 265 ff = f.read().strip() 237 266 result = ff == text.strip() … … 241 270 # transform path to canonical form 242 271 def canonical_path(path): 243 abspath = os.path.abspath( __main__.__file__)272 abspath = os.path.abspath(os.path.realpath(__main__.__file__)) 244 273 dname = os.path.dirname(abspath) 245 274 return os.path.join(dname, os.path.normpath(path) ) … … 322 351 raise argparse.ArgumentTypeError(msg) 323 352 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 324 357 def fancy_print(text): 325 358 column = which('column') … … 378 411 while True: 379 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" -
tests/raii/.expect/ctor-autogen.txt
r3c64c668 r58fe85a 1 done -
tests/raii/.expect/init_once.txt
r3c64c668 r58fe85a 1 done -
tests/raii/ctor-autogen.cfa
r3c64c668 r58fe85a 151 151 identity(gcs); 152 152 identity(gcu); 153 printf( "done\n" ); // non-empty .expect file 153 154 } -
tests/raii/init_once.cfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Jun 14 15:43:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Mar 22 13:41:26 201913 // Update Count : 412 // Last Modified On : Fri Sep 25 15:36:39 2020 13 // Update Count : 5 14 14 // 15 15 … … 188 188 static_variable(); 189 189 } 190 printf( "done\n" ); // non-empty .expect file 190 191 } 191 192 -
tests/references.cfa
r3c64c668 r58fe85a 124 124 int *p = &a; 125 125 asm ( 126 "incl %[p]\n\t" 127 : [p] "+m" (*p) 126 #if defined( __i386 ) || defined( __x86_64 ) 127 "incl %[p]\n\t" 128 : [p] "+m" (*p) 129 #elif defined( __aarch64__ ) 130 "ldr w1, %[p]\n\t" 131 "add w1, w1, 1\n\t" 132 "str w1, %[p]\n\t" 133 : [p] "+m" (*p) ::"w1" 134 #endif 128 135 ); 129 136 printf("%d\n", a); -
tests/result.cfa
r3c64c668 r58fe85a 10 10 // Created On : Thr May 25 16:50:00 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jul 20 15:24:12 201713 // Update Count : 112 // Last Modified On : Fri Sep 25 15:22:59 2020 13 // Update Count : 2 14 14 // 15 15 … … 66 66 checkGetters(); 67 67 checkSetters(); 68 printf( "done\n" ); // non-empty .expect file 68 69 } -
tests/searchsort.cfa
r3c64c668 r58fe85a 38 38 } // for 39 39 sout | nl; 40 for ( i; 0 ~ size ) { // C version 40 for ( i; 0 ~ size ) { // C version, returns void* 41 41 int key = size - i; 42 int * v = bsearch( &key, iarr, size, sizeof( iarr[0] ), comp );42 int * v = ( int * ) bsearch( &key, iarr, size, sizeof( iarr[0] ), comp ); 43 43 sout | key | ':' | *v | ", "; 44 44 } // for -
tests/stdincludes.cfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Aug 29 08:26:14 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 18:00:53 201813 // Update Count : 612 // Last Modified On : Sun Sep 27 08:51:38 2020 13 // Update Count : 8 14 14 // 15 15 … … 47 47 #include <wctype.h> 48 48 49 int main() {} 49 int main() { 50 #pragma message( "Compiled" ) // force non-empty .expect file 51 } 50 52 51 53 // Local Variables: // -
tests/switch.cfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Jul 12 06:50:22 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 18:01:34 201813 // Update Count : 3712 // Last Modified On : Sun Sep 27 08:35:02 2020 13 // Update Count : 43 14 14 // 15 15 … … 100 100 j = 5; 101 101 } // choose 102 103 #pragma message( "Compiled" ) // force non-empty .expect file 102 104 } // main 103 105 -
tests/test.py
r3c64c668 r58fe85a 6 6 7 7 import argparse 8 import itertools 8 9 import re 9 10 import sys … … 23 24 24 25 def match_test(path): 25 match = re.search("^%s\/([\w\/\-_]*).expect\/([\w\-_]+)(\. [\w\-_]+)?\.txt$" % settings.SRCDIR, path)26 match = re.search("^%s\/([\w\/\-_]*).expect\/([\w\-_]+)(\.nast|\.oast)?(\.[\w\-_]+)?\.txt$" % settings.SRCDIR, path) 26 27 if match : 27 28 test = Test() 28 29 test.name = match.group(2) 29 30 test.path = match.group(1) 30 test.arch = match.group(3)[1:] if match.group(3) else None 31 if settings.arch.match(test.arch): 32 expected.append(test) 31 test.arch = match.group(4)[1:] if match.group(4) else None 32 33 astv = match.group(3)[1:] if match.group(3) else None 34 if astv == 'oast': 35 test.astv = 'old' 36 elif astv == 'nast': 37 test.astv = 'new' 38 elif astv: 39 print('ERROR: "%s", expect file has astv but it is not "nast" or "oast"' % testname, file=sys.stderr) 40 sys.exit(1) 41 42 expected.append(test) 33 43 34 44 path_walk( match_test ) … … 53 63 ] 54 64 65 # sort the test alphabetically for convenience 66 test_list.sort(key=lambda t: ('~' if t.arch else '') + t.target() + (t.arch if t.arch else '')) 67 55 68 return test_list 56 69 … … 63 76 if options.regenerate_expected : 64 77 for testname in options.tests : 65 testname = canonical_path( testname ) 78 testname = os.path.normpath( os.path.join(settings.SRCDIR, testname) ) 79 80 # first check if this is a valid name to regenerate 66 81 if Test.valid_name(testname): 82 # this is a valid name, let's check if it already exists 67 83 found = [test for test in all_tests if canonical_path( test.target() ) == testname] 68 tests.append( found[0] if len(found) == 1 else Test.from_target(testname) ) 84 setup = itertools.product(settings.all_arch if options.arch else [None], settings.all_ast if options.ast else [None]) 85 if not found: 86 # it's a new name, create it according to the name and specified architecture/ast version 87 tests.extend( [Test.new_target(testname, arch, ast) for arch, ast in setup] ) 88 elif len(found) == 1 and not found[0].arch: 89 # we found a single test, the user better be wanting to create a cross platform test 90 if options.arch: 91 print('ERROR: "%s", test has no specified architecture but --arch was specified, ignoring it' % testname, file=sys.stderr) 92 elif options.ast: 93 print('ERROR: "%s", test has no specified ast version but --ast was specified, ignoring it' % testname, file=sys.stderr) 94 else: 95 tests.append( found[0] ) 96 else: 97 # this test is already cross platform, just add a test for each platform the user asked 98 tests.extend( [Test.new_target(testname, arch, ast) for arch, ast in setup] ) 99 100 # print a warning if it users didn't ask for a specific architecture 101 found_arch = [f.arch for f in found if f.arch] 102 if found_arch and not options.arch: 103 print('WARNING: "%s", test has architecture specific expected files but --arch was not specified, regenerating only for current host' % testname, file=sys.stderr) 104 105 106 # print a warning if it users didn't ask for a specific ast version 107 found_astv = [f.astv for f in found if f.astv] 108 if found_astv and not options.ast: 109 print('WARNING: "%s", test has ast version specific expected files but --ast was not specified, regenerating only for current ast' % testname, file=sys.stderr) 110 69 111 else : 70 112 print('ERROR: "%s", tests are not allowed to end with a C/C++/CFA extension, ignoring it' % testname, file=sys.stderr) … … 76 118 77 119 if test : 78 tests. append( test[0])120 tests.extend( test ) 79 121 else : 80 122 print('ERROR: No expected file for test %s, ignoring it' % testname, file=sys.stderr) … … 86 128 # create a parser with the arguments for the tests script 87 129 parser = argparse.ArgumentParser(description='Script which runs cforall tests') 88 parser.add_argument('--debug', help='Run all tests in debug or release', type=yes_no, default='yes') 89 parser.add_argument('--install', help='Run all tests based on installed binaries or tree binaries', type=yes_no, default='no') 90 parser.add_argument('--arch', help='Test for specific architecture', type=str, default='') 91 parser.add_argument('--timeout', help='Maximum duration in seconds after a single test is considered to have timed out', type=int, default=60) 130 parser.add_argument('--ast', help='Test for specific ast', type=comma_separated(str), default=None) 131 parser.add_argument('--arch', help='Test for specific architecture', type=comma_separated(str), default=None) 132 parser.add_argument('--debug', help='Run all tests in debug or release', type=comma_separated(yes_no), default='yes') 133 parser.add_argument('--install', help='Run all tests based on installed binaries or tree binaries', type=comma_separated(yes_no), default='no') 134 parser.add_argument('--continue', help='When multiple specifications are passed (debug/install/arch), sets whether or not to continue if the last specification failed', type=yes_no, default='yes', dest='continue_') 135 parser.add_argument('--timeout', help='Maximum duration in seconds after a single test is considered to have timed out', type=int, default=120) 92 136 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") 93 138 parser.add_argument('--dry-run', help='Don\'t run the tests, only output the commands', action='store_true') 94 139 parser.add_argument('--list', help='List all test available', action='store_true') … … 98 143 parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously', type=int) 99 144 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') 100 146 parser.add_argument('-I','--include', help='Directory of test to include, can be used multiple time, All if omitted', action='append') 101 147 parser.add_argument('-E','--exclude', help='Directory of test to exclude, can be used multiple time, None if omitted', action='append') … … 110 156 111 157 # script must have at least some tests to run or be listing 112 listing = options.list or options.list_comp 158 listing = options.list or options.list_comp or options.list_dist 113 159 all_tests = options.all 114 160 some_tests = len(options.tests) > 0 … … 145 191 test.prepare() 146 192 193 # ---------- 194 # MAKE 195 # ---------- 147 196 # build, skipping to next test on error 148 197 with Timed() as comp_dur: 149 198 make_ret, _ = make( test.target(), output_file=subprocess.DEVNULL, error=out_file, error_file = err_file ) 150 199 200 # ---------- 201 # RUN 202 # ---------- 203 # run everything in a temp directory to make sure core file are handled properly 151 204 run_dur = None 152 # run everything in a temp directory to make sure core file are handled properly153 205 with tempdir(): 154 206 # if the make command succeeds continue otherwise skip to diff … … 166 218 if success(retcode): 167 219 if settings.generating : 168 # if we are o unly generating the output we still need to check that the test actually exists220 # if we are only generating the output we still need to check that the test actually exists 169 221 if no_rule(out_file, test.target()) : 170 222 retcode = 1 … … 178 230 179 231 else: 180 with open (out_file, "r") as myfile: 181 error = myfile.read() 232 if os.stat(out_file).st_size < 1048576: 233 with open (out_file, "r", encoding='latin-1') as myfile: # use latin-1 so all chars mean something. 234 error = myfile.read() 235 else: 236 error = "Output log can't be read, file is bigger than 1MB, see {} for actual error\n".format(out_file) 182 237 183 238 ret, info = core_info(exe_file) … … 214 269 except KeyboardInterrupt: 215 270 return False, "" 216 except Exception as ex:217 print("Unexpected error in worker thread: %s" % ex, file=sys.stderr)218 sys.stderr.flush()219 return False, ""271 # except Exception as ex: 272 # print("Unexpected error in worker thread running {}: {}".format(t.target(), ex), file=sys.stderr) 273 # sys.stderr.flush() 274 # return False, "" 220 275 221 276 … … 225 280 make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL) 226 281 227 # since python prints stacks by default on a interrupt, redo the interrupt handling to be silent 228 def worker_init(): 229 def sig_int(signal_num, frame): 230 pass 231 232 signal.signal(signal.SIGINT, sig_int) 233 234 # create the executor for our jobs and handle the signal properly 235 pool = multiprocessing.Pool(jobs, worker_init) 282 # create the executor for our jobs 283 pool = multiprocessing.Pool(jobs) 236 284 237 285 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)243 286 244 287 # for each test to run … … 278 321 make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL) 279 322 280 return 1 if failed else 0323 return failed 281 324 282 325 … … 292 335 settings.init( options ) 293 336 337 # -------------------------------------------------- 338 # list all the test for auto completion programs 339 # not pretty, single line, with the command line options 340 if options.list_comp : 341 # fetch the liest of all valid tests 342 tests = list_tests( None, None ) 343 344 # print the possible options 345 print("-h --help --debug --dry-run --list --ast=new --ast=old --arch --all --regenerate-expected --archive-errors --install --timeout --global-timeout --timeout-with-gdb -j --jobs -I --include -E --exclude --continue ", end='') 346 print(" ".join(map(lambda t: "%s" % (t.target()), tests))) 347 348 # done 349 sys.exit(0) 350 351 # -------------------------------------------------- 352 # list all the test for auto completion programs 353 if options.list_dist : 354 # fetch the liest of all valid tests 355 tests = list_tests( None, None ) 356 357 for t in tests: 358 print(os.path.relpath(t.expect(), settings.SRCDIR), end=' ') 359 print(os.path.relpath(t.input() , settings.SRCDIR), end=' ') 360 code, out = make_recon(t.target()) 361 362 if code != 0: 363 print('ERROR: recond failed for test {}'.format(t.target()), file=sys.stderr) 364 sys.exit(1) 365 366 print(' '.join(re.findall('([^\s]+\.cfa)', out)), end=' ') 367 368 print('') 369 370 # done 371 sys.exit(0) 372 373 374 # -------------------------------------------------- 375 # list all the tests for users, in a pretty format 376 if options.list : 377 # fetch the liest of all valid tests 378 tests = list_tests( options.include, options.exclude ) 379 380 # print the available tests 381 fancy_print("\n".join(map(lambda t: t.toString(), tests))) 382 383 # done 384 sys.exit(0) 385 294 386 # fetch the liest of all valid tests 295 387 all_tests = list_tests( options.include, options.exclude ) 296 388 297 298 389 # if user wants all tests than no other treatement of the test list is required 299 if options.all or options. list or options.list_comp or options.include :390 if options.all or options.include : 300 391 tests = all_tests 301 392 … … 309 400 sys.exit(1) 310 401 311 312 # sort the test alphabetically for convenience 313 tests.sort(key=lambda t: (t.arch if t.arch else '') + t.target()) 314 315 # users may want to simply list the tests 316 if options.list_comp : 317 print("-h --help --debug --dry-run --list --arch --all --regenerate-expected --archive-errors --install --timeout --global-timeout -j --jobs ", end='') 318 print(" ".join(map(lambda t: "%s" % (t.target()), tests))) 319 320 elif options.list : 321 print("Listing for %s:%s"% (settings.arch.string, settings.debug.string)) 322 fancy_print("\n".join(map(lambda t: t.toString(), tests))) 323 324 else : 325 # check the build configuration works 326 settings.prep_output(tests) 327 settings.validate() 328 329 options.jobs, forceJobs = job_count( options, tests ) 330 settings.update_make_cmd(forceJobs, options.jobs) 331 332 print('%s %i tests on %i cores (%s:%s)' % ( 333 'Regenerating' if settings.generating else 'Running', 334 len(tests), 335 options.jobs, 336 settings.arch.string, 337 settings.debug.string 338 )) 339 340 # otherwise run all tests and make sure to return the correct error code 341 sys.exit( run_tests(tests, options.jobs) ) 402 # prep invariants 403 settings.prep_output(tests) 404 failed = 0 405 406 # check if the expected files aren't empty 407 if not options.regenerate_expected: 408 for t in tests: 409 if is_empty(t.expect()): 410 print('WARNING: test "{}" has empty .expect file'.format(t.target()), file=sys.stderr) 411 412 # for each build configurations, run the test 413 with Timed() as total_dur: 414 for ast, arch, debug, install in itertools.product(settings.all_ast, settings.all_arch, settings.all_debug, settings.all_install): 415 settings.ast = ast 416 settings.arch = arch 417 settings.debug = debug 418 settings.install = install 419 420 # filter out the tests for a different architecture 421 # tests are the same across debug/install 422 local_tests = settings.ast.filter( tests ) 423 local_tests = settings.arch.filter( local_tests ) 424 options.jobs, forceJobs = job_count( options, local_tests ) 425 settings.update_make_cmd(forceJobs, options.jobs) 426 427 # check the build configuration works 428 settings.validate() 429 430 # print configuration 431 print('%s %i tests on %i cores (%s:%s - %s)' % ( 432 'Regenerating' if settings.generating else 'Running', 433 len(local_tests), 434 options.jobs, 435 settings.ast.string, 436 settings.arch.string, 437 settings.debug.string 438 )) 439 if not local_tests : 440 print('WARNING: No tests for this configuration') 441 continue 442 443 # otherwise run all tests and make sure to return the correct error code 444 failed = run_tests(local_tests, options.jobs) 445 if failed: 446 result = 1 447 if not settings.continue_: 448 break 449 450 print('Tests took %s' % fmtDur( total_dur.duration )) 451 sys.exit( failed ) -
tests/time.cfa
r3c64c668 r58fe85a 10 10 // Created On : Tue Mar 27 17:24:56 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 5 18:27:37202013 // Update Count : 3 412 // Last Modified On : Thu Jun 18 18:14:49 2020 13 // Update Count : 37 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; 22 23 d1 = 0; 23 24 sout | d1 | d2 | d3; -
tests/tuple/tupleAssign.cfa
r3c64c668 r58fe85a 44 44 double d = 0.0; 45 45 int i = 0; 46 char c = '\0';46 signed 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=" | c | ' ' | "t=[" | t | "]";57 sout | "d=" | d | "i=" | i | "c=" | (char)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=" | c | ' ' | "t=[" | t | "]";60 sout | "d=" | d | "i=" | i | "c=" | (char)c | ' ' | "t=[" | t | "]"; 61 61 } 62 62 } -
tests/typedefRedef.cfa
r3c64c668 r58fe85a 27 27 typedef int ARR[]; 28 28 typedef int ARR[]; 29 // #ifdef ERR1 30 // if a typedef has an array dimension, 31 // it can only be redefined to the same dimension 29 #ifdef ERR1 30 // if a typedef has an array dimension, it can only be redefined to the same dimension 32 31 typedef int ARR[2]; 33 //#endif32 #endif 34 33 35 34 typedef int X; … … 54 53 55 54 int main() { 56 typedef int ARR[sz];55 typedef int ARR[sz]; 57 56 58 // can't redefine typedef which is VLA57 // can't redefine typedef which is VLA 59 58 #if ERR1 60 typedef int ARR[sz];59 typedef int ARR[sz]; 61 60 #endif 62 61 63 Foo *x;62 Foo * x; 64 63 65 typedef struct Bar Foo;66 Foo *y;64 typedef struct Bar Foo; 65 Foo * y; 67 66 68 typedef int *** pt; 67 typedef int *** pt; 68 69 #pragma message( "Compiled" ) // force non-empty .expect file 69 70 } -
tests/typeof.cfa
r3c64c668 r58fe85a 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 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 11 12 } -
tests/variableDeclarator.cfa
r3c64c668 r58fe85a 10 10 // Created On : Wed Aug 17 08:41:42 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Nov 6 18:02:16 201813 // Update Count : 212 // Last Modified On : Sun Sep 27 07:46:17 2020 13 // Update Count : 13 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[ ];35 int f15[0]; 36 36 int f16[10]; 37 int (f17[ ]);37 int (f17[0]); 38 38 int (f18[10]); 39 39 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[ ];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]; 47 47 int * const * const f26[10]; 48 48 49 int *(f27[ ]);49 int *(f27[0]); 50 50 int *(f28[10]); 51 int **(f29[ ]);51 int **(f29[0]); 52 52 int **(f30[10]); 53 int * const *(f31[ ]);53 int * const *(f31[0]); 54 54 int * const *(f32[10]); 55 int * const * const (f33[ ]);55 int * const * const (f33[0]); 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[ ][3];67 int f43[0][3]; 68 68 int f44[3][3]; 69 int (f45[ ])[3];69 int (f45[0])[3]; 70 70 int (f46[3])[3]; 71 int ((f47[ ]))[3];71 int ((f47[0]))[3]; 72 72 int ((f48[3]))[3]; 73 73 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];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]; 81 81 int * const * const f56[3][3]; 82 82 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]);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]); 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__ 124 125 // Cforall extensions 125 126 … … 129 130 const * const * int cf6; 130 131 131 [ ] int cf15;132 [0] int cf15; 132 133 [10] int cf16; 133 134 134 [ ] * int cf19;135 [0] * int cf19; 135 136 [10] * int cf20; 136 int ** cf21[];137 int ** cf21[0]; 137 138 [10] * * int cf22; 138 [ ] * const * int cf23;139 [0] * const * int cf23; 139 140 [10] * const * int cf24; 140 [ ] const * const * int cf25;141 [0] const * const * int cf25; 141 142 [10] const * const * int cf26; 142 143 … … 150 151 const * const * [10] int cf42; 151 152 152 [ ][3] int cf43;153 [0][3] int cf43; 153 154 [3][3] int cf44; 154 155 155 [ ][3] * int cf49;156 [0][3] * int cf49; 156 157 [3][3] * int cf50; 157 [ ][3] * * int cf51;158 [0][3] * * int cf51; 158 159 [3][3] * * int cf52; 159 [ ][3] const * int cf53;160 [0][3] const * int cf53; 160 161 [3][3] * const * int cf54; 161 [ ][3] const * const * int cf55;162 [0][3] const * const * int cf55; 162 163 [3][3] const * const * int cf56; 163 164 … … 173 174 174 175 *[]*[]* [ *[]*[] int ]( *[]*[] int, *[]*[] int ) v3; 176 #endif // __CFA__ 175 177 176 178 //Dummy main 177 int main(int argc, char const *argv[]) 178 { 179 return 0; 179 int main( int argc, char const * argv[] ) { 180 #pragma message( "Compiled" ) // force non-empty .expect file 180 181 } 181 182 -
tests/vector.cfa
r3c64c668 r58fe85a 14 14 // 15 15 16 #include <vector.hfa> 16 17 #include <fstream.hfa> 17 #include <vector.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)) ); 30 34 31 35 assert( empty( &iv ) ); -
tests/voidPtr.cfa
r3c64c668 r58fe85a 13 13 if ( ! a ) { 14 14 abort(); 15 } 15 } 16 printf( "done\n" ); // non-empty .expect file 16 17 } 17 18 -
tests/warnings/self-assignment.cfa
r3c64c668 r58fe85a 10 10 // Created On : Thu Mar 1 13:53:57 2018 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 20 07:56:17 201913 // Update Count : 312 // Last Modified On : Sun Sep 27 09:24:34 2020 13 // Update Count : 6 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 file 33 35 } 34 36 35 37 // Local Variables: // 36 38 // tab-width: 4 // 37 // compile-command: "cfa dtor-early-exit" //39 // compile-command: "cfa self-assignment.cfa" // 38 40 // End: // -
tests/zombies/structMember.cfa
r3c64c668 r58fe85a 53 53 // C useless declarations 54 54 55 #ifdef ERROR 55 56 int; 56 57 TD; … … 70 71 W(int); 71 72 W(int).X; 73 #endif // ERROR 72 74 }; 73 75 -
tools/Makefile.am
r3c64c668 r58fe85a 18 18 ACLOCAL_AMFLAGS = -I automake 19 19 20 noinst_PROGRAMS = busy catchsig repeat watchdog 20 21 AM_CFLAGS = -Wall -Wextra -O2 -g 22 busy_LDFLAGS = -pthread 21 23 22 noinst_PROGRAMS = busy catchsig repeat watchdog 23 24 busy_SOURCES = busy.c 25 busy_LDFLAGS = -pthread 26 catchsig_SOURCES = catchsig.c 27 repeat_SOURCES = repeat.c 28 watchdog_SOURCES = watchdog.c 24 nodist_busy_SOURCES = busy.c 25 nodist_catchsig_SOURCES = catchsig.c 26 nodist_repeat_SOURCES = repeat.c 27 nodist_watchdog_SOURCES = watchdog.c -
tools/build/push2dist.sh
r3c64c668 r58fe85a 2 2 3 3 hash="$1" 4 bwlim="$2" 4 5 valid=$(distcc -j 2> /dev/null) 5 6 # if test "${valid}" != 0 … … 19 20 # echo "Copying to machines : ${hosts} (hash=${hash})" 20 21 21 files="../../../driver/cfa ../../../driver/cfa-cpp ../../../driver/cc1 ../../../driver/as $(find . -name '*.c*' | tr '\n' ' ')"22 files="../../../driver/cfa ../../../driver/cfa-cpp ../../../driver/cc1 ../../../driver/as defines.hfa $(find . -name '*.c*' | tr '\n' ' ')" 22 23 # echo "Files ${files}" 23 24 24 25 function push() { 25 26 ssh ${host} "mkdir -p ~/.cfadistcc/${hash}/" 26 rsync - a ${dV} ${files} ${host}:~/.cfadistcc/${hash}/.27 rsync --bwlimit=${bwlim} -a ${dV} ${files} ${host}:~/.cfadistcc/${hash}/. 27 28 } 28 29 -
tools/cfa.nanorc
r3c64c668 r58fe85a 14 14 15 15 # Declarations 16 color brightgreen "\<(struct|union|typedef|trait|coroutine| monitor|thread)\>"17 color brightgreen "\<( with)\>"16 color brightgreen "\<(struct|union|typedef|trait|coroutine|generator)\>" 17 color brightgreen "\<(monitor|thread|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 )\>"21 color brightyellow "\<(disable|enable|waitfor|when|timeout|suspend)\>" 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
r3c64c668 r58fe85a 30 30 tools_prettyprinter_PROGRAMS = pretty 31 31 tools_prettyprinterdir = ../ 32 pretty_SOURCES = ${SRC}32 nodist_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
r3c64c668 r58fe85a 1 #!/usr/bin/python 1 #!/usr/bin/python3 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
r3c64c668 r58fe85a 2 2 "name": "cforall", 3 3 "version": "0.1.0", 4 "displayName": "C forallLanguage Support",4 "displayName": "Cā (C-for-all) 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. svg",11 "icon": "images/icon.png", 12 12 "categories": [ 13 " Languages",13 "Programming Languages", 14 14 "Linters", 15 15 "Other" 16 16 ], 17 "activationEvents": [ 18 "onLanguage:cforall" 19 ], 20 "main": "./client/main.js", 17 21 "contributes": { 18 22 "languages": [ … … 21 25 "aliases": [ 22 26 "Cā", 27 "CForAll", 23 28 "Cforall", 24 "CForAll",25 29 "cforall" 26 30 ], 27 31 "extensions": [ 28 ".cf" 32 ".cfa", 33 ".hfa", 34 ".ifa" 29 35 ], 30 36 "configuration": "./cforall.configuration.json" … … 34 40 { 35 41 "language": "cforall", 36 "scopeName": "source.cf ",37 "path": "./syntaxes/cfa.tmLanguage "42 "scopeName": "source.cfa", 43 "path": "./syntaxes/cfa.tmLanguage.json" 38 44 } 39 ] 45 ], 46 "configuration": { 47 "type": "object", 48 "title": "Example configuration", 49 "properties": { 50 "cforall.maxNumberOfProblems": { 51 "scope": "resource", 52 "type": "number", 53 "default": 100, 54 "description": "Controls the maximum number of problems produced by the server." 55 }, 56 "cforall.trace.server": { 57 "scope": "window", 58 "type": "string", 59 "enum": [ 60 "off", 61 "messages", 62 "verbose" 63 ], 64 "default": "off", 65 "description": "Traces the communication between VS Code and the language server." 66 } 67 } 68 } 69 }, 70 "dependencies": { 71 "vscode-languageclient": "^4.1.4" 72 }, 73 "devDependencies": { 74 "vscode-languageclient": "^4.1.4" 40 75 } 41 76 }
Note:
See TracChangeset
for help on using the changeset viewer.