User-facing changes to the Pancake language and compiler are
documented here when they are merged into master.
It is now possible to use __add_with_carry__(left, right, carry_in)
in user code, which is compiled to wordLang's AddCarry.
Syntax example:
fun {1,1} f() {
var a = 1;
var b = 2;
var c = 0;
var {1,1} r = __add_with_carry__(a, b, c);
r = __add_with_carry__(a, b, c);
return r;
}
Permitted positions for __add_with_carry__ are declaration RHS and assignment RHS;
standalone, handler-attached, and tail-return calls are not supported.
Named (C-like) structs have now been added. Syntax examples:
struct my_struct { // Declare a named struct called "my_struct"
1 first, // Declare a named struct field with shape 1 called "first"
{1,1} second // Declare another field with shape {1,1} called "second"
}
var my_struct x = my_struct < first = 1, second = <2, 3> >; // Named struct values require explicit struct and field names
fun 1 foo(my_struct a) {
return a.second.0; // Named field access is similar to unnamed fields and nests appropriately
}
Like global variables, struct name declaration order only matters to other struct names. That is, this is valid:
var outer os = outer < s = inner < x = 1 > >;
struct inner {
1 x
}
struct outer {
inner s
}
But this is not:
var outer os = outer < s = inner < x = 1 > >;
struct outer {
inner s
}
struct inner {
1 x
}
Named structs are treated as separate shapes to their unnamed counterparts during static checking.
The Pancake compiler now unconditionally compiles with GC set to none; any
--gc=... flag passed alongside --pancake is silently ignored. This removes
the unused GC runtime that Zhewen Shen's BSc thesis (p. 35) noted was being
linked into every Pancake binary.
16-bit shared memory loads and stores have now been added. These are primarily intended for reading and writing to device registers for 16-bit devices. The syntax is as follows:
!st16 1000, v; // store 16 bits from variable v (12) to shared memory address 1000
!ld16 v, 1000 + 12; // load 16 bits from shared memory address 1012 to v
Shapes are now expected for all local variable declarations and function returns, in addition to global variables and function arguments:
var 1 x = 0;
fun 1 foo(1 a) {
var 1 y = 0;
return y;
}
If a shape is not provided in any place it is expected, the compiler assumes a default shape of 1.
The compiler has new errors to enforce sensible usage of shapes. Warnings for estimated locations of memory addresses now support struct fields.
The Silver (ag32) compilation target is no longer supported by the Pancake compiler,
and no longer considered in the Pancake compiler correctness proofs.
Pancake now supports global variables. Syntax examples:
var 1 x = 5; // Declare a global variable with shape 1 and value 5
var 1 y = x+1; // Global variables are initialised sequentially
fun f() {
var x = 3; // local variables can shadow globals
return z+x; // all globals are in scope in all function bodies...
}
var z = 7; // ...regardless of declaration order
The new @top keyword is an analogue to @base, which tells you where internal memory ends.
The addressable internal memory is thus all aligned addresses w such that @base <= w < @top.
@base and @top are always word-aligned.
Previous versions of Pancake allowed storing function pointers in variables and internal memory. This feature is now dropped.
Pancake now supports 32-bit load expressions and store statements for its internal memory. The syntax is as exemplified here.
st32 @base, v; // store 32 bits from variable v to @base
v = ld32 @base+4 // load 32 bits from address @base+4 to variable v.
An issue where warnings would result in the compiler giving a non-zero exit code is now fixed.
The Pancake compiler now provides a variety of new compilation errors and
warnings, such as missing return statements in function definitions.
Previously, this was limited to out-of-scope errors. Errors and warnings
include the location where the issue was found.
The syntax for annotations is changed from /*@ ... @*/ to
/@ ... @/. This is to allow annotation strings to be processed
by the C preprocessor, rather than being ignored as comments.
The parser now gives more relevant locations in error messages.
Furthermore, empty blocks are now supported in functions, while
loops and if statements. So, e.g.,
while(1) {}
is now well-formed, and equivalent to
while(1) { skip; }
A parser bug that caused struct field accesses to be ordered incorrectly is now fixed. That is, the value of the expression
<<1,2>,<3,4>>.0.1
is now 2, whereas previously it was 3.
st has replaced the former stw operator for storing shapes. As the
new name indicates, this operator can store not just words but
arbitrary shapes.
The precedence of load expressions (lds and ld8) has also been
changed to be between that of comparisons and bitwise operators.
A new keyword, @biw (bytes in word), has been added. @biw is a
constant expression whose value is 8 on 64-bit architectures and 4
on 32-bit architectures. Its purpose is to make portable code easier
to write.
32-bit shared memory loads and stores have now been added. These are primarily intended for reading and writing to device registers for 32-bit devices. The syntax is as follows:
!st32 1000, v; // store 32 bits from variable v (12) to shared memory address 1000
!ld32 v, 1000 + 12; // load 32 bits from shared memory address 1012 to v
The parser now supports a special comment format called annotations,
which are any comments of the form /*@ ... @*/. These are retained
by the parser in the form of Annot nodes and are made visible in
explorer output; they can be used to convey information to tools that
consume Pancake abstract syntax produced by the explorer.
Location information is also retained in the AST in the form of
such Annot nodes.
The compiler flag --main_return=true now passes the return value
from the Pancake main function to the caller via cml_main.
The precedence of bitwise operators (&, ^ and |) is now higher
than comparison operators. For example, 1 & 2 != 0 will now parse as
(1 & 2) != 0 instead of 1 & (2 != 0).
The compiler now supports the --explore command line parameter when
compiling Pancake programs. It can be used to output the various
intermediate representations produced during compilation as text.
Pancake now supports multiple entry points, to simplify interaction
between Pancake and other languages (usually C). Functions that
should be callable from outside Pancake should be prefixed with the
export keyword, as follows:
export fun my_function(<args>) {
<body>
}
Exported functions can take at most four arguments, and the arguments
must have shape 1. Calling any exported function before the initial
call to cml_main results in a runtime error. main cannot be
explicitly exported.
The Pancake main function (that gets invoked via cml_main) is
now taken to be the function named main if one exists; previously,
the first function was taken to be the main function regardless of
name. If no function named main exists, a default main function is
used:
fun main() { return 1; }
The new compiler flag main_return can be used to return control to
the caller, instead of exiting, after the main function has finished
executing. By default, this flag is set to false.
Signed word comparison operators <+, <=+, >=+ and >+ have been added.
The operators && (logical AND) and || (logical OR) have been added.
! in expressions now denotes boolean negation. ! was previously
used for obtaining the address of a function; & now serves this
role instead.
The argument order of shared memory stores has been changed so that it
agrees with local memory stores. For example, instead of
!st8 <payload>, <address> the order is now !st8 <address>, <payload>.
The first argument to a shared memory store can now be an arbitrary expression; previously, it had to be a variable.
Function calls now have different syntax. Previously, two kinds of function calls were allowed:
fun f() {
var x = 5;
g(); // tail call, causes f() to return
x = g(); // assigning call (formerly and confusingly known as "returning call")
return 0;
}
In the new style, we allow four forms of function calls:
fun f() {
var 1 x = g(); // declaring call
g(); // stand-alone call, does not cause f() to return
x = g(); // assigning call
return g(); // tail call, causes f() to return
}
Declaring calls need a shape annotation on the variable, because in general the shape of the return value is not statically known.
Line number reporting in error messages now accounts for comments correctly.
Rudimentary line number reporting has been added to parse errors.
The Pancake compiler now checks that all variable and function names used in a program are declared and scoped correctly, and reports a compiler error otherwise. Previously, the compiler would happily generate nonsense.
The keywords for local loads and stores have been changed: st8, stw,
ld8 instead of strb, str, and ldb.
Shared memory load and store operators have been added to
Pancake. These are intended for reading and writing to memory outside
the local Pancake heap, such as shared memory pages or device
registers. Unlike local memory accesses, these cannot be optimised
away or reordered by the compiler, similarly to volatile in C.
Shared memory operations are prefixed with !, so !st8 stores a
byte to shared memory and !ldw stores a word to shared memory. Note
that shared memory loads are statements, whereas local memory loads
are expressions; this is to retain the side-effect freedom of
expressions.
Foreign function names are now written @name instead of #name.
Not-equals is now != instead of <>, and function calls can now be
written simply f(<args>) instead of !f(<args>).
Three new syntax elements have been added to Pancake: infix * for
unsigned word multiplication, and true and false as keywords
denoting 1 and 0, respectively.
Syntax for function declarations has now been added. Previously, only statements were implemented.
Support for compiling Pancake source programs has been added to the
CakeML compiler binary. Use the compiler flag --pancake to indicate
that the source is a Pancake program.
Only statement parsing is implemented currently; the expected input is
a semicolon-separated list of statements, which is interpreted as the
body of the main function.