- Timestamp:
- Feb 19, 2018, 10:44:05 AM (6 years ago)
- Branches:
- ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, resolv-new, with_gc
- Children:
- b060aba
- Parents:
- 1280f95
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/papers/general/Paper.tex
r1280f95 r9fd06ae 158 158 159 159 160 \title{ Generic and Tuple Types with Efficient Dynamic Layout in \protect\CFA}160 \title{\protect\CFA : Adding Modern Programming Language Features to C} 161 161 162 162 \author{Aaron Moss, Robert Schluntz, Peter Buhr} … … 184 184 The C programming language is a foundational technology for modern computing with millions of lines of code implementing everything from commercial operating-systems to hobby projects. 185 185 This installation base and the programmers producing it represent a massive software-engineering investment spanning decades and likely to continue for decades more. 186 Nonetheless, C, first standardized over thirty years ago, lacks many features that make programming in more modern languages safer and more productive. 186 Nevertheless, C, first standardized over thirty years ago, lacks many features that make programming in more modern languages safer and more productive. 187 187 188 The goal of the \CFA project is to create an extension of C that provides modern safety and productivity features while still ensuring strong backwards compatibility with C and its programmers. 188 189 Prior projects have attempted similar goals but failed to honour C programming-style; for instance, adding object-oriented or functional programming with garbage collection is a non-starter for many C developers. 189 Specifically, \CFA is designed to have an orthogonal feature-set based closely on the C programming paradigm, so that \CFA features can be added \emph{incrementally} to existing C code-bases, and C programmers can learn \CFA extensions on an as-needed basis, preserving investment in existing code and engineers. 190 This paper describes two \CFA extensions, generic and tuple types, details how their design avoids shortcomings of similar features in C and other C-like languages, and presents experimental results validating the design. 190 Specifically, \CFA is designed to have an orthogonal feature-set based closely on the C programming paradigm, so that \CFA features can be added \emph{incrementally} to existing C code-bases, and C programmers can learn \CFA extensions on an as-needed basis, preserving investment in existing code and programmers. 191 This paper presents a quick tour of \CFA features showing how their design avoids shortcomings of similar features in C and other C-like languages. 192 Finally, experimental results are presented to validate several of the new features. 191 193 \end{abstract} 192 194 … … 222 224 \CC is used similarly, but has the disadvantages of multiple legacy design-choices that cannot be updated and active divergence of the language model from C, requiring significant effort and training to incrementally add \CC to a C-based project. 223 225 224 \CFA is currently implemented as a source-to-source translator from \CFA to the GCC-dialect of C~\cite{GCCExtensions}, allowing it to leverage the portability and code optimizations provided by GCC, meeting goals (1)--(3).226 \CFA is currently implemented as a source-to-source translator from \CFA to the gcc-dialect of C~\cite{GCCExtensions}, allowing it to leverage the portability and code optimizations provided by gcc, meeting goals (1)--(3). 225 227 Ultimately, a compiler is necessary for advanced features and optimal performance. 226 228 … … 268 270 269 271 The signature feature of \CFA is parametric-polymorphic functions~\cite{forceone:impl,Cormack90,Duggan96} with functions generalized using a @forall@ clause (giving the language its name): 270 \begin{ lstlisting}272 \begin{cfa} 271 273 `forall( otype T )` T identity( T val ) { return val; } 272 274 int forty_two = identity( 42 ); $\C{// T is bound to int, forty\_two == 42}$ 273 \end{ lstlisting}275 \end{cfa} 274 276 The @identity@ function above can be applied to any complete \newterm{object type} (or @otype@). 275 277 The type variable @T@ is transformed into a set of additional implicit parameters encoding sufficient information about @T@ to create and return a variable of that type. … … 283 285 Since bare polymorphic-types provide a restricted set of available operations, \CFA provides a \newterm{type assertion}~\cite[pp.~37-44]{Alphard} mechanism to provide further type information, where type assertions may be variable or function declarations that depend on a polymorphic type-variable. 284 286 For example, the function @twice@ can be defined using the \CFA syntax for operator overloading: 285 \begin{ lstlisting}287 \begin{cfa} 286 288 forall( otype T `| { T ?+?(T, T); }` ) T twice( T x ) { return x + x; } $\C{// ? denotes operands}$ 287 289 int val = twice( twice( 3.7 ) ); 288 \end{ lstlisting}290 \end{cfa} 289 291 which works for any type @T@ with a matching addition operator. 290 292 The polymorphism is achieved by creating a wrapper function for calling @+@ with @T@ bound to @double@, then passing this function to the first call of @twice@. … … 296 298 Like \CC, \CFA inherits a massive compatible library-base, where other programming languages must rewrite or provide fragile inter-language communication with C. 297 299 A simple example is leveraging the existing type-unsafe (@void *@) C @bsearch@ to binary search a sorted float array: 298 \begin{ lstlisting}300 \begin{cfa} 299 301 void * bsearch( const void * key, const void * base, size_t nmemb, size_t size, 300 302 int (* compar)( const void *, const void * )); … … 305 307 double key = 5.0, vals[10] = { /* 10 sorted float values */ }; 306 308 double * val = (double *)bsearch( &key, vals, 10, sizeof(vals[0]), comp ); $\C{// search sorted array}$ 307 \end{ lstlisting}309 \end{cfa} 308 310 which can be augmented simply with a generalized, type-safe, \CFA-overloaded wrappers: 309 \begin{ lstlisting}311 \begin{cfa} 310 312 forall( otype T | { int ?<?( T, T ); } ) T * bsearch( T key, const T * arr, size_t size ) { 311 313 int comp( const void * t1, const void * t2 ) { /* as above with double changed to T */ } … … 318 320 double * val = bsearch( 5.0, vals, 10 ); $\C{// selection based on return type}$ 319 321 int posn = bsearch( 5.0, vals, 10 ); 320 \end{ lstlisting}322 \end{cfa} 321 323 The nested function @comp@ provides the hidden interface from typed \CFA to untyped (@void *@) C, plus the cast of the result. 322 324 Providing a hidden @comp@ function in \CC is awkward as lambdas do not use C calling-conventions and template declarations cannot appear at block scope. … … 326 328 \CFA has replacement libraries condensing hundreds of existing C functions into tens of \CFA overloaded functions, all without rewriting the actual computations (see Section~\ref{sec:libraries}). 327 329 For example, it is possible to write a type-safe \CFA wrapper @malloc@ based on the C @malloc@: 328 \begin{ lstlisting}330 \begin{cfa} 329 331 forall( dtype T | sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); } 330 332 int * ip = malloc(); $\C{// select type and size from left-hand side}$ 331 333 double * dp = malloc(); 332 334 struct S {...} * sp = malloc(); 333 \end{ lstlisting}335 \end{cfa} 334 336 where the return type supplies the type/size of the allocation, which is impossible in most type systems. 335 337 … … 337 339 For example, the \CFA @qsort@ only sorts in ascending order using @<@. 338 340 However, it is trivial to locally change this behaviour: 339 \begin{ lstlisting}341 \begin{cfa} 340 342 forall( otype T | { int ?<?( T, T ); } ) void qsort( const T * arr, size_t size ) { /* use C qsort */ } 341 343 { int ?<?( double x, double y ) { return x `>` y; } $\C{// locally override behaviour}$ 342 344 qsort( vals, size ); $\C{// descending sort}$ 343 345 } 344 \end{ lstlisting}346 \end{cfa} 345 347 Within the block, the nested version of @?<?@ performs @?>?@ and this local version overrides the built-in @?<?@ so it is passed to @qsort@. 346 348 Hence, programmers can easily form local environments, adding and modifying appropriate functions, to maximize reuse of other existing functions and types. 347 349 348 %% Redundant with Section~\ref{sec:libraries} %%349 350 % Finally, \CFA allows variable overloading:351 % \begin{lstlisting}352 % short int MAX = ...; int MAX = ...; double MAX = ...;353 % short int s = MAX; int i = MAX; double d = MAX; $\C{// select correct MAX}$354 % \end{lstlisting}355 % Here, the single name @MAX@ replaces all the C type-specific names: @SHRT_MAX@, @INT_MAX@, @DBL_MAX@.356 350 357 351 \subsection{Traits} 358 352 359 353 \CFA provides \newterm{traits} to name a group of type assertions, where the trait name allows specifying the same set of assertions in multiple locations, preventing repetition mistakes at each function declaration: 360 \begin{ lstlisting}354 \begin{cfa} 361 355 trait `summable`( otype T ) { 362 356 void ?{}( T *, zero_t ); $\C{// constructor from 0 literal}$ … … 370 364 for ( unsigned int i = 0; i < size; i += 1 ) total `+=` a[i]; $\C{// select appropriate +}$ 371 365 return total; } 372 \end{ lstlisting}366 \end{cfa} 373 367 374 368 In fact, the set of @summable@ trait operators is incomplete, as it is missing assignment for type @T@, but @otype@ is syntactic sugar for the following implicit trait: 375 \begin{ lstlisting}369 \begin{cfa} 376 370 trait otype( dtype T | sized(T) ) { // sized is a pseudo-trait for types with known size and alignment 377 371 void ?{}( T * ); $\C{// default constructor}$ … … 379 373 void ?=?( T *, T ); $\C{// assignment operator}$ 380 374 void ^?{}( T * ); }; $\C{// destructor}$ 381 \end{ lstlisting}375 \end{cfa} 382 376 Given the information provided for an @otype@, variables of polymorphic type can be treated as if they were a complete type: stack-allocatable, default or copy-initialized, assigned, and deleted. 383 377 … … 392 386 393 387 % Nominal inheritance can be simulated with traits using marker variables or functions: 394 % \begin{ lstlisting}388 % \begin{cfa} 395 389 % trait nominal(otype T) { 396 390 % T is_nominal; 397 391 % }; 398 392 % int is_nominal; $\C{// int now satisfies the nominal trait}$ 399 % \end{ lstlisting}393 % \end{cfa} 400 394 % 401 395 % Traits, however, are significantly more powerful than nominal-inheritance interfaces; most notably, traits may be used to declare a relationship \emph{among} multiple types, a property that may be difficult or impossible to represent in nominal-inheritance type systems: 402 % \begin{ lstlisting}396 % \begin{cfa} 403 397 % trait pointer_like(otype Ptr, otype El) { 404 398 % lvalue El *?(Ptr); $\C{// Ptr can be dereferenced into a modifiable value of type El}$ … … 411 405 % 412 406 % lvalue int *?( list_iterator it ) { return it->value; } 413 % \end{ lstlisting}407 % \end{cfa} 414 408 % In the example above, @(list_iterator, int)@ satisfies @pointer_like@ by the user-defined dereference function, and @(list_iterator, list)@ also satisfies @pointer_like@ by the built-in dereference operator for pointers. Given a declaration @list_iterator it@, @*it@ can be either an @int@ or a @list@, with the meaning disambiguated by context (\eg @int x = *it;@ interprets @*it@ as an @int@, while @(*it).value = 42;@ interprets @*it@ as a @list@). 415 409 % While a nominal-inheritance system with associated types could model one of those two relationships by making @El@ an associated type of @Ptr@ in the @pointer_like@ implementation, few such systems could model both relationships simultaneously. … … 432 426 433 427 A generic type can be declared by placing a @forall@ specifier on a @struct@ or @union@ declaration, and instantiated using a parenthesized list of types after the type name: 434 \begin{ lstlisting}428 \begin{cfa} 435 429 forall( otype R, otype S ) struct pair { 436 430 R first; … … 447 441 pair( double *, double * ) r = { &d, &d }; 448 442 d = value_p( r ); 449 \end{ lstlisting}443 \end{cfa} 450 444 451 445 \CFA classifies generic types as either \newterm{concrete} or \newterm{dynamic}. … … 456 450 \CFA generic types also allow checked argument-constraints. 457 451 For example, the following declaration of a sorted set-type ensures the set key supports equality and relational comparison: 458 \begin{ lstlisting}452 \begin{cfa} 459 453 forall( otype Key | { _Bool ?==?(Key, Key); _Bool ?<?(Key, Key); } ) struct sorted_set; 460 \end{ lstlisting}454 \end{cfa} 461 455 462 456 … … 467 461 A function declaration that accepts or returns a concrete generic-type produces a declaration for the instantiated structure in the same scope, which all callers may reuse. 468 462 For example, the concrete instantiation for @pair( const char *, int )@ is: 469 \begin{ lstlisting}463 \begin{cfa} 470 464 struct _pair_conc1 { 471 465 const char * first; 472 466 int second; 473 467 }; 474 \end{ lstlisting}468 \end{cfa} 475 469 476 470 A concrete generic-type with dtype-static parameters is also expanded to a structure type, but this type is used for all matching instantiations. 477 471 In the above example, the @pair( F *, T * )@ parameter to @value_p@ is such a type; its expansion is below and it is used as the type of the variables @q@ and @r@ as well, with casts for member access where appropriate: 478 \begin{ lstlisting}472 \begin{cfa} 479 473 struct _pair_conc0 { 480 474 void * first; 481 475 void * second; 482 476 }; 483 \end{ lstlisting}477 \end{cfa} 484 478 485 479 … … 518 512 The reuse of dtype-static structure instantiations enables useful programming patterns at zero runtime cost. 519 513 The most important such pattern is using @forall(dtype T) T *@ as a type-checked replacement for @void *@, \eg creating a lexicographic comparison for pairs of pointers used by @bsearch@ or @qsort@: 520 \begin{ lstlisting}514 \begin{cfa} 521 515 forall(dtype T) int lexcmp( pair( T *, T * ) * a, pair( T *, T * ) * b, int (* cmp)( T *, T * ) ) { 522 516 return cmp( a->first, b->first ) ? : cmp( a->second, b->second ); 523 517 } 524 \end{ lstlisting}518 \end{cfa} 525 519 Since @pair(T *, T * )@ is a concrete type, there are no implicit parameters passed to @lexcmp@, so the generated code is identical to a function written in standard C using @void *@, yet the \CFA version is type-checked to ensure the fields of both pairs and the arguments to the comparison function match in type. 526 520 527 521 Another useful pattern enabled by reused dtype-static type instantiations is zero-cost \newterm{tag-structures}. 528 522 Sometimes information is only used for type-checking and can be omitted at runtime, \eg: 529 \begin{ lstlisting}523 \begin{cfa} 530 524 forall(dtype Unit) struct scalar { unsigned long value; }; 531 525 struct metres {}; … … 540 534 scalar(litres) two_pools = swimming_pool + swimming_pool; 541 535 marathon + swimming_pool; $\C{// compilation ERROR}$ 542 \end{ lstlisting}536 \end{cfa} 543 537 @scalar@ is a dtype-static type, so all uses have a single structure definition, containing @unsigned long@, and can share the same implementations of common functions like @?+?@. 544 538 These implementations may even be separately compiled, unlike \CC template functions. … … 552 546 however, many operations have multiple outcomes, some exceptional. 553 547 Consider C's @div@ and @remquo@ functions, which return the quotient and remainder for a division of integer and float values, respectively. 554 \begin{ lstlisting}548 \begin{cfa} 555 549 typedef struct { int quo, rem; } div_t; $\C{// from include stdlib.h}$ 556 550 div_t div( int num, int den ); … … 559 553 int q; 560 554 double r = remquo( 13.5, 5.2, &q ); $\C{// return remainder, alias quotient}$ 561 \end{ lstlisting}555 \end{cfa} 562 556 @div@ aggregates the quotient/remainder in a structure, while @remquo@ aliases a parameter to an argument. 563 557 Both approaches are awkward. 564 558 Alternatively, a programming language can directly support returning multiple values, \eg in \CFA: 565 \begin{ lstlisting}559 \begin{cfa} 566 560 [ int, int ] div( int num, int den ); $\C{// return two integers}$ 567 561 [ double, double ] div( double num, double den ); $\C{// return two doubles}$ … … 570 564 [ q, r ] = div( 13, 5 ); $\C{// select appropriate div and q, r}$ 571 565 [ q, r ] = div( 13.5, 5.2 ); $\C{// assign into tuple}$ 572 \end{ lstlisting}566 \end{cfa} 573 567 Clearly, this approach is straightforward to understand and use; 574 568 therefore, why do few programming languages support this obvious feature or provide it awkwardly? … … 584 578 585 579 However, functions also use \newterm{composition} (nested calls), with the direct consequence that MRVFs must also support composition to be orthogonal with single-returning-value functions (SRVF), \eg: 586 \begin{ lstlisting}580 \begin{cfa} 587 581 printf( "%d %d\n", div( 13, 5 ) ); $\C{// return values seperated into arguments}$ 588 \end{ lstlisting}582 \end{cfa} 589 583 Here, the values returned by @div@ are composed with the call to @printf@ by flattening the tuple into separate arguments. 590 584 However, the \CFA type-system must support significantly more complex composition: 591 \begin{ lstlisting}585 \begin{cfa} 592 586 [ int, int ] foo$\(_1\)$( int ); $\C{// overloaded foo functions}$ 593 587 [ double ] foo$\(_2\)$( int ); 594 588 void bar( int, double, double ); 595 589 bar( foo( 3 ), foo( 3 ) ); 596 \end{ lstlisting}590 \end{cfa} 597 591 The type-resolver only has the tuple return-types to resolve the call to @bar@ as the @foo@ parameters are identical, which involves unifying the possible @foo@ functions with @bar@'s parameter list. 598 592 No combination of @foo@s are an exact match with @bar@'s parameters, so the resolver applies C conversions. … … 604 598 An important observation from function composition is that new variable names are not required to initialize parameters from an MRVF. 605 599 \CFA also allows declaration of tuple variables that can be initialized from an MRVF, since it can be awkward to declare multiple variables of different types, \eg: 606 \begin{ lstlisting}600 \begin{cfa} 607 601 [ int, int ] qr = div( 13, 5 ); $\C{// tuple-variable declaration and initialization}$ 608 602 [ double, double ] qr = div( 13.5, 5.2 ); 609 \end{ lstlisting}603 \end{cfa} 610 604 where the tuple variable-name serves the same purpose as the parameter name(s). 611 605 Tuple variables can be composed of any types, except for array types, since array sizes are generally unknown in C. 612 606 613 607 One way to access the tuple-variable components is with assignment or composition: 614 \begin{ lstlisting}608 \begin{cfa} 615 609 [ q, r ] = qr; $\C{// access tuple-variable components}$ 616 610 printf( "%d %d\n", qr ); 617 \end{ lstlisting}611 \end{cfa} 618 612 \CFA also supports \newterm{tuple indexing} to access single components of a tuple expression: 619 \begin{ lstlisting}613 \begin{cfa} 620 614 [int, int] * p = &qr; $\C{// tuple pointer}$ 621 615 int rem = qr`.1`; $\C{// access remainder}$ … … 624 618 bar( qr`.1`, qr ); $\C{// pass remainder and quotient/remainder}$ 625 619 rem = [div( 13, 5 ), 42]`.0.1`; $\C{// access 2nd component of 1st component of tuple expression}$ 626 \end{ lstlisting}620 \end{cfa} 627 621 628 622 … … 635 629 %\par\smallskip 636 630 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}} 637 \begin{ lstlisting}631 \begin{cfa} 638 632 int f( int, int ); 639 633 int g( [int, int] ); … … 644 638 g( y, 10 ); $\C{// structure}$ 645 639 h( x, y ); $\C{// flatten and structure}$ 646 \end{ lstlisting}647 %\end{ lstlisting}640 \end{cfa} 641 %\end{cfa} 648 642 %& 649 %\begin{ lstlisting}643 %\begin{cfa} 650 644 %\end{tabular} 651 645 %\smallskip\par\noindent … … 664 658 %\par\smallskip 665 659 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}} 666 \begin{ lstlisting}660 \begin{cfa} 667 661 int x = 10; 668 662 double y = 3.5; … … 672 666 z = 10; $\C{// mass assignment}$ 673 667 [y, x] = 3.14; $\C{// mass assignment}$ 674 \end{ lstlisting}675 %\end{ lstlisting}668 \end{cfa} 669 %\end{cfa} 676 670 %& 677 %\begin{ lstlisting}671 %\begin{cfa} 678 672 %\end{tabular} 679 673 %\smallskip\par\noindent … … 686 680 Finally, tuple assignment is an expression where the result type is the type of the left-hand side of the assignment, just like all other assignment expressions in C. 687 681 This example shows mass, multiple, and cascading assignment used in one expression: 688 \begin{ lstlisting}682 \begin{cfa} 689 683 void f( [int, int] ); 690 684 f( [x, y] = z = 1.5 ); $\C{// assignments in parameter list}$ 691 \end{ lstlisting}685 \end{cfa} 692 686 693 687 … … 696 690 It is also possible to access multiple fields from a single expression using a \newterm{member-access}. 697 691 The result is a single tuple-valued expression whose type is the tuple of the types of the members, \eg: 698 \begin{ lstlisting}692 \begin{cfa} 699 693 struct S { int x; double y; char * z; } s; 700 694 s.[x, y, z] = 0; 701 \end{ lstlisting}695 \end{cfa} 702 696 Here, the mass assignment sets all members of @s@ to zero. 703 697 Since tuple-index expressions are a form of member-access expression, it is possible to use tuple-index expressions in conjunction with member tuple expressions to manually restructure a tuple (\eg rearrange, drop, and duplicate components). … … 705 699 %\par\smallskip 706 700 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}} 707 \begin{ lstlisting}701 \begin{cfa} 708 702 [int, int, long, double] x; 709 703 void f( double, long ); … … 711 705 f( x.[0, 3] ); $\C{// drop: f(x.0, x.3)}$ 712 706 [int, int, int] y = x.[2, 0, 2]; $\C{// duplicate: [y.0, y.1, y.2] = [x.2, x.0.x.2]}$ 713 \end{ lstlisting}714 %\end{ lstlisting}707 \end{cfa} 708 %\end{cfa} 715 709 %& 716 %\begin{ lstlisting}710 %\begin{cfa} 717 711 %\end{tabular} 718 712 %\smallskip\par\noindent 719 713 %\lstMakeShortInline@% 720 714 It is also possible for a member access to contain other member accesses, \eg: 721 \begin{ lstlisting}715 \begin{cfa} 722 716 struct A { double i; int j; }; 723 717 struct B { int * k; short l; }; 724 718 struct C { int x; A y; B z; } v; 725 719 v.[x, y.[i, j], z.k]; $\C{// [v.x, [v.y.i, v.y.j], v.z.k]}$ 726 \end{ lstlisting}720 \end{cfa} 727 721 728 722 … … 733 727 In \CFA, the cast operator has a secondary use as type ascription. 734 728 That is, a cast can be used to select the type of an expression when it is ambiguous, as in the call to an overloaded function: 735 \begin{ lstlisting}729 \begin{cfa} 736 730 int f(); // (1) 737 731 double f(); // (2) … … 739 733 f(); // ambiguous - (1),(2) both equally viable 740 734 (int)f(); // choose (2) 741 \end{ lstlisting}735 \end{cfa} 742 736 743 737 Since casting is a fundamental operation in \CFA, casts should be given a meaningful interpretation in the context of tuples. 744 738 Taking a look at standard C provides some guidance with respect to the way casts should work with tuples: 745 \begin{ lstlisting}739 \begin{cfa} 746 740 int f(); 747 741 void g(); … … 749 743 (void)f(); // (1) 750 744 (int)g(); // (2) 751 \end{ lstlisting}745 \end{cfa} 752 746 In C, (1) is a valid cast, which calls @f@ and discards its result. 753 747 On the other hand, (2) is invalid, because @g@ does not produce a result, so requesting an @int@ to materialize from nothing is nonsensical. … … 759 753 760 754 For example, in 761 \begin{ lstlisting}755 \begin{cfa} 762 756 [int, int, int] f(); 763 757 [int, [int, int], int] g(); … … 768 762 ([int, int, int, int])g(); $\C{// (4)}$ 769 763 ([int, [int, int, int]])g(); $\C{// (5)}$ 770 \end{ lstlisting}764 \end{cfa} 771 765 772 766 (1) discards the last element of the return value and converts the second element to @double@. … … 786 780 Tuples also integrate with \CFA polymorphism as a kind of generic type. 787 781 Due to the implicit flattening and structuring conversions involved in argument passing, @otype@ and @dtype@ parameters are restricted to matching only with non-tuple types, \eg: 788 \begin{ lstlisting}782 \begin{cfa} 789 783 forall(otype T, dtype U) void f( T x, U * y ); 790 784 f( [5, "hello"] ); 791 \end{ lstlisting}785 \end{cfa} 792 786 where @[5, "hello"]@ is flattened, giving argument list @5, "hello"@, and @T@ binds to @int@ and @U@ binds to @const char@. 793 787 Tuples, however, may contain polymorphic components. 794 788 For example, a plus operator can be written to add two triples together. 795 \begin{ lstlisting}789 \begin{cfa} 796 790 forall(otype T | { T ?+?( T, T ); }) [T, T, T] ?+?( [T, T, T] x, [T, T, T] y ) { 797 791 return [x.0 + y.0, x.1 + y.1, x.2 + y.2]; … … 800 794 int i1, i2, i3; 801 795 [i1, i2, i3] = x + ([10, 20, 30]); 802 \end{ lstlisting}796 \end{cfa} 803 797 804 798 Flattening and restructuring conversions are also applied to tuple types in polymorphic type assertions. 805 \begin{ lstlisting}799 \begin{cfa} 806 800 int f( [int, double], double ); 807 801 forall(otype T, otype U | { T f( T, U, U ); }) void g( T, U ); 808 802 g( 5, 10.21 ); 809 \end{ lstlisting}803 \end{cfa} 810 804 Hence, function parameter and return lists are flattened for the purposes of type unification allowing the example to pass expression resolution. 811 805 This relaxation is possible by extending the thunk scheme described by Bilson~\cite{Bilson03}. 812 806 Whenever a candidate's parameter structure does not exactly match the formal parameter's structure, a thunk is generated to specialize calls to the actual function: 813 \begin{ lstlisting}807 \begin{cfa} 814 808 int _thunk( int _p0, double _p1, double _p2 ) { return f( [_p0, _p1], _p2 ); } 815 \end{ lstlisting}809 \end{cfa} 816 810 so the thunk provides flattening and structuring conversions to inferred functions, improving the compatibility of tuples and polymorphism. 817 These thunks take advantage of GCCC nested-functions to produce closures that have the usual function-pointer signature.811 These thunks take advantage of gcc C nested-functions to produce closures that have the usual function-pointer signature. 818 812 819 813 … … 830 824 Unlike variadic templates, @ttype@ polymorphic functions can be separately compiled. 831 825 For example, a generalized @sum@ function written using @ttype@: 832 \begin{ lstlisting}826 \begin{cfa} 833 827 int sum$\(_0\)$() { return 0; } 834 828 forall(ttype Params | { int sum( Params ); } ) int sum$\(_1\)$( int x, Params rest ) { … … 836 830 } 837 831 sum( 10, 20, 30 ); 838 \end{ lstlisting}832 \end{cfa} 839 833 Since @sum@\(_0\) does not accept any arguments, it is not a valid candidate function for the call @sum(10, 20, 30)@. 840 834 In order to call @sum@\(_1\), @10@ is matched with @x@, and the argument resolution moves on to the argument pack @rest@, which consumes the remainder of the argument list and @Params@ is bound to @[20, 30]@. … … 843 837 844 838 It is reasonable to take the @sum@ function a step further to enforce a minimum number of arguments: 845 \begin{ lstlisting}839 \begin{cfa} 846 840 int sum( int x, int y ) { return x + y; } 847 841 forall(ttype Params | { int sum( int, Params ); } ) int sum( int x, int y, Params rest ) { 848 842 return sum( x + y, rest ); 849 843 } 850 \end{ lstlisting}844 \end{cfa} 851 845 One more step permits the summation of any summable type with all arguments of the same type: 852 \begin{ lstlisting}846 \begin{cfa} 853 847 trait summable(otype T) { 854 848 T ?+?( T, T ); … … 860 854 return sum( x + y, rest ); 861 855 } 862 \end{ lstlisting}856 \end{cfa} 863 857 Unlike C variadic functions, it is unnecessary to hard code the number and expected types. 864 858 Furthermore, this code is extendable for any user-defined type with a @?+?@ operator. … … 866 860 867 861 It is also possible to write a type-safe variadic print function to replace @printf@: 868 \begin{ lstlisting}862 \begin{cfa} 869 863 struct S { int x, y; }; 870 864 forall(otype T, ttype Params | { void print(T); void print(Params); }) void print(T arg, Params rest) { … … 875 869 void print( S s ) { print( "{ ", s.x, ",", s.y, " }" ); } 876 870 print( "s = ", (S){ 1, 2 }, "\n" ); 877 \end{ lstlisting}871 \end{cfa} 878 872 This example showcases a variadic-template-like decomposition of the provided argument list. 879 873 The individual @print@ functions allow printing a single element of a type. … … 883 877 Finally, it is possible to use @ttype@ polymorphism to provide arbitrary argument forwarding functions. 884 878 For example, it is possible to write @new@ as a library function: 885 \begin{ lstlisting}879 \begin{cfa} 886 880 forall( otype R, otype S ) void ?{}( pair(R, S) *, R, S ); 887 881 forall( dtype T, ttype Params | sized(T) | { void ?{}( T *, Params ); } ) T * new( Params p ) { … … 889 883 } 890 884 pair( int, char ) * x = new( 42, '!' ); 891 \end{ lstlisting}885 \end{cfa} 892 886 The @new@ function provides the combination of type-safe @malloc@ with a \CFA constructor call, making it impossible to forget constructing dynamically allocated objects. 893 887 This function provides the type-safety of @new@ in \CC, without the need to specify the allocated type again, thanks to return-type inference. … … 898 892 Tuples are implemented in the \CFA translator via a transformation into \newterm{generic types}. 899 893 For each $N$, the first time an $N$-tuple is seen in a scope a generic type with $N$ type parameters is generated, \eg: 900 \begin{ lstlisting}894 \begin{cfa} 901 895 [int, int] f() { 902 896 [double, double] x; 903 897 [int, double, int] y; 904 898 } 905 \end{ lstlisting}899 \end{cfa} 906 900 is transformed into: 907 \begin{ lstlisting}901 \begin{cfa} 908 902 forall(dtype T0, dtype T1 | sized(T0) | sized(T1)) struct _tuple2 { 909 903 T0 field_0; $\C{// generated before the first 2-tuple}$ … … 919 913 _tuple3(int, double, int) y; 920 914 } 921 \end{ lstlisting}915 \end{cfa} 922 916 \begin{sloppypar} 923 917 Tuple expressions are then simply converted directly into compound literals, \eg @[5, 'x', 1.24]@ becomes @(_tuple3(int, char, double)){ 5, 'x', 1.24 }@. … … 926 920 \begin{comment} 927 921 Since tuples are essentially structures, tuple indexing expressions are just field accesses: 928 \begin{ lstlisting}922 \begin{cfa} 929 923 void f(int, [double, char]); 930 924 [int, double] x; … … 933 927 printf("%d %g\n", x); 934 928 f(x, 'z'); 935 \end{ lstlisting}929 \end{cfa} 936 930 Is transformed into: 937 \begin{ lstlisting}931 \begin{cfa} 938 932 void f(int, _tuple2(double, char)); 939 933 _tuple2(int, double) x; … … 942 936 printf("%d %g\n", x.field_0, x.field_1); 943 937 f(x.field_0, (_tuple2){ x.field_1, 'z' }); 944 \end{ lstlisting}938 \end{cfa} 945 939 Note that due to flattening, @x@ used in the argument position is converted into the list of its fields. 946 940 In the call to @f@, the second and third argument components are structured into a tuple argument. … … 949 943 Expressions that may contain side effects are made into \newterm{unique expressions} before being expanded by the flattening conversion. 950 944 Each unique expression is assigned an identifier and is guaranteed to be executed exactly once: 951 \begin{ lstlisting}945 \begin{cfa} 952 946 void g(int, double); 953 947 [int, double] h(); 954 948 g(h()); 955 \end{ lstlisting}949 \end{cfa} 956 950 Internally, this expression is converted to two variables and an expression: 957 \begin{ lstlisting}951 \begin{cfa} 958 952 void g(int, double); 959 953 [int, double] h(); … … 965 959 (_unq0_finished_ ? _unq0 : (_unq0 = f(), _unq0_finished_ = 1, _unq0)).1, 966 960 ); 967 \end{ lstlisting}961 \end{cfa} 968 962 Since argument evaluation order is not specified by the C programming language, this scheme is built to work regardless of evaluation order. 969 963 The first time a unique expression is executed, the actual expression is evaluated and the accompanying boolean is set to true. … … 1359 1353 1360 1354 1361 % \subsection{Exception Handling ???} 1355 \subsection{Exception Handling} 1356 1357 \CFA provides two forms of exception handling: \newterm{resumption} (fix-up) and \newterm{recovery}. 1358 Both mechanisms provide dynamic call to a handler using dynamic name-lookup, where fix-up has dynamic return and recovery has static return from the handler. 1359 \begin{cquote} 1360 \lstDeleteShortInline@% 1361 \begin{tabular}{@{}l@{\hspace{\parindentlnth}}l@{}} 1362 \multicolumn{1}{c@{\hspace{\parindentlnth}}}{\textbf{Resumption}} & \multicolumn{1}{c}{\textbf{Recovery}} \\ 1363 \begin{cfa} 1364 _Exception E { int fix; }; 1365 void f() { 1366 ... _Resume E; 1367 // control returns here after handler 1368 try { 1369 f(); 1370 } catchResume( E e ) { 1371 ... e.fix = ...; // return correction to raise 1372 } // dynamic return to _Resume 1373 \end{cfa} 1374 & 1375 \begin{cfa} 1376 _Exception E {}; 1377 void f() { 1378 ... _Throw E; 1379 // control does NOT return here after handler 1380 try { 1381 f(); 1382 } catch( E e ) { 1383 ... // recover and continue 1384 } // static return to next statement 1385 \end{cfa} 1386 \end{tabular} 1387 \lstMakeShortInline@% 1388 \end{cquote} 1362 1389 1363 1390 … … 1367 1394 An important part of this subjective feel is maintaining C's procedural paradigm, as opposed to the object-oriented paradigm of other systems languages such as \CC and Rust. 1368 1395 Maintaining this procedural paradigm means that C coding-patterns remain not only functional but idiomatic in \CFA, reducing the mental burden of retraining C programmers and switching between C and \CFA development. 1369 Nonetheless, some features of object-oriented languages are undeniably conv ienient but are independent of object-oriented programming;1396 Nonetheless, some features of object-oriented languages are undeniably convenient but are independent of object-oriented programming; 1370 1397 \CFA adapts these features to a procedural paradigm. 1371 1398 … … 1535 1562 as well, parameter names are optional, \eg: 1536 1563 \begin{cfa} 1537 [ int x ] f ( );$\C{// returning int with no parameters}$1564 [ int x ] f ( /* void */ ); $\C{// returning int with no parameters}$ 1538 1565 [ int x ] f (...); $\C{// returning int with unknown parameters}$ 1539 1566 [ * int ] g ( int y ); $\C{// returning pointer to int with int parameter}$ 1540 [ ] h ( int, char );$\C{// returning no result with int and char parameters}$1567 [ void ] h ( int, char ); $\C{// returning no result with int and char parameters}$ 1541 1568 [ * int, int ] j ( int ); $\C{// returning pointer to int and int, with int parameter}$ 1542 1569 \end{cfa} … … 1693 1720 \subsection{Constructors and Destructors} 1694 1721 1695 One of the strengths (and weaknesses) of C is control over memory management, allowing resource release to be more consistent and precisely timed than possible with garbage-collected memory-management.1696 However, this manual approach is often verbose, furthermoreit is useful to manage resources other than memory (\eg file handles) using the same mechanism as memory.1722 One of the strengths (and weaknesses) of C is memory-management control, allowing resource release to be precisely specified versus unknown release with garbage-collected memory-management. 1723 However, this manual approach is often verbose, and it is useful to manage resources other than memory (\eg file handles) using the same mechanism as memory. 1697 1724 \CC addresses these issues using Resource Aquisition Is Initialization (RAII), implemented by means of \newterm{constructor} and \newterm{destructor} functions; 1698 1725 \CFA adopts constructors and destructors (and @finally@) to facilitate RAII. 1699 While constructors and destructors are a common feature of object-oriented programming-languages, they are independent capabilities allowing \CFA to retaina procedural paradigm.1726 While constructors and destructors are a common feature of object-oriented programming-languages, they are an independent capability allowing \CFA to adopt them while retaining a procedural paradigm. 1700 1727 Specifically, \CFA constructors and destructors are denotes by name and first parameter-type versus name and nesting in an aggregate type. 1701 1702 In \CFA, a constructor is named @?{}@ and a destructor is named @^?{}@; 1703 like other \CFA operators, these names represent the syntax used to call the constructor or destructor, \eg @x{ ... };@ or @^x{...};@.1728 Constructor calls seamlessly integrate with existing C initialization syntax, providing a simple and familiar syntax to C programmers and allowing constructor calls to be inserted into legacy C code with minimal code changes. 1729 1730 In \CFA, a constructor is named @?{}@ and a destructor is named @^?{}@. 1704 1731 The name @{}@ comes from the syntax for the initializer: @struct S { int i, j; } s = `{` 2, 3 `}`@. 1732 The symbol \lstinline+^+ is used because it was the last remaining binary operator that could be used in a unary context. 1733 Like other \CFA operators, these names represent the syntax used to call the constructor or destructor, \eg @?{}(x, ...)@ or @^{}(x, ...)@. 1705 1734 The constructor and destructor have return type @void@ and a first parameter of reference to the object type to be constructed or destructs. 1706 1735 While the first parameter is informally called the @this@ parameter, as in object-oriented languages, any variable name may be used. 1707 Both constructors and destructors allow additional parametes after the @this@ parameter for specifying values for initialization/de-initialization\footnote{Destruction parameters are useful for specifying storage-management actions, such as de-initialize but not de-allocate.}. 1736 Both constructors and destructors allow additional parametes after the @this@ parameter for specifying values for initialization/de-initialization\footnote{ 1737 Destruction parameters are useful for specifying storage-management actions, such as de-initialize but not deallocate.}. 1708 1738 \begin{cfa} 1709 1739 struct VLA { 1710 1740 int len, * data; 1711 1741 }; 1712 void ?{}( VLA& vla ) with ( vla ) { $\C{// default constructor}$ 1713 len = 10; data = calloc( len ); 1714 } 1715 void ^?{}( VLA& vla ) { $\C{// destructor}$ 1716 free( vla.data ); 1717 } 1718 { VLA x; `?{}(x);` $\C{// compiler generated constructor call}$ 1719 // ... use x 1720 `^?{}(x);` } $\C{// compiler generated desturctor call}$ 1721 \end{cfa} 1722 @VLA@ is an example of a \newterm{managed type}\footnote{A managed type affects the runtime environment versus being self-contained.} in \CFA: a type requiring a non-trivial constructor or destructor, or with a field of a managed type. 1723 A managed types is implicitly constructed upon allocation, and destructed upon deallocation to ensure proper interaction of runtime resources, in this case the @data@ array in the heap. 1724 The exact details of the placement of these implicit constructor and destructor calls are omitted here for brevity, the interested reader should consult \cite{Schluntz17}. 1725 1726 Constructor calls seamlessly integrate with existing C initialization syntax, providing a simple and familiar syntax to C programmers and allowing constructor calls to be inserted into legacy C code with minimal code changes. 1727 As such, \CFA also provides syntax for \newterm{initialization} and \newterm{copy}: 1728 \begin{cfa} 1729 void ?{}( VLA & vla, int size, int fill ); $\C{// initialization}$ 1730 void ?{}( VLA & vla, VLA other ); $\C{// copy}$ 1731 VLA y = { 20, 0xdeadbeef }, // initialization 1732 z = y; // copy 1733 \end{cfa} 1734 1735 Copy constructors have exactly two parameters, the second of which has the same type as the base type of the @this@ parameter; appropriate care is taken in the implementation to avoid recursive calls to the copy constructor when initializing this second parameter. 1736 Other constructor calls look just like C initializers, except rather than using field-by-field initialization (as in C), an initialization which matches a defined constructor will call the constructor instead. 1737 1738 In addition to initialization syntax, \CFA provides two ways to explicitly call constructors and destructors. 1739 Explicit calls to constructors double as a placement syntax, useful for construction of member fields in user-defined constructors and reuse of large storage allocations. 1740 While the existing function-call syntax works for explicit calls to constructors and destructors, \CFA also provides a more concise \newterm{operator syntax} for both: 1741 1742 \begin{cfa} 1743 VLA a, b; 1744 a{}; $\C{// default construct}$ 1745 b{ a }; $\C{// copy construct}$ 1746 ^a{}; $\C{// destruct}$ 1747 a{ 5, 0xFFFFFFFF }; $\C{// explicit constructor call}$ 1742 void ?{}( VLA & vla ) with ( vla ) { $\C{// default constructor}$ 1743 len = 10; data = alloc( len ); 1744 } 1745 void ^?{}( VLA & vla ) with ( vla ) { $\C{// destructor}$ 1746 free( data ); 1747 } 1748 { 1749 VLA x; $\C{// implicit: ?\{\}( x );}$ 1750 } $\C{// implicit: ?\^{}\{\}( x );}$ 1751 \end{cfa} 1752 @VLA@ is a \newterm{managed type}\footnote{ 1753 A managed type affects the runtime environment versus a self-contained type.}: a type requiring a non-trivial constructor or destructor, or with a field of a managed type. 1754 A managed types is implicitly constructed upon allocation and destructed upon deallocation to ensure proper interaction with runtime resources, in this case the @data@ array in the heap. 1755 For details of the placement of implicit constructor and destructor calls among complex executable statements see~\cite[\S~2.2]{Schluntz17}. 1756 1757 \CFA also provides syntax for \newterm{initialization} and \newterm{copy}: 1758 \begin{cfa} 1759 void ?{}( VLA & vla, int size, char fill ) with ( vla ) { $\C{// initialization}$ 1760 len = size; data = alloc( len, fill ); 1761 } 1762 void ?{}( VLA & vla, VLA other ) { $\C{// copy}$ 1763 vla.len = other.len; vla.data = other.data; 1764 } 1765 \end{cfa} 1766 An initialization constructor-call has the same syntax as a C initializer, except the initialization values are passed as arguments to a matching constructor (number and type of paremeters). 1767 \begin{cfa} 1768 VLA va = `{` 20, 0 `}`, * arr = alloc()`{` 5, 0 `}`; 1769 \end{cfa} 1770 Note, the use of a \newterm{constructor expression} to initialize the storage from the dynamic storage-allocation. 1771 Like \CC, the copy constructor has two parameters, the second of which is a value parameter with the same type as the first parameter; 1772 appropriate care is taken to not recursively call the copy constructor when initializing the second parameter. 1773 1774 \CFA constructors may be explicitly call, like Java, and destructors may be explicitly called, like \CC. 1775 Explicit calls to constructors double as a \CC-style \emph{placement syntax}, useful for construction of member fields in user-defined constructors and reuse of existing storage allocations. 1776 While existing call syntax works for explicit calls to constructors and destructors, \CFA also provides a more concise \newterm{operator syntax} for both: 1777 \begin{cfa} 1778 { 1779 VLA x, y = { 20, 0x01 }, z = y; 1780 // implicit: ?{}( x ); ?{}( y, 20, 0x01 ); ?{}( z, y ); z points to y 1781 ^x{}; $\C{// deallocate x}$ 1782 x{}; $\C{// reallocate x}$ 1783 z{ 5, 0xff }; $\C{// reallocate z, not pointing to y}$ 1784 ^y{}; $\C{// deallocate y}$ 1785 y{ x }; $\C{// reallocate y, points to x}$ 1786 x{}; $\C{// reallocate x, not pointing to y}$ 1787 // implicit: ^?{}(z); ^?{}(y); ^?{}(x); 1788 } 1748 1789 \end{cfa} 1749 1790 1750 1791 To provide a uniform type interface for @otype@ polymorphism, the \CFA compiler automatically generates a default constructor, copy constructor, assignment operator, and destructor for all types. 1751 These default functions can be overridden by user-generated versions of them. 1752 For compatibility with the standard behaviour of C, the default constructor and destructor for all basic, pointer, and reference types do nothing, while the copy constructor and assignment operator are bitwise copies; if default zero-initialization is desired, the default constructors can be overridden. 1792 These default functions can be overridden by user-generated versions. 1793 For compatibility with the standard behaviour of C, the default constructor and destructor for all basic, pointer, and reference types do nothing, while the copy constructor and assignment operator are bitwise copies; 1794 if default zero-initialization is desired, the default constructors can be overridden. 1753 1795 For user-generated types, the four functions are also automatically generated. 1754 1796 @enum@ types are handled the same as their underlying integral type, and unions are also bitwise copied and no-op initialized and destructed. … … 1756 1798 For @struct@ types, each of the four functions are implicitly defined to call their corresponding functions on each member of the struct. 1757 1799 To better simulate the behaviour of C initializers, a set of \newterm{field constructors} is also generated for structures. 1758 A constructor is generated for each non-empty prefix of a structure's member-list which copy-constructs the members passed as parameters and default-constructs the remaining members. 1759 To allow users to limit the set of constructors available for a type, when a user declares any constructor or destructor, the corresponding generated function and all field constructors for that type are hidden from expression resolution; similarly, the generated default constructor is hidden upon declaration of any constructor. 1800 A constructor is generated for each non-empty prefix of a structure's member-list to copy-construct the members passed as parameters and default-construct the remaining members. 1801 To allow users to limit the set of constructors available for a type, when a user declares any constructor or destructor, the corresponding generated function and all field constructors for that type are hidden from expression resolution; 1802 similarly, the generated default constructor is hidden upon declaration of any constructor. 1760 1803 These semantics closely mirror the rule for implicit declaration of constructors in \CC\cite[p.~186]{ANSI98:C++}. 1761 1804 1762 In rare situations user programmers may not wish to have constructors and destructors called; in these cases, \CFA provides an ``escape hatch'' to not call them.1763 I f a variable is initialized using the syntax \lstinline|S x @= {}| it will be an \newterm{unmanaged object}, and will not have constructors or destructors called.1764 Any C initializer can be the right-hand side of an \lstinline|@=| initializer, \eg 1765 In addition to the expressive power, \lstinline|@=| provides a simple path for migrating legacy C code to \CFA, by providing a mechanism to incrementally convert initializers; the \CFA design team decided to introduce a new syntax for this escape hatch because we believe that our RAII implementation will handle the vast majority of code in a desirable way, and we wished to maintain familiar syntax for this common case.1805 In some circumstance programmers may not wish to have constructor and destructor calls. 1806 In these cases, \CFA provides the initialization syntax \lstinline|S x @= {}|, and the object becomes unmanaged, so constructors and destructors calls are not generated. 1807 Any C initializer can be the right-hand side of an \lstinline|@=| initializer, \eg \lstinline|VLA a @= { 0, 0x0 }|, with the usual C initialization semantics. 1808 The point of \lstinline|@=| is to provide a migration path from legacy C code to \CFA, by providing a mechanism to incrementally convert to implicit initialization. 1766 1809 1767 1810 … … 1840 1883 1841 1884 1842 \subsection{Default Parameters}1885 % \subsection{Default Parameters} 1843 1886 1844 1887 … … 2218 2261 A separator does not appear before a C string starting with the characters: \lstinline[mathescape=off,basicstyle=\tt]@([{=$@ 2219 2262 \item 2220 A sep erator does not appear after a C string ending with the characters: \lstinline[basicstyle=\tt]@,.;!?)]}%@2263 A separator does not appear after a C string ending with the characters: \lstinline[basicstyle=\tt]@,.;!?)]}%@ 2221 2264 \item 2222 2265 {\lstset{language=CFA,deletedelim=**[is][]{`}{`}} 2223 A sep erator does not appear before or after a C string begining/ending with the quote or whitespace characters: \lstinline[basicstyle=\tt,showspaces=true]@`'": \t\v\f\r\n@2266 A separator does not appear before or after a C string beginning/ending with the quote or whitespace characters: \lstinline[basicstyle=\tt,showspaces=true]@`'": \t\v\f\r\n@ 2224 2267 }% 2225 2268 \item … … 2284 2327 2285 2328 \begin{figure} 2286 \begin{ lstlisting}[xleftmargin=3\parindentlnth,aboveskip=0pt,belowskip=0pt]2329 \begin{cfa}[xleftmargin=3\parindentlnth,aboveskip=0pt,belowskip=0pt] 2287 2330 int main( int argc, char * argv[] ) { 2288 2331 FILE * out = fopen( "cfa-out.txt", "w" ); … … 2308 2351 fclose(out); 2309 2352 } 2310 \end{ lstlisting}2353 \end{cfa} 2311 2354 \caption{\protect\CFA Benchmark Test} 2312 2355 \label{fig:BenchmarkTest} … … 2322 2365 Figure~\ref{fig:eval} and Table~\ref{tab:eval} show the results of running the benchmark in Figure~\ref{fig:BenchmarkTest} and its C, \CC, and \CCV equivalents. 2323 2366 The graph plots the median of 5 consecutive runs of each program, with an initial warm-up run omitted. 2324 All code is compiled at \texttt{-O2} by GCC or G++ 6.2.0, with all \CC code compiled as \CCfourteen.2367 All code is compiled at \texttt{-O2} by gcc or g++ 6.2.0, with all \CC code compiled as \CCfourteen. 2325 2368 The benchmarks are run on an Ubuntu 16.04 workstation with 16 GB of RAM and a 6-core AMD FX-6300 CPU with 3.5 GHz maximum clock frequency. 2326 2369 … … 2351 2394 \CCV is slower than C largely due to the cost of runtime type-checking of down-casts (implemented with @dynamic_cast@); 2352 2395 There are two outliers in the graph for \CFA: all prints and pop of @pair@. 2353 Both of these cases result from the complexity of the C-generated polymorphic code, so that the GCCcompiler is unable to optimize some dead code and condense nested calls.2396 Both of these cases result from the complexity of the C-generated polymorphic code, so that the gcc compiler is unable to optimize some dead code and condense nested calls. 2354 2397 A compiler designed for \CFA could easily perform these optimizations. 2355 2398 Finally, the binary size for \CFA is larger because of static linking with the \CFA libraries. … … 2479 2522 \smallskip\noindent 2480 2523 \CFA 2481 \begin{ lstlisting}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt]2524 \begin{cfa}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt] 2482 2525 forall(otype T) struct stack_node { 2483 2526 T value; … … 2521 2564 s->head = 0; 2522 2565 } 2523 \end{ lstlisting}2566 \end{cfa} 2524 2567 2525 2568 \medskip\noindent 2526 2569 \CC 2527 \begin{ lstlisting}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt]2570 \begin{cfa}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt] 2528 2571 template<typename T> class stack { 2529 2572 struct node { … … 2576 2619 } 2577 2620 }; 2578 \end{ lstlisting}2621 \end{cfa} 2579 2622 2580 2623 \medskip\noindent 2581 2624 C 2582 \begin{ lstlisting}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt]2625 \begin{cfa}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt] 2583 2626 struct stack_node { 2584 2627 void * value; … … 2617 2660 s->head = NULL; 2618 2661 } 2619 \end{ lstlisting}2662 \end{cfa} 2620 2663 2621 2664 \medskip\noindent 2622 2665 \CCV 2623 \begin{ lstlisting}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt]2666 \begin{cfa}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt] 2624 2667 stack::node::node( const object & v, node * n ) : value( v.new_copy() ), next( n ) {} 2625 2668 void stack::copy(const stack & o) { … … 2664 2707 head = nullptr; 2665 2708 } 2666 \end{ lstlisting}2709 \end{cfa} 2667 2710 2668 2711
Note: See TracChangeset
for help on using the changeset viewer.