Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebAssembly backend #500

Open
wants to merge 14 commits into
base: scala-2
Choose a base branch
from
Open

WebAssembly backend #500

wants to merge 14 commits into from

Conversation

romac
Copy link
Member

@romac romac commented May 20, 2019

reclang IR

Defines a language, 'reclang', that functions as an intermediate language between stainless trees and wasm code.

The main feature of reclang is that its structured types are extensible record, which are meant to be easily translatable both to the current memory model of wasm (linear memory) and the planned future extension of reference types.
The class stainless.wasmgen.intermediate.Lowering is responsible for the lowering to this intermediate language.

We now give an informal overview of reclang.
In the following grammars, nonterminal symbols are given in single quotes('), and terminal symbols without quotes.

The types of reclang are defined as follows:

'type' ::= Boolean | Char | Int | Long | Unit | Double | String | Array | ('type'*) => 'type' | 'id'

where id refers to the name of a record sort defined in the program.
There is no type polymorphism, including arrays, which always contain boxed values.

A record sort is defined as follows:

'sort' ::= struct 'id' ('field'*) [extends 'id']?
'field' ::= 'id' : 'type'

There is a single sort, called "anyref", that does not have a parent:

struct anyref (typeTag: Int)

anyref has a single field which represents the type tag of a given runtime value.
anyref is the root of the record hierarchy. All other records have a parent.
There is no subtyping in reclang, but there are operators which can cast one record to another, provided they are connected with the ancestor (transitive reflexive closure of parent) relation.

At runtime, a record's representation contains a list of values corresponding to the fields it defines as well as all fields that its ancestors define.
The order of fields is from the further ancestor until the current field type.

The expressions of reclang are given below:

'e' ::= error | 'id' // variable, or pointer to a named function
      | val 'id' = 'e'; 'e' | println('e')
      | 'id'('e'*) // function invocation
      | 'e'; 'e' | if ('e') 'e' else 'e' | 'e' == 'e'
      | 'intconst' | 'doubleconst' | 'charconst' | 'stingliteral'
      | true | false | ()
      | 'id'({'e' {, 'e'}*}?) // record constructor
      | 'e'.'id' // record field selector
      | 'e'({'e' {, 'e'}*}?) // higher-order application
      | 'e'.asInstanceOf['id']

      | new Array['type']('e'{, 'e'}?) // length, optional initializer
      | 'e'('e') // array select
      | 'e'('e') = 'e' // array set
      | 'e'.length // array or string length
      | 'e'.substing('e', 'e') // string operations

      | 'e' 'binop' 'e'
      | ! 'e'

'binop' ::= + | - | * | / | mod | remainder
          | < | > | <= | >= | & | "|"
          | ^ | << | >> | >>>
          | (bitvector casts)
          | "||" | && | concat

The notable typing rules are for records: a record constructor takes as arguments expressions of the types of the fields of all the ancestors of the current type.
Also, a selector is allowed with the name of a field of an ancestor.
Casts have to be between compatible record types, i.e., that are related by the ancestor relation.
An upcast is always successful.
A downcast to an incompatible type fails the program.

The only values that can have a function type in reclang are function pointers.
Functions are represented by closures, i.e., records containing a function pointer and the environment captured by the function.

Polymorphism is implemented with type erasure and boxing.
There are boxed versions of the elementary types (which are regular record types).
Boxed types are records containing one additional field named "value" of the corresponding type.

The typetag field (which is defined in anyref, therefore is present in every record) defines the type of a record value at runtime as follows:

  • 0 for boxed Unit,
  • 1 for boxed Boolean,
  • 2 for boxed Char,
  • 3 for boxed Int,
  • 4 for boxed Long,
  • 5 for boxed Float,
  • 6 for boxed Double,
  • 7 for boxed Array,
  • 8 for boxed String,
  • 9 for closure,

and then as many tags as are needed for record sorts that are instantiated in the program.
We differentiate between records which correspond to abstract types (cannot be instantiated, do not possess a tag) and records which correspond to constructors (can be instantiated, possess a tag).
Two record values are equal if their type tags are equal and all fields defined by their tagged type are equal.
Arrays, functions and strings are compared by reference.
All other types are compared by value.

by @manoskouk

@romac romac mentioned this pull request May 20, 2019
@CLAassistant
Copy link

CLAassistant commented Jul 23, 2019

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants