Thursday, December 12, 2024

ParaSail now on GitHub

Thanks to my colleague Olivier Henley, the full sources of ParaSail (and more) are now on GitHub:

     https://github.com/parasail-lang/parasail

There is a discussion forum there for asking questions about the implementation:

     https://github.com/parasail-lang/parasail/discussions

Below is the "readme" file from this GitHub repository:

The ParaSail Programming Language

This is the main source code repository for ParaSail. It contains the compiler, the interpreter, the standard library, and documentation.

Note: this README is for users rather than contributors. If you wish to contribute to the compiler, you should read the Getting Started section of the parasail-dev-guide instead. You can ask for help at Gitter.

import Clock;

interface Dining_Philosophers<Num_Phils : Univ_Integer := 5; Context<>> is

    func Dinner_Party (Length_Of_Party : Clock::Interval_Type; Context);

    type Philosopher_Index is new Integer<1..Num_Phils>;
    type Left_Or_Right is Enum<[#is_left_fork, #is_right_fork]>;

    concurrent interface Fork<> is  
        func Pick_Up_Fork(queued var F : Fork; Which_Fork : Left_Or_Right);
        func Put_Down_Fork(locked var F : Fork);
        func Create(Index : Philosopher_Index) -> Fork;
    end interface Fork;

    type Fork_Array is Array<Fork, Indexed_By => Philosopher_Index>;
        
end interface Dining_Philosophers;

class Dining_Philosophers<Num_Phils : Univ_Integer := 5; Context<>> is

  exports

    func Dinner_Party(Length_Of_Party : Clock::Interval_Type; Context) is
        Delay(Context.Clock.Wall_Clock, Length_Of_Party);
        Display(Context.IO.Standard_Output, "Dinner Party is over\n");
        return; 
      ||
        var Forks : Fork_Array := [for I in 1..Num_Phils => Create(I)];
        
        for Phil in Philosopher_Index concurrent loop
            const Left_Fork := Phil;
            const Right_Fork := Phil mod Num_Phils + 1;
           
            while True loop
                Display(Context.IO.Standard_Output, "Philosopher " | Phil | " is thinking\n");
                Delay(Clock, Next(Context.Random));  // Think
              then
                Pick_Up_Fork(Forks[Left_Fork], #is_left_fork);
              ||
                Pick_Up_Fork(Forks[Right_Fork], #is_right_fork);
              then
                Display(Context.IO.Standard_Output, "Philosopher " | Phil | " is eating\n");
                Delay(Clock, Next(Context.Random));  // Eat
              then
                Put_Down_Fork(Forks[Left_Fork]);
              ||
                Put_Down_Fork(Forks[Right_Fork]);
            end loop;
        end loop;
    end func Dinner_Party;
    
    concurrent class Fork<> is
        var Is_Available : Boolean;
        
      exports

        func Create(Index : Philosopher_Index) -> Fork is
            return (Is_Available => True);
        end func Create;
        
        func Pick_Up_Fork (queued var F : Fork; Which_Fork : Left_Or_Right) is 
          queued until F.Is_Available then
            F.Is_Available := False;
        end func Pick_Up_Fork;       
            
        func Put_Down_Fork(locked var F : Fork) is
            F.Is_Available := True;
        end func Put_Down_Fork;
        
    end class Fork;

end class Dining_Philosophers;

Quick Start

Getting Help

The ParaSail community congregates in a few places:

  • Stack Overflow - Direct questions about using the language.
  • Gitter - General discussion and broader questions.

Contributing

If you are interested in contributing to the ParaSail project, please take a look at the Getting Started section of the parasail-dev-guide.

License

ParaSail is primarily distributed under the terms of the ParaSail Copyright, the ParaSail Copyright 2 and the ParaSail Copyright Lib.

See ParaSail Copyright, ParaSail Copyright 2, and ParaSail Copyright Lib for details.

Friday, July 10, 2020

A new blog on the Design and Engineering of Programming Languages

I am happy to announce a new blog about the Design and Engineering of Programming Languages.  Only two entries so far.  Here is the first:


The intent is ultimately to create a book-length series of writings on the topic.  Of course, we know where good intentions can lead!  In any case, we hope these blog entries will at least make some interesting reading, even if together they never quite reach book length.

My day job went down to 60% as of July 1st, 2020, so my plan is to find more time to work on ParaSail, its various siblings such as Parython and Javallel, this new blog/book, and a new language on the drawing board, "ParaDISO" (Parallel Distributed, Incremental, Streamable Objects), for distributed/cloud computing.   So now you know what is my definition of fun...

Thursday, October 31, 2019

Release 8.4 of ParaSail Interpreter, Compiler, and Debugger

We are happy to announce a new release of ParaSail.  This new release incorporates an enhanced interactive debugger that is automatically invoked when the interpreter encounters an assertion, precondition, or postcondition that fails at run-time.  In addition, the sources for run-time library code that is potentially linked into compiled ParaSail programs now carries a license which allows such compiled programs to be distributed as the user sees fit.

In any case, here is the new release [link updated -- was incorrect]:


It is a "zip" archive of sources and binaries for Mac, Linux, and Windows.  Mac and Linux include executable binaries for a bootstrapped LLVM-generating ParaSail compiler.  Windows only contains the executable binaries for the ParaSail interpreter.

The web page for ParaSail is still:


From a documentation point of view, the most exciting news is that we have recently published a full description of the ParaSail language in the new academic "Programming Journal":


Please take a look at this article to see a comprehensive description of ParaSail, and how it fits into the world of programming language design.

Here is the latest reference manual (which is also linked from the ParaSail web page, and included in the "zip" archive for release 8.4):


Enjoy!

Sunday, February 10, 2019

ParaSail Published in Programming Journal Vol. 3, Issue 3

We very recently had a long paper on ParaSail published in the relatively new Journal of the Art, Science, and Engineering of Programming (http://programming-journal.org).  This is an interesting journal, which is trying to rejuvenate the writing of Computer Science journal articles, rather than having essentially all interesting research showing up as Computer Science conference papers.  Conference papers often tend to be incremental, being a report on recent progress in CS research activities, but not necessarily having the space or time to provide an archival-level quality and depth that might be possible in a journal article.  In any case, here is the abstract for the ParaSail paper:

   http://programming-journal.org/2019/3/7/

and from there you can click through to the full PDF (the journal is all open access).  Many thanks to the editors and reviewers for their guidance in bringing this paper up to journalistic standards.

In fact, the journal has kind of flipped things around.  If a paper is accepted for publication in the journal in a given year, they invite the author(s) to present the paper at their annual conference, which this year is in Genoa, Italy, the first week in April:

   https://2019.programming-conference.org/

So come on down to Genoa in April if you are in the neighborhood.  The presentation on ParaSail will be on Thursday, April 4th.

String interpolation comes to ParaSail 8.0

ParaSail supports generic functions where the type of the parameter is determined at the point of call.  The most common use of this capability is with the string concatenation operator "|":

  interface PSL::Core::Univ_String<> is
      ...
    op "|"(Left, Right : optional Univ_String) -> Univ_String
      is import(#concat_string)

     op "|"(Left : optional Univ_String;
           Right : optional Right_Type is Imageable<>)
      -> Univ_String

    op "|"(Left : optional Left_Type is Imageable<>;
           Right : optional Univ_String)
      -> Univ_String

      ...
  end interface PSL::Core::Univ_String



The first "|" provides the builtin string concatenation.  The other two are generic functions defined in terms of this builtin concatenation, as follows:


  class PSL::Core::Univ_String is
     ...
   exports
     ...
    op "|"(Left : optional Univ_String;
           Right : optional Right_Type is Imageable<>)
      -> Univ_String is
        if Right is null then
            return Left | "null"
        else
            return Left | Right_Type::To_String(Right)
        end if
    end op "|"

    op "|"(Left : optional Left_Type is Imageable<>;
           Right : optional Univ_String)
      -> Univ_String is
        if Left is null then
            return "null" | Right
        else
            return Left_Type::To_String(Left) | Right
        end if
    end op "|"
      ...

  end class PSL::Core::Univ_String


---


What the above means is that when you write:

    "X = " | X

this will be allowed so long as "X" is of a type that satisfies the "Imageable" interface, which is defined as follows:

 abstract interface PSL::Core::Imageable<> is
    func To_String(Val : Imageable) -> Univ_String<>

    optional func From_String(Str : Univ_String<>) -> optional Imageable

    // NOTE: We include Hashable<> operations here
    //       so that Set works nicely.
    //       Clearly if something is Imageable it is possible
    //       to implement "=?" and Hash using the string image,
    //       so we might as well requires these operations too.

    op "=?"(Left, Right : Imageable) -> Ordering
    func Hash(Val : Imageable) -> Univ_Integer
 end interface PSL::Core::Imageable
 

---
The availability of the To_String operation is the critical aspect of Imageable that we use for the "|" operator.  We expand "X = " | X into:

   "X = " | To_String(X)

and so long as X's type has an appropriate To_String operation, everything works smoothly.  What this means is that you almost never have to write a call on To_String explicitly, by just alternating between string literals and expressions that you want to print, you can write things like:

 Println ("X = " | X | ", Y = " |  Y | ", X + Y = " | X + Y | "!");

which is clearly less verbose than:

 Println ("X = " | To_String(X) | ", Y = " |  To_String(Y) | ", X + Y = " | To_String(X + Y) | "!");

Nevertheless, even in the more concise version, all of those alternating quotes and '|' characters can become hard for the human to parse, and it is easy to forget one of the needed characters.  So what to do?

STRING INTERPOLATION

A (growing) number of languages support the ability to "interpolate" the values of variables, or in some cases expressions, directly into string literals.  This has been true for simple variables since the beginning for languages like the Unix/GNU-Linux shell:

    echo "X = $X, Y = $Y"

Interpolating the value of more complex expressions is sometimes supported, but generally requires some additional syntax.  

Very early on, most variants of the LISP language supported the ability to construct list structures explicitly, using the "quote" operation, along with an ability to insert a LISP expression (aka "S-expression") in the middle that was to be evaluated and substituted into the list structure that was being defined.  E.g.:

     (define celebrate (lambda (name) (quote (Happy Birthday to (unquote (capitalize name))))))

and then "(celebrate (quote sam))" evaluates to "(Happy Birthday to Sam)".

As a short-hand, in most versions of LISP:

   (quote (a b c))

becomes something like:

    '(a b c)

and (unquote blah) becomes, depending on the version of LISP, something like:

   `blah
or
   ,blah

So the above definition of "celebrate" using these short-hands becomes:

   (define celebrate (lambda (name) '(Happy Birthday to ,(capitalize name))))

with (celebrate 'sam) becoming (Happy Birthday to Sam)

PARASAIL STRING INTERPOLATION

Ok, so how does this relate to ParaSail?  So the explicit use of the "|" operator and having to end a string literal, insert the expressions between | ... |, and then restart the string literal gets old.  Given that there aren't an infinite number of ParaSail programs already in existence, and the fact that the back-quote character is almost never used, we decided to hi-jack the meaning of back-quote when it appears in the middle of a string literal to reduce all the back and forth needed when using an explicit "|" operator.  Hence, we now allow:

   Println("X = `(X), Y = `(Y),  X + Y = `(X + Y)!");

In other words, you can "interpolate" the value of an arbitrary (Imageable) expression into the middle of a ParaSail string literal by using `().  This is actually recognized during lexical analysis, and a mechanical expansion is performed by the lexical analyzer, of a back-quote character (unless preceded by '\') into the two characters:  " |

The lexical analyzer then expects a left parenthesis to be the next character, and counts matching parentheses, and when it reaches the matching right parenthesis, it emits the two characters: | "

The net effect is that "X = `(X), ..." becomes:

      "X = " | (X) | ", ..."

Note that the parentheses are preserved, to avoid issues with precedence between the "|" operator and operators that might occur within the parentheses.  Also note that the parenthesized expression can be separated from the back-quote by white space, and the expression can also span multiple lines if necessary.  The lexer is smart enough to use a stack, so that the parenthesized expression can also have nested string literals, that themselves use interpolation.

If you have looked at past source releases of the ParaSail LLVM-based compiler (which is itself written in Parasail -- see lib/compiler.psl), you might want to take a look at the version of lib/compiler.psl in the 8.0 release.   It makes heavy use of string interpolation, and hopefully is easier to read because of that.
 

 



 



Saturday, February 9, 2019

Release 8.0 of ParaSail Interpreter, Compiler, and Debugger

Finally, at long last a new release of ParaSail.  This new release incorporates an interactive debugger that is automatically invoked when the interpreter encounters an assertion, precondition, or postcondition that fails at run-time.  This is also the first release where pre- and postconditions are fully analyzed, and checked at run-time.  Finally, this has one modest enhancement to the ParaSail syntax -- "interpolation" of the value of an expression directly into a string literal.  For example:

   Println("The value of X + Y is `(X + Y).");

The back-quote character followed by a parenthesized expression may now appear within a string literal, and the value of the expression is "interpolated" into the middle of the string, in place of the back-quoted expression.  Hence, presume X is 31 and Y is 11, the above will print:

   The value of X + Y is 42.

In any case, here is the new release:


It is a "zip" archive of sources and binaries for Mac, Linux, and Windows.  Mac and Linux include executable binaries for a bootstrapped LLVM-generating ParaSail compiler.  Windows only contains the executable binaries for the ParaSail interpreter.

The web page for ParaSail has also been updated:


From a documentation point of view, the most exciting news is that we have just published a full description of the ParaSail language in the new academic "Programming Journal":


Please take a look at this article to see a comprehensive description of ParaSail, and how it fits into the world of programming language design.

Here is the latest reference manual (which is also linked from the ParaSail web page, and included in the "zip" archive for release 8.0):


We will be posting some follow-up blog entries about some of the new features in this release, in particular the debugger, and the implementation of postconditions that require automatically saving some values only available on entry to an operation.

 Enjoy!

Wednesday, November 2, 2016

Release 7.0 of ParaSail interpreter, VM, and compiler -- sources plus linux/mac binaries

We have just made a release of the ParaSail interpreter, VM, llvm-based compiler, and ParaScope static analysis tool (aka "static catcher of programming errors").  This is version 7.0.  It includes a nearly complete re-write of the llvm-based compiler.  After attempting other approaches, we finally went back to the ParaSail front end and had it annotate every PSVM instruction with a set of virtual register numbers in addition to a stack offset for local variables.  This allows the backend to generate much better LLVM (and eventually will also simplify the job of doing more advanced static analysis).  The compiler also automatically inlines small routines, including routines from the ParaSail standard library, so function call overhead in the presence of layers of abstraction can be much reduced.

This release is the first in a while to include binaries, though it only includes binaries for Mac and Linux (Windows is being difficult ... ;-{).  The release is at:

   http://bit.ly/psl7rel

If you have any problems, please report them on the ParaSail google group:

 https://groups.google.com/forum/#!forum/parasail-programming-language

Friday, November 6, 2015

ParaSail source release 6.5 now available.

We have just made a release of the ParaSail interpreter, VM, and compiler.  This is version 6.5, and is mostly a bug-fix release relative to 6.3.  We are working on a static analyzer and optimizer for ParaSail, and this release contains prototypes for both of those (ParaScope, and a combination of ParaScope and the compiler).  The static analyzer can be invoked using "bin/scope.csh file1.psl file2.psl ...".  The optimizer is not quite ready for prime time at this point, but feel free to investigate lib/vn_il.ps? to see how the compiler and static analyzer are being integrated to provide LLVM optimization.  As before, the source release is available at:

   http://bit.ly/ps6xsrc

If you have any problems, please report them at:

   http://groups.google.com/group/parasail-programming-language

Monday, January 5, 2015

Compiling ParaSail to LLVM, first in a series...

We now have an LLVM-based compiler for ParaSail, thanks to great work this past summer by Justin Hendrick, a summer intern from Cornell.  To download the latest source release for both the ParaSail interpreter and the LLVM-based compiler, visit http://parasail-lang.org 

There are a number of interesting design issues associated with creating a compiler for a pointer-free, pervasively parallel language, and it seemed worth writing about some of them here.  So here is the first in a series of such blog entries.

Clearly pervasive fine-grained parallelism is one of the things that makes ParaSail interesting and a bit different from typical languages.  So let's start with that -- how are picothreads, or parallel work items (as we have taken to describing them in some contexts), represented in the generated LLVM?  The ParaSail front end already does the work to decide where it is worth creating a separate thread of control as part of generating instructions for the ParaSail Virtual Machine (PSVM instructions).  In the PSVM, these are represented as nested blocks within a single PSVM routine.  That is, a single ParaSail operation (func or op) produces a single PSVM routine, but that routine may have zero or more nested blocks to represent code suitable for execution by a separate picothread.  A nested block is invoked by the Start_Parallel_Op or Add_Parallel_Op PSVM instructions, awaited by the Wait_For_Parallel_Op, and terminated by an Exit_Op instruction.  On the other hand, a whole routine is invoked by a Call_Op, Start_Parallel_Call_Op, or Add_Parallel_Call_Op instruction, and terminated by the Return_Op instruction -- Wait_For_Parallel_Op is used for parallel invocations of both nested blocks and whole routines.

At the LLVM level, we made the decision to make the calling sequence essentially the same whether it was an invocation of a nested block or a routine.  Every such call has three arguments.  These are the Context, the Param_List, and the Static_Link.  The Param_List points to a stack-based parameter list, where the first parameter is the result, if any, and the other parameters are the input parameters.  The Context points to a data structure that is created when a new picothread is created, and is updated when a new storage region is created (more about that in a later blog entry).  The Context is effectively the per-picothread information.  The Static_Link either points at a type descriptor, for a routine that corresponds to an operation of a module, or to the enclosing stack frame, for a nested block or a routine that corresponds to a nested operation.  The first word of a stack frame holds a copy of the passed-in static link, so this supports up-level references across multiple levels, as well as access to the type descriptor passed into an enclosing module operation.

Type descriptors are fundamental to the ParaSail run-time model.  Remember that at the source level, all ParaSail modules are considered parameterized, and a ParaSail type is produced by instantiating a module.  Operations are generally associated with modules, and unlike in C++ or Ada, instantiating a module does not produce a new set of code for these operations.  Instead, the code is generated once for a given module, and then used for all instantiations.  But that means that the code needs to be passed some information that identifies the particular instantiation for which it is being executed.  This is what a type descriptor provides -- all of the information that is dependent on the particular instantiation being used. 

For example, a Set module might have various operations like Union and Intersection and Insert and Count, but the type of the elements being manipulated is determined by the particular instantiation, be it Set<Integer>, Set<String>, or Set<Map<String, Tree<Float>>>.  Presuming it is a hashed Set, the only requirements on the element type is that it have the operations of the abstract interface Hashable, which generally means Hash and "=?" (aka compare).  So when we call the Insert operation of Set, we pass it the type descriptor (as the Static_Link argument) for the particular instance of Set we are using.  Since Set only has one type parameter, the type descriptor of Set consists primarily of a reference to a type descriptor for this one type parameter.  In addition to having references to the type descriptors for the type parameters, a type descriptor also includes the equivalent of a C++ virtual function table.  That is, for each of the operations of Set, we have a reference to the (compiled or interpreted) routine for that operation.  This is not generally needed for the code within Set itself, since that code generally knows at compile time what routines are used for all of the other Set operations.  However, since we also use a type descriptor for the type parameter of Set, we will need to use the routine table within that type descriptor to gain access to the Hash and "=?" operation for the particular element type being used.  So in the type descriptor for Set<Integer>, there will be a reference to the type descriptor for Integer, and from there you can gain access to the Integer Hash and Integer "=?" routines, which will certainly be needed at least for the Insert routine.

One last wrinkle on type descriptors -- because ParaSail uses a variant of the class-and-interface model of inheritance, it supports multiple inheritance, so different types that implement the Hashable interface might have different sets of routines in their routine table.  So how does the code for the Insert routine know where it can find the Hash and "=?" routines for Integer or String, given that they might also have many other routines?  The solution here is an extra level of indirection.  We actually have two types of type descriptors, one is the full type descriptor, and has all of the information so far described (and more, such as nested constants).  The second kind of type descriptor is called an operation map (or op map for short).  This consists of a reference to the full type descriptor for the type, plus a simple mapping from an interface's operation index to the underlying type's operation index.  If we sequentially number the operations of any given module's interface, then that gives us an operation index which we can use at run-time for selecting from the routine table in a type descriptor for an instance of that module.  The op map provides the necessary conversion when the operations defined in the abstract interface (specified for the element's type when declaring a module like Set) are numbered differently than the actual element type's operations.  An op-map for a type that implements Hashable might be defined by a simple mapping such as (1 => 5, 2 => 3), meaning that the first operation of Hashable is in fact the fifth operation of the actual type, and the second operation of Hashable is the third operation of the actual type.  An op-map provides for constant-time execution of calls on operations, even in the presence of multiple inheritance.

Note that this op-map approach does not work for languages where all objects carry around a reference to their type descriptor at run-time, since a single type descriptor must work for all uses of the object.  One of the features of ParaSail is that typical objects have no type descriptor overhead.  The compiler generally knows where to find a type descriptor for any object, without the object having to carry it around.  The one exception to this are explicitly polymorphic objects.  In ParaSail these are declared with a syntax of a type name followed by a "+".   For example, Set<Imageable+> would be a set of objects that implement the Imageable interface, where each object might be of a different type.  This means that each Imageable+ object consists of a reference to the underlying object, plus a reference to a type descriptor identifying the actual type of the object.  But even these sorts of objects can preserve the constant-time execution of operation calls, because an object is polymorphic for a particular interface (Imageable in this case), and so the type descriptor that such a polymorphic object carries around will likely be an op-map tailored to the particular interface being implemented.   The ParaSail Imageable interface actually has four operations (To_String, From_String, Hash, and "=?"), so such an op-map might look like (1 => 13, 2 => 5, 3 =>9, 4 => 11), which provides constant-time access to any of the operations of Imageable.

One final point worth noting -- type descriptors are used in various contexts, as suggested above (as a static link, as a type parameter, and for run-time-type identification).  In most of these contexts op-maps can be used instead of a full type descriptor.  An important exception to this is a type descriptor used as a static link.  In that case, it is always a full type descriptor, and the compiled code can rely on that fact.

Sunday, January 4, 2015

Expanded paper on ParaSail pointer-free parallelism

Here is a PDF for a recent paper describing in more depth ParaSail's pointer-free approach to parallelism:

   http://bit.ly/ps15pfp

Here is the abstract from the paper:


ParaSail is a language specifically designed to simplify the construction of programs that make full, safe use of parallel hardware. ParaSail achieves this largely through simplification of the language, rather than by adding numerous rules. In particular, ParaSail eliminates global variables, parameter aliasing, and most significantly, re-assignable pointers. ParaSail has adopted a pointer-free approach to defining data structures. Rather than using pointers, ParaSail supports flexible data structuring using expandable (and shrinkable) objects, along with generalized indexing. By eliminating global variables, parameter aliasing, and pointers, ParaSail reduces the complexity for the programmer, while also allowing ParaSail to provide pervasive, safe, object-oriented parallel programming. 

Friday, July 25, 2014

Progress Update

Bootstrapping

The compiler is now able to compile many ParaSail programs and most of the ParaSail Standard Library. It's probably easier to just list the things we know it can't do yet:

  • functions as parameters
  • functions with locked or queued parameters

Pretty small list, right! Well, to be honest, it's the list of known bugs. There are probably bugs we don't know about.

The most exciting thing about this is that the compiler itself uses neither of these features, so it's able to compile itself! The process is called bootstrapping. The executable it produces is able to compile other ParaSail programs.

Debugging Information

Over the past few weeks, we've spent a lot of time debugging compiled ParaSail programs. We're tired of stepping through assembly, and so, if given a "-g", the compiler will produce some debugging information usable by gdb. Right now it's only ParaSail source file line numbers and function names, but it's a start.

Syntax Highlighting on the Web with Pygments

Pygments is a generic syntax highlighter written in python. We've submitted a pull request to add a ParaSail highlighter and are awaiting a response from the maintainers.

Static Analysis

Next, we're going to spend more time on static analysis, especially checking for nullness of objects and recognizing unused variables.

OpenMP

We've been evaluating different alternatives for synchronization primitives in the ParaSail runtime and we've found that OpenMP performs the best when compared against Ada protected objects and our own library. Thus, we are moving our runtime system to OpenMP for the performance benefits and the fact that it's supported in both gcc and llvm.

Thursday, June 12, 2014

Linkers and Types and Built-ins! Oh, my!

Hello, It's Justin again. See the previous blog post for a bit of context. (Hint: we're building the compiler)

Linking to the Built-ins

The interpreter has a library of functions it uses to evaluate ParaSail code. We don't want to and can't write every ParaSail operation directly in llvm. So, it was necessary to link the generated llvm code with the interpreter's built-in functions. At first we thought the built-ins needed to be translated to llvm code to successfully link with our generated llvm. To accomplish this, we used a tool called AdaMagic to convert the Ada source code (in which the built-ins are currently written) and Ada's run time system (RTS) to C source code then used the llvm C front-end "clang" to compile the rest of the way. Clang complained with hundreds of warnings, but, it worked. We were able to print integers, floats, and characters! We couldn't do much more with the built-ins at the time because we didn't yet have type descriptors working (see below).

After all the effort and dealing with the messy generated C code, we found out that the system linker could link object files compiled with the llvm backend called "llc" together with object files produced by the GNAT Ada compiler with no problem. That was a relief, as we really didn't want to go mucking around in the generated C code more than we had to.

Types

ParaSail types are represented in the interpreter with Type_Descriptors. Basically,a type descriptor is a large record with everything you need to know about a type. Most importantly, it holds a list of operations, a "null" value to be used for the type, and descriptors for any other type it depends on (such as the component type for an array type). When we want to execute compiled ParaSail code that has types in it, we need much of this information at run time, particularly when executing code that is "generic" in some way (such as a Set where we need to get the Hash operation for Some_Hashable_Type, and perhaps its representation for "null").

Here's an example:

func Count_Nulls(Numbers : Basic_Array<optional Univ_Integer>) -> Univ_Integer is
   var Count := 0;
   var I := 1;
   while I <= Length(Numbers) loop
      if Numbers[I] is null then
         Count += 1;
      end if;
      I += 1;
   end loop;
   return Count;
end func Count_Nulls;

func main(Args : Basic_Array<Univ_String>) is
   var A : Basic_Array<optional Univ_Integer> := Create(10, null);
   A[1] := 42;
   A[5] := 0;
   Print("There are ");
   Print(Count_Nulls(A));
   Println(" nulls");
end func main;

There are quite a few places that need type information, but the easiest to show is "Numbers[I] is null". There is a specific 64 bit value that represents null for the Univ_Integer type that needs to be checked against Numbers[I].

To accomplish this, we encode the type descriptors as "linkonce" global byte arrays in llvm. Before the ParaSail main routine is invoked, these byte streams are reconstructed into Type_Descriptors for use at run time. We reconstruct these by appending our own parameterless function to the @llvm.global_ctors global variable. But, we need to call into the Ada RTS while reconstructing type descriptors, and, much to our dismay, the functions in @llvm.global_ctors are called before the Ada RTS has a chance to initialize. To solve this problem, the parameterless function in global_ctors adds a node to a todo (linked) list. Then, after the Ada RTS has been initialized, we walk the "todo" list and reconstruct the type descriptors from the streams.

A very similar method is used to reconstruct the appropriate run-time representations for Univ_Strings and Univ_Enumerations from their stream representations.

Back to the example

First of all: It compiles and runs! Here's what it prints out:

There are 8 nulls

Woohoo!
(that part was me)

You might wonder why we used a 'while' loop instead of a 'for each' loop. Well, that's because for loops use parallel stuff we haven't implemented yet. Even a forward/reverse for loop uses parallel stuff because it allows you to do things like this

for I := 1 while I < N forward loop
   block
      continue loop with I => I * 2;
   ||
      continue loop with I => I + 1;
   end block;
end loop;

While loops can be implemented with simple llvm 'br' instructions.

We can't compile aaa.psi (the ParaSail standard library) yet, so we don't have access to all the modules defined there, but Basic_Array::Create, Basic_Array::"var_indexing", and Basic_Array::"indexing" are all built-ins, so we're able to use that container.

Other Updates

Main

If a ParaSail function has the name "main," with one argument of type Basic_Array<Univ_String>, and no return value, then that is treated as a special case by the llvm code generator, and it's name will be changed to "_parasail_main_routine". A real main function  "int main(int argc, char** argv)" function exists in ParaSail's RTS that builds up the Args array (well actually not yet, but it will!)  and calls this _parasail_main_routine. Why the preceding underscore you ask? Well, it's not legal to start an identifier with an underscore in ParaSail, so, we're insuring no name collisions this way. However, one downside of this is that it is currently not possible to call main from elsewhere in your program. However, that's not unprecedented, C++ disallows it as well. This isn't set in stone, though.

Reflection

   "Reflection" is a ParaSail module that allows ParaSail code to inspect itself and retrieve the ParaSail Virtual Machine (PSVM) instructions for any routine that is in the Interpreter's memory at the same time. This is how we compile: the user loads compiler.psl (the llvm code generator written in ParaSail itself), its dependent module(s), and the ParaSail source file to be compiled. Then, the main function in compiler.psl, called "Compile," searches through all the routines in the interpreter's memory (using the reflection module) until it finds those that come from the file whose name matches the file name given as an argument to Compile.  It then proceeds to translate each of those routines from PSVM instructions into LLVM instructions, as well as build up the various tables need for reconstructing type descriptors, strings, etc.

Static Link

   The Static Link passed implicitly in each ParaSail function call is used by the called function to retrieve data from outside the function.  If the function is nested inside another function, then the static link points to the local data area of that enclosing function.  If the function is a top-level operation of some module, then the static link points to a type descriptor for some instance of that module.  In a call on a stand-alone function, the static link is null.

We're making tremendous headway on this compiler and I'm very excited about where we could reach by the end of this summer.

Tuesday, May 27, 2014

From an Interpreter to a Compiler

My name is Justin Hendrick and I'm an intern working on ParaSail for the summer. My first task is code generation.

Up until now, ParaSail has been interpreted. We’re proud to announce that we’ve begun building the code generation phase, written in ParaSail, that generates LLVM Intermediate Representation code. For information about LLVM see llvm.org and the reference manual.

So far, we can successfully compile simple functions that branch and do arithmetic on Univ_Integers and Univ_Reals.

For example, this ParaSail max function:

func Max(A : Univ_Integer; B : Univ_Integer) -> Univ_Integer is
   if A > B then
      return A;
   else
      return B;
   end if
end func Max;

Is converted to ParaSail Virtual Machine Intermediate Representation. See this post for more details on the PSVM and the calling convention in use.

 ----- ParaSail Virtual Machine Instructions ---- 
Max: Routine # 720
(A : Univ_Integer<>; B : Univ_Integer<>) -> Max : Univ_Integer<>
Uses_Queuing = FALSE
Local_Area_Length = 17
Start_Callee_Locals = 7
Code_Length = 13
(COPY_WORD_OP, Destination => (Local_Area, 5), Source => (Param_Area, 1))
(COPY_WORD_OP, Destination => (Local_Area, 6), Source => (Param_Area, 2))
(CALL_OP, Params => (Local_Area, 4), Static_Link => (Zero_Base, 5) = Univ_Integer<>, Call_Target => (Zero_Base, 59) = "=?")
(STORE_INT_LIT_OP, Destination => (Local_Area, 5), Int_Value => 4)
(CALL_OP, Params => (Local_Area, 3), Static_Link => (Zero_Base, 30) = Ordering<>, Call_Target => (Zero_Base, 19) = "to_bool")
(IF_OP, If_Source => (Local_Area, 3), If_Condition => 2, Skip_If_False => 4)
(COPY_WORD_OP, Destination => (Local_Area, 3), Source => (Param_Area, 1))
(COPY_WORD_OP, Destination => (Param_Area, 0), Source => (Local_Area, 3))
(RETURN_OP)
(SKIP_OP, Skip_Count => 3)
(COPY_WORD_OP, Destination => (Local_Area, 3), Source => (Param_Area, 2))
(COPY_WORD_OP, Destination => (Param_Area, 0), Source => (Local_Area, 3))
(RETURN_OP)

After code generation and a pass through LLVM’s optimizer this becomes

define void @Max(i64* %_Static_Link, i64* %_Param_Area) {
  %_source1 = getelementptr inbounds i64* %_Param_Area, i64 1
  %_source_val1 = load i64* %_source1, align 8
  %_source2 = getelementptr inbounds i64* %_Param_Area, i64 2
  %_source_val2 = load i64* %_source2, align 8
  %_result3 = icmp sgt i64 %_source_val1, %_source_val2
  br i1 %_result3, label %_lbl7, label %_lbl11

_lbl7:                                            ; preds = %0
  store i64 %_source_val1, i64* %_Param_Area, align 8
  ret void

_lbl11:                                           ; preds = %0
  store i64 %_source_val2, i64* %_Param_Area, align 8
  ret void
}

And from there, LLVM can generate machine code for many architectures.

The next steps are to enable calls to built-in functions, handle types other than Univ_Integer and Univ_Real, and implement Parallel Calls.

We plan to make more frequent releases this summer than in the past.

Stay tuned for more updates on the state of the compiler.

Saturday, April 5, 2014

A simple and useful iterator pattern in ParaSail

ParaSail has three basic iterator formats, with some sub-formats (unbolded "[ ... ]" indicate optional parts, unbolded "|" indicate alternatives, bold indicates reserved words or symbols that are part of the syntax itself):

    1. Set iterator:
    for I in Set [forward | reverse | concurrent] loop ...

    2. Element iterator:
    for each E of Container [forward | reverse | concurrent] loop ...
         or
    for each [K => V] of Container 
                         [forward | reverse | concurrent] loop ...

    3. Value iterator:
    for V := Initial [then Next_Value(V)] [while Predicate(V)] loop ...
         or
    for T => Root [then T.Left | T.Right] [while T not null] loop ...

These iterators can be combined into a single loop by parenthesizing them;  the loop quits as soon as any of the iterators completes:

   for (each E of Container; I in 1..100) forward loop ...
   //  Display up to 100 elements of Container

As we have written more ParaSail, one pattern that has come up multiple times (particularly when doing output) is the case where you have a special value to be used for the first iteration, and then the same value thereafter.  For example, presume we want to display each of the values of a Vector, separated by commas, enclosed in brackets.  A common way of doing this is to have an "Is_First" boolean value which is tested, and then set to #false, to decide whether to display the opening "[" or the separating ", ".  By using a combination of two iterators, this becomes simpler, with no extra conditionals:

    for (each V of Vec; Sep := "[" then ", ") forward loop
       Print(Sep | V);
    end loop
    Print("]")

Note that the value iterator has no "while" part, so it will continue forever to have the loop variable "Sep" bound to the value ", "; when combined with another iterator as it is in this case, the loop will end once the container iterator ends.

Nothing too magical here, but it is sometimes interesting to see a pattern like this that keeps cropping up.  The ParaSail reference manual has more details on these iterators.  The most recent manual may be found at http://parasail-lang.org .

Thursday, February 20, 2014

ParaSail Work Stealing speeds up

We are now releasing revision 5.2 of the ParaSail compiler and interpreter, which incorporates a new implementation of work stealing.  Links to both binaries and sources can be found at:

    http://parasail-lang.org

in the Documentation and Download section of the web page.

Here is the new entry in the release notes for revision 5.2:

* [rev 5.2] Re-implementation of work stealing to reduce contention between processors.  Each server now has a private double-ended queue (deque) of pico-threads, along with the shared triplet of deques which has existed before.  This produced another two times speedup (in addition to the rev 4.9 improvements), thereby speeding up execution by four times or more since rev 4.8. The main procedures for each language (ParaSail, Sparkel, etc.) have been refactored to share more common code. Allow a command-line flag "-servers nnn" to specify the number of heavy-weight server threads to use.  Default is 6. Command can also be given interactively, as "servers nnn". Interactively it only works to increase the number; there is no way to shut down servers that already have been activated.

Wednesday, February 19, 2014

Worklist Algorithms and Parallel Iterators

ParaSail has some very general parallel iterator constructs.  One of the more unusual involves the use of a continue statement to initiate a new iteration of an outer loop statement, as illustrated in this breadth-first search of a graph:

        var Seen : Array<Atomic<Boolean>, Indexed_By => Node_Id> :=
          [for I in 1 .. |DGraph.G| => Create(#false)];

       *Outer*
        for Next_Set => Root_Set loop  // Start with the initial set of roots
            for N in Next_Set concurrent loop  // Look at each node in set
                if not Value(Seen[N]) then
                    Set_Value(Seen[N], #true);
                    if Is_Desired_Node(DGraph.G[N]) then // Found a node
                        return N;
                    end if;
                    // Continue outer loop with successors of this node
                    continue loop Outer with Next_Set => DGraph.G[N].Succs;
                end if;
            end loop;
        end loop Outer;
        // No node found
        return null;

It has been a bit hard to explain what it means for an inner concurrent loop to continue an outer loop.   More recently, we have developed an alternative explanation.  The basic idea is to appeal to the notion of a worklist algorithm.  Various (sequential) algorithms are organized around the use of a list of work items to be processed, with the overall algorithm continuing until the worklist is empty, or, for a search, until an item of interest is found.  For example, the work-list approach to data flow analysis algorithms is described here:
Here is another worklist-based algorithm, for solving a constraint satisfaction problem using the Arc Consistency approach:
Finally, here is a breadth-first graph search algorithm, which uses a work queue instead of a work list:
So one way of explaining this kind of ParaSail iterator is as a built-in work-list-based iterator, where the loop keeps going until there are no more work items to process, and continue adds a new work-item to the worklist.  This approach makes it clear that such iterators can work even if there is only one physical processor available, and also suggests that the work items are not full threads of control, but rather light-weight representations of some work to be done.  This terminology of work items and work list or work queue also matches well with the terminology of the underlying work stealing scheduling mechanism in use.

We used to refer to such ParaSail's parallel iterators as a bag of threads, but now it seems easier to describe each iterator as having its own list of work items (i.e. a worklist) and the loop keeps going until the worklist is empty, or until we encounter a return or exit (aka break) statement.

One interesting detail at this point is that for a breadth-first search, you want the work list/queue to be processed in a FIFO manner, while for a depth-first search, you want to process the work list/queue as a stack, in a LIFO manner.  The work-stealing scheduler we use does some of both, using FIFO when stealing, and LIFO when the work-items are served by the same core that generated them.  This argues for perhaps giving more control to the programmer when using an explicit continue statement, to be able to specify FIFO or LIFO.  However, it is not entirely clear if that added complexity is worthwhile.

Monday, December 23, 2013

Concurrency vs. Parallelism

Over the past few years there seems to have been an increasing number of discussions of the difference between concurrency and parallelism.  These discussions didn't seem very convincing at first, but over time a useful distinction did begin to emerge.  So here is another attempt at trying to distinguish these two:
  • Concurrent programming constructs allow a programmer to simplify their program by using multiple (heavy-weight) threads to reflect the natural concurrency in the problem domain.  Because restructuring the program in this way can provide significant benefits in understandability, relatively heavy weight constructs are acceptable.  Performance is also not necessarily as critical, so long as it is predictable.
  • Parallel programming constructs allow a programmer to divide and conquer a problem, using multiple (light-weight) threads to work in parallel on independent parts of the problem.  Such restructuring does not necessarily simplify the program, and as such parallel programming constructs need to be relatively light weight, both syntactically and at run-time.  If the constructs introduce too much overhead, they defeat the purpose.
Given sufficiently flexible parallel programming constructs, it is not clear that you also need traditional concurrent programming constructs.  But it may be useful to provide some higher-level notion of process, which forgoes the single-threaded presumption of a thread, but brings some of the benefits from explicitly representing the natural concurrency of the problem domain within the programming language.

ParaSail is focused on providing parallel programming constructs, but concurrent objects are useful for more traditional concurrent programming approaches, particularly when coupled with explicit use of the "||" operator.  It is an interesting question whether some higher-level process-like construct might be useful.

Thursday, November 21, 2013

First release of Javallel, Parython; new release 5.1 of ParaSail and Sparkel

The ParaSail family of languages is growing, with two more additions now available for experimentation.  We have made a new release 5.1 which includes all four members of the family -- ParaSail itself, Sparkel based on the SPARK subset of Ada, Javallel based on Java, and Parython based on Python.  Binaries plus examples for these are all available in a single (large) download:

   http://bit.ly/ps51bin

As before, if you are interested in sources, visit:

   http://sparkel.org

The biggest change in ParaSail was a rewrite of the region-based storage manager (actually, this same storage manager is used for all four languages), to dramatically reduce the contention between cores/processors related to storage management.  The old implementation was slower, and nevertheless still had a number of race conditions.  This one is faster and (knock on wood) free of (at least those ;-) race conditions.

As far as how Javallel relates to Java, here are some of the key differences:
  1. Classes require a "class interface" to declare their visible operations and fields
  2. There is no notion of a "this" parameter -- all parameters must be declared
  3. There is no notion of "static" -- a method is effectively static if it doesn't have any parameters whose type matches the enclosing class; no variables are static
  4. You only say "public" once, in the class, separating the private stuff (before the word "public") from the implementation of the visible methods.
  5. Semi-colons are optional at the end of a line
  6. Parentheses are optional around the condition of an "if"
  7. "for" statements use a different syntax; e.g:
    •  for I in 1..10 [forward | reverse | parallel] { ... }
  8. "{" and "}" are mandatory for all control structures
  9. You can give a name to the result of a method via:  
    • Vector createVec(...) as Result { ... Result = blah; ... } 
    and then use that name (Result) inside as a variable whose final value is returned
  10. You have to say "T+" rather than simply "T" if you want to accept actual parameter that are of any subclass of T (aka polymorphic).  "T" by itself only allows actuals of exactly class T.
  11. Object declarations must start with "var," "final," or "ref" corresponding to variable objects, final objects, or ref objects (short-lived renames).
  12. There are no special constructors; any method that returns a value of the enclosing is effectively a constructor;  objects may be created inside a method using a tuple-like syntax "(a => 2, b => 3)" whose type is determined from context
  13. X.Foo(Y) is equivalent to Foo(X, Y)
  14. Top-level methods are permitted, to simplify creating a "main" method
  15. uses "and then" and "or else" instead of "&&" and "||"; uses "||" to introduce explicit parallelism.
  16. "synchronized" applies to classes, not to methods or blocks
  17. enumeration literals start with "#"
There are examples in javallel_examples/*.jl?, which should give a better idea of what javallel is really like.  Parython examples are in parython_examples/*.pr?
 

Thursday, November 14, 2013

Using ParaSail as a Modeling Language for Distributed Systems

The ACM HILT 2013 conference just completed in Pittsburgh, and we had some great tutorials, keynotes, and sessions on model-based engineering, as well as on formal methods applied to both modeling and programming languages.  One of the biggest challenges identified was integrating complex systems with components defined in various modeling or domain-specific languages, along with an overall architecture, which might be specified in a language like AADL or SysML or might just be sketches on a whiteboard somewhere.  A big part of the challenge is that different languages have different underlying semantic models, with different type systems, different notions of time, different concurrency and synchronization models (if any),  etc.  The programming language designer in me wants to somehow bring these various threads (so to speak) together in a well-defined semantic framework, ideally founded on a common underlying language.

One way to start is by asking how can you "grow" a programming language into a modeling language (without killing it ;-)?  ParaSail has some nice features that might fit well at the modeling level, in that its pointer-free, implicitly parallel control and data semantics are already at a relatively high level, and don't depend on a single-address-space view, nor a central-processor-oriented view.  As an aside, Sebastian Burckhardt from Microsoft Research gave a nice talk on "cloud sessions" at the recent SPLASH/OOPSLA conference in Indianapolis (http://splashcon.org/2013/program/991, http://research.microsoft.com/apps/pubs/default.aspx?id=163842), and we chatted afterward about what a perfect fit the ParaSail pointer-free type model was to the Cloud Sessions indefinitely-persistent data model. Modeling often abstracts away some of the details of the distribution and persistence of processing and data, so the friendliness of the ParaSail model to Cloud Sessions might also bode well for its friendliness to modeling of other kinds of long-lived distributed systems.

ParaSail's basic model is quite simple, involving parameterized modules, with separate definition of interface and implementation, types as instantiations of modules, objects as instances of types, and operations defined as part of defining modules, operating on objects.  Polymorphism is possible in that an object may be explicitly identified as having a polymorphic type (denoted by T+ rather than simply T) and then the object carries a run-time type identifier, and the object can hold a value of any type that implements the interface defined by the type T, including T itself (if T is not an abstract type), as well as types that provide in their interface all the same operations defined in T's interface.

So how does this model relate to a modeling language like Simulink or a Statemate?  Is a Simulink "block" a module, a type, an object, or an operation (or something entirely different)?  What about a box on a state-machine chart?  For Simulink, one straightforward answer is that a Simulink block is a ParaSail object.  The associated type of the block object defines a set of operations or parameter values that determine how it is displayed, how it is simulated, how it is code-generated, how it is imported/exported using some XML-like representation, etc.  A Simulink graph would be an object as well, being an instance of a directed graph type, with a polymorphic type, say "Simulink_Block+," being the type of the elements in the graph (e.g. DGraph).

Clearly it would be useful to define new block types using the Simulink-like modeling language itself, rather than having to "drop down" to the underlying programming language.  One could imagine a predefined block type "User_Defined_Block" used to represent such blocks, where the various display/simulation/code-generation/import/export operations would be defined in a sub-language that is itself graphical, but relies on some additional (built-in) block types specifically designed for defining such lower-level operations.  Performing code-generation on these graphs defining the various primitive operations of this new user-defined block type would ideally create code in the underlying programming language (e.g. ParaSail) that mimics closely what a (ParaSail) programmer might have written to define a new block type directly in the (ParaSail) programming language.  This begins to become somewhat of a "meta" programming language, which always makes my head spin a little...

A practical issue at the programming language level when you go this direction is that, what was a simple interface/implementation module model, may want to support "sub-modules" in various dimensions.  In particular, there may be sets of operations associated with a given type devoted to relatively distinct problems, such as display vs. code generation, and it might be useful to allow both the interface, and certainly the implementation of a block-type-defining module to be broken up into sub-modules.  The ParaSail design includes this notion, which we have called "interface extensions" (which is a bit ambiguous, so the term "interface add-on" might be clearer).  These were described in:
http://parasail-programming-language.blogspot.com/2010/08/eliminating-need-for-visitor-pattern-in.html
but have not as of yet been implemented.  Clearly interface add-ons, for say, [#display] or [#code_gen], could help separate out the parts of the definition of a given block type.

A second dimension for creating sub-modules would be alternative implementations of the same interface, with automatic selection of the particular implementation based on properties of the parameters specified when the module is instantiated.  In particular, each implementation might have its own instantiation "preconditions" which indicate what must be true about the actual module parameters provided before a given implementation is chosen.  In addition, there needs to be some sort of a preference rule to use when more than one implementations' preconditions are satisfied by a given instantiation.  For example, presume we have one implementation of an Integer interface that handles 32-bit ranges of integers, a second that handles 64-bit ranges, and one that handles infinite range.  Clearly the 32-bit implementation would have a precondition that the range required be within +/- 2^31, the 64-bit one would require the range be within +/- 2^63, and the infinite-range-handling implementation would have no precondition.  If we were to instantiate this Integer module with a 25-bit range, the preconditions of all three of the implementations would be satisfied, but there would presumably be a preference to use the 32-bit implementation over the other two.  The approach we have considered for this is to allow a numeric "preference" level to be specified when providing an implementation of a module along with the implementation "preconditions," with the default level being "0" and the default precondition being "#true." The compiler would choose the implementation with the maximum preference level with satisfied preconditions.  It would complain if there were a tie, requiring the user to specify explicitly which implementation of the module is to be chosen at the point of instantiation.

Wednesday, November 6, 2013

Tech talk and Tutorial at SPLASH 2013 on parallel programming

I gave an 80-minute "tech talk" and a 3-hour tutorial on parallel programming last week at SPLASH 2013 in Indianapolis.  The audiences were modest but enthusiastic. 

The tech talk was entitled:

  Living without Pointers: Bringing Value Semantics to Object-Oriented Parallel Programming

Here is the summary:

The heavy use of pointers in modern object-oriented programming languages interferes with the ability to adapt computations to the new distributed and/or multicore architectures. This tech talk will introduce an alternative pointer-free approach to object-oriented programming, which dramatically simplifies adapting computations to the new hardware architectures. This tech talk will illustrate the pointer-free approach by demonstrating the transformation of two popular object-oriented languages, one compiled, and one scripting, into pointer-free languages, gaining easier adaptability to distributed and/or multicore architectures, while retaining power and ease of use.

Here is a link to the presentation:  http://bit.ly/sp13pf

The tutorial was entitled:

   Multicore Programming using Divide-and-Conquer and Work Stealing

Here is the summary for the tutorial:

This tutorial will introduce attendees to the various paradigms for creating algorithms that take advantage of the growing number of multicore processors, while avoiding the overhead of excessive synchronization overhead. Many of these approaches build upon the basic divide-and-conquer approach, while others might be said to use a multiply-and-conquer paradigm. Attendees will also learn the theory and practice of "work stealing," a multicore scheduling approach which is being adopted in numerous multicore languages and frameworks, and how classic work-list algorithms can be restructured to take best advantage of the load balancing inherent in work stealing. Finally, the tutorial will investigate some of the tradeoffs associated with different multicore storage management approaches, including task-local garbage collection and region-based storage management.
  
Here is a link to the presentation:  http://bit.ly/sp13mc

Comments welcome!

-Tuck