Changes in / [f845e80:98b4b12]
- Files:
-
- 49 added
- 14 deleted
- 92 edited
Legend:
- Unmodified
- Added
- Removed
-
benchmark/Makefile.am
rf845e80 r98b4b12 21 21 include $(top_srcdir)/src/cfa.make 22 22 23 AM_CFLAGS = -O2 -Wall - I$(srcdir) -lrt -pthread24 AM_CFAFLAGS = -quiet - in-tree -nodebug25 AM_UPPFLAGS = -quiet -nodebug -multi 23 AM_CFLAGS = -O2 -Wall -Wextra -Werror -I$(srcdir) -lrt -pthread 24 AM_CFAFLAGS = -quiet -nodebug -in-tree 25 AM_UPPFLAGS = -quiet -nodebug -multi -std=c++14 26 26 27 27 BENCH_V_CC = $(__bench_v_CC_$(__quiet)) … … 139 139 $(BENCH_V_CC)$(COMPILE) -DBENCH_N=500000000 $(srcdir)/fetch_add.c 140 140 141 tls-fetch_add$(EXEEXT): 142 $(BENCH_V_CC)$(COMPILE) -DBENCH_N=500000000 $(srcdir)/tls-fetch_add.c 143 141 144 ## ========================================================================================================= 142 145 CTXSWITCH_DEPEND = \ … … 144 147 function.run \ 145 148 fetch_add.run \ 149 tls-fetch_add.run \ 146 150 ctxswitch-pthread.run \ 147 151 ctxswitch-cfa_coroutine.run \ -
benchmark/Makefile.in
rf845e80 r98b4b12 371 371 372 372 # applies to both programs 373 AM_CFLAGS = -O2 -Wall - I$(srcdir) -lrt -pthread374 AM_CFAFLAGS = -quiet - in-tree -nodebug375 AM_UPPFLAGS = -quiet -nodebug -multi 373 AM_CFLAGS = -O2 -Wall -Wextra -Werror -I$(srcdir) -lrt -pthread 374 AM_CFAFLAGS = -quiet -nodebug -in-tree 375 AM_UPPFLAGS = -quiet -nodebug -multi -std=c++14 376 376 BENCH_V_CC = $(__bench_v_CC_$(__quiet)) 377 377 BENCH_V_CFA = $(__bench_v_CFA_$(__quiet)) … … 402 402 dummy_SOURCES = dummyC.c dummyCXX.cpp 403 403 CTXSWITCH_DEPEND = loop.run function.run fetch_add.run \ 404 ctxswitch-pthread.run ctxswitch-cfa_coroutine.run \405 ctxswitch-cfa_ thread.run ctxswitch-cfa_thread2.run \406 ctxswitch- upp_coroutine.run ctxswitch-upp_thread.run \407 ctxswitch- goroutine.run ctxswitch-java_thread.run \408 $(am__append_1)404 tls-fetch_add.run ctxswitch-pthread.run \ 405 ctxswitch-cfa_coroutine.run ctxswitch-cfa_thread.run \ 406 ctxswitch-cfa_thread2.run ctxswitch-upp_coroutine.run \ 407 ctxswitch-upp_thread.run ctxswitch-goroutine.run \ 408 ctxswitch-java_thread.run $(am__append_1) 409 409 testdir = $(top_srcdir)/tests 410 410 all: all-am … … 799 799 $(BENCH_V_CC)$(COMPILE) -DBENCH_N=500000000 $(srcdir)/fetch_add.c 800 800 801 tls-fetch_add$(EXEEXT): 802 $(BENCH_V_CC)$(COMPILE) -DBENCH_N=500000000 $(srcdir)/tls-fetch_add.c 803 801 804 @WITH_LIBFIBRE_TRUE@ctxswitch-kos_fibre$(EXEEXT): 802 805 @WITH_LIBFIBRE_TRUE@ $(BENCH_V_CXX)$(CXXCOMPILE) -DBENCH_N=50000000 $(srcdir)/ctxswitch/kos_fibre.cpp -I$(LIBFIBRE_DIR) -lfibre -
configure
rf845e80 r98b4b12 16738 16738 16739 16739 #============================================================================== 16740 ac_config_files="$ac_config_files Makefile driver/Makefile src/Makefile benchmark/Makefile tests/Makefile tests/preempt_longrun/Makefile tools/Makefile tools/prettyprinter/Makefile"16740 ac_config_files="$ac_config_files Makefile driver/Makefile src/Makefile benchmark/Makefile tests/Makefile longrun_tests/Makefile tools/Makefile tools/prettyprinter/Makefile" 16741 16741 16742 16742 … … 17876 17876 "benchmark/Makefile") CONFIG_FILES="$CONFIG_FILES benchmark/Makefile" ;; 17877 17877 "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; 17878 " tests/preempt_longrun/Makefile") CONFIG_FILES="$CONFIG_FILES tests/preempt_longrun/Makefile" ;;17878 "longrun_tests/Makefile") CONFIG_FILES="$CONFIG_FILES longrun_tests/Makefile" ;; 17879 17879 "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; 17880 17880 "tools/prettyprinter/Makefile") CONFIG_FILES="$CONFIG_FILES tools/prettyprinter/Makefile" ;; -
configure.ac
rf845e80 r98b4b12 211 211 benchmark/Makefile 212 212 tests/Makefile 213 tests/preempt_longrun/Makefile213 longrun_tests/Makefile 214 214 tools/Makefile 215 215 tools/prettyprinter/Makefile -
doc/bibliography/pl.bib
rf845e80 r98b4b12 1163 1163 title = {Checked C: Making C Safe by Extension}, 1164 1164 booktitle = {2018 IEEE Cybersecurity Development (SecDev)}, 1165 year 1166 month 1167 pages 1168 publisher 1169 url 1165 year = {2018}, 1166 month = {September}, 1167 pages = {53-60}, 1168 publisher = {IEEE}, 1169 url = {https://www.microsoft.com/en-us/research/publication/checkedc-making-c-safe-by-extension/}, 1170 1170 } 1171 1171 1172 1172 @misc{Clang, 1173 keywords 1174 contributer 1175 title 1176 howpublished 1173 keywords = {clang}, 1174 contributer = {a3moss@uwaterloo.ca}, 1175 title = {Clang: a {C} language family frontend for {LLVM}}, 1176 howpublished= {\href{https://clang.llvm.org/}{https://\-clang.llvm.org/}} 1177 1177 } 1178 1178 … … 2347 2347 } 2348 2348 2349 @article{Ritchie93, 2350 keywords = {C, history}, 2351 contributer = {pabuhr@plg}, 2352 author = {Ritchie, Dennis M.}, 2353 title = {The Development of the {C} Language}, 2354 journal = sigplan, 2355 volume = 28, 2356 number = 3, 2357 month = mar, 2358 year = 1993, 2359 pages = {201--208}, 2360 url = {http://doi.acm.org/10.1145/155360.155580}, 2361 publisher = {ACM}, 2362 address = {New York, NY, USA}, 2363 } 2364 2349 2365 @article{design, 2350 2366 keywords = {Smalltalk, designing classes}, … … 2354 2370 journal = joop, 2355 2371 year = 1988, 2356 volume = 1, number = 2, pages = {22-35}, 2372 volume = 1, 2373 number = 2, 2374 pages = {22-35}, 2357 2375 comment = { 2358 2376 Abstract classes represent standard protocols. ``It is better to … … 3789 3807 optaddress = {Waterloo, Ontario, Canada, N2L 3G1}, 3790 3808 note = {\href{https://uwspace.uwaterloo.ca/handle/10012/13935}{https://\-uwspace.uwaterloo.ca/\-handle/\-10012/\-13935}}, 3809 } 3810 3811 @article{Swift05, 3812 contributer = {pabuhr@plg}, 3813 author = {Michael M. Swift and Brian N. Bershad and Henry M. Levy}, 3814 title = {Improving the Reliability of Commodity Operating Systems}, 3815 journal = tocs, 3816 volume = 23, 3817 number = 1, 3818 month = feb, 3819 year = 2005, 3820 pages = {77-110}, 3791 3821 } 3792 3822 -
doc/papers/concurrency/Paper.tex
rf845e80 r98b4b12 215 215 {} 216 216 \lstnewenvironment{Go}[1][] 217 {\lstset{#1}} 217 {\lstset{language=go,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 218 {} 219 \lstnewenvironment{python}[1][] 220 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 218 221 {} 219 222 … … 228 231 } 229 232 230 \title{\texorpdfstring{Advanced Control-flow in \protect\CFA}{Advanced Control-flow in Cforall}}233 \title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}} 231 234 232 235 \author[1]{Thierry Delisle} … … 242 245 \abstract[Summary]{ 243 246 \CFA is a modern, polymorphic, non-object-oriented, backwards-compatible extension of the C programming language. 244 This paper discusses the advanced control-flow features in \CFA, which include concurrency and parallelism, and its supporting runtime system.245 These features are created from scratch as ISO C's concurrency is low-level and unimplemented, so C programmers continue to rely on the C pthreads library.246 \CFA provides high-level control-flow mechanisms, like coroutines and user-level threads, and monitors for mutual exclusion and synchronization.247 A unique contribution of this work is allowing multiple monitors to be safely acquired \emph{simultaneously} (deadlock free), while integrating this capability with allmonitor synchronization mechanisms.248 All features respect the expectations of C programmers, while being fully integrate with the \CFA polymorphic type-system and other language features.247 This paper discusses some advanced control-flow and concurrency/parallelism features in \CFA, along with the supporting runtime. 248 These features are created from scratch because they do not exist in ISO C, or are low-level and/or unimplemented, so C programmers continue to rely on library features, like C pthreads. 249 \CFA introduces language-level control-flow mechanisms, like coroutines, user-level threading, and monitors for mutual exclusion and synchronization. 250 A unique contribution of this work is allowing multiple monitors to be safely acquired \emph{simultaneously} (deadlock free), while integrating this capability with monitor synchronization mechanisms. 251 These features also integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers. 249 252 Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming-languages. 250 253 }% … … 261 264 \section{Introduction} 262 265 263 This paper discusses the design of advanced, high-level control-flow extensions (especially concurrency and parallelism)in \CFA and its runtime.266 This paper discusses the design of language-level control-flow and concurrency/parallelism extensions in \CFA and its runtime. 264 267 \CFA is a modern, polymorphic, non-object-oriented\footnote{ 265 268 \CFA has features often associated with object-oriented programming languages, such as constructors, destructors, virtuals and simple inheritance. 266 269 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.}, 267 270 backwards-compatible extension of the C programming language~\cite{Moss18}. 268 Within the \CFA framework, new control-flow features were created from scratch. 269 ISO \Celeven defines only a subset of the \CFA extensions, and with respect to concurrency~\cite[\S~7.26]{C11}, the features are largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}. 270 Furthermore, \Celeven and pthreads concurrency is basic, based on thread fork/join in a function and a few locks, which is low-level and error prone; 271 no high-level language concurrency features exist. 272 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-8 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C concurrency approach. 273 Finally, while the \Celeven standard does not state a concurrent threading-model, the historical association with pthreads suggests the threading model is kernel-level threading (1:1)~\cite{ThreadModel}. 271 Within the \CFA framework, new control-flow features are created from scratch. 272 ISO \Celeven defines only a subset of the \CFA extensions, where the overlapping features are concurrency~\cite[\S~7.26]{C11}. 273 However, \Celeven concurrency is largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}. 274 Furthermore, \Celeven and pthreads concurrency is simple, based on thread fork/join in a function and a few locks, which is low-level and error prone; 275 no high-level language concurrency features are defined. 276 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-8 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C11 concurrency approach. 277 Finally, while the \Celeven standard does not state a concurrent threading-model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}. 274 278 275 279 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. … … 284 288 285 289 A further effort over the past decade is the development of language memory-models to deal with the conflict between certain language features and compiler/hardware optimizations. 286 This issue can be rephrased as some features are pervasive (language and runtime) and cannot be safely added via a library to prevent invalidation by sequential optimizations~\cite{Buhr95a,Boehm05}.290 This issue can be rephrased as: some language features are pervasive (language and runtime) and cannot be safely added via a library to prevent invalidation by sequential optimizations~\cite{Buhr95a,Boehm05}. 287 291 The consequence is that a language must be cognizant of these features and provide sufficient tools to program around any safety issues. 288 292 For example, C created the @volatile@ qualifier to provide correct execution for @setjmp@/@logjmp@ (concurrency came later). 289 The simplestsolution is to provide a handful of complex qualifiers and functions (e.g., @volatile@ and atomics) allowing programmers to write consistent/race-free programs, often in the sequentially-consistent memory-model~\cite{Boehm12}.293 The common solution is to provide a handful of complex qualifiers and functions (e.g., @volatile@ and atomics) allowing programmers to write consistent/race-free programs, often in the sequentially-consistent memory-model~\cite{Boehm12}. 290 294 291 295 While having a sufficient memory-model allows sound libraries to be constructed, writing these libraries can quickly become awkward and error prone, and using these low-level libraries has the same issues. 292 296 Essentially, using low-level explicit locks is the concurrent equivalent of assembler programming. 293 Just as most assembler programming is replaced with programming in a high-level language, explicit locks can be replaced with high-level concurrency constructsin a programming language.294 The goal is to getthe compiler to check for correct usage and follow any complex coding conventions implicitly.297 Just as most assembler programming is replaced with high-level programming, explicit locks can be replaced with high-level concurrency in a programming language. 298 Then the goal is for the compiler to check for correct usage and follow any complex coding conventions implicitly. 295 299 The drawback is that language constructs may preclude certain specialized techniques, therefore introducing inefficiency or inhibiting concurrency. 296 300 For most concurrent programs, these drawbacks are insignificant in comparison to the speed of composition, and subsequent reliability and maintainability of the high-level concurrent program. … … 299 303 As stated, this observation applies to non-concurrent forms of complex control-flow, like exception handling and coroutines. 300 304 301 Adapting the programming language allows matching the control-flow model with the programming-language style, versus adapting toone general (sound) library/paradigm.305 Adapting the programming language to these features also allows matching the control-flow model with the programming-language style, versus adopting one general (sound) library/paradigm. 302 306 For example, it is possible to provide exceptions, coroutines, monitors, and tasks as specialized types in an object-oriented language, integrating these constructs to allow leveraging the type-system (static type-checking) and all other object-oriented capabilities~\cite{uC++}. 303 307 It is also possible to leverage call/return for blocking communication via new control structures, versus switching to alternative communication paradigms, like channels or message passing. … … 307 311 however, the reverse is seldom true, i.e., given implicit concurrency, e.g., actors, it is virtually impossible to create explicit concurrency, e.g., blocking thread objects.} 308 312 Finally, with extended language features and user-level threading it is possible to discretely fold locking and non-blocking I/O multiplexing into the language's I/O libraries, so threading implicitly dovetails with the I/O subsystem. 309 310 \CFA embraces language extensions and user-level threading to provide advanced control-flow and concurrency. 311 We attempt to show the \CFA extensions and runtime are demonstrably better than those proposed for \CC and other concurrent, imperative programming languages. 312 The contributions of this work are: 313 \CFA embraces language extensions and user-level threading to provide advanced control-flow (exception handling\footnote{ 314 \CFA exception handling will be presented in a separate paper. 315 The key feature that dovetails with this paper is non-local 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++} 316 } and coroutines) and concurrency. 317 318 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. 319 As a result, there is a significant learning curve to move to these languages, and C legacy-code must be rewritten. 320 While \CC, like \CFA, takes an evolutionary approach to extend C, \CC's constantly growing complex and interdependent features-set (e.g., objects, inheritance, templates, etc.) mean idiomatic \CC code is difficult to use from C, and C programmers must expend significant effort learning \CC. 321 Hence, rewriting and retraining costs for these languages, even \CC, are prohibitive for companies with a large C software-base. 322 \CFA with its orthogonal feature-set, its high-performance runtime, and direct access to all existing C libraries circumvents these problems. 323 324 We present comparative examples so the reader can judge if the \CFA control-flow extensions are equivalent or better than those in or proposed for \Celeven, \CC and other concurrent, imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms. 325 The detailed contributions of this work are: 313 326 \begin{itemize} 314 327 \item … … 615 628 616 629 617 \section{Coroutines: A Stepping Stone}\label{coroutine}618 619 Advanced controlWhile the focus of this discussion is concurrency and parallelism, it is important to address coroutines, which are a significant building block of a concurrency system (but not concurrent among themselves). 630 \section{Coroutines: Stepping Stone} 631 \label{coroutine} 632 620 633 Coroutines are generalized routines allowing execution to be temporarily suspended and later resumed. 621 634 Hence, unlike a normal routine, a coroutine may not terminate when it returns to its caller, allowing it to be restarted with the values and execution location present at the point of suspension. … … 641 654 \centering 642 655 \newbox\myboxA 656 % \begin{lrbox}{\myboxA} 657 % \begin{cfa}[aboveskip=0pt,belowskip=0pt] 658 % `int fn1, fn2, state = 1;` // single global variables 659 % int fib() { 660 % int fn; 661 % `switch ( state )` { // explicit execution state 662 % case 1: fn = 0; fn1 = fn; state = 2; break; 663 % case 2: fn = 1; fn2 = fn1; fn1 = fn; state = 3; break; 664 % case 3: fn = fn1 + fn2; fn2 = fn1; fn1 = fn; break; 665 % } 666 % return fn; 667 % } 668 % int main() { 669 % 670 % for ( int i = 0; i < 10; i += 1 ) { 671 % printf( "%d\n", fib() ); 672 % } 673 % } 674 % \end{cfa} 675 % \end{lrbox} 643 676 \begin{lrbox}{\myboxA} 644 677 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 645 `int f1, f2, state = 1;` // single global variables 646 int fib() { 647 int fn; 648 `switch ( state )` { // explicit execution state 649 case 1: fn = 0; f1 = fn; state = 2; break; 650 case 2: fn = 1; f2 = f1; f1 = fn; state = 3; break; 651 case 3: fn = f1 + f2; f2 = f1; f1 = fn; break; 652 } 653 return fn; 654 } 678 #define FIB_INIT { 0, 1 } 679 typedef struct { int fn1, fn; } Fib; 680 int fib( Fib * f ) { 681 682 int ret = f->fn1; 683 f->fn1 = f->fn; 684 f->fn = ret + f->fn; 685 return ret; 686 } 687 688 689 655 690 int main() { 656 691 Fib f1 = FIB_INIT, f2 = FIB_INIT; 657 692 for ( int i = 0; i < 10; i += 1 ) { 658 printf( "%d\n", fib() ); 693 printf( "%d %d\n", 694 fib( &f1 ), fib( &f2 ) ); 659 695 } 660 696 } … … 665 701 \begin{lrbox}{\myboxB} 666 702 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 667 #define FIB_INIT `{ 0, 1 }` 668 typedef struct { int f2, f1; } Fib; 669 int fib( Fib * f ) { 670 671 int ret = f->f2; 672 int fn = f->f1 + f->f2; 673 f->f2 = f->f1; f->f1 = fn; 674 675 return ret; 676 } 677 int main() { 678 Fib f1 = FIB_INIT, f2 = FIB_INIT; 679 for ( int i = 0; i < 10; i += 1 ) { 680 printf( "%d %d\n", fib( &f1 ), fib( &f2 ) ); 703 `coroutine` Fib { int fn1; }; 704 void main( Fib & fib ) with( fib ) { 705 int fn; 706 [fn1, fn] = [0, 1]; 707 for () { 708 `suspend();` 709 [fn1, fn] = [fn, fn1 + fn]; 681 710 } 682 711 } 683 \end{cfa} 684 \end{lrbox} 685 686 \subfloat[3 States: global variables]{\label{f:GlobalVariables}\usebox\myboxA} 687 \qquad 688 \subfloat[1 State: external variables]{\label{f:ExternalState}\usebox\myboxB} 689 \caption{C Fibonacci Implementations} 690 \label{f:C-fibonacci} 691 692 \bigskip 693 694 \newbox\myboxA 695 \begin{lrbox}{\myboxA} 696 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 697 `coroutine` Fib { int fn; }; 698 void main( Fib & fib ) with( fib ) { 699 int f1, f2; 700 fn = 0; f1 = fn; `suspend()`; 701 fn = 1; f2 = f1; f1 = fn; `suspend()`; 702 for ( ;; ) { 703 fn = f1 + f2; f2 = f1; f1 = fn; `suspend()`; 704 } 705 } 706 int next( Fib & fib ) with( fib ) { 707 `resume( fib );` 708 return fn; 712 int ?()( Fib & fib ) with( fib ) { 713 `resume( fib );` return fn1; 709 714 } 710 715 int main() { 711 716 Fib f1, f2; 712 for ( int i = 1; i <= 10; i += 1 ) { 713 sout | next( f1 ) | next( f2 ); 714 } 715 } 717 for ( 10 ) { 718 sout | f1() | f2(); 719 } 720 721 716 722 \end{cfa} 717 723 \end{lrbox} 718 \newbox\myboxB 719 \begin{lrbox}{\myboxB} 720 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 721 `coroutine` Fib { int ret; }; 722 void main( Fib & f ) with( fib ) { 723 int fn, f1 = 1, f2 = 0; 724 for ( ;; ) { 725 ret = f2; 726 727 fn = f1 + f2; f2 = f1; f1 = fn; `suspend();` 728 } 729 } 730 int next( Fib & fib ) with( fib ) { 731 `resume( fib );` 732 return ret; 733 } 734 735 736 737 738 739 740 \end{cfa} 724 725 \newbox\myboxC 726 \begin{lrbox}{\myboxC} 727 \begin{python}[aboveskip=0pt,belowskip=0pt] 728 729 def Fib(): 730 731 fn1, fn = 0, 1 732 while True: 733 `yield fn1` 734 fn1, fn = fn, fn1 + fn 735 736 737 // next prewritten 738 739 740 f1 = Fib() 741 f2 = Fib() 742 for i in range( 10 ): 743 print( next( f1 ), next( f2 ) ) 744 745 746 747 \end{python} 741 748 \end{lrbox} 742 \subfloat[3 States, internal variables]{\label{f:Coroutine3States}\usebox\myboxA} 743 \qquad\qquad 744 \subfloat[1 State, internal variables]{\label{f:Coroutine1State}\usebox\myboxB} 745 \caption{\CFA Coroutine Fibonacci Implementations} 746 \label{f:cfa-fibonacci} 749 750 \subfloat[C]{\label{f:GlobalVariables}\usebox\myboxA} 751 \hspace{3pt} 752 \vrule 753 \hspace{3pt} 754 \subfloat[\CFA]{\label{f:ExternalState}\usebox\myboxB} 755 \hspace{3pt} 756 \vrule 757 \hspace{3pt} 758 \subfloat[Python]{\label{f:ExternalState}\usebox\myboxC} 759 \caption{Fibonacci Generator} 760 \label{f:C-fibonacci} 761 762 % \bigskip 763 % 764 % \newbox\myboxA 765 % \begin{lrbox}{\myboxA} 766 % \begin{cfa}[aboveskip=0pt,belowskip=0pt] 767 % `coroutine` Fib { int fn; }; 768 % void main( Fib & fib ) with( fib ) { 769 % fn = 0; int fn1 = fn; `suspend()`; 770 % fn = 1; int fn2 = fn1; fn1 = fn; `suspend()`; 771 % for () { 772 % fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `suspend()`; } 773 % } 774 % int next( Fib & fib ) with( fib ) { `resume( fib );` return fn; } 775 % int main() { 776 % Fib f1, f2; 777 % for ( 10 ) 778 % sout | next( f1 ) | next( f2 ); 779 % } 780 % \end{cfa} 781 % \end{lrbox} 782 % \newbox\myboxB 783 % \begin{lrbox}{\myboxB} 784 % \begin{python}[aboveskip=0pt,belowskip=0pt] 785 % 786 % def Fibonacci(): 787 % fn = 0; fn1 = fn; `yield fn` # suspend 788 % fn = 1; fn2 = fn1; fn1 = fn; `yield fn` 789 % while True: 790 % fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `yield fn` 791 % 792 % 793 % f1 = Fibonacci() 794 % f2 = Fibonacci() 795 % for i in range( 10 ): 796 % print( `next( f1 )`, `next( f2 )` ) # resume 797 % 798 % \end{python} 799 % \end{lrbox} 800 % \subfloat[\CFA]{\label{f:Coroutine3States}\usebox\myboxA} 801 % \qquad 802 % \subfloat[Python]{\label{f:Coroutine1State}\usebox\myboxB} 803 % \caption{Fibonacci input coroutine, 3 states, internal variables} 804 % \label{f:cfa-fibonacci} 747 805 \end{figure} 748 806 … … 784 842 \begin{lrbox}{\myboxA} 785 843 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 786 `coroutine` F ormat {787 char ch; // used for communication788 int g, b; // global because used in destructor844 `coroutine` Fmt { 845 char ch; // communication variables 846 int g, b; // needed in destructor 789 847 }; 790 void main( F ormat & fmt ) with( fmt ) {791 for ( ;;) {792 for ( g = 0; g < 5; g += 1 ) { // group793 for ( b = 0; b < 4; b += 1 ) { // block 848 void main( Fmt & fmt ) with( fmt ) { 849 for () { 850 for ( g = 0; g < 5; g += 1 ) { // groups 851 for ( b = 0; b < 4; b += 1 ) { // blocks 794 852 `suspend();` 795 sout | ch; // separator 796 } 797 sout | " "; // separator 798 } 799 sout | nl; 800 } 801 } 802 void ?{}( Format & fmt ) { `resume( fmt );` } 803 void ^?{}( Format & fmt ) with( fmt ) { 804 if ( g != 0 || b != 0 ) sout | nl; 805 } 806 void format( Format & fmt ) { 807 `resume( fmt );` 808 } 853 sout | ch; } // print character 854 sout | " "; } // block separator 855 sout | nl; } // group separator 856 } 857 void ?{}( Fmt & fmt ) { `resume( fmt );` } // prime 858 void ^?{}( Fmt & fmt ) with( fmt ) { // destructor 859 if ( g != 0 || b != 0 ) // special case 860 sout | nl; } 861 void send( Fmt & fmt, char c ) { fmt.ch = c; `resume( fmt )`; } 809 862 int main() { 810 Format fmt; 811 eof: for ( ;; ) { 812 sin | fmt.ch; 813 if ( eof( sin ) ) break eof; 814 format( fmt ); 815 } 863 Fmt fmt; 864 sout | nlOff; // turn off auto newline 865 for ( 41 ) 866 send( fmt, 'a' ); 816 867 } 817 868 \end{cfa} … … 820 871 \newbox\myboxB 821 872 \begin{lrbox}{\myboxB} 822 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 823 struct Format { 824 char ch; 825 int g, b; 826 }; 827 void format( struct Format * fmt ) { 828 if ( fmt->ch != -1 ) { // not EOF ? 829 printf( "%c", fmt->ch ); 830 fmt->b += 1; 831 if ( fmt->b == 4 ) { // block 832 printf( " " ); // separator 833 fmt->b = 0; 834 fmt->g += 1; 835 } 836 if ( fmt->g == 5 ) { // group 837 printf( "\n" ); // separator 838 fmt->g = 0; 839 } 840 } else { 841 if ( fmt->g != 0 || fmt->b != 0 ) printf( "\n" ); 842 } 843 } 844 int main() { 845 struct Format fmt = { 0, 0, 0 }; 846 for ( ;; ) { 847 scanf( "%c", &fmt.ch ); 848 if ( feof( stdin ) ) break; 849 format( &fmt ); 850 } 851 fmt.ch = -1; 852 format( &fmt ); 853 } 854 \end{cfa} 873 \begin{python}[aboveskip=0pt,belowskip=0pt] 874 875 876 877 def Fmt(): 878 try: 879 while True: 880 for g in range( 5 ): 881 for b in range( 4 ): 882 883 print( `(yield)`, end='' ) 884 print( ' ', end='' ) 885 print() 886 887 888 except GeneratorExit: 889 if g != 0 | b != 0: 890 print() 891 892 893 fmt = Fmt() 894 `next( fmt )` # prime 895 for i in range( 41 ): 896 `fmt.send( 'a' );` # send to yield 897 898 \end{python} 855 899 \end{lrbox} 856 \subfloat[\CFA Coroutine]{\label{f:CFAFmt}\usebox\myboxA}900 \subfloat[\CFA]{\label{f:CFAFmt}\usebox\myboxA} 857 901 \qquad 858 \subfloat[ C Linearized]{\label{f:CFmt}\usebox\myboxB}859 \caption{ Formatting text into lines of 5 blocks of 4 characters.}902 \subfloat[Python]{\label{f:CFmt}\usebox\myboxB} 903 \caption{Output formatting text} 860 904 \label{f:fmt-line} 861 905 \end{figure} … … 878 922 void main( Prod & prod ) with( prod ) { 879 923 // 1st resume starts here 880 for ( i nt i = 0; i < N; i += 1) {924 for ( i; N ) { 881 925 int p1 = random( 100 ), p2 = random( 100 ); 882 926 sout | p1 | " " | p2; … … 894 938 } 895 939 void start( Prod & prod, int N, Cons &c ) { 896 &prod.c = &c; 940 &prod.c = &c; // reassignable reference 897 941 prod.[N, receipt] = [N, 0]; 898 942 `resume( prod );` … … 909 953 Prod & p; 910 954 int p1, p2, status; 911 _Bool done;955 bool done; 912 956 }; 913 957 void ?{}( Cons & cons, Prod & p ) { 914 &cons.p = &p; 958 &cons.p = &p; // reassignable reference 915 959 cons.[status, done ] = [0, false]; 916 960 } … … 969 1013 The program main restarts after the resume in @start@. 970 1014 @start@ returns and the program main terminates. 1015 1016 One \emph{killer} application for a coroutine is device drivers, which at one time caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}. 1017 Many device drivers are a finite state-machine parsing a protocol, e.g.: 1018 \begin{tabbing} 1019 \ldots STX \= \ldots message \ldots \= ESC \= ETX \= \ldots message \ldots \= ETX \= 2-byte crc \= \ldots \kill 1020 \ldots STX \> \ldots message \ldots \> ESC \> ETX \> \ldots message \ldots \> ETX \> 2-byte crc \> \ldots 1021 \end{tabbing} 1022 where a network message begins with the control character STX and ends with an ETX, followed by a 2-byte cyclic-redundancy check. 1023 Control characters may appear in a message if preceded by an ESC. 1024 Because FSMs can be complex and occur frequently in important domains, direct support of the coroutine is crucial in a systems programminglanguage. 1025 1026 \begin{figure} 1027 \begin{cfa} 1028 enum Status { CONT, MSG, ESTX, ELNTH, ECRC }; 1029 `coroutine` Driver { 1030 Status status; 1031 char * msg, byte; 1032 }; 1033 void ?{}( Driver & d, char * m ) { d.msg = m; } $\C[3.0in]{// constructor}$ 1034 Status next( Driver & d, char b ) with( d ) { $\C{// 'with' opens scope}$ 1035 byte = b; `resume( d );` return status; 1036 } 1037 void main( Driver & d ) with( d ) { 1038 enum { STX = '\002', ESC = '\033', ETX = '\003', MaxMsg = 64 }; 1039 unsigned short int crc; $\C{// error checking}$ 1040 msg: for () { $\C{// parse message}$ 1041 status = CONT; 1042 unsigned int lnth = 0, sum = 0; 1043 while ( byte != STX ) `suspend();` 1044 emsg: for () { 1045 `suspend();` $\C{// process byte}$ 1046 choose ( byte ) { $\C{// switch with default break}$ 1047 case STX: 1048 status = ESTX; `suspend();` continue msg; 1049 case ETX: 1050 break emsg; 1051 case ESC: 1052 suspend(); 1053 } // choose 1054 if ( lnth >= MaxMsg ) { $\C{// buffer full ?}$ 1055 status = ELNTH; `suspend();` continue msg; } 1056 msg[lnth++] = byte; 1057 sum += byte; 1058 } // for 1059 msg[lnth] = '\0'; $\C{// terminate string}\CRT$ 1060 `suspend();` 1061 crc = (unsigned char)byte << 8; // prevent sign extension for signed char 1062 `suspend();` 1063 status = (crc | (unsigned char)byte) == sum ? MSG : ECRC; 1064 `suspend();` 1065 } // for 1066 } 1067 \end{cfa} 1068 \caption{Device driver for simple communication protocol} 1069 \end{figure} 971 1070 972 1071 -
doc/proposals/vtable.md
rf845e80 r98b4b12 11 11 should be able to store anything that goes into a trait. 12 12 13 I also include notes on a sample implementation, which primarly exists to show 14 there is a resonable implementation. The code samples for that are in a slight 15 psudo-code to help avoid name mangling and keeps some CFA features while they 16 would actually be writen in C. 17 13 18 Trait Instances 14 19 --------------- … … 42 47 before. 43 48 44 Internally a trait object is a pair of pointers. One to an underlying object 45 and the other to the vtable. All calls on an trait are implemented by looking 46 up the matching function pointer and passing the underlying object and the 47 remaining arguments to it. 48 49 Trait objects can be moved by moving the pointers. Almost all other operations 50 require some functions to be implemented on the underlying type. Depending on 51 what is in the virtual table a trait type could be a dtype or otype. 49 For traits to be used this way they should meet two requirements. First they 50 should only have a single polymorphic type and each assertion should use that 51 type once as a parameter. Extentions may later loosen these requirements. 52 53 If a trait object is used it should generate a series of implicate functions 54 each of which implements one of the functions required by the trait. So for 55 combiner there is an implicate: 56 57 void combine(trait combiner & this, int); 58 59 This function is the one actually called at the end 60 61 The main use case for trait objects is that they can be stored. They can be 62 passed into functions, but using the trait directly is prefred in this case. 63 64 trait drawable(otype T) { 65 void draw(Surface & to, T & draw); 66 Rect(int) drawArea(T & draw); 67 }; 68 69 struct UpdatingSurface { 70 Surface * surface; 71 vector(trait drawable) drawables; 72 }; 73 74 void updateSurface(UpdatingSurface & us) { 75 for (size_t i = 0 ; i < us.drawables.size ; ++i) { 76 draw(us.surface, us.drawables[i]); 77 } 78 } 79 80 Currently these traits are limited to 1 trait parameter and functions should 81 have exactly 1 parameter. We cannot abstract away pairs of types and still 82 pass them into normal functions, which take them seperately. 83 84 The second is required the because we need to get the vtable from somewhere. 85 If there are 0 trait objects than no vtable is avalible, if we have more than 86 1 than the vtables give conflicting answers on what underlying function to 87 call. And even then the underlying type assumes a concrete type. 88 89 This loop can sort of be broken by using the trait object directly in the 90 signature. This has well defined meaning, but might not be useful. 91 92 trait example(otype T) { 93 bool test(T & this, trait example & that); 94 } 95 96 #### Sample Implementation 97 A simple way to implement trait objects is by a pair of pointers. One to the 98 underlying object and one to the vtable. 99 100 struct vtable_drawable { 101 void (*draw)(Surface &, void *); 102 Rect(int) (*drawArea)(void *); 103 }; 104 105 struct drawable { 106 void * object; 107 vtable_drawable * vtable; 108 }; 109 110 The functions that run on the trait object would generally be generated using 111 the following pattern: 112 113 void draw(Surface & surface, drawable & traitObj) { 114 return traitObj.vtable->draw(surface, traitObj.object); 115 } 116 117 There may have to be special cases for things like copy construction, that 118 might require a more sigificant wrapper. On the other hand moving could be 119 implemented by moving the pointers without any need to refer to the base 120 object. 121 122 ### Extention: Multiple Trait Parameters 123 Currently, this gives traits two independent uses. They use the same syntax, 124 except for limits boxable traits have, and yet don't really mix. The most 125 natural way to do this is to allow trait instances to pick one parameter 126 that they are generic over, the others they choose types to implement. 127 128 The two ways to do the selection, the first is do it at the trait definition. 129 Each trait picks out a single parameter which it can box (here the `virtual` 130 qualifier). When you create an instance of a trait object you provide 131 arguments like for a generic structure, but skip over the marked parameter. 132 133 trait combiner(virtual otype T, otype Combined) { 134 void combine(T &, Combined &); 135 } 136 137 trait combiner(int) int_combiner; 138 139 The second is to do it at the instaniation point. A placeholder (here the 140 keyword `virtual`) is used to explicately skip over the parameter that will be 141 abstracted away, with the same rules as above if it was the marked parameter. 142 143 trait combiner(otype T, otype Combined) { 144 void combine(T &, Combined &); 145 }; 146 147 trait combiner(virtual, int) int_combiner; 148 149 Using both (first to set the default, second as a local override) would also 150 work, although might be exessively complicated. 151 152 This is useful in cases where you want to use a generic type, but leave part 153 of it open and store partially generic result. As a simple example 154 155 trait folder(otype T, otype In, otype Out) { 156 void fold(T & this, In); 157 Out fold_result(T & this); 158 } 159 160 Which allows you to fold values without putting them in a container. If they 161 are already in a container this is exessive, but if they are generated over 162 time this gives you a simple interface. This could for instance be used in 163 a profile, where T changes for each profiling statistic and you can plug in 164 multiple profilers for any run by adding them to an array. 52 165 53 166 Hierarchy … … 90 203 the pointer to it. 91 204 205 Exception Example: 206 (Also I'm not sure where I got these casing rules.) 207 208 trait exception(otype T) virtual() { 209 char const * what(T & this); 210 } 211 212 trait io_error(otype T) virtual(exception) { 213 FILE * which_file(T & this); 214 } 215 216 struct eof_error(otype T) virtual(io_error) { 217 FILE * file; 218 } 219 220 char const * what(eof_error &) { 221 return "Tried to read from an empty file."; 222 } 223 224 FILE * which_file(eof_error & this) { 225 return eof_error.file; 226 } 227 228 Ast Example: 229 230 trait ast_node(otype T) virtual() { 231 void print(T & this, ostream & out); 232 void visit(T & this, Visitor & visitor); 233 CodeLocation const & get_code_location(T & this); 234 } 235 236 trait expression_node(otype T) virtual(ast_node) { 237 Type eval_type(T const & this); 238 } 239 240 struct operator_expression virtual(expression_node) { 241 enum operator_kind kind; 242 trait expression_node rands[2]; 243 } 244 245 trait statement_node(otype T) virtual(ast_node) { 246 vector(Label) & get_labels(T & this); 247 } 248 249 struct goto_statement virtual(statement_node) { 250 vector(Label) labels; 251 Label target; 252 } 253 254 trait declaration_node(otype T) virtual(ast_node) { 255 string name_of(T const & this); 256 Type type_of(T const & this); 257 } 258 259 struct using_declaration virtual(declaration_node) { 260 string new_type; 261 Type old_type; 262 } 263 264 struct variable_declaration virtual(declaration_node) { 265 string name; 266 Type type; 267 } 268 269 #### Sample Implementation 270 The type id may be as little as: 271 272 struct typeid { 273 struct typeid const * const parent; 274 }; 275 276 Some linker magic would have to be used to ensure exactly one copy of each 277 structure for each type exists in memory. There seem to be spectial once 278 sections that support this and it should be easier than generating unique 279 ids across compilation units. 280 281 The structure could be extended to contain any additional type information. 282 283 There are two general designs for vtables with type ids. The first is to put 284 the type id at the top of the vtable, this is the most compact and efficient 285 solution but only works if we have exactly 1 vtable for each type. The second 286 is to put a pointer to the type id in each vtable. This has more overhead but 287 allows multiple vtables. 288 289 struct <trait>_vtable { 290 struct typeid const id; 291 292 // Trait dependent list of vtable members. 293 }; 294 295 struct <trait>_vtable { 296 struct typeid const * const id; 297 298 // Trait dependent list of vtable members. 299 }; 300 301 ### Virtual Casts 302 To convert from a pointer to a type higher on the hierarchy to one lower on 303 the hierarchy a check is used to make sure that the underlying type is also 304 of that lower type. 305 306 The proposed syntax for this is: 307 308 trait SubType * new_value = (virtual trait SubType *)super_type; 309 310 It will return the same pointer if it does point to the subtype and null if 311 it does not, doing the check and conversion in one operation. 312 92 313 ### Inline vtables 93 314 Since the structures here are usually made to be turned into trait objects 94 315 it might be worth it to have fields on them to store the virtual table 95 pointer. This would have to be declared on the trait as an assertion, but if 96 it is the trait object could be a single pointer. 97 98 It is trivial to do if the field with the virtual table pointer is fixed. 99 Otherwise some trickery with pointing to the field and storing the offset in 100 the virtual table to recover the main object would have to be used. 316 pointer. This would have to be declared on the trait as an assertion (example: 317 `vtable;` or `T.vtable;`), but if it is the trait object could be a single 318 pointer. 319 320 There are also three options for where the pointer to the vtable. It could be 321 anywhere, a fixed location for each trait or always at the front. For the per- 322 trait solution an extention to specify what it is (example `vtable[0];`) which 323 could also be used to combine it with others. So these options can be combined 324 to allow access to all three options. 101 325 102 326 ### Virtual Tables as Types 103 Here we consider encoding plus the implementation of functions on it . Which104 is to say in the type hierarchy structures aren't concrete types anymore, 105 instead they are parent types to vtables, which combine the encoding and 106 implementation.327 Here we consider encoding plus the implementation of functions on it to be a 328 type. Which is to say in the type hierarchy structures aren't concrete types 329 anymore, instead they are parent types to vtables, which combine the encoding 330 and implementation. 107 331 108 332 Resolution Scope … … 123 347 other. 124 348 125 Some syntax would have to be added. All resolutions can be found at compile 126 time and a single vtable created for each type at compilation time. 349 Some syntax would have to be added to specify the resolution point. To ensure 350 a single instance there may have to be two variants, one forward declaration 351 and one to create the instance. With some compiler magic the forward 352 declaration maybe enough. 353 354 extern trait combiner(struct summation) vtable; 355 trait combiner(struct summation) vtable; 356 357 Or (with the same variants): 358 359 vtable combiner(struct summation); 360 361 The extern variant promises that the vtable will exist while the normal one 362 is where the resolution actually happens. 127 363 128 364 ### Explicit Resolution Points: … … 141 377 vtable. 142 378 379 extern trait combiner(struct summation) vtable sum; 380 trait combiner(struct summation) vtable sum; 381 382 extern trait combiner(struct summation) vtable sum default; 383 trait combiner(struct summation) vtable sum default; 384 385 The extern difference is the same before. The name (sum in the samples) is 386 used at the binding site to say which one is picked. The default keyword can 387 be used in only some of the declarations. 388 389 trait combiner fee = (summation_instance, sum); 390 trait combiner foe = summation_instance; 391 392 (I am not really happy about this syntax, but it kind of works.) 393 The object being bound is required. The name of the vtable is optional if 394 there is exactly one vtable name marked with default. 395 396 These could also be placed inside functions. In which case both the name and 397 the default keyword might be optional. If the name is ommited in an assignment 398 the closest vtable is choosen (returning to the global default rule if no 399 approprate local vtable is in scope). 400 143 401 ### Site Based Resolution: 144 402 Every place in code where the binding of a vtable to an object occurs has -
doc/user/user.tex
rf845e80 r98b4b12 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Tue Dec 11 23:19:26 201814 %% Update Count : 34 0013 %% Last Modified On : Sun Apr 14 11:02:34 2019 14 %% Update Count : 3443 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 508 508 509 509 As for \Index{division}, there are exponentiation operators for integral and floating types, including the builtin \Index{complex} types. 510 Unsigned integral exponentiation\index{exponentiation!unsigned integral} is performed with repeated multiplication\footnote{The multiplication computation is $O(\log y)$.} (or shifting if the base is 2). 511 Signed integral exponentiation\index{exponentiation!signed integral} is performed with repeated multiplication (or shifting if the base is 2), but yields a floating result because $x^{-y}=1/x^y$. 512 Hence, it is important to designate exponent integral-constants as unsigned or signed: ©3 \ 3u© return an integral result, while ©3 \ 3© returns a floating result. 513 Floating exponentiation\index{exponentiation!floating} is performed using \Index{logarithm}s\index{exponentiation!logarithm}, so the base cannot be negative. 514 \begin{cfa} 515 sout | 2 ®\® 8u | 4 ®\® 3u | -4 ®\® 3u | 4 ®\® -3 | -4 ®\® -3 | 4.0 ®\® 2.1 | (1.0f+2.0fi) ®\® (3.0f+2.0fi); 516 256 64 -64 0.015625 -0.015625 18.3791736799526 0.264715-1.1922i 517 \end{cfa} 510 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). 511 Overflow from large exponents or negative exponents return zero. 512 Floating exponentiation\index{exponentiation!floating} is performed using \Index{logarithm}s\index{exponentiation!logarithm}, so the exponent cannot be negative. 513 \begin{cfa} 514 sout | 1 ®\® 0 | 1 ®\® 1 | 2 ®\® 8 | -4 ®\® 3 | 5 ®\® 3 | 5 ®\® 32 | 5L ®\® 32 | 5L ®\® 64 | -4 ®\® -3 | -4.0 ®\® -3 | 4.0 ®\® 2.1 515 | (1.0f+2.0fi) ®\® (3.0f+2.0fi); 516 1 1 256 -64 125 ®0® 3273344365508751233 ®0® ®0® -0.015625 18.3791736799526 0.264715-1.1922i 517 \end{cfa} 518 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. 518 519 Parenthesis are necessary for complex constants or the expression is parsed as ©1.0f+®(®2.0fi \ 3.0f®)®+2.0fi©. 519 The exponentiation operator is available for all the basic types, but for user-defined types, only the integral-computation versions are available. 520 For returning an integral value, the user type ©T© must define multiplication, ©*©, and one, ©1©; 521 for returning a floating value, an additional divide of type ©T© into a ©double© returning a ©double© (©double ?/?( double, T )©) is necessary for negative exponents. 520 The exponentiation operator is available for all the basic types, but for user-defined types, only the integral-computation version is available. 521 \begin{cfa} 522 forall( otype OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) 523 OT ?®\®?( OT ep, unsigned int y ); 524 forall( otype OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) 525 OT ?®\®?( OT ep, unsigned long int y ); 526 \end{cfa} 527 The user type ©T© must define multiplication, one, ©1©, and, ©*©. 522 528 523 529 … … 549 555 \subsection{Loop Control} 550 556 551 The ©for©/©while©/©do-while© loop-control allows empty or simplified ranges. 557 The ©for©/©while©/©do-while© loop-control allows empty or simplified ranges (see Figure~\ref{f:LoopControlExamples}). 558 \begin{itemize} 559 \item 552 560 An empty conditional implies ©1©. 553 The up-to range ©~©\index{~@©~©} means exclusive range [M,N); 554 the up-to range ©~=©\index{~=@©~=©} means inclusive range [M,N]. 555 The down-to range ©-~©\index{-~@©-~©} means exclusive range [N,M); 556 the down-to range ©-~=©\index{-~=@©-~=©} means inclusive range [N,M]. 561 \item 562 The up-to range ©~©\index{~@©~©} means exclusive range [M,N). 563 \item 564 The up-to range ©~=©\index{~=@©~=©} means inclusive range [M,N]. 565 \item 566 The down-to range ©-~©\index{-~@©-~©} means exclusive range [N,M). 567 \item 568 The down-to range ©-~=©\index{-~=@©-~=©} means inclusive range [N,M]. 569 \item 570 ©@© means put nothing in this field. 571 \item 557 572 ©0© is the implicit start value; 573 \item 558 574 ©1© is the implicit increment value. 575 \item 559 576 The up-to range uses ©+=© for increment; 560 the down-to range uses ©-=© for decrement. 577 \item 578 The down-to range uses ©-=© for decrement. 579 \item 561 580 The loop index is polymorphic in the type of the start value or comparison value when start is implicitly ©0©. 581 \end{itemize} 582 583 \begin{figure} 562 584 \begin{cquote} 563 \begin{tabular}{@{}l l|l@{}}564 \multicolumn{ 2}{c|}{loop control} & \multicolumn{1}{c}{output} \\585 \begin{tabular}{@{}l|l@{}} 586 \multicolumn{1}{c|}{loop control} & \multicolumn{1}{c}{output} \\ 565 587 \hline 566 588 \begin{cfa} 567 while ®()® { sout | "empty"; break; } 568 do { sout | "empty"; break; } while ®()®; 569 for ®()® { sout | "empty"; break; } 570 for ( ®0® ) { sout | "A"; } 571 for ( ®1® ) { sout | "A"; } 572 for ( ®10® ) { sout | "A"; } 573 for ( ®1 ~= 10 ~ 2® ) { sout | "B"; } 574 for ( ®10 -~= 1 ~ 2® ) { sout | "C"; } 575 for ( ®0.5 ~ 5.5® ) { sout | "D"; } 576 for ( ®5.5 -~ 0.5® ) { sout | "E"; } 577 for ( ®i; 10® ) { sout | i; } 578 for ( ®i; 1 ~= 10 ~ 2® ) { sout | i; } 579 for ( ®i; 10 -~= 1 ~ 2® ) { sout | i; } 580 for ( ®i; 0.5 ~ 5.5® ) { sout | i; } 581 for ( ®i; 5.5 -~ 0.5® ) { sout | i; } 582 for ( ®ui; 2u ~= 10u ~ 2u® ) { sout | ui; } 583 for ( ®ui; 10u -~= 2u ~ 2u® ) { sout | ui; } 589 sout | nlOff; 590 while ®()® { sout | "empty"; break; } sout | nl; 591 do { sout | "empty"; break; } while ®()®; sout | nl; 592 for ®()® { sout | "empty"; break; } sout | nl; 593 for ( ®0® ) { sout | "A"; } sout | "zero" | nl; 594 for ( ®1® ) { sout | "A"; } sout | nl; 595 for ( ®10® ) { sout | "A"; } sout | nl; 596 for ( ®1 ~= 10 ~ 2® ) { sout | "B"; } sout | nl; 597 for ( ®10 -~= 1 ~ 2® ) { sout | "C"; } sout | nl; 598 for ( ®0.5 ~ 5.5® ) { sout | "D"; } sout | nl; 599 for ( ®5.5 -~ 0.5® ) { sout | "E"; } sout | nl; 600 for ( ®i; 10® ) { sout | i; } sout | nl; 601 for ( ®i; 1 ~= 10 ~ 2® ) { sout | i; } sout | nl; 602 for ( ®i; 10 -~= 1 ~ 2® ) { sout | i; } sout | nl; 603 for ( ®i; 0.5 ~ 5.5® ) { sout | i; } sout | nl; 604 for ( ®i; 5.5 -~ 0.5® ) { sout | i; } sout | nl; 605 for ( ®ui; 2u ~= 10u ~ 2u® ) { sout | ui; } sout | nl; 606 for ( ®ui; 10u -~= 2u ~ 2u® ) { sout | ui; } sout | nl; 584 607 enum { N = 10 }; 585 for ( ®N® ) { sout | "N"; } 586 for ( ®i; N® ) { sout | i; } 587 for ( ®i; N -~ 0® ) { sout | i; } 608 for ( ®N® ) { sout | "N"; } sout | nl; 609 for ( ®i; N® ) { sout | i; } sout | nl; 610 for ( ®i; N -~ 0® ) { sout | i; } sout | nl; 588 611 const int start = 3, comp = 10, inc = 2; 589 for ( ®i; start ~ comp ~ inc + 1® ) { sout | i; } 612 for ( ®i; start ~ comp ~ inc + 1® ) { sout | i; } sout | nl; 613 for ( ®i; 1 ~ @® ) { if ( i > 10 ) break; 614 sout | i; } sout | nl; 615 for ( ®i; 10 -~ @® ) { if ( i < 0 ) break; 616 sout | i; } sout | nl; 617 for ( ®i; 2 ~ @ ~ 2® ) { if ( i > 10 ) break; 618 sout | i; } sout | nl; 619 for ( ®i; 2.1 ~ @ ~ @® ) { if ( i > 10.5 ) break; 620 sout | i; i += 1.7; } sout | nl; 621 for ( ®i; 10 -~ @ ~ 2® ) { if ( i < 0 ) break; 622 sout | i; } sout | nl; 623 for ( ®i; 12.1 ~ @ ~ @® ) { if ( i < 2.5 ) break; 624 sout | i; i -= 1.7; } sout | nl; 625 for ( ®i; 5 : j; -5 ~ @® ) { sout | i | j; } sout | nl; 626 for ( ®i; 5 : j; -5 -~ @® ) { sout | i | j; } sout | nl; 627 for ( ®i; 5 : j; -5 ~ @ ~ 2® ) { sout | i | j; } sout | nl; 628 for ( ®i; 5 : j; -5 -~ @ ~ 2® ) { sout | i | j; } sout | nl; 629 for ( ®j; -5 ~ @ : i; 5® ) { sout | i | j; } sout | nl; 630 for ( ®j; -5 -~ @ : i; 5® ) { sout | i | j; } sout | nl; 631 for ( ®j; -5 ~ @ ~ 2 : i; 5® ) { sout | i | j; } sout | nl; 632 for ( ®j; -5 -~ @ ~ 2 : i; 5® ) { sout | i | j; } sout | nl; 633 for ( ®j; -5 -~ @ ~ 2 : i; 5 : k; 1.5 ~ @® ) { 634 sout | i | j | k; } sout | nl; 635 for ( ®j; -5 -~ @ ~ 2 : k; 1.5 ~ @ : i; 5® ) { 636 sout | i | j | k; } sout | nl; 637 for ( ®k; 1.5 ~ @ : j; -5 -~ @ ~ 2 : i; 5® ) { 638 sout | i | j | k; } sout | nl; 590 639 \end{cfa} 591 640 & 592 641 \begin{cfa} 593 sout | nl; 594 sout | nl; 595 sout | nl; 596 sout | "zero" | nl; 597 sout | nl; 598 sout | nl; 599 sout | nl; 600 sout | nl; 601 sout | nl; 602 sout | nl; 603 sout | nl; 604 sout | nl; 605 sout | nl; 606 sout | nl; 607 sout | nl; 608 sout | nl; 609 sout | nl | nl; 610 611 sout | nl; 612 sout | nl; 613 sout | nl | nl; 614 615 sout | nl; 616 \end{cfa} 617 & 618 \begin{cfa} 642 619 643 empty 620 644 empty … … 640 664 641 665 3 6 9 666 667 1 2 3 4 5 6 7 8 9 10 668 669 10 9 8 7 6 5 4 3 2 1 0 670 671 2 4 6 8 10 672 673 2.1 3.8 5.5 7.2 8.9 674 675 10 8 6 4 2 0 676 677 12.1 10.4 8.7 7 5.3 3.6 678 0 -5 1 -4 2 -3 3 -2 4 -1 679 0 -5 1 -6 2 -7 3 -8 4 -9 680 0 -5 1 -3 2 -1 3 1 4 3 681 0 -5 1 -7 2 -9 3 -11 4 -13 682 0 -5 1 -4 2 -3 3 -2 4 -1 683 0 -5 1 -6 2 -7 3 -8 4 -9 684 0 -5 1 -3 2 -1 3 1 4 3 685 0 -5 1 -7 2 -9 3 -11 4 -13 686 687 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 688 689 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 690 691 0 -5 1.5 1 -7 2.5 2 -9 3.5 3 -11 4.5 4 -13 5.5 642 692 \end{cfa} 643 693 \end{tabular} 644 694 \end{cquote} 695 \caption{Loop Control Examples} 696 \label{f:LoopControlExamples} 697 \end{figure} 645 698 646 699 … … 1320 1373 \end{cfa} 1321 1374 Essentially, the return type is wrapped around the routine name in successive layers (like an \Index{onion}). 1322 While attempting to make the two contexts consistent is a laudable goal, it has not worked out in practice. 1375 While attempting to make the two contexts consistent is a laudable goal, it has not worked out in practice, even though Dennis Richie believed otherwise: 1376 \begin{quote} 1377 In spite of its difficulties, I believe that the C's approach to declarations remains plausible, and am comfortable with it; it is a useful unifying principle.~\cite[p.~12]{Ritchie93} 1378 \end{quote} 1323 1379 1324 1380 \CFA provides its own type, variable and routine declarations, using a different syntax. -
libcfa/configure
rf845e80 r98b4b12 2977 2977 ;; 2978 2978 esac 2979 2980 CONFIG_CFAFLAGS="${CONFIG_CFAFLAGS} ${CFAFLAGS}" 2979 2981 2980 2982 -
libcfa/configure.ac
rf845e80 r98b4b12 64 64 esac 65 65 66 CONFIG_CFAFLAGS="${CONFIG_CFAFLAGS} ${CFAFLAGS}" 67 66 68 AC_SUBST(CONFIG_CFLAGS) 67 69 AC_SUBST(CONFIG_CFAFLAGS) -
libcfa/prelude/builtins.c
rf845e80 r98b4b12 10 10 // Created On : Fri Jul 21 16:21:03 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Mar 10 10:52:50201913 // Update Count : 3112 // Last Modified On : Tue Mar 26 23:10:36 2019 13 // Update Count : 95 14 14 // 15 15 … … 18 18 typedef unsigned long long __cfaabi_abi_exception_type_t; 19 19 20 #include <limits.h> // CHAR_BIT 20 21 #include "../src/virtual.h" 21 22 #include "../src/exception.h" … … 26 27 // increment/decrement unification 27 28 28 static inline forall( dtype T | { T & ?+=?( T &, one_t ); } ) 29 T & ++? ( T & x ) { return x += 1; } 29 static inline { 30 forall( dtype DT | { DT & ?+=?( DT &, one_t ); } ) 31 DT & ++?( DT & x ) { return x += 1; } 30 32 31 static inline forall( dtype T | sized(T) | { void ?{}( T &, T ); void ^?{}( T & ); T & ?+=?(T &, one_t ); } )32 T & ?++ ( T & x ) {T tmp = x; x += 1; return tmp; }33 forall( dtype DT | sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?+=?( DT &, one_t ); } ) 34 DT & ?++( DT & x ) { DT tmp = x; x += 1; return tmp; } 33 35 34 static inline forall( dtype T | { T & ?-=?(T &, one_t ); } )35 T & --? (T & x ) { return x -= 1; }36 forall( dtype DT | { DT & ?-=?( DT &, one_t ); } ) 37 DT & --?( DT & x ) { return x -= 1; } 36 38 37 static inline forall( dtype T | sized(T) | { void ?{}( T &, T ); void ^?{}( T & ); T & ?-=?( T &, one_t ); } ) 38 T & ?-- ( T & x ) { T tmp = x; x -= 1; return tmp; } 39 forall( dtype DT | sized(DT) | { void ?{}( DT &, DT ); void ^?{}( DT & ); DT & ?-=?( DT &, one_t ); } ) 40 DT & ?--( DT & x ) { DT tmp = x; x -= 1; return tmp; } 41 } // distribution 39 42 40 43 // universal typed pointer constant 41 42 static inline forall( dtype T ) T * intptr( uintptr_t addr ) { return (T *)addr; }44 // Compiler issue: there is a problem with anonymous types that do not have a size. 45 static inline forall( dtype DT | sized(DT) ) DT * intptr( uintptr_t addr ) { return (DT *)addr; } 43 46 44 47 // exponentiation operator implementation … … 53 56 } // extern "C" 54 57 55 static inline float ?\?( float x, float y ) { return powf( x, y ); } 56 static inline double ?\?( double x, double y ) { return pow( x, y ); } 57 static inline long double ?\?( long double x, long double y ) { return powl( x, y ); } 58 static inline float _Complex ?\?( float _Complex x, _Complex float y ) { return cpowf(x, y ); } 59 static inline double _Complex ?\?( double _Complex x, _Complex double y ) { return cpow( x, y ); } 60 static inline long double _Complex ?\?( long double _Complex x, _Complex long double y ) { return cpowl( x, y ); } 58 static inline { 59 float ?\?( float x, float y ) { return powf( x, y ); } 60 double ?\?( double x, double y ) { return pow( x, y ); } 61 long double ?\?( long double x, long double y ) { return powl( x, y ); } 62 float _Complex ?\?( float _Complex x, _Complex float y ) { return cpowf(x, y ); } 63 double _Complex ?\?( double _Complex x, _Complex double y ) { return cpow( x, y ); } 64 long double _Complex ?\?( long double _Complex x, _Complex long double y ) { return cpowl( x, y ); } 65 } // distribution 61 66 62 static inline long int ?\?( long int ep, unsigned long int y ) { // disallow negative exponent 63 if ( y == 0 ) return 1; // base case 64 if ( ep == 2 ) return ep << (y - 1); // special case, positive shifting only 65 typeof( ep ) op = 1; // accumulate odd product 66 for ( ; y > 1; y >>= 1 ) { // squaring exponentiation, O(log2 y) 67 if ( (y & 1) == 1 ) op *= ep; // odd ? 68 ep *= ep; 69 } // for 70 return ep * op; 71 } // ?\? 67 #define __CFA_BASE_COMP_1__() if ( ep == 1 ) return 1 68 #define __CFA_BASE_COMP_2__() if ( ep == 2 ) return ep << (y - 1) 69 #define __CFA_EXP_OVERFLOW__() if ( y >= sizeof(y) * CHAR_BIT ) return 0 72 70 73 static inline forall( otype T | { void ?{}( T & this, one_t ); T ?*?( T, T ); } ) 74 T ?\?( T ep, unsigned long int y ) { 75 if ( y == 0 ) return 1; 76 T op = 1; 77 for ( ; y > 1; y >>= 1 ) { // squaring exponentiation, O(log2 y) 78 if ( (y & 1) == 1 ) op = op * ep; // odd ? 79 ep = ep * ep; 80 } // for 81 return ep * op; 82 } // ?\? 71 #define __CFA_EXP__() \ 72 if ( y == 0 ) return 1; /* convention */ \ 73 __CFA_BASE_COMP_1__(); /* base case */ \ 74 __CFA_BASE_COMP_2__(); /* special case, positive shifting for integral types */ \ 75 __CFA_EXP_OVERFLOW__(); /* immediate overflow, negative exponent > 2^size-1 */ \ 76 typeof(ep) op = 1; /* accumulate odd product */ \ 77 for ( ; y > 1; y >>= 1 ) { /* squaring exponentiation, O(log2 y) */ \ 78 if ( (y & 1) == 1 ) op = op * ep; /* odd ? */ \ 79 ep = ep * ep; \ 80 } \ 81 return ep * op 83 82 84 // unsigned computation may be faster and larger 85 static inline unsigned long int ?\?( unsigned long int ep, unsigned long int y ) { // disallow negative exponent 86 if ( y == 0 ) return 1; // base case 87 if ( ep == 2 ) return ep << (y - 1); // special case, positive shifting only 88 typeof( ep ) op = 1; // accumulate odd product 89 for ( ; y > 1; y >>= 1 ) { // squaring exponentiation, O(log2 y) 90 if ( (y & 1) == 1 ) op *= ep; // odd ? 91 ep *= ep; 92 } // for 93 return ep * op; 94 } // ?\? 83 static inline { 84 long int ?\?( int ep, unsigned int y ) { __CFA_EXP__(); } 85 long int ?\?( long int ep, unsigned long int y ) { __CFA_EXP__(); } 86 // unsigned computation may be faster and larger 87 unsigned long int ?\?( unsigned int ep, unsigned int y ) { __CFA_EXP__(); } 88 unsigned long int ?\?( unsigned long int ep, unsigned long int y ) { __CFA_EXP__(); } 89 } // distribution 95 90 96 static inline double ?\?( long int x, signed long int y ) { // allow negative exponent 97 if ( y >= 0 ) return (double)(x \ (unsigned long int)y); 98 else return 1.0 / x \ (unsigned int)(-y); 99 } // ?\? 91 #undef __CFA_BASE_COMP_1__ 92 #undef __CFA_BASE_COMP_2__ 93 #undef __CFA_EXP_OVERFLOW__ 94 #define __CFA_BASE_COMP_1__() 95 #define __CFA_BASE_COMP_2__() 96 #define __CFA_EXP_OVERFLOW__() 100 97 101 // FIXME (x \ (unsigned long int)y) relies on X ?\?(T, unsigned long) a function that is neither 102 // defined, nor passed as an assertion parameter. Without user-defined conversions, cannot specify 103 // X as a type that casts to double, yet it doesn't make sense to write functions with that type 104 // signature where X is double. 98 static inline forall( otype OT | { void ?{}( OT & this, one_t ); OT ?*?( OT, OT ); } ) { 99 OT ?\?( OT ep, unsigned int y ) { __CFA_EXP__(); } 100 OT ?\?( OT ep, unsigned long int y ) { __CFA_EXP__(); } 101 } // distribution 105 102 106 // static inline forall( otype T | { void ?{}( T & this, one_t ); T ?*?( T, T ); double ?/?( double, T ); } ) 107 // double ?\?( T x, signed long int y ) { 108 // if ( y >= 0 ) return (double)(x \ (unsigned long int)y); 109 // else return 1.0 / x \ (unsigned long int)(-y); 110 // } // ?\? 103 #undef __CFA_BASE_COMP_1__ 104 #undef __CFA_BASE_COMP_2__ 105 #undef __CFA_EXP_OVERFLOW__ 111 106 112 static inline long int ?\=?( long int & x, unsigned long int y ) { x = x \ y; return x; } 113 static inline unsigned long int ?\=?( unsigned long int & x, unsigned long int y ) { x = x \ y; return x; } 114 static inline int ?\=?( int & x, unsigned long int y ) { x = x \ y; return x; } 115 static inline unsigned int ?\=?( unsigned int & x, unsigned long int y ) { x = x \ y; return x; } 107 static inline { 108 long int ?\=?( long int & x, unsigned long int y ) { x = x \ y; return x; } 109 unsigned long int ?\=?( unsigned long int & x, unsigned long int y ) { x = x \ y; return x; } 110 int ?\=?( int & x, unsigned long int y ) { x = x \ y; return x; } 111 unsigned int ?\=?( unsigned int & x, unsigned long int y ) { x = x \ y; return x; } 112 } // distribution 116 113 117 114 // Local Variables: // -
libcfa/prelude/prelude-gen.cc
rf845e80 r98b4b12 10 10 // Created On : Sat Feb 16 08:44:58 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Mar 8 16:00:22201913 // Update Count : 2612 // Last Modified On : Tue Apr 2 17:18:24 2019 13 // Update Count : 37 14 14 // 15 15 … … 118 118 { "?!=?", false, "signed int", Normal, "" }, 119 119 { "?=?", true, "", Normal, "" }, // void * LHS, zero_t RHS ??? 120 { "*?", false, "&", Normal, " | sized(DT)" }, // & ??? 120 // { "*?", false, "&", Normal, " | sized(DT)" }, // & ??? 121 { "*?", false, "&", Normal, "" }, // & ??? 121 122 122 123 { "?-?", false, "ptrdiff_t", Normal, " | sized(DT)" }, … … 208 209 cout << "void ?{} (" << type << " &);" << endl; 209 210 cout << "void ?{} (" << type << " &, " << type << ");" << endl; 210 cout << type << " 211 cout << type << " ?=? (" << type << " &, " << type << ")"; 211 212 if ( do_volatile ) { 212 cout << ", 213 cout << ", ?=?(volatile " << type << " &, " << type << ")"; 213 214 } 214 215 cout << ";" << endl; … … 217 218 218 219 otype("zero_t"); 220 cout << endl; 219 221 otype("one_t"); 222 cout << endl; 220 223 otype("_Bool", true); 221 224 cout << endl; … … 225 228 cout << "void ?{}(" << type.name << " &, " << type.name << ");" << endl; 226 229 cout << "void ?{}(" << type.name << " &, zero_t);" << endl; 230 cout << "void ?{}(" << type.name << " &, one_t);" << endl; 227 231 cout << "void ^?{}(" << type.name << " &);" << endl; 228 232 cout << endl; -
libcfa/src/Makefile.am
rf845e80 r98b4b12 74 74 75 75 prelude.o : prelude.cfa extras.cf gcc-builtins.cf builtins.cf @CFACC@ @CFACPP@ 76 ${AM_V_GEN} @CFACC@ ${AM_CFLAGS} ${CFLAGS} -quiet -in-tree @CONFIG_CFAFLAGS@-XCFA -l ${<} -c -o ${@}76 ${AM_V_GEN}$(CFACOMPILE) -quiet -in-tree -XCFA -l ${<} -c -o ${@} 77 77 78 78 prelude.lo: prelude.cfa extras.cf gcc-builtins.cf builtins.cf @CFACC@ @CFACPP@ 79 79 ${AM_V_GEN}$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile \ 80 @CFACC@ ${AM_CFLAGS} ${CFLAGS} -quiet -in-tree @CONFIG_CFAFLAGS@-XCFA -l ${<} -c -o ${@}80 $(CFACOMPILE) -quiet -in-tree -XCFA -l ${<} -c -o ${@} 81 81 82 82 -
libcfa/src/Makefile.in
rf845e80 r98b4b12 926 926 927 927 prelude.o : prelude.cfa extras.cf gcc-builtins.cf builtins.cf @CFACC@ @CFACPP@ 928 ${AM_V_GEN} @CFACC@ ${AM_CFLAGS} ${CFLAGS} -quiet -in-tree @CONFIG_CFAFLAGS@-XCFA -l ${<} -c -o ${@}928 ${AM_V_GEN}$(CFACOMPILE) -quiet -in-tree -XCFA -l ${<} -c -o ${@} 929 929 930 930 prelude.lo: prelude.cfa extras.cf gcc-builtins.cf builtins.cf @CFACC@ @CFACPP@ 931 931 ${AM_V_GEN}$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile \ 932 @CFACC@ ${AM_CFLAGS} ${CFLAGS} -quiet -in-tree @CONFIG_CFAFLAGS@-XCFA -l ${<} -c -o ${@}932 $(CFACOMPILE) -quiet -in-tree -XCFA -l ${<} -c -o ${@} 933 933 934 934 #---------------------------------------------------------------------------------------------------------------- -
libcfa/src/concurrency/CtxSwitch-x86_64.S
rf845e80 r98b4b12 88 88 ret 89 89 90 //.text 91 // .align 2 92 //.globl CtxStore 93 //CtxStore: 94 // // Save floating & SSE control words on the stack. 95 // 96 // subq $8,%rsp 97 // stmxcsr 0(%rsp) // 4 bytes 98 // fnstcw 4(%rsp) // 2 bytes 99 // 100 // // Save volatile registers on the stack. 101 // 102 // pushq %r15 103 // pushq %r14 104 // pushq %r13 105 // pushq %r12 106 // pushq %rbx 107 // 108 // // Save old context in the "from" area. 109 // 110 // movq %rsp,SP_OFFSET(%rdi) 111 // movq %rbp,FP_OFFSET(%rdi) 112 // 113 // // Return to thread 114 // 115 // ret 116 // 117 //.text 118 // .align 2 119 //.globl CtxRet 120 //CtxRet: 121 // // Load new context from the "to" area. 122 // 123 // movq SP_OFFSET(%rdi),%rsp 124 // movq FP_OFFSET(%rdi),%rbp 125 // 126 // // Load volatile registers from the stack. 127 // 128 // popq %rbx 129 // popq %r12 130 // popq %r13 131 // popq %r14 132 // popq %r15 133 // 134 // // Load floating & SSE control words from the stack. 135 // 136 // fldcw 4(%rsp) 137 // ldmxcsr 0(%rsp) 138 // addq $8,%rsp 139 // 140 // // Return to thread. 141 // 142 // ret 143 144 90 145 .text 91 146 .align 2 -
libcfa/src/concurrency/coroutine.cfa
rf845e80 r98b4b12 35 35 36 36 extern "C" { 37 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage ) __attribute__ ((__noreturn__));37 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc *) __attribute__ ((__noreturn__)); 38 38 static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) __attribute__ ((__noreturn__)); 39 39 static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) { … … 84 84 void ^?{}(coroutine_desc& this) { 85 85 if(this.state != Halted && this.state != Start) { 86 coroutine_desc * src = TL_GET( this_ coroutine );86 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 87 87 coroutine_desc * dst = &this; 88 88 … … 115 115 // Wrapper for co 116 116 void CoroutineCtxSwitch(coroutine_desc* src, coroutine_desc* dst) { 117 // Safety note : This could cause some false positives due to preemption 117 // Safety note : Preemption must be disabled since there is a race condition 118 // kernelTLS.this_thread->curr_cor and $rsp/$rbp must agree at all times 118 119 verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate ); 119 120 disable_interrupts(); … … 123 124 124 125 // set new coroutine that task is executing 125 kernelTLS.this_coroutine= dst;126 TL_GET( this_thread )->curr_cor = dst; 126 127 127 128 // context switch to specified coroutine … … 134 135 135 136 enable_interrupts( __cfaabi_dbg_ctx ); 136 // Safety note : This could cause some false positives due to preemption137 137 verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate ); 138 138 139 139 140 if( unlikely(src->cancellation != NULL) ) { 140 _CtxCoroutine_Unwind(src->cancellation );141 _CtxCoroutine_Unwind(src->cancellation, src); 141 142 } 142 143 } //ctxSwitchDirect … … 197 198 } 198 199 199 void __leave_coroutine() { 200 coroutine_desc * src = TL_GET( this_coroutine ); // optimization 200 void __leave_coroutine( coroutine_desc * src ) { 201 201 coroutine_desc * starter = src->cancellation != 0 ? src->last : src->starter; 202 202 -
libcfa/src/concurrency/coroutine.hfa
rf845e80 r98b4b12 46 46 //----------------------------------------------------------------------------- 47 47 // Public coroutine API 48 static inline void suspend( );48 static inline void suspend(void); 49 49 50 50 forall(dtype T | is_coroutine(T)) … … 71 71 72 72 // Suspend implementation inlined for performance 73 static inline void suspend( ) {73 static inline void suspend(void) { 74 74 // optimization : read TLS once and reuse it 75 75 // Safety note: this is preemption safe since if … … 77 77 // will also migrate which means this value will 78 78 // stay in syn with the TLS 79 coroutine_desc * src = TL_GET( this_ coroutine );79 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 80 80 81 81 assertf( src->last != 0, … … 99 99 // will also migrate which means this value will 100 100 // stay in syn with the TLS 101 coroutine_desc * src = TL_GET( this_ coroutine );101 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 102 102 coroutine_desc * dst = get_coroutine(cor); 103 103 … … 129 129 // will also migrate which means this value will 130 130 // stay in syn with the TLS 131 coroutine_desc * src = TL_GET( this_ coroutine );131 coroutine_desc * src = TL_GET( this_thread )->curr_cor; 132 132 133 133 // not resuming self ? … … 146 146 } 147 147 148 149 150 // static inline bool suspend_checkpoint(void) { 151 // // optimization : read TLS once and reuse it 152 // // Safety note: this is preemption safe since if 153 // // preemption occurs after this line, the pointer 154 // // will also migrate which means this value will 155 // // stay in syn with the TLS 156 // // set state of current coroutine to inactive 157 // this->state = Checkpoint; 158 159 // // context switch to specified coroutine 160 // assert( src->stack.context ); 161 162 // CtxStore(src->stack.context); 163 164 // bool ret = this->state == Checkpoint; 165 166 // // set state of new coroutine to active 167 // src->state = Active; 168 169 // enable_interrupts( __cfaabi_dbg_ctx ); 170 // // Safety note : This could cause some false positives due to preemption 171 // verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate ); 172 173 // if( unlikely(src->cancellation != NULL) ) { 174 // _CtxCoroutine_Unwind(src->cancellation); 175 // } 176 177 // return ret; 178 // } 179 180 // static inline void suspend_return(void) { 181 // // optimization : read TLS once and reuse it 182 // // Safety note: this is preemption safe since if 183 // // preemption occurs after this line, the pointer 184 // // will also migrate which means this value will 185 // // stay in syn with the TLS 186 // coroutine_desc * src = TL_GET( this_thread )->curr_cor; 187 188 // assertf( src->last != 0, 189 // "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n" 190 // "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.", 191 // src->name, src ); 192 // assertf( src->last->state != Halted, 193 // "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n" 194 // "Possible cause is terminated coroutine's main routine has already returned.", 195 // src->name, src, src->last->name, src->last ); 196 197 // // Safety note : Preemption must be disabled here since kernelTLS.this_coroutine must always be up to date 198 // verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate ); 199 // disable_interrupts(); 200 201 // // set state of current coroutine to inactive 202 // src->state = src->state == Halted ? Halted : Inactive; 203 204 // // set new coroutine that task is executing 205 // kernelTLS.this_coroutine = dst; 206 207 // // context switch to specified coroutine 208 // assert( src->stack.context ); 209 // CtxRet( src->stack.context ); 210 211 // abort(); 212 // } 213 148 214 // Local Variables: // 149 215 // mode: c // -
libcfa/src/concurrency/invoke.c
rf845e80 r98b4b12 28 28 29 29 extern void __suspend_internal(void); 30 extern void __leave_coroutine( void);31 extern void __finish_creation( void);30 extern void __leave_coroutine( struct coroutine_desc * ); 31 extern void __finish_creation( struct coroutine_desc * ); 32 32 extern void __leave_thread_monitor( struct thread_desc * this ); 33 33 extern void disable_interrupts(); … … 52 52 53 53 //Final suspend, should never return 54 __leave_coroutine( );54 __leave_coroutine( cor ); 55 55 __cabi_abort( "Resumed dead coroutine" ); 56 56 } … … 62 62 __attribute((__unused__)) struct _Unwind_Exception * unwind_exception, 63 63 __attribute((__unused__)) struct _Unwind_Context * context, 64 __attribute((__unused__))void * param64 void * param 65 65 ) { 66 66 if( actions & _UA_END_OF_STACK ) { 67 67 // We finished unwinding the coroutine, 68 68 // leave it 69 __leave_coroutine( );69 __leave_coroutine( param ); 70 70 __cabi_abort( "Resumed dead coroutine" ); 71 71 } … … 75 75 } 76 76 77 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage ) __attribute__ ((__noreturn__));78 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage ) {79 _Unwind_Reason_Code ret = _Unwind_ForcedUnwind( storage, _CtxCoroutine_UnwindStop, NULL);77 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc * cor) __attribute__ ((__noreturn__)); 78 void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc * cor) { 79 _Unwind_Reason_Code ret = _Unwind_ForcedUnwind( storage, _CtxCoroutine_UnwindStop, cor ); 80 80 printf("UNWIND ERROR %d after force unwind\n", ret); 81 81 abort(); … … 88 88 void *this 89 89 ) { 90 // Fetch the thread handle from the user defined thread structure 91 struct thread_desc* thrd = get_thread( this ); 92 90 93 // First suspend, once the thread arrives here, 91 94 // the function pointer to main can be invalidated without risk 92 __finish_creation( );95 __finish_creation(&thrd->self_cor); 93 96 94 // Fetch the thread handle from the user defined thread structure 95 struct thread_desc* thrd = get_thread( this ); 97 // Restore the last to NULL, we clobbered because of the thunk problem 96 98 thrd->self_cor.last = NULL; 97 99 -
libcfa/src/concurrency/invoke.h
rf845e80 r98b4b12 50 50 51 51 extern thread_local struct KernelThreadData { 52 struct coroutine_desc * volatile this_coroutine;53 52 struct thread_desc * volatile this_thread; 54 53 struct processor * volatile this_processor; … … 61 60 } kernelTLS __attribute__ ((tls_model ( "initial-exec" ))); 62 61 } 63 64 static inline struct coroutine_desc * volatile active_coroutine() { return TL_GET( this_coroutine ); }65 static inline struct thread_desc * volatile active_thread () { return TL_GET( this_thread ); }66 static inline struct processor * volatile active_processor() { return TL_GET( this_processor ); } // UNSAFE67 62 #endif 68 63 … … 170 165 struct thread_desc * prev; 171 166 } node; 172 }; 173 174 #ifdef __cforall 175 extern "Cforall" { 167 }; 168 169 #ifdef __cforall 170 extern "Cforall" { 171 static inline struct coroutine_desc * volatile active_coroutine() { return TL_GET( this_thread )->curr_cor; } 172 static inline struct thread_desc * volatile active_thread () { return TL_GET( this_thread ); } 173 static inline struct processor * volatile active_processor() { return TL_GET( this_processor ); } // UNSAFE 174 176 175 static inline thread_desc * & get_next( thread_desc & this ) { 177 176 return this.next; … … 232 231 extern void CtxInvokeStub( void ); 233 232 void CtxSwitch( void * from, void * to ) asm ("CtxSwitch"); 233 // void CtxStore ( void * this ) asm ("CtxStore"); 234 // void CtxRet ( void * dst ) asm ("CtxRet"); 234 235 235 236 #if defined( __i386 ) -
libcfa/src/concurrency/kernel.cfa
rf845e80 r98b4b12 60 60 NULL, 61 61 NULL, 62 NULL,63 62 { 1, false, false } 64 63 }; … … 263 262 static void returnToKernel() { 264 263 coroutine_desc * proc_cor = get_coroutine(kernelTLS.this_processor->runner); 265 coroutine_desc * thrd_cor = kernelTLS.this_thread->curr_cor = kernelTLS.this_coroutine;264 coroutine_desc * thrd_cor = kernelTLS.this_thread->curr_cor; 266 265 ThreadCtxSwitch(thrd_cor, proc_cor); 267 266 } … … 307 306 processor * proc = (processor *) arg; 308 307 kernelTLS.this_processor = proc; 309 kernelTLS.this_coroutine = NULL;310 308 kernelTLS.this_thread = NULL; 311 309 kernelTLS.preemption_state.[enabled, disable_count] = [false, 1]; … … 321 319 322 320 //Set global state 323 kernelTLS.this_coroutine = get_coroutine(proc->runner);324 321 kernelTLS.this_thread = NULL; 325 322 … … 351 348 // KERNEL_ONLY 352 349 void kernel_first_resume(processor * this) { 353 coroutine_desc * src = kernelTLS.this_coroutine;350 coroutine_desc * src = mainThread->curr_cor; 354 351 coroutine_desc * dst = get_coroutine(this->runner); 355 352 … … 366 363 // set state of current coroutine to inactive 367 364 src->state = src->state == Halted ? Halted : Inactive; 368 369 // set new coroutine that task is executing370 kernelTLS.this_coroutine = dst;371 365 372 366 // SKULLDUGGERY normally interrupts are enable before leaving a coroutine ctxswitch. … … 599 593 kernelTLS.this_processor = mainProcessor; 600 594 kernelTLS.this_thread = mainThread; 601 kernelTLS.this_coroutine = &mainThread->self_cor;602 595 603 596 // Enable preemption … … 720 713 __cfaabi_dbg_bits_write( abort_text, len ); 721 714 722 if ( get_coroutine(thrd) != kernelTLS.this_coroutine) {723 len = snprintf( abort_text, abort_text_size, " in coroutine %.256s (%p).\n", kernelTLS.this_coroutine->name, kernelTLS.this_coroutine);715 if ( &thrd->self_cor != thrd->curr_cor ) { 716 len = snprintf( abort_text, abort_text_size, " in coroutine %.256s (%p).\n", thrd->curr_cor->name, thrd->curr_cor ); 724 717 __cfaabi_dbg_bits_write( abort_text, len ); 725 718 } -
libcfa/src/concurrency/thread.cfa
rf845e80 r98b4b12 75 75 coroutine_desc* thrd_c = get_coroutine(this); 76 76 thread_desc * thrd_h = get_thread (this); 77 thrd_c->last = TL_GET( this_ coroutine );77 thrd_c->last = TL_GET( this_thread )->curr_cor; 78 78 79 79 // __cfaabi_dbg_print_safe("Thread start : %p (t %p, c %p)\n", this, thrd_c, thrd_h); … … 81 81 disable_interrupts(); 82 82 create_stack(&thrd_c->stack, thrd_c->stack.size); 83 kernelTLS.this_coroutine = thrd_c;84 83 CtxStart(&this, CtxInvokeThread); 85 84 assert( thrd_c->last->stack.context ); … … 92 91 extern "C" { 93 92 // KERNEL ONLY 94 void __finish_creation(void) { 95 coroutine_desc* thrd_c = kernelTLS.this_coroutine; 93 void __finish_creation(coroutine_desc * thrd_c) { 96 94 ThreadCtxSwitch( thrd_c, thrd_c->last ); 97 95 } … … 120 118 // set new coroutine that the processor is executing 121 119 // and context switch to it 122 kernelTLS.this_coroutine = dst;123 120 assert( src->stack.context ); 124 121 CtxSwitch( src->stack.context, dst->stack.context ); 125 kernelTLS.this_coroutine = src;126 122 127 123 // set state of new coroutine to active -
libcfa/src/fstream.cfa
rf845e80 r98b4b12 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 24 18:33:38 201813 // Update Count : 3 0412 // Last Modified On : Sat Apr 20 12:03:43 2019 13 // Update Count : 311 14 14 // 15 15 … … 23 23 #include <complex.h> // creal, cimag 24 24 #include <assert.h> 25 #include <errno.h> // errno 25 26 26 27 #define IO_MSG "I/O error: " … … 32 33 os.nlOnOff = nlOnOff; 33 34 os.prt = prt; 35 os.sawNL = false; 34 36 sepSet( os, separator ); 35 37 sepSetCur( os, sepGet( os ) ); … … 105 107 #ifdef __CFA_DEBUG__ 106 108 if ( file == 0 ) { 107 fprintf( stderr, IO_MSG "open output file \"%s\", ", name ); 108 perror( 0 ); 109 exit( EXIT_FAILURE ); 109 abort( IO_MSG "open output file \"%s\", %s", name, strerror( errno ) ); 110 110 } // if 111 111 #endif // __CFA_DEBUG__ … … 121 121 122 122 if ( fclose( (FILE *)(os.file) ) == EOF ) { 123 perror( IO_MSG "close output");123 abort( IO_MSG "close output %s", strerror( errno ) ); 124 124 } // if 125 125 } // close … … 127 127 ofstream & write( ofstream & os, const char * data, size_t size ) { 128 128 if ( fail( os ) ) { 129 fprintf( stderr, "attempt write I/O on failed stream\n" ); 130 exit( EXIT_FAILURE ); 129 abort( "attempt write I/O on failed stream\n" ); 131 130 } // if 132 131 133 132 if ( fwrite( data, 1, size, (FILE *)(os.file) ) != size ) { 134 perror( IO_MSG "write" ); 135 exit( EXIT_FAILURE ); 133 abort( IO_MSG "write %s", strerror( errno ) ); 136 134 } // if 137 135 return os; … … 144 142 if ( len == EOF ) { 145 143 if ( ferror( (FILE *)(os.file) ) ) { 146 fprintf( stderr, "invalid write\n" ); 147 exit( EXIT_FAILURE ); 144 abort( "invalid write\n" ); 148 145 } // if 149 146 } // if … … 166 163 void ?{}( ifstream & is, void * file ) { 167 164 is.file = file; 165 is.nlOnOff = false; 168 166 } 169 167 … … 177 175 open( is, name, "r" ); 178 176 } 177 178 void nlOn( ifstream & os ) { os.nlOnOff = true; } 179 void nlOff( ifstream & os ) { os.nlOnOff = false; } 180 bool getANL( ifstream & os ) { return os.nlOnOff; } 179 181 180 182 int fail( ifstream & is ) { … … 187 189 188 190 void open( ifstream & is, const char * name, const char * mode ) { 189 FILE * file = fopen( name, mode );191 FILE * file = fopen( name, mode ); 190 192 #ifdef __CFA_DEBUG__ 191 193 if ( file == 0 ) { 192 fprintf( stderr, IO_MSG "open input file \"%s\", ", name ); 193 perror( 0 ); 194 exit( EXIT_FAILURE ); 194 abort( IO_MSG "open input file \"%s\", %s\n", name, strerror( errno ) ); 195 195 } // if 196 196 #endif // __CFA_DEBUG__ … … 206 206 207 207 if ( fclose( (FILE *)(is.file) ) == EOF ) { 208 perror( IO_MSG "close input");208 abort( IO_MSG "close input %s", strerror( errno ) ); 209 209 } // if 210 210 } // close … … 212 212 ifstream & read( ifstream & is, char * data, size_t size ) { 213 213 if ( fail( is ) ) { 214 fprintf( stderr, "attempt read I/O on failed stream\n" ); 215 exit( EXIT_FAILURE ); 214 abort( "attempt read I/O on failed stream\n" ); 216 215 } // if 217 216 218 217 if ( fread( data, size, 1, (FILE *)(is.file) ) == 0 ) { 219 perror( IO_MSG "read" ); 220 exit( EXIT_FAILURE ); 218 abort( IO_MSG "read %s", strerror( errno ) ); 221 219 } // if 222 220 return is; … … 225 223 ifstream &ungetc( ifstream & is, char c ) { 226 224 if ( fail( is ) ) { 227 fprintf( stderr, "attempt ungetc I/O on failed stream\n" ); 228 exit( EXIT_FAILURE ); 225 abort( "attempt ungetc I/O on failed stream\n" ); 229 226 } // if 230 227 231 228 if ( ungetc( c, (FILE *)(is.file) ) == EOF ) { 232 perror( IO_MSG "ungetc" ); 233 exit( EXIT_FAILURE ); 229 abort( IO_MSG "ungetc %s", strerror( errno ) ); 234 230 } // if 235 231 return is; … … 243 239 if ( len == EOF ) { 244 240 if ( ferror( (FILE *)(is.file) ) ) { 245 fprintf( stderr, "invalid read\n" ); 246 exit( EXIT_FAILURE ); 241 abort( "invalid read\n" ); 247 242 } // if 248 243 } // if -
libcfa/src/fstream.hfa
rf845e80 r98b4b12 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 24 18:33:41 201813 // Update Count : 1 4912 // Last Modified On : Sat Apr 20 12:03:58 2019 13 // Update Count : 151 14 14 // 15 15 … … 73 73 struct ifstream { 74 74 void * file; 75 bool nlOnOff; 75 76 }; // ifstream 76 77 77 78 // public 79 void nlOn( ifstream & ); 80 void nlOff( ifstream & ); 81 bool getANL( ifstream & ); 78 82 int fail( ifstream & is ); 79 83 int eof( ifstream & is ); -
libcfa/src/gmp.hfa
rf845e80 r98b4b12 10 10 // Created On : Tue Apr 19 08:43:43 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Dec 4 23:25:51 201813 // Update Count : 2 212 // Last Modified On : Sat Apr 20 09:01:52 2019 13 // Update Count : 24 14 14 // 15 15 … … 271 271 272 272 void ?|?( ostype & os, Int mp ) { 273 (ostype)(os | mp); if ( getANL( os ) )nl( os );273 (ostype)(os | mp); nl( os ); 274 274 } // ?|? 275 275 } // distribution -
libcfa/src/heap.cfa
rf845e80 r98b4b12 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Sep 6 09:01:30 201813 // Update Count : 51 312 // Last Modified On : Fri Mar 22 13:43:10 2019 13 // Update Count : 514 14 14 // 15 15 … … 1034 1034 // Local Variables: // 1035 1035 // tab-width: 4 // 1036 // compile-command: "cfa -nodebug -O2 heap.c " //1036 // compile-command: "cfa -nodebug -O2 heap.cfa" // 1037 1037 // End: // -
libcfa/src/iostream.cfa
rf845e80 r98b4b12 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Mar 4 20:57:24201913 // Update Count : 59312 // Last Modified On : Sat Apr 20 14:02:43 2019 13 // Update Count : 617 14 14 // 15 15 … … 396 396 397 397 istype & ?|?( istype & is, char & c ) { 398 fmt( is, "%c", &c ); // must pass pointer through varg to fmt 398 char temp; 399 for () { 400 fmt( is, "%c", &temp ); // must pass pointer through varg to fmt 401 // do not overwrite parameter with newline unless appropriate 402 if ( temp != '\n' || getANL( is ) ) { c = temp; break; } 403 if ( eof( is ) ) break; 404 } // for 399 405 return is; 400 406 } // ?|? … … 498 504 return is; 499 505 } // nl 506 507 istype & nlOn( istype & is ) { 508 nlOn( is ); // call void returning 509 return is; 510 } // nlOn 511 512 istype & nlOff( istype & is ) { 513 nlOff( is ); // call void returning 514 return is; 515 } // nlOff 500 516 } // distribution 501 517 -
libcfa/src/iostream.hfa
rf845e80 r98b4b12 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 26 16:57:22201913 // Update Count : 22 112 // Last Modified On : Sat Apr 20 12:04:07 2019 13 // Update Count : 226 14 14 // 15 15 … … 149 149 150 150 trait istream( dtype istype ) { 151 void nlOn( istype & ); // read newline 152 void nlOff( istype & ); // scan newline 153 bool getANL( istype & ); // get scan newline (on/off) 151 154 int fail( istype & ); 152 155 int eof( istype & ); … … 187 190 188 191 // manipulators 192 istype & nlOn( istype & ); 193 istype & nlOff( istype & ); 189 194 istype & ?|?( istype &, istype & (*)( istype & ) ); 190 195 istype & nl( istype & is ); -
libcfa/src/rational.cfa
rf845e80 r98b4b12 10 10 // Created On : Wed Apr 6 17:54:28 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Dec 23 22:56:49 201813 // Update Count : 1 7012 // Last Modified On : Thu Mar 28 17:33:03 2019 13 // Update Count : 181 14 14 // 15 15 … … 35 35 static RationalImpl simplify( RationalImpl & n, RationalImpl & d ) { 36 36 if ( d == (RationalImpl){0} ) { 37 serr | "Invalid rational number construction: denominator cannot be equal to 0."; 38 exit( EXIT_FAILURE ); 37 abort( "Invalid rational number construction: denominator cannot be equal to 0.\n" ); 39 38 } // exit 40 39 if ( d < (RationalImpl){0} ) { d = -d; n = -n; } // move sign to numerator … … 54 53 void ?{}( Rational(RationalImpl) & r, RationalImpl n, RationalImpl d ) { 55 54 RationalImpl t = simplify( n, d ); // simplify 56 r.numerator = n / t; 57 r.denominator = d / t; 55 r.[numerator, denominator] = [n / t, d / t]; 58 56 } // rational 59 57 … … 78 76 RationalImpl prev = r.numerator; 79 77 RationalImpl t = gcd( abs( n ), r.denominator ); // simplify 80 r.numerator = n / t; 81 r.denominator = r.denominator / t; 78 r.[numerator, denominator] = [n / t, r.denominator / t]; 82 79 return prev; 83 80 } // numerator … … 86 83 RationalImpl prev = r.denominator; 87 84 RationalImpl t = simplify( r.numerator, d ); // simplify 88 r.numerator = r.numerator / t; 89 r.denominator = d / t; 85 r.[numerator, denominator] = [r.numerator / t, d / t]; 90 86 return prev; 91 87 } // denominator … … 120 116 121 117 Rational(RationalImpl) +?( Rational(RationalImpl) r ) { 122 Rational(RationalImpl) t = { r.numerator, r.denominator }; 123 return t; 118 return (Rational(RationalImpl)){ r.numerator, r.denominator }; 124 119 } // +? 125 120 126 121 Rational(RationalImpl) -?( Rational(RationalImpl) r ) { 127 Rational(RationalImpl) t = { -r.numerator, r.denominator }; 128 return t; 122 return (Rational(RationalImpl)){ -r.numerator, r.denominator }; 129 123 } // -? 130 124 131 125 Rational(RationalImpl) ?+?( Rational(RationalImpl) l, Rational(RationalImpl) r ) { 132 126 if ( l.denominator == r.denominator ) { // special case 133 Rational(RationalImpl) t = { l.numerator + r.numerator, l.denominator }; 134 return t; 127 return (Rational(RationalImpl)){ l.numerator + r.numerator, l.denominator }; 135 128 } else { 136 Rational(RationalImpl) t = { l.numerator * r.denominator + l.denominator * r.numerator, l.denominator * r.denominator }; 137 return t; 129 return (Rational(RationalImpl)){ l.numerator * r.denominator + l.denominator * r.numerator, l.denominator * r.denominator }; 138 130 } // if 139 131 } // ?+? … … 141 133 Rational(RationalImpl) ?-?( Rational(RationalImpl) l, Rational(RationalImpl) r ) { 142 134 if ( l.denominator == r.denominator ) { // special case 143 Rational(RationalImpl) t = { l.numerator - r.numerator, l.denominator }; 144 return t; 135 return (Rational(RationalImpl)){ l.numerator - r.numerator, l.denominator }; 145 136 } else { 146 Rational(RationalImpl) t = { l.numerator * r.denominator - l.denominator * r.numerator, l.denominator * r.denominator }; 147 return t; 137 return (Rational(RationalImpl)){ l.numerator * r.denominator - l.denominator * r.numerator, l.denominator * r.denominator }; 148 138 } // if 149 139 } // ?-? 150 140 151 141 Rational(RationalImpl) ?*?( Rational(RationalImpl) l, Rational(RationalImpl) r ) { 152 Rational(RationalImpl) t = { l.numerator * r.numerator, l.denominator * r.denominator }; 153 return t; 142 return (Rational(RationalImpl)){ l.numerator * r.numerator, l.denominator * r.denominator }; 154 143 } // ?*? 155 144 156 145 Rational(RationalImpl) ?/?( Rational(RationalImpl) l, Rational(RationalImpl) r ) { 157 146 if ( r.numerator < (RationalImpl){0} ) { 158 r.numerator = -r.numerator; 159 r.denominator = -r.denominator; 147 r.[numerator, denominator] = [-r.numerator, -r.denominator]; 160 148 } // if 161 Rational(RationalImpl) t = { l.numerator * r.denominator, l.denominator * r.numerator }; 162 return t; 149 return (Rational(RationalImpl)){ l.numerator * r.denominator, l.denominator * r.numerator }; 163 150 } // ?/? 164 151 … … 167 154 forall( dtype istype | istream( istype ) | { istype & ?|?( istype &, RationalImpl & ); } ) 168 155 istype & ?|?( istype & is, Rational(RationalImpl) & r ) { 169 RationalImpl t;170 156 is | r.numerator | r.denominator; 171 t = simplify( r.numerator, r.denominator );157 RationalImpl t = simplify( r.numerator, r.denominator ); 172 158 r.numerator /= t; 173 159 r.denominator /= t; … … 185 171 } // distribution 186 172 } // distribution 173 174 forall( otype RationalImpl | arithmetic( RationalImpl ) | { RationalImpl ?\?( RationalImpl, unsigned long ); } ) 175 Rational(RationalImpl) ?\?( Rational(RationalImpl) x, long int y ) { 176 if ( y < 0 ) { 177 return (Rational(RationalImpl)){ x.denominator \ -y, x.numerator \ -y }; 178 } else { 179 return (Rational(RationalImpl)){ x.numerator \ y, x.denominator \ y }; 180 } // if 181 } 187 182 188 183 // conversion -
libcfa/src/rational.hfa
rf845e80 r98b4b12 12 12 // Created On : Wed Apr 6 17:56:25 2016 13 13 // Last Modified By : Peter A. Buhr 14 // Last Modified On : Tue Dec 4 23:07:46 201815 // Update Count : 10 614 // Last Modified On : Tue Mar 26 23:16:10 2019 15 // Update Count : 109 16 16 // 17 17 … … 98 98 } // distribution 99 99 100 forall( otype RationalImpl | arithmetic( RationalImpl ) |{RationalImpl ?\?( RationalImpl, unsigned long );} ) 101 Rational(RationalImpl) ?\?( Rational(RationalImpl) x, long int y ); 102 100 103 // conversion 101 104 forall( otype RationalImpl | arithmetic( RationalImpl ) | { double convert( RationalImpl ); } ) -
libcfa/src/stdhdr/stdbool.h
rf845e80 r98b4b12 10 10 // Created On : Mon Jul 4 23:25:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Jul 5 20:39:51 201613 // Update Count : 1 212 // Last Modified On : Mon Mar 25 08:00:08 2019 13 // Update Count : 15 14 14 // 15 15 16 16 extern "C" { 17 17 #include_next <stdbool.h> // has internal check for multiple expansion 18 19 // allows printing as true/false 20 #if defined( true ) 21 #undef true 22 #define true ((_Bool)1) 23 #endif // true 24 25 #if defined( false ) 26 #undef false 27 #define false ((_Bool)0) 28 #endif // false 18 29 } // extern "C" 19 30 -
libcfa/src/stdlib.hfa
rf845e80 r98b4b12 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Dec 17 15:37:45 201813 // Update Count : 3 4612 // Last Modified On : Wed Apr 24 17:35:43 2019 13 // Update Count : 352 14 14 // 15 15 … … 40 40 } // malloc 41 41 42 // T & malloc( void ) {43 // int & p = *(T *)(void *)malloc( (size_t)sizeof(T) ); // C malloc44 // printf( "& malloc %p\n", &p );45 // return p;46 // // return (T &)*(T *)(void *)malloc( (size_t)sizeof(T) ); // C malloc47 // } // malloc48 49 42 T * calloc( size_t dim ) { 50 43 return (T *)(void *)calloc( dim, sizeof(T) ); // C calloc … … 76 69 T * alloc( char fill ) { 77 70 T * ptr = (T *)(void *)malloc( (size_t)sizeof(T) ); // C malloc 78 return (T *)memset( ptr, (int)fill, sizeof(T) ); // initialwith fill value71 return (T *)memset( ptr, (int)fill, sizeof(T) ); // initialize with fill value 79 72 } // alloc 80 73 … … 84 77 85 78 T * alloc( size_t dim, char fill ) { 86 T * ptr = (T *)(void *)malloc( dim * (size_t)sizeof(T) ); // C malloc87 return (T *)memset( ptr, (int)fill, dim * sizeof(T) ); // initialwith fill value79 T * ptr = (T *)(void *)malloc( dim * (size_t)sizeof(T) ); // C calloc 80 return (T *)memset( ptr, (int)fill, dim * sizeof(T) ); // initialize with fill value 88 81 } // alloc 89 82 -
src/Common/PassVisitor.cc
rf845e80 r98b4b12 17 17 18 18 PassVisitorStats pass_visitor_stats; 19 Stats::Counters::SimpleCounter* BaseSyntaxNode::new_nodes = nullptr; -
src/Common/Stats/Counter.h
rf845e80 r98b4b12 37 37 class SimpleCounter { 38 38 public: 39 inline void operator++() {} 39 40 inline void operator++(int) {} 40 41 inline void operator+=(size_t) {} … … 84 85 virtual void print(std::ostream & os) override { os << count; } 85 86 87 inline void operator++() { if(!enabled) return; count++; } 86 88 inline void operator++(int) { if(!enabled) return; count++; } 87 89 inline void operator+=(size_t value) { if(!enabled) return; count += value; } -
src/ControlStruct/ForExprMutator.cc
rf845e80 r98b4b12 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Fri Aug 18 10:22:00 201713 // Update Count : 1 211 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Mar 11 22:26:52 2019 13 // Update Count : 14 14 14 // 15 15 … … 21 21 22 22 namespace ControlStruct { 23 Statement * hoist( Statement *originalStmt, std::list<Statement *> &init ) {23 Statement * hoist( Statement * originalStmt, std::list<Statement *> & init ) { 24 24 // If no hoisting is needed, skip: 25 25 if ( 0 == init.size() ) { … … 29 29 // Create compound statement, move initializers outside, 30 30 // the resut of the original stays as is. 31 CompoundStmt * block = new CompoundStmt();32 std::list<Statement *> & stmts = block->get_kids();31 CompoundStmt * block = new CompoundStmt(); 32 std::list<Statement *> & stmts = block->get_kids(); 33 33 stmts.splice( stmts.end(), init ); 34 34 … … 38 38 } 39 39 40 Statement * ForExprMutator::postmutate( IfStmt *ifStmt ) {40 Statement * ForExprMutator::postmutate( IfStmt * ifStmt ) { 41 41 return hoist( ifStmt, ifStmt->initialization ); 42 42 } 43 Statement * ForExprMutator::postmutate( ForStmt *forStmt ) {43 Statement * ForExprMutator::postmutate( ForStmt * forStmt ) { 44 44 // hoist any initializer declarations to make them C89 (rather than C99) 45 45 return hoist( forStmt, forStmt->initialization ); 46 46 } 47 Statement * ForExprMutator::postmutate( WhileStmt *whileStmt ) {47 Statement * ForExprMutator::postmutate( WhileStmt * whileStmt ) { 48 48 return hoist( whileStmt, whileStmt->initialization ); 49 49 } -
src/ControlStruct/LabelFixer.cc
rf845e80 r98b4b12 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Rob Schluntz12 // Last Modified On : Tue Jul 28 13:32:43 201513 // Update Count : 15 611 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Mar 11 22:26:02 2019 13 // Update Count : 159 14 14 // 15 15 … … 32 32 } 33 33 34 LabelFixer::LabelFixer( LabelGenerator * gen ) : generator ( gen ) {34 LabelFixer::LabelFixer( LabelGenerator * gen ) : generator ( gen ) { 35 35 if ( generator == 0 ) 36 36 generator = LabelGenerator::getGenerator(); … … 49 49 50 50 // prune to at most one label definition for each statement 51 void LabelFixer::previsit( Statement * stmt ) {51 void LabelFixer::previsit( Statement * stmt ) { 52 52 std::list< Label > &labels = stmt->get_labels(); 53 53 … … 58 58 } 59 59 60 void LabelFixer::previsit( BranchStmt * branchStmt ) {60 void LabelFixer::previsit( BranchStmt * branchStmt ) { 61 61 previsit( ( Statement *)branchStmt ); 62 62 … … 75 75 76 76 77 // sets the definition of the labelTable entry to be the provided 78 // statement for every label in the listparameter. Happens for every kind of statement79 Label LabelFixer::setLabelsDef( std::list< Label > & llabel, Statement *definition ) {77 // sets the definition of the labelTable entry to be the provided statement for every label in the list 78 // parameter. Happens for every kind of statement 79 Label LabelFixer::setLabelsDef( std::list< Label > & llabel, Statement * definition ) { 80 80 assert( definition != 0 ); 81 81 assert( llabel.size() > 0 ); … … 100 100 } // for 101 101 102 // produce one of the labels attached to this statement to be 103 // temporarily used as the canonical label 102 // produce one of the labels attached to this statement to be temporarily used as the canonical label 104 103 return labelTable[ llabel.front() ]->get_label(); 105 104 } … … 117 116 118 117 // Builds a table that maps a label to its defining statement. 119 std::map<Label, Statement * > * LabelFixer::resolveJumps() throw ( SemanticErrorException ) {118 std::map<Label, Statement * > * LabelFixer::resolveJumps() throw ( SemanticErrorException ) { 120 119 std::map< Label, Statement * > *ret = new std::map< Label, Statement * >(); 121 120 for ( std::map< Label, Entry * >::iterator i = labelTable.begin(); i != labelTable.end(); ++i ) { -
src/ControlStruct/LabelGenerator.cc
rf845e80 r98b4b12 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Thr Aug 14 14:14:00 201513 // Update Count : 1 411 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Mar 11 22:23:20 2019 13 // Update Count : 15 14 14 // 15 15 … … 24 24 25 25 namespace ControlStruct { 26 LabelGenerator * LabelGenerator::labelGenerator = 0;26 LabelGenerator * LabelGenerator::labelGenerator = 0; 27 27 28 LabelGenerator * LabelGenerator::getGenerator() {28 LabelGenerator * LabelGenerator::getGenerator() { 29 29 if ( LabelGenerator::labelGenerator == 0 ) 30 30 LabelGenerator::labelGenerator = new LabelGenerator(); 31 32 31 return labelGenerator; 33 32 } … … 38 37 if ( stmt && ! stmt->get_labels().empty() ) { 39 38 os << "_" << stmt->get_labels().front() << "__"; 40 } 39 } // if 41 40 std::string ret = os.str(); 42 41 Label l( ret ); -
src/Parser/ParseNode.h
rf845e80 r98b4b12 10 10 // Created On : Sat May 16 13:28:16 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 13 17:36:49 201913 // Update Count : 8 6712 // Last Modified On : Mon Apr 15 14:22:39 2019 13 // Update Count : 874 14 14 // 15 15 … … 132 132 void printOneLine( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {} 133 133 134 Expression *get_expr() const { return expr.get(); }135 134 template<typename T> 136 135 bool isExpressionType() const { return nullptr != dynamic_cast<T>(expr.get()); } 137 136 138 137 Expression * build() const { return const_cast<ExpressionNode *>(this)->expr.release(); } 138 139 std::unique_ptr<Expression> expr; // public because of lifetime implications 139 140 private: 140 141 bool extension = false; 141 std::unique_ptr<Expression> expr;142 142 }; // ExpressionNode 143 143 -
src/Parser/lex.ll
rf845e80 r98b4b12 10 10 * Created On : Sat Sep 22 08:58:10 2001 11 11 * Last Modified By : Peter A. Buhr 12 * Last Modified On : Sun Mar 10 09:13:09201913 * Update Count : 70 612 * Last Modified On : Wed Mar 13 14:54:30 2019 13 * Update Count : 707 14 14 */ 15 15 … … 226 226 char { KEYWORD_RETURN(CHAR); } 227 227 choose { KEYWORD_RETURN(CHOOSE); } // CFA 228 coerce { KEYWORD_RETURN(COERCE); } // CFA 228 229 _Complex { KEYWORD_RETURN(COMPLEX); } // C99 229 230 __complex { KEYWORD_RETURN(COMPLEX); } // GCC -
src/Parser/parser.yy
rf845e80 r98b4b12 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 21 08:45:07201913 // Update Count : 42 3212 // Last Modified On : Mon Apr 15 15:02:56 2019 13 // Update Count : 4290 14 14 // 15 15 … … 185 185 186 186 ForCtrl * forCtrl( ExpressionNode * type, string * index, ExpressionNode * start, enum OperKinds compop, ExpressionNode * comp, ExpressionNode * inc ) { 187 ConstantExpr * constant = dynamic_cast<ConstantExpr *>(type-> get_expr());187 ConstantExpr * constant = dynamic_cast<ConstantExpr *>(type->expr.get()); 188 188 if ( constant && (constant->get_constant()->get_value() == "0" || constant->get_constant()->get_value() == "1") ) { 189 189 type = new ExpressionNode( new CastExpr( maybeMoveBuild< Expression >(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) ); … … 198 198 199 199 ForCtrl * forCtrl( ExpressionNode * type, ExpressionNode * index, ExpressionNode * start, enum OperKinds compop, ExpressionNode * comp, ExpressionNode * inc ) { 200 if ( NameExpr * identifier = dynamic_cast<NameExpr *>(index-> get_expr()) ) {200 if ( NameExpr * identifier = dynamic_cast<NameExpr *>(index->expr.get()) ) { 201 201 return forCtrl( type, new string( identifier->name ), start, compop, comp, inc ); 202 } else if ( CommaExpr * commaExpr = dynamic_cast<CommaExpr *>(index-> get_expr()) ) {202 } else if ( CommaExpr * commaExpr = dynamic_cast<CommaExpr *>(index->expr.get()) ) { 203 203 if ( NameExpr * identifier = dynamic_cast<NameExpr *>(commaExpr->arg1 ) ) { 204 204 return forCtrl( type, new string( identifier->name ), start, compop, comp, inc ); … … 265 265 %token RESTRICT // C99 266 266 %token ATOMIC // C11 267 %token FORALL MUTEX VIRTUAL 267 %token FORALL MUTEX VIRTUAL COERCE // CFA 268 268 %token VOID CHAR SHORT INT LONG FLOAT DOUBLE SIGNED UNSIGNED 269 269 %token BOOL COMPLEX IMAGINARY // C99 … … 334 334 %type<en> subrange 335 335 %type<decl> asm_name_opt 336 %type<en> asm_operands_opt asm_operands_listasm_operand336 %type<en> asm_operands_opt asm_operands_list asm_operand 337 337 %type<label> label_list 338 338 %type<en> asm_clobbers_list_opt 339 339 %type<flag> asm_volatile_opt 340 340 %type<en> handler_predicate_opt 341 %type<genexpr> generic_association 341 %type<genexpr> generic_association generic_assoc_list 342 342 343 343 // statements … … 795 795 | '(' type_no_function ')' cast_expression 796 796 { $$ = new ExpressionNode( build_cast( $2, $4 ) ); } 797 // keyword cast cannot be grouped because of reduction in aggregate_key 797 798 | '(' COROUTINE '&' ')' cast_expression // CFA 798 799 { $$ = new ExpressionNode( build_keyword_cast( KeywordCastExpr::Coroutine, $5 ) ); } … … 806 807 | '(' VIRTUAL type_no_function ')' cast_expression // CFA 807 808 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression >( $5 ), maybeMoveBuildType( $3 ) ) ); } 809 | '(' RETURN type_no_function ')' cast_expression // CFA 810 { SemanticError( yylloc, "Return cast is currently unimplemented." ); $$ = nullptr; } 811 | '(' COERCE type_no_function ')' cast_expression // CFA 812 { SemanticError( yylloc, "Coerce cast is currently unimplemented." ); $$ = nullptr; } 813 | '(' qualifier_cast_list ')' cast_expression // CFA 814 { SemanticError( yylloc, "Qualifier cast is currently unimplemented." ); $$ = nullptr; } 808 815 // | '(' type_no_function ')' tuple 809 816 // { $$ = new ExpressionNode( build_cast( $2, $4 ) ); } 817 ; 818 819 qualifier_cast_list: 820 cast_modifier type_qualifier_name 821 | cast_modifier MUTEX 822 | qualifier_cast_list cast_modifier type_qualifier_name 823 | qualifier_cast_list cast_modifier MUTEX 824 ; 825 826 cast_modifier: 827 '-' 828 | '+' 810 829 ; 811 830 … … 1145 1164 for_control_expression 1146 1165 | for_control_expression_list ':' for_control_expression 1147 { $$ = $3; } 1166 // ForCtrl + ForCtrl: 1167 // init + init => multiple declaration statements that are hoisted 1168 // condition + condition => (expression) && (expression) 1169 // change + change => (expression), (expression) 1170 { 1171 $1->init->set_last( $3->init ); 1172 if ( $1->condition ) { 1173 if ( $3->condition ) { 1174 $1->condition->expr.reset( new LogicalExpr( $1->condition->expr.release(), $3->condition->expr.release(), true ) ); 1175 } // if 1176 } else $1->condition = $3->condition; 1177 if ( $1->change ) { 1178 if ( $3->change ) { 1179 $1->change->expr.reset( new CommaExpr( $1->change->expr.release(), $3->change->expr.release() ) ); 1180 } // if 1181 } else $1->change = $3->change; 1182 $$ = $1; 1183 } 1148 1184 ; 1149 1185 … … 1155 1191 | declaration comma_expression_opt ';' comma_expression_opt // C99, declaration has ';' 1156 1192 { $$ = new ForCtrl( $1, $2, $4 ); } 1193 1157 1194 | comma_expression // CFA 1158 1195 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ), … … 1169 1206 | comma_expression ';' comma_expression inclexcl comma_expression '~' comma_expression // CFA 1170 1207 { $$ = forCtrl( $3, $1, $3->clone(), $4, $5, $7 ); } 1208 1209 // There is a S/R conflicit if ~ and -~ are factored out. 1210 | comma_expression ';' comma_expression '~' '@' // CFA 1211 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::LThan, nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } 1212 | comma_expression ';' comma_expression ErangeDown '@' // CFA 1213 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::GThan, nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); } 1171 1214 | comma_expression ';' comma_expression '~' '@' '~' comma_expression // CFA 1172 1215 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::LThan, nullptr, $7 ); } -
src/ResolvExpr/AlternativeFinder.cc
rf845e80 r98b4b12 258 258 // - necessary pre-requisite to pruning 259 259 AltList candidates; 260 std::list<std::string> errors; 260 261 for ( unsigned i = 0; i < alternatives.size(); ++i ) { 261 resolveAssertions( alternatives[i], indexer, candidates );262 resolveAssertions( alternatives[i], indexer, candidates, errors ); 262 263 } 263 264 // fail early if none such 264 265 if ( mode.failFast && candidates.empty() ) { 265 266 std::ostringstream stream; 266 stream << "No resolvable alternatives for expression " << expr << "\n" 267 << "Alternatives with failing assertions are:\n"; 268 printAlts( alternatives, stream, 1 ); 267 stream << "No alternatives with satisfiable assertions for " << expr << "\n"; 268 // << "Alternatives with failing assertions are:\n"; 269 // printAlts( alternatives, stream, 1 ); 270 for ( const auto& err : errors ) { 271 stream << err; 272 } 269 273 SemanticError( expr->location, stream.str() ); 270 274 } -
src/ResolvExpr/ResolveAssertions.cc
rf845e80 r98b4b12 20 20 #include <list> // for list 21 21 #include <memory> // for unique_ptr 22 #include <string> 22 #include <sstream> // for ostringstream 23 #include <string> // for string 23 24 #include <unordered_map> // for unordered_map, unordered_multimap 24 25 #include <utility> // for move … … 27 28 #include "Alternative.h" // for Alternative, AssertionItem, AssertionList 28 29 #include "Common/FilterCombos.h" // for filterCombos 30 #include "Common/Indenter.h" // for Indenter 29 31 #include "Common/utility.h" // for sort_mins 30 32 #include "ResolvExpr/RenameVars.h" // for renameTyVars … … 97 99 return { item, item.matches[i] }; 98 100 } 101 102 const DeclarationWithType* get_decl() const { return cache->at(key).deferIds[0].decl; } 99 103 100 104 // sortable by key … … 364 368 static const int recursionLimit = /* 10 */ 4; 365 369 366 void resolveAssertions( Alternative& alt, const SymTab::Indexer& indexer, AltList& out ) {370 void resolveAssertions( Alternative& alt, const SymTab::Indexer& indexer, AltList& out, std::list<std::string>& errors ) { 367 371 // finish early if no assertions to resolve 368 372 if ( alt.need.empty() ) { … … 385 389 for ( auto& assn : resn.need ) { 386 390 // fail early if any assertion is not resolvable 387 if ( ! resolveAssertion( assn, resn, assnCache ) ) goto nextResn; 391 if ( ! resolveAssertion( assn, resn, assnCache ) ) { 392 Indenter tabs{ Indenter::tabsize, 3 }; 393 std::ostringstream ss; 394 ss << tabs << "Unsatisfiable alternative:\n"; 395 resn.alt.print( ss, ++tabs ); 396 ss << --tabs << "Could not satisfy assertion:\n"; 397 assn.decl->print( ss, ++tabs ); 398 399 errors.emplace_back( ss.str() ); 400 goto nextResn; 401 } 388 402 } 389 403 … … 404 418 resn.deferred, 405 419 CandidateEnvMerger{ resn.alt.env, resn.alt.openVars, resn.indexer } ); 420 // fail early if no mutually-compatible assertion satisfaction 421 if ( compatible.empty() ) { 422 Indenter tabs{ Indenter::tabsize, 3 }; 423 std::ostringstream ss; 424 ss << tabs << "Unsatisfiable alternative:\n"; 425 resn.alt.print( ss, ++tabs ); 426 ss << --tabs << "No mutually-compatible satisfaction for assertions:\n"; 427 ++tabs; 428 for ( const auto& d : resn.deferred ) { 429 d.get_decl()->print( ss, tabs ); 430 } 431 432 errors.emplace_back( ss.str() ); 433 goto nextResn; 434 } 406 435 // sort by cost 407 436 CandidateCost coster{ resn.indexer }; -
src/ResolvExpr/ResolveAssertions.h
rf845e80 r98b4b12 24 24 namespace ResolvExpr { 25 25 /// Recursively resolves all assertions provided in an alternative; returns true iff succeeds 26 void resolveAssertions( Alternative& alt, const SymTab::Indexer& indexer, AltList& out );26 void resolveAssertions( Alternative& alt, const SymTab::Indexer& indexer, AltList& out, std::list<std::string>& errors ); 27 27 } // namespace ResolvExpr 28 28 -
src/ResolvExpr/TypeEnvironment.cc
rf845e80 r98b4b12 386 386 } 387 387 388 bool TypeEnvironment::bindVarToVar( TypeInstType *var1, TypeInstType *var2, const TypeDecl::Data & data, AssertionSet &need, AssertionSet &have, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) { 388 bool TypeEnvironment::bindVarToVar( TypeInstType *var1, TypeInstType *var2, 389 TypeDecl::Data && data, AssertionSet &need, AssertionSet &have, 390 const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) { 389 391 390 392 auto class1 = internal_lookup( var1->get_name() ); … … 428 430 class1->set_type( common ); 429 431 } 432 class1->data.isComplete |= data.isComplete; 430 433 env.erase( class2 ); 431 434 } else return false; … … 435 438 class1->vars.insert( class2->vars.begin(), class2->vars.end() ); 436 439 class1->allowWidening = widen1; 440 class1->data.isComplete |= data.isComplete; 437 441 env.erase( class2 ); 438 442 } else { 439 443 class2->vars.insert( class1->vars.begin(), class1->vars.end() ); 440 444 class2->allowWidening = widen2; 445 class2->data.isComplete |= data.isComplete; 441 446 env.erase( class1 ); 442 447 } // if … … 445 450 class1->vars.insert( var2->get_name() ); 446 451 class1->allowWidening = widen1; 452 class1->data.isComplete |= data.isComplete; 447 453 } else if ( class2 != env.end() ) { 448 454 // var1 unbound, add to class2 449 455 class2->vars.insert( var1->get_name() ); 450 456 class2->allowWidening = widen2; 457 class2->data.isComplete |= data.isComplete; 451 458 } else { 452 459 // neither var bound, create new class -
src/ResolvExpr/TypeEnvironment.h
rf845e80 r98b4b12 139 139 /// Binds the type classes represented by `var1` and `var2` together; will add 140 140 /// one or both classes if needed. Returns false on failure. 141 bool bindVarToVar( TypeInstType *var1, TypeInstType *var2, const TypeDecl::Data& data, AssertionSet &need, AssertionSet &have, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer );141 bool bindVarToVar( TypeInstType *var1, TypeInstType *var2, TypeDecl::Data && data, AssertionSet &need, AssertionSet &have, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ); 142 142 143 143 /// Disallows widening for all bindings in the environment -
src/ResolvExpr/Unify.cc
rf845e80 r98b4b12 172 172 bool isopen2 = var2 && ( entry2 != openVars.end() ); 173 173 174 if ( isopen1 && isopen2 && entry1->second == entry2->second ) { 175 result = env.bindVarToVar( var1, var2, entry1->second, needAssertions, haveAssertions, openVars, widenMode, indexer ); 174 if ( isopen1 && isopen2 ) { 175 if ( entry1->second.kind != entry2->second.kind ) { 176 result = false; 177 } else { 178 result = env.bindVarToVar( 179 var1, var2, TypeDecl::Data{ entry1->second, entry2->second }, needAssertions, 180 haveAssertions, openVars, widenMode, indexer ); 181 } 176 182 } else if ( isopen1 ) { 177 183 result = env.bindVar( var1, type2, entry1->second, needAssertions, haveAssertions, openVars, widenMode, indexer ); -
src/SymTab/Indexer.cc
rf845e80 r98b4b12 9 9 // Author : Richard C. Bilson 10 10 // Created On : Sun May 17 21:37:33 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Thu Aug 17 16:08:40 201713 // Update Count : 2 011 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Fri Mar 8 13:55:00 2019 13 // Update Count : 21 14 14 // 15 15 … … 17 17 18 18 #include <cassert> // for assert, strict_dynamic_cast 19 #include <iostream> // for operator<<, basic_ostream, ostream20 19 #include <string> // for string, operator<<, operator!= 20 #include <memory> // for shared_ptr, make_shared 21 21 #include <unordered_map> // for operator!=, unordered_map<>::const... 22 22 #include <unordered_set> // for unordered_set 23 23 #include <utility> // for pair, make_pair, move 24 #include <vector> // for vector 24 25 25 26 #include "CodeGen/OperatorTable.h" // for isCtorDtor, isCtorDtorAssign 26 27 #include "Common/SemanticError.h" // for SemanticError 27 28 #include "Common/utility.h" // for cloneAll 28 #include "Common/Stats/Counter.h" // for counters29 #include "GenPoly/GenPoly.h" 29 #include "Common/Stats/Counter.h" // for counters 30 #include "GenPoly/GenPoly.h" // for getFunctionType 30 31 #include "InitTweak/InitTweak.h" // for isConstructor, isCopyFunction, isC... 31 32 #include "Mangler.h" // for Mangler … … 39 40 #include "SynTree/Type.h" // for Type, StructInstType, UnionInstType 40 41 41 #define debugPrint(x) if ( doDebug ) { std::cerr << x; }42 43 42 namespace SymTab { 44 43 45 44 // Statistics block 46 45 namespace { 47 48 static inline auto stats_idtable() { 49 using namespace Stats::Counters; 50 static auto group = build<CounterGroup>("IdTable"); 51 static struct { 52 SimpleCounter * find; 53 AverageCounter<double> * size; 54 AverageCounter<double> * key; 55 } ret = { 56 .find = build<SimpleCounter>("Find calls", group), 57 .size = build<AverageCounter<double>>("Average Size", group), 58 .key = build<AverageCounter<double>>("Average Key Size", group), 59 }; 60 return ret; 61 } 62 63 static inline auto stats_indexers() { 46 static inline auto stats() { 64 47 using namespace Stats::Counters; 65 48 static auto group = build<CounterGroup>("Indexers"); … … 67 50 SimpleCounter * count; 68 51 AverageCounter<double> * size; 69 AverageCounter<double> * depth_a; 70 MaxCounter<size_t> * depth_m; 52 SimpleCounter * new_scopes; 53 SimpleCounter * lazy_scopes; 54 AverageCounter<double> * avg_scope_depth; 55 MaxCounter<size_t> * max_scope_depth; 56 SimpleCounter * add_calls; 57 SimpleCounter * lookup_calls; 58 SimpleCounter * map_lookups; 59 SimpleCounter * map_mutations; 71 60 } ret = { 72 61 .count = build<SimpleCounter>("Count", group), 73 62 .size = build<AverageCounter<double>>("Average Size", group), 74 .depth_a = build<AverageCounter<double>>("Average Depth", group), 75 .depth_m = build<MaxCounter<size_t>>("Max Depth", group), 63 .new_scopes = build<SimpleCounter>("Scopes", group), 64 .lazy_scopes = build<SimpleCounter>("Lazy Scopes", group), 65 .avg_scope_depth = build<AverageCounter<double>>("Average Scope", group), 66 .max_scope_depth = build<MaxCounter<size_t>>("Max Scope", group), 67 .add_calls = build<SimpleCounter>("Add Calls", group), 68 .lookup_calls = build<SimpleCounter>("Lookup Calls", group), 69 .map_lookups = build<SimpleCounter>("Map Lookups", group), 70 .map_mutations = build<SimpleCounter>("Map Mutations", group) 76 71 }; 77 72 return ret; … … 79 74 } 80 75 81 std::ostream & operator<<( std::ostream & out, const Indexer::IdData & data ) { 82 return out << "(" << data.id << "," << data.baseExpr << ")"; 83 } 84 85 typedef std::unordered_map< std::string, Indexer::IdData > MangleTable; 86 typedef std::unordered_map< std::string, MangleTable > IdTable; 87 typedef std::unordered_map< std::string, NamedTypeDecl* > TypeTable; 88 typedef std::unordered_map< std::string, StructDecl* > StructTable; 89 typedef std::unordered_map< std::string, EnumDecl* > EnumTable; 90 typedef std::unordered_map< std::string, UnionDecl* > UnionTable; 91 typedef std::unordered_map< std::string, TraitDecl* > TraitTable; 92 93 void dump( const IdTable &table, std::ostream &os ) { 94 for ( IdTable::const_iterator id = table.begin(); id != table.end(); ++id ) { 95 for ( MangleTable::const_iterator mangle = id->second.begin(); mangle != id->second.end(); ++mangle ) { 96 os << mangle->second << std::endl; 97 } 98 } 99 } 100 101 template< typename Decl > 102 void dump( const std::unordered_map< std::string, Decl* > &table, std::ostream &os ) { 103 for ( typename std::unordered_map< std::string, Decl* >::const_iterator it = table.begin(); it != table.end(); ++it ) { 104 os << it->second << std::endl; 105 } // for 106 } 107 108 struct Indexer::Impl { 109 Impl( unsigned long _scope ) : refCount(1), scope( _scope ), size( 0 ), base(), 110 idTable(), typeTable(), structTable(), enumTable(), unionTable(), traitTable() {} 111 Impl( unsigned long _scope, Indexer &&_base ) : refCount(1), scope( _scope ), size( 0 ), base( _base ), 112 idTable(), typeTable(), structTable(), enumTable(), unionTable(), traitTable() {} 113 unsigned long refCount; ///< Number of references to these tables 114 unsigned long scope; ///< Scope these tables are associated with 115 unsigned long size; ///< Number of elements stored in this table 116 const Indexer base; ///< Base indexer this extends 117 118 IdTable idTable; ///< Identifier namespace 119 TypeTable typeTable; ///< Type namespace 120 StructTable structTable; ///< Struct namespace 121 EnumTable enumTable; ///< Enum namespace 122 UnionTable unionTable; ///< Union namespace 123 TraitTable traitTable; ///< Trait namespace 124 }; 125 126 Indexer::Impl *Indexer::newRef( Indexer::Impl *toClone ) { 127 if ( ! toClone ) return 0; 128 129 // shorten the search chain by skipping empty links 130 Indexer::Impl *ret = toClone->size == 0 ? toClone->base.tables : toClone; 131 if ( ret ) { ++ret->refCount; } 132 133 return ret; 134 } 135 136 void Indexer::deleteRef( Indexer::Impl *toFree ) { 137 if ( ! toFree ) return; 138 139 if ( --toFree->refCount == 0 ) delete toFree; 140 } 141 142 void Indexer::removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const { 143 // only need to perform this step for constructors, destructors, and assignment functions 144 if ( ! CodeGen::isCtorDtorAssign( id ) ) return; 145 146 // helpful data structure to organize properties for a type 147 struct ValueType { 148 struct DeclBall { // properties for this particular decl 149 IdData decl; 150 bool isUserDefinedFunc; 151 bool isCopyFunc; 152 }; 153 // properties for this type 154 bool existsUserDefinedCopyFunc = false; // user-defined copy ctor found 155 BaseSyntaxNode * deleteStmt = nullptr; // non-null if a user-defined function is found 156 std::list< DeclBall > decls; 157 158 // another FunctionDecl for the current type was found - determine 159 // if it has special properties and update data structure accordingly 160 ValueType & operator+=( IdData data ) { 161 DeclarationWithType * function = data.id; 162 bool isUserDefinedFunc = ! LinkageSpec::isOverridable( function->linkage ); 163 bool isCopyFunc = InitTweak::isCopyFunction( function, function->name ); 164 decls.push_back( DeclBall{ data, isUserDefinedFunc, isCopyFunc } ); 165 existsUserDefinedCopyFunc = existsUserDefinedCopyFunc || (isUserDefinedFunc && isCopyFunc); 166 if ( isUserDefinedFunc && ! deleteStmt ) { 167 // any user-defined function can act as an implicit delete statement for generated constructors. 168 // a delete stmt should not act as an implicit delete statement. 169 deleteStmt = data.id; 170 } 171 return *this; 172 } 173 }; // ValueType 174 175 std::list< IdData > copy; 176 copy.splice( copy.end(), out ); 177 178 // organize discovered declarations by type 179 std::unordered_map< std::string, ValueType > funcMap; 180 for ( auto decl : copy ) { 181 if ( FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl.id ) ) { 182 std::list< DeclarationWithType * > & params = function->type->parameters; 183 assert( ! params.empty() ); 184 // use base type of pointer, so that qualifiers on the pointer type aren't considered. 185 Type * base = InitTweak::getPointerBase( params.front()->get_type() ); 186 assert( base ); 187 funcMap[ Mangler::mangle( base ) ] += decl; 188 } else { 189 out.push_back( decl ); 190 } 191 } 192 193 // if a type contains user defined ctor/dtor/assign, then special rules trigger, which determine 194 // the set of ctor/dtor/assign that can be used by the requester. In particular, if the user defines 195 // a default ctor, then the generated default ctor is unavailable, likewise for copy ctor 196 // and dtor. If the user defines any ctor/dtor, then no generated field ctors are available. 197 // If the user defines any ctor then the generated default ctor is unavailable (intrinsic default 198 // ctor must be overridden exactly). If the user defines anything that looks like a copy constructor, 199 // then the generated copy constructor is unavailable, and likewise for the assignment operator. 200 for ( std::pair< const std::string, ValueType > & pair : funcMap ) { 201 ValueType & val = pair.second; 202 for ( ValueType::DeclBall ball : val.decls ) { 203 bool isNotUserDefinedFunc = ! ball.isUserDefinedFunc && ball.decl.id->linkage != LinkageSpec::Intrinsic; 204 bool isCopyFunc = ball.isCopyFunc; 205 bool existsUserDefinedCopyFunc = val.existsUserDefinedCopyFunc; 206 207 // only implicitly delete non-user defined functions that are not intrinsic, and are 208 // not copy functions (assignment or copy constructor). If a user-defined copy function exists, 209 // do not pass along the non-user-defined copy functions since signatures do not have to match, 210 // and the generated functions will often be cheaper. 211 if ( isNotUserDefinedFunc ) { 212 if ( isCopyFunc ) { 213 // Skip over non-user-defined copy functions when there is a user-defined copy function. 214 // Since their signatures do not have to be exact, deleting them is the wrong choice. 215 if ( existsUserDefinedCopyFunc ) continue; 216 } else { 217 // delete non-user-defined non-copy functions if applicable. 218 // deleteStmt will be non-null only if a user-defined function is found. 219 ball.decl.deleteStmt = val.deleteStmt; 220 } 221 } 222 out.push_back( ball.decl ); 223 } 224 } 225 } 226 227 void Indexer::makeWritable() { 228 if ( ! tables ) { 229 // create indexer if not yet set 230 tables = new Indexer::Impl( scope ); 231 } else if ( tables->refCount > 1 || tables->scope != scope ) { 232 // make this indexer the base of a fresh indexer at the current scope 233 tables = new Indexer::Impl( scope, std::move( *this ) ); 234 } 235 } 236 237 Indexer::Indexer() : tables( 0 ), scope( 0 ) { 238 (*stats_indexers().count)++; 239 } 240 241 Indexer::Indexer( const Indexer &that ) : doDebug( that.doDebug ), tables( newRef( that.tables ) ), scope( that.scope ) { 242 (*stats_indexers().count)++; 243 } 244 245 Indexer::Indexer( Indexer &&that ) : doDebug( that.doDebug ), tables( that.tables ), scope( that.scope ) { 246 that.tables = 0; 247 } 76 Indexer::Indexer() 77 : idTable(), typeTable(), structTable(), enumTable(), unionTable(), traitTable(), 78 prevScope(), scope( 0 ), repScope( 0 ) { ++*stats().count; } 248 79 249 80 Indexer::~Indexer() { 250 if(tables) { 251 stats_indexers().size->push( tables->idTable.size() ); 252 size_t depth = 1; 253 for( auto crnt = tables->base.tables; crnt; crnt = crnt->base.tables ) { 254 ++depth; 255 } 256 stats_indexers().depth_a->push( depth ); 257 stats_indexers().depth_m->push( depth ); 258 } 259 deleteRef( tables ); 260 } 261 262 Indexer& Indexer::operator= ( const Indexer &that ) { 263 deleteRef( tables ); 264 265 tables = newRef( that.tables ); 266 scope = that.scope; 267 doDebug = that.doDebug; 268 269 return *this; 270 } 271 272 Indexer& Indexer::operator= ( Indexer &&that ) { 273 deleteRef( tables ); 274 275 tables = that.tables; 276 scope = that.scope; 277 doDebug = that.doDebug; 278 279 that.tables = 0; 280 281 return *this; 81 stats().size->push( idTable ? idTable->size() : 0 ); 82 } 83 84 void Indexer::lazyInitScope() { 85 if ( repScope < scope ) { 86 ++*stats().lazy_scopes; 87 // create rollback 88 prevScope = std::make_shared<Indexer>( *this ); 89 // update repScope 90 repScope = scope; 91 } 92 } 93 94 void Indexer::enterScope() { 95 ++scope; 96 97 ++*stats().new_scopes; 98 stats().avg_scope_depth->push( scope ); 99 stats().max_scope_depth->push( scope ); 100 } 101 102 void Indexer::leaveScope() { 103 if ( repScope == scope ) { 104 Ptr prev = prevScope; // make sure prevScope stays live 105 *this = std::move(*prevScope); // replace with previous scope 106 } 107 108 --scope; 282 109 } 283 110 284 111 void Indexer::lookupId( const std::string &id, std::list< IdData > &out ) const { 285 std::unordered_set< std::string > foundMangleNames; 286 287 Indexer::Impl *searchTables = tables; 288 while ( searchTables ) { 289 290 (*stats_idtable().find)++; 291 stats_idtable().key->push( id.size() ); 292 stats_idtable().size->push( searchTables->idTable.size() ); 293 IdTable::const_iterator decls = searchTables->idTable.find( id ); 294 if ( decls != searchTables->idTable.end() ) { 295 const MangleTable &mangleTable = decls->second; 296 for ( MangleTable::const_iterator decl = mangleTable.begin(); decl != mangleTable.end(); ++decl ) { 297 // mark the mangled name as found, skipping this insertion if a declaration for that name has already been found 298 if ( foundMangleNames.insert( decl->first ).second == false ) continue; 299 300 out.push_back( decl->second ); 301 } 302 } 303 304 // get declarations from base indexers 305 searchTables = searchTables->base.tables; 306 } 307 308 // some special functions, e.g. constructors and destructors 309 // remove autogenerated functions when they are defined so that 310 // they can never be matched 311 removeSpecialOverrides( id, out ); 112 ++*stats().lookup_calls; 113 if ( ! idTable ) return; 114 115 ++*stats().map_lookups; 116 auto decls = idTable->find( id ); 117 if ( decls == idTable->end() ) return; 118 119 for ( auto decl : *(decls->second) ) { 120 out.push_back( decl.second ); 121 } 312 122 } 313 123 314 124 NamedTypeDecl *Indexer::lookupType( const std::string &id ) const { 315 if ( ! tables ) return 0; 316 317 TypeTable::const_iterator ret = tables->typeTable.find( id ); 318 return ret != tables->typeTable.end() ? ret->second : tables->base.lookupType( id ); 125 ++*stats().lookup_calls; 126 if ( ! typeTable ) return nullptr; 127 ++*stats().map_lookups; 128 auto it = typeTable->find( id ); 129 return it == typeTable->end() ? nullptr : it->second.decl; 319 130 } 320 131 321 132 StructDecl *Indexer::lookupStruct( const std::string &id ) const { 322 if ( ! tables ) return 0; 323 324 StructTable::const_iterator ret = tables->structTable.find( id ); 325 return ret != tables->structTable.end() ? ret->second : tables->base.lookupStruct( id ); 133 ++*stats().lookup_calls; 134 if ( ! structTable ) return nullptr; 135 ++*stats().map_lookups; 136 auto it = structTable->find( id ); 137 return it == structTable->end() ? nullptr : it->second.decl; 138 } 139 140 EnumDecl *Indexer::lookupEnum( const std::string &id ) const { 141 ++*stats().lookup_calls; 142 if ( ! enumTable ) return nullptr; 143 ++*stats().map_lookups; 144 auto it = enumTable->find( id ); 145 return it == enumTable->end() ? nullptr : it->second.decl; 146 } 147 148 UnionDecl *Indexer::lookupUnion( const std::string &id ) const { 149 ++*stats().lookup_calls; 150 if ( ! unionTable ) return nullptr; 151 ++*stats().map_lookups; 152 auto it = unionTable->find( id ); 153 return it == unionTable->end() ? nullptr : it->second.decl; 154 } 155 156 TraitDecl *Indexer::lookupTrait( const std::string &id ) const { 157 ++*stats().lookup_calls; 158 if ( ! traitTable ) return nullptr; 159 ++*stats().map_lookups; 160 auto it = traitTable->find( id ); 161 return it == traitTable->end() ? nullptr : it->second.decl; 162 } 163 164 const Indexer* Indexer::atScope( unsigned long target ) const { 165 // by lazy construction, final indexer in list has repScope 0, cannot be > target 166 // otherwise, will find first scope representing the target 167 const Indexer* indexer = this; 168 while ( indexer->repScope > target ) { 169 indexer = indexer->prevScope.get(); 170 } 171 return indexer; 326 172 } 327 173 328 174 NamedTypeDecl *Indexer::globalLookupType( const std::string &id ) const { 329 return lookupTypeAtScope( id, 0);175 return atScope( 0 )->lookupType( id ); 330 176 } 331 177 332 178 StructDecl *Indexer::globalLookupStruct( const std::string &id ) const { 333 return lookupStructAtScope( id, 0);179 return atScope( 0 )->lookupStruct( id ); 334 180 } 335 181 336 182 UnionDecl *Indexer::globalLookupUnion( const std::string &id ) const { 337 return lookupUnionAtScope( id, 0);183 return atScope( 0 )->lookupUnion( id ); 338 184 } 339 185 340 186 EnumDecl *Indexer::globalLookupEnum( const std::string &id ) const { 341 return lookupEnumAtScope( id, 0 ); 342 } 343 344 EnumDecl *Indexer::lookupEnum( const std::string &id ) const { 345 if ( ! tables ) return 0; 346 347 EnumTable::const_iterator ret = tables->enumTable.find( id ); 348 return ret != tables->enumTable.end() ? ret->second : tables->base.lookupEnum( id ); 349 } 350 351 UnionDecl *Indexer::lookupUnion( const std::string &id ) const { 352 if ( ! tables ) return 0; 353 354 UnionTable::const_iterator ret = tables->unionTable.find( id ); 355 return ret != tables->unionTable.end() ? ret->second : tables->base.lookupUnion( id ); 356 } 357 358 TraitDecl *Indexer::lookupTrait( const std::string &id ) const { 359 if ( ! tables ) return 0; 360 361 TraitTable::const_iterator ret = tables->traitTable.find( id ); 362 return ret != tables->traitTable.end() ? ret->second : tables->base.lookupTrait( id ); 363 } 364 365 const Indexer::IdData * Indexer::lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const { 366 if ( ! tables ) return nullptr; 367 if ( tables->scope < scope ) return nullptr; 368 369 (*stats_idtable().find)++; 370 stats_idtable().key->push( id.size() ); 371 stats_idtable().size->push( tables->idTable.size() ); 372 IdTable::const_iterator decls = tables->idTable.find( id ); 373 if ( decls != tables->idTable.end() ) { 374 const MangleTable &mangleTable = decls->second; 375 MangleTable::const_iterator decl = mangleTable.find( mangleName ); 376 if ( decl != mangleTable.end() ) return &decl->second; 377 } 378 379 return tables->base.lookupIdAtScope( id, mangleName, scope ); 380 } 381 382 Indexer::IdData * Indexer::lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) { 383 return const_cast<IdData *>(const_cast<const Indexer *>(this)->lookupIdAtScope( id, mangleName, scope )); 384 } 385 386 bool Indexer::hasIncompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const { 387 if ( ! tables ) return false; 388 if ( tables->scope < scope ) return false; 389 390 (*stats_idtable().find)++; 391 stats_idtable().key->push( id.size() ); 392 stats_idtable().size->push( tables->idTable.size() ); 393 IdTable::const_iterator decls = tables->idTable.find( id ); 394 if ( decls != tables->idTable.end() ) { 395 const MangleTable &mangleTable = decls->second; 396 for ( MangleTable::const_iterator decl = mangleTable.begin(); decl != mangleTable.end(); ++decl ) { 397 // check for C decls with the same name, skipping those with a compatible type (by mangleName) 398 if ( ! LinkageSpec::isMangled( decl->second.id->get_linkage() ) && decl->first != mangleName ) return true; 399 } 400 } 401 402 return tables->base.hasIncompatibleCDecl( id, mangleName, scope ); 403 } 404 405 bool Indexer::hasCompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const { 406 if ( ! tables ) return false; 407 if ( tables->scope < scope ) return false; 408 409 (*stats_idtable().find)++; 410 stats_idtable().key->push( id.size() ); 411 stats_idtable().size->push( tables->idTable.size() ); 412 IdTable::const_iterator decls = tables->idTable.find( id ); 413 if ( decls != tables->idTable.end() ) { 414 const MangleTable &mangleTable = decls->second; 415 for ( MangleTable::const_iterator decl = mangleTable.begin(); decl != mangleTable.end(); ++decl ) { 416 // check for C decls with the same name, skipping 417 // those with an incompatible type (by mangleName) 418 if ( ! LinkageSpec::isMangled( decl->second.id->get_linkage() ) && decl->first == mangleName ) return true; 419 } 420 } 421 422 return tables->base.hasCompatibleCDecl( id, mangleName, scope ); 423 } 424 425 NamedTypeDecl *Indexer::lookupTypeAtScope( const std::string &id, unsigned long scope ) const { 426 if ( ! tables ) return 0; 427 if ( tables->scope < scope ) return 0; 428 if ( tables->scope > scope ) return tables->base.lookupTypeAtScope( id, scope ); 429 430 TypeTable::const_iterator ret = tables->typeTable.find( id ); 431 return ret != tables->typeTable.end() ? ret->second : tables->base.lookupTypeAtScope( id, scope ); 432 } 433 434 StructDecl *Indexer::lookupStructAtScope( const std::string &id, unsigned long scope ) const { 435 if ( ! tables ) return 0; 436 if ( tables->scope < scope ) return 0; 437 if ( tables->scope > scope ) return tables->base.lookupStructAtScope( id, scope ); 438 439 StructTable::const_iterator ret = tables->structTable.find( id ); 440 return ret != tables->structTable.end() ? ret->second : tables->base.lookupStructAtScope( id, scope ); 441 } 442 443 EnumDecl *Indexer::lookupEnumAtScope( const std::string &id, unsigned long scope ) const { 444 if ( ! tables ) return 0; 445 if ( tables->scope < scope ) return 0; 446 if ( tables->scope > scope ) return tables->base.lookupEnumAtScope( id, scope ); 447 448 EnumTable::const_iterator ret = tables->enumTable.find( id ); 449 return ret != tables->enumTable.end() ? ret->second : tables->base.lookupEnumAtScope( id, scope ); 450 } 451 452 UnionDecl *Indexer::lookupUnionAtScope( const std::string &id, unsigned long scope ) const { 453 if ( ! tables ) return 0; 454 if ( tables->scope < scope ) return 0; 455 if ( tables->scope > scope ) return tables->base.lookupUnionAtScope( id, scope ); 456 457 UnionTable::const_iterator ret = tables->unionTable.find( id ); 458 return ret != tables->unionTable.end() ? ret->second : tables->base.lookupUnionAtScope( id, scope ); 459 } 460 461 TraitDecl *Indexer::lookupTraitAtScope( const std::string &id, unsigned long scope ) const { 462 if ( ! tables ) return 0; 463 if ( tables->scope < scope ) return 0; 464 if ( tables->scope > scope ) return tables->base.lookupTraitAtScope( id, scope ); 465 466 TraitTable::const_iterator ret = tables->traitTable.find( id ); 467 return ret != tables->traitTable.end() ? ret->second : tables->base.lookupTraitAtScope( id, scope ); 187 return atScope( 0 )->lookupEnum( id ); 468 188 } 469 189 … … 487 207 } 488 208 489 bool addedIdConflicts( Indexer::IdData & existing, DeclarationWithType *added, BaseSyntaxNode * deleteStmt, Indexer::ConflictFunction handleConflicts ) { 490 // if we're giving the same name mangling to things of different types then there is something wrong 209 210 bool Indexer::addedIdConflicts( 211 const Indexer::IdData & existing, DeclarationWithType *added, 212 Indexer::OnConflict handleConflicts, BaseSyntaxNode * deleteStmt ) { 213 // if we're giving the same name mangling to things of different types then there is 214 // something wrong 491 215 assert( (isObject( added ) && isObject( existing.id ) ) 492 216 || ( isFunction( added ) && isFunction( existing.id ) ) ); 493 217 494 if ( LinkageSpec::isOverridable( existing.id-> get_linkage()) ) {218 if ( LinkageSpec::isOverridable( existing.id->linkage ) ) { 495 219 // new definition shadows the autogenerated one, even at the same scope 496 220 return false; 497 } else if ( LinkageSpec::isMangled( added->get_linkage() ) || ResolvExpr::typesCompatible( added->get_type(), existing.id->get_type(), Indexer() ) ) { 221 } else if ( LinkageSpec::isMangled( added->linkage ) 222 || ResolvExpr::typesCompatible( 223 added->get_type(), existing.id->get_type(), Indexer() ) ) { 498 224 499 225 // it is a conflict if one declaration is deleted and the other is not 500 226 if ( deleteStmt && ! existing.deleteStmt ) { 501 return handleConflicts( existing, "deletion of defined identifier " ); 227 if ( handleConflicts.mode == OnConflict::Error ) { 228 SemanticError( added, "deletion of defined identifier " ); 229 } 230 return true; 502 231 } else if ( ! deleteStmt && existing.deleteStmt ) { 503 return handleConflicts( existing, "definition of deleted identifier " ); 232 if ( handleConflicts.mode == OnConflict::Error ) { 233 SemanticError( added, "definition of deleted identifier " ); 234 } 235 return true; 504 236 } 505 237 506 238 if ( isDefinition( added ) && isDefinition( existing.id ) ) { 507 if ( isFunction( added ) ) { 508 return handleConflicts( existing, "duplicate function definition for " ); 239 if ( handleConflicts.mode == OnConflict::Error ) { 240 SemanticError( added, 241 isFunction( added ) ? 242 "duplicate function definition for " : 243 "duplicate object definition for " ); 244 } 245 return true; 246 } // if 247 } else { 248 if ( handleConflicts.mode == OnConflict::Error ) { 249 SemanticError( added, "duplicate definition for " ); 250 } 251 return true; 252 } // if 253 254 return true; 255 } 256 257 bool Indexer::hasCompatibleCDecl( const std::string &id, const std::string &mangleName ) const { 258 if ( ! idTable ) return false; 259 260 ++*stats().map_lookups; 261 auto decls = idTable->find( id ); 262 if ( decls == idTable->end() ) return false; 263 264 for ( auto decl : *(decls->second) ) { 265 // skip other scopes (hidden by this decl) 266 if ( decl.second.scope != scope ) continue; 267 // check for C decl with compatible type (by mangleName) 268 if ( ! LinkageSpec::isMangled( decl.second.id->linkage ) && decl.first == mangleName ) { 269 return true; 270 } 271 } 272 273 return false; 274 } 275 276 bool Indexer::hasIncompatibleCDecl( 277 const std::string &id, const std::string &mangleName ) const { 278 if ( ! idTable ) return false; 279 280 ++*stats().map_lookups; 281 auto decls = idTable->find( id ); 282 if ( decls == idTable->end() ) return false; 283 284 for ( auto decl : *(decls->second) ) { 285 // skip other scopes (hidden by this decl) 286 if ( decl.second.scope != scope ) continue; 287 // check for C decl with incompatible type (by manglename) 288 if ( ! LinkageSpec::isMangled( decl.second.id->linkage ) && decl.first != mangleName ) { 289 return true; 290 } 291 } 292 293 return false; 294 } 295 296 /// gets the base type of the first parameter; decl must be a ctor/dtor/assignment function 297 std::string getOtypeKey( FunctionDecl* function ) { 298 auto& params = function->type->parameters; 299 assert( ! params.empty() ); 300 // use base type of pointer, so that qualifiers on the pointer type aren't considered. 301 Type* base = InitTweak::getPointerBase( params.front()->get_type() ); 302 assert( base ); 303 return Mangler::mangle( base ); 304 } 305 306 /// gets the declaration for the function acting on a type specified by otype key, 307 /// nullptr if none such 308 FunctionDecl * getFunctionForOtype( DeclarationWithType * decl, const std::string& otypeKey ) { 309 FunctionDecl * func = dynamic_cast< FunctionDecl * >( decl ); 310 if ( ! func || otypeKey != getOtypeKey( func ) ) return nullptr; 311 return func; 312 } 313 314 bool Indexer::removeSpecialOverrides( 315 Indexer::IdData& data, Indexer::MangleTable::Ptr& mangleTable ) { 316 // if a type contains user defined ctor/dtor/assign, then special rules trigger, which 317 // determinethe set of ctor/dtor/assign that can be used by the requester. In particular, 318 // if the user defines a default ctor, then the generated default ctor is unavailable, 319 // likewise for copy ctor and dtor. If the user defines any ctor/dtor, then no generated 320 // field ctors are available. If the user defines any ctor then the generated default ctor 321 // is unavailable (intrinsic default ctor must be overridden exactly). If the user defines 322 // anything that looks like a copy constructor, then the generated copy constructor is 323 // unavailable, and likewise for the assignment operator. 324 325 // only relevant on function declarations 326 FunctionDecl * function = dynamic_cast< FunctionDecl * >( data.id ); 327 if ( ! function ) return true; 328 // only need to perform this check for constructors, destructors, and assignment functions 329 if ( ! CodeGen::isCtorDtorAssign( data.id->name ) ) return true; 330 331 // set up information for this type 332 bool dataIsUserDefinedFunc = ! LinkageSpec::isOverridable( function->linkage ); 333 bool dataIsCopyFunc = InitTweak::isCopyFunction( function, function->name ); 334 std::string dataOtypeKey = getOtypeKey( function ); 335 336 if ( dataIsUserDefinedFunc && dataIsCopyFunc ) { 337 // this is a user-defined copy function 338 // if this is the first such, delete/remove non-user-defined overloads as needed 339 std::vector< std::string > removed; 340 std::vector< MangleTable::value_type > deleted; 341 bool alreadyUserDefinedFunc = false; 342 343 for ( const auto& entry : *mangleTable ) { 344 // skip decls that aren't functions or are for the wrong type 345 FunctionDecl * decl = getFunctionForOtype( entry.second.id, dataOtypeKey ); 346 if ( ! decl ) continue; 347 348 bool isCopyFunc = InitTweak::isCopyFunction( decl, decl->name ); 349 if ( ! LinkageSpec::isOverridable( decl->linkage ) ) { 350 // matching user-defined function 351 if ( isCopyFunc ) { 352 // mutation already performed, return early 353 return true; 354 } else { 355 // note that non-copy deletions already performed 356 alreadyUserDefinedFunc = true; 357 } 509 358 } else { 510 return handleConflicts( existing, "duplicate object definition for " ); 511 } // if 512 } // if 513 } else { 514 return handleConflicts( existing, "duplicate definition for " ); 515 } // if 516 359 // non-user-defined function; mark for deletion/removal as appropriate 360 if ( isCopyFunc ) { 361 removed.push_back( entry.first ); 362 } else if ( ! alreadyUserDefinedFunc ) { 363 deleted.push_back( entry ); 364 } 365 } 366 } 367 368 // perform removals from mangle table, and deletions if necessary 369 for ( const auto& key : removed ) { 370 ++*stats().map_mutations; 371 mangleTable = mangleTable->erase( key ); 372 } 373 if ( ! alreadyUserDefinedFunc ) for ( const auto& entry : deleted ) { 374 ++*stats().map_mutations; 375 mangleTable = mangleTable->set( entry.first, IdData{ entry.second, function } ); 376 } 377 } else if ( dataIsUserDefinedFunc ) { 378 // this is a user-defined non-copy function 379 // if this is the first user-defined function, delete non-user-defined overloads 380 std::vector< MangleTable::value_type > deleted; 381 382 for ( const auto& entry : *mangleTable ) { 383 // skip decls that aren't functions or are for the wrong type 384 FunctionDecl * decl = getFunctionForOtype( entry.second.id, dataOtypeKey ); 385 if ( ! decl ) continue; 386 387 // exit early if already a matching user-defined function; 388 // earlier function will have mutated table 389 if ( ! LinkageSpec::isOverridable( decl->linkage ) ) return true; 390 391 // skip mutating intrinsic functions 392 if ( decl->linkage == LinkageSpec::Intrinsic ) continue; 393 394 // user-defined non-copy functions do not override copy functions 395 if ( InitTweak::isCopyFunction( decl, decl->name ) ) continue; 396 397 // this function to be deleted after mangleTable iteration is complete 398 deleted.push_back( entry ); 399 } 400 401 // mark deletions to update mangle table 402 // this needs to be a separate loop because of iterator invalidation 403 for ( const auto& entry : deleted ) { 404 ++*stats().map_mutations; 405 mangleTable = mangleTable->set( entry.first, IdData{ entry.second, function } ); 406 } 407 } else if ( function->linkage != LinkageSpec::Intrinsic ) { 408 // this is an overridable generated function 409 // if there already exists a matching user-defined function, delete this appropriately 410 for ( const auto& entry : *mangleTable ) { 411 // skip decls that aren't functions or are for the wrong type 412 FunctionDecl * decl = getFunctionForOtype( entry.second.id, dataOtypeKey ); 413 if ( ! decl ) continue; 414 415 // skip non-user-defined functions 416 if ( LinkageSpec::isOverridable( decl->linkage ) ) continue; 417 418 if ( dataIsCopyFunc ) { 419 // remove current function if exists a user-defined copy function 420 // since the signatures for copy functions don't need to match exactly, using 421 // a delete statement is the wrong approach 422 if ( InitTweak::isCopyFunction( decl, decl->name ) ) return false; 423 } else { 424 // mark current function deleted by first user-defined function found 425 data.deleteStmt = decl; 426 return true; 427 } 428 } 429 } 430 431 // nothing (more) to fix, return true 517 432 return true; 518 433 } 519 434 520 void Indexer::addId( DeclarationWithType *decl, ConflictFunction handleConflicts, Expression * baseExpr, BaseSyntaxNode * deleteStmt ) { 521 if ( decl->name == "" ) return; 522 debugPrint( "Adding Id " << decl->name << std::endl ); 523 makeWritable(); 524 435 void Indexer::addId( 436 DeclarationWithType *decl, OnConflict handleConflicts, Expression * baseExpr, 437 BaseSyntaxNode * deleteStmt ) { 438 ++*stats().add_calls; 525 439 const std::string &name = decl->name; 440 if ( name == "" ) return; 441 526 442 std::string mangleName; 527 443 if ( LinkageSpec::isOverridable( decl->linkage ) ) { 528 // mangle the name without including the appropriate suffix, so overridable routines are placed into the529 // same "bucket" as their user defined versions.444 // mangle the name without including the appropriate suffix, so overridable routines 445 // are placed into the same "bucket" as their user defined versions. 530 446 mangleName = Mangler::mangle( decl, false ); 531 447 } else { … … 533 449 } // if 534 450 535 // this ensures that no two declarations with the same unmangled name at the same scope both have C linkage 536 if ( ! LinkageSpec::isMangled( decl->linkage ) ) { 537 // NOTE this is broken in Richard's original code in such a way that it never triggers (it 538 // doesn't check decls that have the same manglename, and all C-linkage decls are defined to 539 // have their name as their manglename, hence the error can never trigger). 540 // The code here is closer to correct, but name mangling would have to be completely 541 // isomorphic to C type-compatibility, which it may not be. 542 if ( hasIncompatibleCDecl( name, mangleName, scope ) ) { 451 // this ensures that no two declarations with the same unmangled name at the same scope 452 // both have C linkage 453 if ( LinkageSpec::isMangled( decl->linkage ) ) { 454 // Check that a Cforall declaration doesn't override any C declaration 455 if ( hasCompatibleCDecl( name, mangleName ) ) { 456 SemanticError( decl, "Cforall declaration hides C function " ); 457 } 458 } else { 459 // NOTE: only correct if name mangling is completely isomorphic to C 460 // type-compatibility, which it may not be. 461 if ( hasIncompatibleCDecl( name, mangleName ) ) { 543 462 SemanticError( decl, "conflicting overload of C function " ); 544 463 } 545 } else { 546 // Check that a Cforall declaration doesn't override any C declaration 547 if ( hasCompatibleCDecl( name, mangleName, scope ) ) { 548 SemanticError( decl, "Cforall declaration hides C function " ); 549 } 550 } 551 552 // Skip repeat declarations of the same identifier 553 IdData * existing = lookupIdAtScope( name, mangleName, scope ); 554 if ( existing && existing->id && addedIdConflicts( *existing, decl, deleteStmt, handleConflicts ) ) return; 555 556 // add to indexer 557 tables->idTable[ name ][ mangleName ] = IdData{ decl, baseExpr, deleteStmt }; 558 ++tables->size; 464 } 465 466 // ensure tables exist and add identifier 467 MangleTable::Ptr mangleTable; 468 if ( ! idTable ) { 469 idTable = IdTable::new_ptr(); 470 mangleTable = MangleTable::new_ptr(); 471 } else { 472 ++*stats().map_lookups; 473 auto decls = idTable->find( name ); 474 if ( decls == idTable->end() ) { 475 mangleTable = MangleTable::new_ptr(); 476 } else { 477 mangleTable = decls->second; 478 // skip in-scope repeat declarations of same identifier 479 ++*stats().map_lookups; 480 auto existing = mangleTable->find( mangleName ); 481 if ( existing != mangleTable->end() 482 && existing->second.scope == scope 483 && existing->second.id ) { 484 if ( addedIdConflicts( existing->second, decl, handleConflicts, deleteStmt ) ) { 485 if ( handleConflicts.mode == OnConflict::Delete ) { 486 // set delete expression for conflicting identifier 487 lazyInitScope(); 488 *stats().map_mutations += 2; 489 idTable = idTable->set( 490 name, 491 mangleTable->set( 492 mangleName, 493 IdData{ existing->second, handleConflicts.deleteStmt } ) ); 494 } 495 return; 496 } 497 } 498 } 499 } 500 501 // add/overwrite with new identifier 502 lazyInitScope(); 503 IdData data{ decl, baseExpr, deleteStmt, scope }; 504 // Ensure that auto-generated ctor/dtor/assignment are deleted if necessary 505 if ( ! removeSpecialOverrides( data, mangleTable ) ) return; 506 *stats().map_mutations += 2; 507 idTable = idTable->set( name, mangleTable->set( mangleName, std::move(data) ) ); 559 508 } 560 509 561 510 void Indexer::addId( DeclarationWithType * decl, Expression * baseExpr ) { 562 511 // default handling of conflicts is to raise an error 563 addId( decl, [decl](IdData &, const std::string & msg) { SemanticError( decl, msg ); return true; }, baseExpr, decl->isDeleted ? decl : nullptr );512 addId( decl, OnConflict::error(), baseExpr, decl->isDeleted ? decl : nullptr ); 564 513 } 565 514 566 515 void Indexer::addDeletedId( DeclarationWithType * decl, BaseSyntaxNode * deleteStmt ) { 567 516 // default handling of conflicts is to raise an error 568 addId( decl, [decl](IdData &, const std::string & msg) { SemanticError( decl, msg ); return true; }, nullptr, deleteStmt );517 addId( decl, OnConflict::error(), nullptr, deleteStmt ); 569 518 } 570 519 … … 581 530 } 582 531 } 583 // does not need to be added to the table if both existing and added have a base that are the same 532 // does not need to be added to the table if both existing and added have a base that are 533 // the same 584 534 return true; 585 535 } 586 536 587 537 void Indexer::addType( NamedTypeDecl *decl ) { 588 debugPrint( "Adding type " << decl->name << std::endl ); 589 makeWritable(); 590 538 ++*stats().add_calls; 591 539 const std::string &id = decl->name; 592 TypeTable::iterator existing = tables->typeTable.find( id ); 593 if ( existing == tables->typeTable.end() ) { 594 NamedTypeDecl *parent = tables->base.lookupTypeAtScope( id, scope ); 595 if ( ! parent || ! addedTypeConflicts( parent, decl ) ) { 596 tables->typeTable.insert( existing, std::make_pair( id, decl ) ); 597 ++tables->size; 598 } 599 } else { 600 if ( ! addedTypeConflicts( existing->second, decl ) ) { 601 existing->second = decl; 602 } 603 } 540 541 if ( ! typeTable ) { 542 typeTable = TypeTable::new_ptr(); 543 } else { 544 ++*stats().map_lookups; 545 auto existing = typeTable->find( id ); 546 if ( existing != typeTable->end() 547 && existing->second.scope == scope 548 && addedTypeConflicts( existing->second.decl, decl ) ) return; 549 } 550 551 lazyInitScope(); 552 ++*stats().map_mutations; 553 typeTable = typeTable->set( id, Scoped<NamedTypeDecl>{ decl, scope } ); 604 554 } 605 555 … … 614 564 615 565 void Indexer::addStruct( const std::string &id ) { 616 debugPrint( "Adding fwd decl for struct " << id << std::endl );617 566 addStruct( new StructDecl( id ) ); 618 567 } 619 568 620 569 void Indexer::addStruct( StructDecl *decl ) { 621 debugPrint( "Adding struct " << decl->name << std::endl ); 622 makeWritable(); 623 570 ++*stats().add_calls; 624 571 const std::string &id = decl->name; 625 StructTable::iterator existing = tables->structTable.find( id ); 626 if ( existing == tables->structTable.end() ) { 627 StructDecl *parent = tables->base.lookupStructAtScope( id, scope ); 628 if ( ! parent || ! addedDeclConflicts( parent, decl ) ) { 629 tables->structTable.insert( existing, std::make_pair( id, decl ) ); 630 ++tables->size; 631 } 632 } else { 633 if ( ! addedDeclConflicts( existing->second, decl ) ) { 634 existing->second = decl; 635 } 636 } 572 573 if ( ! structTable ) { 574 structTable = StructTable::new_ptr(); 575 } else { 576 ++*stats().map_lookups; 577 auto existing = structTable->find( id ); 578 if ( existing != structTable->end() 579 && existing->second.scope == scope 580 && addedDeclConflicts( existing->second.decl, decl ) ) return; 581 } 582 583 lazyInitScope(); 584 ++*stats().map_mutations; 585 structTable = structTable->set( id, Scoped<StructDecl>{ decl, scope } ); 637 586 } 638 587 639 588 void Indexer::addEnum( EnumDecl *decl ) { 640 debugPrint( "Adding enum " << decl->name << std::endl ); 641 makeWritable(); 642 589 ++*stats().add_calls; 643 590 const std::string &id = decl->name; 644 EnumTable::iterator existing = tables->enumTable.find( id ); 645 if ( existing == tables->enumTable.end() ) { 646 EnumDecl *parent = tables->base.lookupEnumAtScope( id, scope ); 647 if ( ! parent || ! addedDeclConflicts( parent, decl ) ) { 648 tables->enumTable.insert( existing, std::make_pair( id, decl ) ); 649 ++tables->size; 650 } 651 } else { 652 if ( ! addedDeclConflicts( existing->second, decl ) ) { 653 existing->second = decl; 654 } 655 } 591 592 if ( ! enumTable ) { 593 enumTable = EnumTable::new_ptr(); 594 } else { 595 ++*stats().map_lookups; 596 auto existing = enumTable->find( id ); 597 if ( existing != enumTable->end() 598 && existing->second.scope == scope 599 && addedDeclConflicts( existing->second.decl, decl ) ) return; 600 } 601 602 lazyInitScope(); 603 ++*stats().map_mutations; 604 enumTable = enumTable->set( id, Scoped<EnumDecl>{ decl, scope } ); 656 605 } 657 606 658 607 void Indexer::addUnion( const std::string &id ) { 659 debugPrint( "Adding fwd decl for union " << id << std::endl );660 608 addUnion( new UnionDecl( id ) ); 661 609 } 662 610 663 611 void Indexer::addUnion( UnionDecl *decl ) { 664 debugPrint( "Adding union " << decl->name << std::endl ); 665 makeWritable(); 666 612 ++*stats().add_calls; 667 613 const std::string &id = decl->name; 668 UnionTable::iterator existing = tables->unionTable.find( id ); 669 if ( existing == tables->unionTable.end() ) { 670 UnionDecl *parent = tables->base.lookupUnionAtScope( id, scope ); 671 if ( ! parent || ! addedDeclConflicts( parent, decl ) ) { 672 tables->unionTable.insert( existing, std::make_pair( id, decl ) ); 673 ++tables->size; 674 } 675 } else { 676 if ( ! addedDeclConflicts( existing->second, decl ) ) { 677 existing->second = decl; 678 } 679 } 614 615 if ( ! unionTable ) { 616 unionTable = UnionTable::new_ptr(); 617 } else { 618 ++*stats().map_lookups; 619 auto existing = unionTable->find( id ); 620 if ( existing != unionTable->end() 621 && existing->second.scope == scope 622 && addedDeclConflicts( existing->second.decl, decl ) ) return; 623 } 624 625 lazyInitScope(); 626 ++*stats().map_mutations; 627 unionTable = unionTable->set( id, Scoped<UnionDecl>{ decl, scope } ); 680 628 } 681 629 682 630 void Indexer::addTrait( TraitDecl *decl ) { 683 debugPrint( "Adding trait " << decl->name << std::endl ); 684 makeWritable(); 685 631 ++*stats().add_calls; 686 632 const std::string &id = decl->name; 687 TraitTable::iterator existing = tables->traitTable.find( id ); 688 if ( existing == tables->traitTable.end() ) { 689 TraitDecl *parent = tables->base.lookupTraitAtScope( id, scope ); 690 if ( ! parent || ! addedDeclConflicts( parent, decl ) ) { 691 tables->traitTable.insert( existing, std::make_pair( id, decl ) ); 692 ++tables->size; 693 } 694 } else { 695 if ( ! addedDeclConflicts( existing->second, decl ) ) { 696 existing->second = decl; 697 } 698 } 699 } 700 701 void Indexer::addMembers( AggregateDecl * aggr, Expression * expr, ConflictFunction handleConflicts ) { 633 634 if ( ! traitTable ) { 635 traitTable = TraitTable::new_ptr(); 636 } else { 637 ++*stats().map_lookups; 638 auto existing = traitTable->find( id ); 639 if ( existing != traitTable->end() 640 && existing->second.scope == scope 641 && addedDeclConflicts( existing->second.decl, decl ) ) return; 642 } 643 644 lazyInitScope(); 645 ++*stats().map_mutations; 646 traitTable = traitTable->set( id, Scoped<TraitDecl>{ decl, scope } ); 647 } 648 649 void Indexer::addMembers( AggregateDecl * aggr, Expression * expr, 650 OnConflict handleConflicts ) { 702 651 for ( Declaration * decl : aggr->members ) { 703 652 if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) { … … 705 654 if ( dwt->name == "" ) { 706 655 Type * t = dwt->get_type()->stripReferences(); 707 if ( dynamic_cast< StructInstType * >( t ) || dynamic_cast< UnionInstType *>( t ) ) {656 if ( dynamic_cast<StructInstType*>( t ) || dynamic_cast<UnionInstType*>( t ) ) { 708 657 Expression * base = expr->clone(); 709 658 ResolvExpr::Cost cost = ResolvExpr::Cost::zero; // xxx - carry this cost into the indexer as a base cost? … … 722 671 assertf( aggr, "WithStmt expr has non-aggregate type: %s", toString( expr->result ).c_str() ); 723 672 724 addMembers( aggr, expr, [withStmt](IdData & existing, const std::string &) { 725 // on conflict, delete the identifier 726 existing.deleteStmt = withStmt; 727 return true; 728 }); 673 addMembers( aggr, expr, OnConflict::deleteWith( withStmt ) ); 729 674 } 730 675 } … … 748 693 addIds( ftype->returnVals ); 749 694 addIds( ftype->parameters ); 750 }751 752 void Indexer::enterScope() {753 ++scope;754 755 if ( doDebug ) {756 std::cerr << "--- Entering scope " << scope << std::endl;757 }758 }759 760 void Indexer::leaveScope() {761 using std::cerr;762 763 assert( scope > 0 && "cannot leave initial scope" );764 if ( doDebug ) {765 cerr << "--- Leaving scope " << scope << " containing" << std::endl;766 }767 --scope;768 769 while ( tables && tables->scope > scope ) {770 if ( doDebug ) {771 dump( tables->idTable, cerr );772 dump( tables->typeTable, cerr );773 dump( tables->structTable, cerr );774 dump( tables->enumTable, cerr );775 dump( tables->unionTable, cerr );776 dump( tables->traitTable, cerr );777 }778 779 // swap tables for base table until we find one at an appropriate scope780 Indexer::Impl *base = newRef( tables->base.tables );781 deleteRef( tables );782 tables = base;783 }784 }785 786 void Indexer::print( std::ostream &os, int indent ) const {787 using std::cerr;788 789 if ( tables ) {790 os << "--- scope " << tables->scope << " ---" << std::endl;791 792 os << "===idTable===" << std::endl;793 dump( tables->idTable, os );794 os << "===typeTable===" << std::endl;795 dump( tables->typeTable, os );796 os << "===structTable===" << std::endl;797 dump( tables->structTable, os );798 os << "===enumTable===" << std::endl;799 dump( tables->enumTable, os );800 os << "===unionTable===" << std::endl;801 dump( tables->unionTable, os );802 os << "===contextTable===" << std::endl;803 dump( tables->traitTable, os );804 805 tables->base.print( os, indent );806 } else {807 os << "--- end ---" << std::endl;808 }809 810 695 } 811 696 -
src/SymTab/Indexer.h
rf845e80 r98b4b12 9 9 // Author : Richard C. Bilson 10 10 // Created On : Sun May 17 21:38:55 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Thu Aug 17 16:09:12 201713 // Update Count : 811 // Last Modified By : Aaron B. Moss 12 // Last Modified On : Fri Mar 8 13:55:00 2019 13 // Update Count : 9 14 14 // 15 15 16 16 #pragma once 17 17 18 #include < iosfwd> // for ostream19 #include <list> // for list20 #include < string> // for string21 #include < functional> // for function18 #include <functional> // for function 19 #include <list> // for list 20 #include <memory> // for shared_ptr, enable_shared_from_this 21 #include <string> // for string 22 22 23 #include " SynTree/Visitor.h" // for Visitor24 #include "SynTree/SynTree.h" // for AST nodes23 #include "Common/PersistentMap.h" // for PersistentMap 24 #include "SynTree/SynTree.h" // for AST nodes 25 25 26 26 namespace ResolvExpr { 27 class Cost;27 class Cost; 28 28 } 29 29 30 30 namespace SymTab { 31 class Indexer {32 31 class Indexer : public std::enable_shared_from_this<SymTab::Indexer> { 32 public: 33 33 explicit Indexer(); 34 virtual ~Indexer(); 34 35 35 Indexer( const Indexer &that ); 36 Indexer( Indexer &&that ); 37 virtual ~Indexer(); 38 Indexer& operator= ( const Indexer &that ); 39 Indexer& operator= ( Indexer &&that ); 40 41 // when using an indexer manually (e.g., within a mutator traversal), it is necessary to tell the indexer 42 // explicitly when scopes begin and end 36 // when using an indexer manually (e.g., within a mutator traversal), it is necessary to 37 // tell the indexer explicitly when scopes begin and end 43 38 void enterScope(); 44 39 void leaveScope(); … … 50 45 /// non-null if this declaration is deleted 51 46 BaseSyntaxNode * deleteStmt = nullptr; 47 /// scope of identifier 48 unsigned long scope = 0; 52 49 53 50 // NOTE: shouldn't need either of these constructors, but gcc-4 does not properly support initializer lists with default members. 54 51 IdData() = default; 55 IdData( DeclarationWithType * id, Expression * baseExpr, BaseSyntaxNode * deleteStmt ) : id( id ), baseExpr( baseExpr ), deleteStmt( deleteStmt ) {} 52 IdData( 53 DeclarationWithType * id, Expression * baseExpr, BaseSyntaxNode * deleteStmt, 54 unsigned long scope ) 55 : id( id ), baseExpr( baseExpr ), deleteStmt( deleteStmt ), scope( scope ) {} 56 IdData( const IdData& o, BaseSyntaxNode * deleteStmt ) 57 : id( o.id ), baseExpr( o.baseExpr ), deleteStmt( deleteStmt ), scope( o.scope ) {} 56 58 57 59 Expression * combine( ResolvExpr::Cost & cost ) const; … … 80 82 EnumDecl *globalLookupEnum( const std::string &id ) const; 81 83 82 void print( std::ostream &os, int indent = 0 ) const;83 84 /// looks up a specific mangled ID at the given scope85 IdData * lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope );86 const IdData * lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const;87 /// returns true if there exists a declaration with C linkage and the given name with a different mangled name88 bool hasIncompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const;89 /// returns true if there exists a declaration with C linkage and the given name with the same mangled name90 bool hasCompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const;91 // equivalents to lookup functions that only look at tables at scope `scope` (which should be >= tables->scope)92 NamedTypeDecl *lookupTypeAtScope( const std::string &id, unsigned long scope ) const;93 StructDecl *lookupStructAtScope( const std::string &id, unsigned long scope ) const;94 EnumDecl *lookupEnumAtScope( const std::string &id, unsigned long scope ) const;95 UnionDecl *lookupUnionAtScope( const std::string &id, unsigned long scope ) const;96 TraitDecl *lookupTraitAtScope( const std::string &id, unsigned long scope ) const;97 98 typedef std::function<bool(IdData &, const std::string &)> ConflictFunction;99 100 84 void addId( DeclarationWithType * decl, Expression * baseExpr = nullptr ); 101 85 void addDeletedId( DeclarationWithType * decl, BaseSyntaxNode * deleteStmt ); … … 112 96 void addWith( std::list< Expression * > & withExprs, BaseSyntaxNode * withStmt ); 113 97 114 /// adds all of the members of the Aggregate (addWith helper)115 void addMembers( AggregateDecl * aggr, Expression * expr, ConflictFunction );116 117 98 /// convenience function for adding a list of Ids to the indexer 118 99 void addIds( const std::list< DeclarationWithType * > & decls ); … … 124 105 void addFunctionType( FunctionType * ftype ); 125 106 126 bool doDebug = false; ///< Display debugging trace?127 107 private: 128 struct Impl; 108 /// Wraps a Decl* with a scope 109 template<typename Decl> 110 struct Scoped { 111 Decl* decl; ///< declaration 112 unsigned long scope; ///< scope of this declaration 129 113 130 Impl *tables; ///< Copy-on-write instance of table data structure131 unsigned long scope; ///< Scope index of this pointer114 Scoped(Decl* d, unsigned long s) : decl(d), scope(s) {} 115 }; 132 116 133 /// Takes a new ref to a table (returns null if null) 134 static Impl *newRef( Impl *toClone ); 135 /// Clears a ref to a table (does nothing if null) 136 static void deleteRef( Impl *toFree ); 117 using Ptr = std::shared_ptr<const Indexer>; 137 118 138 // Removes matching autogenerated constructors and destructors 139 // so that they will not be selected 140 // void removeSpecialOverrides( FunctionDecl *decl ); 141 void removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const; 119 using MangleTable = PersistentMap< std::string, IdData >; 120 using IdTable = PersistentMap< std::string, MangleTable::Ptr >; 121 using TypeTable = PersistentMap< std::string, Scoped<NamedTypeDecl> >; 122 using StructTable = PersistentMap< std::string, Scoped<StructDecl> >; 123 using EnumTable = PersistentMap< std::string, Scoped<EnumDecl> >; 124 using UnionTable = PersistentMap< std::string, Scoped<UnionDecl> >; 125 using TraitTable = PersistentMap< std::string, Scoped<TraitDecl> >; 142 126 143 /// Ensures that tables variable is writable (i.e. allocated, uniquely owned by this Indexer, and at the current scope) 144 void makeWritable(); 127 IdTable::Ptr idTable; ///< identifier namespace 128 TypeTable::Ptr typeTable; ///< type namespace 129 StructTable::Ptr structTable; ///< struct namespace 130 EnumTable::Ptr enumTable; ///< enum namespace 131 UnionTable::Ptr unionTable; ///< union namespace 132 TraitTable::Ptr traitTable; ///< trait namespace 133 134 Ptr prevScope; ///< reference to indexer for parent scope 135 unsigned long scope; ///< Scope index of this indexer 136 unsigned long repScope; ///< Scope index of currently represented scope 137 138 /// Ensures that a proper backtracking scope exists before a mutation 139 void lazyInitScope(); 140 141 /// Gets the indexer at the given scope 142 const Indexer* atScope( unsigned long scope ) const; 143 144 /// Removes matching autogenerated constructors and destructors so that they will not be 145 /// selected. If returns false, passed decl should not be added. 146 bool removeSpecialOverrides( IdData& decl, MangleTable::Ptr& mangleTable ); 147 148 /// Options for handling identifier conflicts 149 struct OnConflict { 150 enum { 151 Error, ///< Throw a semantic error 152 Delete ///< Delete the earlier version with the delete statement 153 } mode; 154 BaseSyntaxNode * deleteStmt; ///< Statement that deletes this expression 155 156 private: 157 OnConflict() : mode(Error), deleteStmt(nullptr) {} 158 OnConflict( BaseSyntaxNode * d ) : mode(Delete), deleteStmt(d) {} 159 public: 160 OnConflict( const OnConflict& ) = default; 161 162 static OnConflict error() { return {}; } 163 static OnConflict deleteWith( BaseSyntaxNode * d ) { return { d }; } 164 }; 165 166 /// true if the existing identifier conflicts with the added identifier 167 bool addedIdConflicts( 168 const IdData& existing, DeclarationWithType * added, OnConflict handleConflicts, 169 BaseSyntaxNode * deleteStmt ); 145 170 146 171 /// common code for addId, addDeletedId, etc. 147 void addId( DeclarationWithType * decl, ConflictFunction, Expression * baseExpr = nullptr, BaseSyntaxNode * deleteStmt = nullptr ); 172 void addId( 173 DeclarationWithType * decl, OnConflict handleConflicts, 174 Expression * baseExpr = nullptr, BaseSyntaxNode * deleteStmt = nullptr ); 175 176 /// adds all of the members of the Aggregate (addWith helper) 177 void addMembers( AggregateDecl * aggr, Expression * expr, OnConflict handleConflicts ); 178 179 /// returns true if there exists a declaration with C linkage and the given name with the same mangled name 180 bool hasCompatibleCDecl( const std::string &id, const std::string &mangleName ) const; 181 /// returns true if there exists a declaration with C linkage and the given name with a different mangled name 182 bool hasIncompatibleCDecl( const std::string &id, const std::string &mangleName ) const; 148 183 }; 149 184 } // namespace SymTab -
src/SynTree/Attribute.cc
rf845e80 r98b4b12 21 21 #include "Expression.h" // for Expression 22 22 23 Attribute::Attribute( const Attribute &other ) : name( other.name ) {23 Attribute::Attribute( const Attribute &other ) : BaseSyntaxNode( other ), name( other.name ) { 24 24 cloneAll( other.parameters, parameters ); 25 25 } -
src/SynTree/BaseSyntaxNode.h
rf845e80 r98b4b12 18 18 #include "Common/CodeLocation.h" 19 19 #include "Common/Indenter.h" 20 #include "Common/Stats.h" 21 20 22 class Visitor; 21 23 class Mutator; … … 23 25 class BaseSyntaxNode { 24 26 public: 27 static Stats::Counters::SimpleCounter* new_nodes; 28 25 29 CodeLocation location; 30 31 BaseSyntaxNode() { ++*new_nodes; } 32 BaseSyntaxNode( const BaseSyntaxNode& o ) : location(o.location) { ++*new_nodes; } 26 33 27 34 virtual ~BaseSyntaxNode() {} -
src/SynTree/Constant.cc
rf845e80 r98b4b12 25 25 Constant::Constant( Type * type, std::string rep, double val ) : type( type ), rep( rep ), val( val ) {} 26 26 27 Constant::Constant( const Constant &other ) : rep( other.rep ), val( other.val ) {27 Constant::Constant( const Constant &other ) : BaseSyntaxNode( other ), rep( other.rep ), val( other.val ) { 28 28 type = other.type->clone(); 29 29 } -
src/SynTree/Declaration.h
rf845e80 r98b4b12 211 211 TypeDecl::Kind kind; 212 212 bool isComplete; 213 213 214 Data() : kind( (TypeDecl::Kind)-1 ), isComplete( false ) {} 214 215 Data( TypeDecl * typeDecl ) : Data( typeDecl->get_kind(), typeDecl->isComplete() ) {} 215 216 Data( Kind kind, bool isComplete ) : kind( kind ), isComplete( isComplete ) {} 217 Data( const Data& d1, const Data& d2 ) 218 : kind( d1.kind ), isComplete ( d1.isComplete || d2.isComplete ) {} 219 216 220 bool operator==(const Data & other) const { return kind == other.kind && isComplete == other.isComplete; } 217 221 bool operator!=(const Data & other) const { return !(*this == other);} -
src/SynTree/Statement.h
rf845e80 r98b4b12 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T hu Mar 8 14:53:02 201813 // Update Count : 7812 // Last Modified On : Tue Mar 12 09:01:53 2019 13 // Update Count : 83 14 14 // 15 15 … … 19 19 #include <list> // for list 20 20 #include <memory> // for allocator 21 #include <vector> 21 #include <vector> // for vector 22 22 23 23 #include "BaseSyntaxNode.h" // for BaseSyntaxNode … … 43 43 const std::list<Label> & get_labels() const { return labels; } 44 44 45 virtual Statement * clone() const override = 0;46 virtual void accept( Visitor & v ) override = 0;47 virtual Statement * acceptMutator( Mutator &m ) override = 0;48 virtual void print( std::ostream & os, Indenter indent = {} ) const override;45 virtual Statement * clone() const override = 0; 46 virtual void accept( Visitor & v ) override = 0; 47 virtual Statement * acceptMutator( Mutator & m ) override = 0; 48 virtual void print( std::ostream & os, Indenter indent = {} ) const override; 49 49 }; 50 50 … … 55 55 CompoundStmt(); 56 56 CompoundStmt( std::list<Statement *> stmts ); 57 CompoundStmt( const CompoundStmt & other );57 CompoundStmt( const CompoundStmt & other ); 58 58 virtual ~CompoundStmt(); 59 59 … … 62 62 void push_front( Statement * stmt ) { kids.push_front( stmt ); } 63 63 64 virtual CompoundStmt * clone() const override { return new CompoundStmt( *this ); }65 virtual void accept( Visitor & v ) override { v.visit( this ); }66 virtual CompoundStmt * acceptMutator( Mutator &m ) override { return m.mutate( this ); }67 virtual void print( std::ostream & os, Indenter indent = {} ) const override;64 virtual CompoundStmt * clone() const override { return new CompoundStmt( *this ); } 65 virtual void accept( Visitor & v ) override { v.visit( this ); } 66 virtual CompoundStmt * acceptMutator( Mutator & m ) override { return m.mutate( this ); } 67 virtual void print( std::ostream & os, Indenter indent = {} ) const override; 68 68 }; 69 69 … … 72 72 NullStmt( const std::list<Label> & labels = {} ); 73 73 74 virtual NullStmt * clone() const override { return new NullStmt( *this ); }75 virtual void accept( Visitor & v ) override { v.visit( this ); }76 virtual NullStmt * acceptMutator( Mutator &m ) override { return m.mutate( this ); }77 virtual void print( std::ostream & os, Indenter indent = {} ) const override;74 virtual NullStmt * clone() const override { return new NullStmt( *this ); } 75 virtual void accept( Visitor & v ) override { v.visit( this ); } 76 virtual NullStmt * acceptMutator( Mutator & m ) override { return m.mutate( this ); } 77 virtual void print( std::ostream & os, Indenter indent = {} ) const override; 78 78 }; 79 79 80 80 class ExprStmt : public Statement { 81 81 public: 82 Expression * expr;83 84 ExprStmt( Expression * expr );85 ExprStmt( const ExprStmt & other );82 Expression * expr; 83 84 ExprStmt( Expression * expr ); 85 ExprStmt( const ExprStmt & other ); 86 86 virtual ~ExprStmt(); 87 87 88 Expression * get_expr() { return expr; }89 void set_expr( Expression * newValue ) { expr = newValue; }90 91 virtual ExprStmt * clone() const override { return new ExprStmt( *this ); }92 virtual void accept( Visitor & v ) override { v.visit( this ); }93 virtual Statement * acceptMutator( Mutator &m ) override { return m.mutate( this ); }94 virtual void print( std::ostream & os, Indenter indent = {} ) const override;88 Expression * get_expr() { return expr; } 89 void set_expr( Expression * newValue ) { expr = newValue; } 90 91 virtual ExprStmt * clone() const override { return new ExprStmt( *this ); } 92 virtual void accept( Visitor & v ) override { v.visit( this ); } 93 virtual Statement * acceptMutator( Mutator & m ) override { return m.mutate( this ); } 94 virtual void print( std::ostream & os, Indenter indent = {} ) const override; 95 95 }; 96 96 … … 98 98 public: 99 99 bool voltile; 100 Expression * instruction;100 Expression * instruction; 101 101 std::list<Expression *> output, input; 102 102 std::list<ConstantExpr *> clobber; 103 103 std::list<Label> gotolabels; 104 104 105 AsmStmt( bool voltile, Expression * instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels );106 AsmStmt( const AsmStmt & other );105 AsmStmt( bool voltile, Expression * instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels ); 106 AsmStmt( const AsmStmt & other ); 107 107 virtual ~AsmStmt(); 108 108 … … 114 114 void set_output( const std::list<Expression *> & newValue ) { output = newValue; } 115 115 std::list<Expression *> & get_input() { return input; } 116 void set_input( const std::list<Expression *> & newValue ) { input = newValue; }116 void set_input( const std::list<Expression *> & newValue ) { input = newValue; } 117 117 std::list<ConstantExpr *> & get_clobber() { return clobber; } 118 void set_clobber( const std::list<ConstantExpr *> & newValue ) { clobber = newValue; }118 void set_clobber( const std::list<ConstantExpr *> & newValue ) { clobber = newValue; } 119 119 std::list<Label> & get_gotolabels() { return gotolabels; } 120 void set_gotolabels( const std::list<Label> & newValue ) { gotolabels = newValue; }120 void set_gotolabels( const std::list<Label> & newValue ) { gotolabels = newValue; } 121 121 122 122 virtual AsmStmt * clone() const { return new AsmStmt( *this ); } … … 141 141 class IfStmt : public Statement { 142 142 public: 143 Expression * condition;144 Statement * thenPart;145 Statement * elsePart;143 Expression * condition; 144 Statement * thenPart; 145 &n