Clock-directed Modular Code-generation for Synchronous Data-flow Languages

Similar documents
Synchronous Kahn Networks (ten years later)

A Proposal for Verified Code Generation from Modelica

A Synchronous-based Code Generator For Explicit Hybrid Systems Languages

Verifying a Lustre Compiler Part 2

Synchronous Specification

Memory optimisation in a first-order dataflow synchronous language

Lecture 2. The SCADE Language Data Flow Kernel. Daniel Kästner AbsInt GmbH 2012

A Conservative Extension of Synchronous Data-flow with State Machines

Programming Embedded Systems

Type-based Initialization Analysis of a Synchronous Data-flow Language

An Introduction to Lustre

An Eect Type System for Modular Distribution of Dataow Programs

TP 2 : A Compiler for mini-lustre

Zélus, a Synchronous Language with ODEs 1

Coq. LASER 2011 Summerschool Elba Island, Italy. Christine Paulin-Mohring

Tutorial and Reference Manual

Automatic Code Generation from Stateflow Models

The Lustre Language Synchronous Programming Pascal Raymond, Nicolas Halbwachs Verimag-CNRS

Using SCADE to Develop Mission-critical High-quality Radar Application Software

Safe Reactive Programming: the FunLoft Proposal

Programming the Simulink Standard Library with Zélus: experience report 1

Simulink/Stateflow. June 2008

The Java Memory Model

Formal verification of an optimizing compiler

Synchronous Dataflow Processong

Embedded software design with Polychrony

An Introduction to Lustre

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

Synchronous reactive programming

State Machines in OpenModelica

Software Engineering using Formal Methods

How much is a mechanized proof worth, certification-wise?

Written Presentation: JoCaml, a Language for Concurrent Distributed and Mobile Programming

G Programming Languages - Fall 2012

Testing. Lydie du Bousquet, Ioannis Parissis. TAROT Summer School July (TAROT 2009)

Modular code generation from synchronous models:

A Formally Verified Compiler for Lustre

Software Engineering using Formal Methods

Provably Correct Software

Towards Coq Formalisation of {log} Set Constraints Resolution

INTEGRATING SYSTEM AND SOFTWARE ENGINEERING FOR CERTIFIABLE AVIONICS APPLICATIONS

ECL: A SPECIFICATION ENVIRONMENT FOR SYSTEM-LEVEL DESIGN

Formal Specification and Verification

A Synchronous-based Code Generator For Explicit Hybrid Systems Languages

Arrays in Lustre. P. Caspi, N. Halbwachs, F. Maraninchi, L. Morel, P. Raymond. Verimag/CNRS Grenoble

Combining Programming with Theorem Proving

Cover Page. The handle holds various files of this Leiden University dissertation

Scade 6: A Formal Language for Embedded Critical Software Development

Modal Models in Ptolemy

Verification Condition Generation

UML Profile for MARTE: Time Model and CCSL

Induction and Semantics in Dafny

Optimizing Simulink R Models

Part II. Hoare Logic and Program Verification. Why specify programs? Specification and Verification. Code Verification. Why verify programs?

Compiler Passes. Optimization. The Role of the Optimizer. Optimizations. The Optimizer (or Middle End) Traditional Three-pass Compiler

Functional Languages. Hwansoo Han

The design of a programming language for provably correct programs: success and failure

Concurrent Models of Computation

3.4 Deduction and Evaluation: Tools Conditional-Equational Logic

LECTURE 16. Functional Programming

Com S 541. Programming Languages I

Coq, a formal proof development environment combining logic and programming. Hugo Herbelin

Static Program Analysis

A Verification Approach for GALS Integration of Synchronous Components

Static Analysis by A. I. of Embedded Critical Software

Denotational Semantics. Domain Theory

Actor-Oriented Design: Concurrent Models as Programs

Programming Languages Lecture 15: Recursive Types & Subtyping

Model Checking Revision: Model Checking for Infinite Systems Revision: Traffic Light Controller (TLC) Revision: 1.12

G Programming Languages Spring 2010 Lecture 4. Robert Grimm, New York University

Efficient compilation of array iterators for Lustre

Deductive Program Verification with Why3, Past and Future

Static analysis and all that

Certified Memory Usage Analysis

Fall Compiler Principles Lecture 6: Intermediate Representation. Roman Manevich Ben-Gurion University of the Negev

Synchronizing the Asynchronous

CS 6110 S14 Lecture 38 Abstract Interpretation 30 April 2014

Programming Languages and Compilers Qualifying Examination. Answer 4 of 6 questions.

CS 242. Fundamentals. Reading: See last slide

Certificates for incremental type checking

Why. an intermediate language for deductive program verification

A Deterministic Concurrent Language for Embedded Systems

CS4215 Programming Language Implementation. Martin Henz

Lecture 15 CIS 341: COMPILERS

Formal Verification of MIX Programs

On Meaning Preservation of a Calculus of Records

Types and Type Inference

Verified Characteristic Formulae for CakeML. Armaël Guéneau, Magnus O. Myreen, Ramana Kumar, Michael Norrish April 27, 2017

Joint Entity Resolution

SpecC Methodology for High-Level Modeling

Applications of Formal Verification

Type checking. Jianguo Lu. November 27, slides adapted from Sean Treichler and Alex Aiken s. Jianguo Lu November 27, / 39

Fifth Generation CS 4100 LISP. What do we need? Example LISP Program 11/13/13. Chapter 9: List Processing: LISP. Central Idea: Function Application

In Our Last Exciting Episode

Applications of Program analysis in Model-Based Design

CIS 1.5 Course Objectives. a. Understand the concept of a program (i.e., a computer following a series of instructions)

Type Theory meets Effects. Greg Morrisett

Revisiting coverage criteria for Scade models Jean-Louis Colaço 7 December 2016

System-On-Chip Architecture Modeling Style Guide

Handout 9: Imperative Programs and State

Transcription:

1 Clock-directed Modular Code-generation for Synchronous Data-flow Languages Dariusz Biernacki Univ. of Worclaw (Poland) Jean-Louis Colaço Prover Technologies (France) Grégoire Hamon The MathWorks (USA) Marc Pouzet Université Paris-Sud (France) LCTES, Tucson June 13th, 2008

A certified compiler for Lustre Develop a certified compiler for synchronous block-diagram formalisms as found in SCADE/Lustre or a subset of Simulink with the help of the proof assistant Coq Very popular formalism for embedded software (and in particular critical systems) Existing certification: the existing SCADE compiler is qualified DO-178B (level A) this is mainly process-based : introduce more functional certification? this would allow to put more formal verification on the source code 2

3 Motivations (first step) Code generation from synchronous block-diagram has been addressed in the early 80 s and is part of folklore Nonetheless, a minimal and precise description of how code-generation is made in industrial compilers is missing First build a reference compiler, as small as possible, in (mostly) a purely functional way and based on local rewriting rules formalize the code generation into imperative sequential code (e.g., C) as small as possible but realistic (the code should be efficient) as a way to: build a certified compiler inside a proof assistant complement previous works on the extension/formalization of synchronous languages

Code Generation Principle: A stream function f : Stream(T ) Stream(T ) is compiled into a pair: an initial state and a transition function: s 0 : S, f t : S T T S a stream equation y = f(x) is computed sequentially by y n, s n+1 = f t (s n, x n ) An alternative (more general) solution: an initial state: s 0 : S a value function: f v : S T T a state modification ( commit ) function: f s : S T S Final remarks: this generalises to MIMO systems in actual implementations, states are modified in place synchrony finds a very practicle justification here: a data-flow can be implemented as a single scalar variable 4

5 Modular Code Generation produce a transition function for each block definition compose them together to produce the main transition function static scheduling following data-dependences But modular sequential code generation is not always feasible even in the absence of causality loops f (y, z) = f(t, y) This observation has led to two different approaches to the compilation problem

Two Traditional Approaches Non Modular Code Generation full static inlining before code generation starts enumeration techniques into (very efficient) automata ([Halbwachs et all., Raymond PhD. thesis, POPL 87, PLILP 91]) keeps maximal expressiveness but at the price of modular compilation and the size of the code may explode difficult to find the adequate boolean variables to get efficient code in both code and size Modular code generation mandatory in industrial compilers no preliminary inlining (unless requested by the user) imposes stronger causality constraints: every feedback loop must cross an explicit delay well accepted by SCADE users and justified by the need for tracability Between the two, it is possible to decompose a function into a minimal set of atomic functions (Raymond, 1988). 6

7 Proposal a compiler where everything can be traced with a precise semantics for every intermediate language introduce a basic clocked data-flow language as the input language general enough to be used as a input language for Lustre be a good input language for modern ones (e.g., mix of automata and data-flow as found in SCADE 6 or Simulink/StateFlow) provides a slightly more general notion of clocks and a reset construct compilation through an intermediate object based intermediate language to represent transition function provide a translation into imperative code (e.g., structured C, Java)

8 Organisation of the Compiler Static checking Translation EmitC ClockedFlowKernel Annotated DK OBL Structured C

9 Static Checking Type checking Clock checking CDK CDK+Types CDK+Types+Clocks Causality Check CDK+Types+Clocks Initialization Check CDK+Types+Clocks

A Clocked Data-flow Basic Language 10 A data-flow kernel where every expression is explicitely annotated with its clock a ::= e ct e ::= v x v fby a a when C(x) op (a,..., a) f (a,..., a) every a merge x (C a)... (C a) D ::= pat = a D and D pat ::= x (pat,..., pat) d ::= node f(p) = p with var p in D p ::= x : ty;...; x : ty v ::= C i ck ::= base ck on C(x) ct ::= ck ct... ct

Informal Semantics statically checked through a dedicated type system (clock calculus) 11 h True False True False... x x 0 x 1 x 2 x 3... y y 0 y 1 y 2 y 3... v fby x v x 0 x 1 x 2... x + y x 0 + y 0 x 1 + y 1 x 2 + y 2 x 3 + y 3... z = x when True(h) x 0 x 2... t = y when False(h) y 1 y 3... merge h (True z) (False t) x 0 y 1 x 2 y 3... z is at a slower rate than x. We say its clock is ck on True(h) the merge constructs needs its two arguments to be on complementary clocks

Derived Operators if x then e 2 else e 3 = merge x (True e 2 when True(x)) (False e 3 when False(x)) y = e 1 -> e 2 = y = if init then e 1 else e 2 and init = True fby False pre (e) = nil fby e Example (counter) node counting (tick:bool; top:bool) = (o:int) with var v: int in o = if tick then v else 0 -> pre o + v and v = if top then 1 else 0 12

13 N-ary Merge merge combines two complementary flows (flows on complementary clocks) to produce a faster one:.. a2 a1 a3 Merge.. b7 b6 b5 b4 b3 b2 b1 a3 a2 a1.. b7 b6 b5 b4 b3 b2 b1 Originaly introduced in Lucid Synchrone (1996), now in SCADE 6 Example: merge c (a when c) (b whenot c) Generalization: can be generalized to n inputs with a specific extension of clocks with enumerated types the sampling e when c is now written e when True(c) the semantics extends naturally and we know how to compile it efficiently thus, a good basic for compilation

14 Reseting a behavior in Lustre, the reset behavior of an operator must be explicitely designed with a specific reset input node count() returns (s:int); let s = 0 fby s + 1 tel; node resetable_counter(r:bool) returns (s:int); let s = if r then 0 else 0 fby s + 1; tel; painful to apply on large model propose a primitive that applies on node instance and allow to reset it

15 Modularity and reset Specific notation in the basic calculus: f (a 1,..., a n ) every c all the node instances used in the definition of node f are reset when c is true the reset is asynchronous : no clock constraint between the condition c and the clock of the node instance is-it a primitive construct? yes and no modular translation of the basic language with reset into the basic language without reset ([PPDP00], with G. Hamon) essentially a translation of the initialization operator -> e 1 -> e 2 becomes if c then e 1 else e 2 very demanding to the code generator whereas it is trivial to compile! useful translation for verification tools, basic for compilation thus, a good basic for compilation

16 Translation Normalization Annotated CDK Annotated CDK (normalized) data flow transformations (CSE, Constant Prop.) Inlining Local Transformation + (naive to clever) scheduling ObjBasedLanguage EmitC Structured C

17 Putting Equations in Normal Form prepare equations before the translation extract delays from nested expressions by a linear traversal equations are transformed such that delays are extracted from nested computation. Normal Form: a ::= e ck e ::= a when C(x) op (a,..., a) x v ce ::= merge x (C ca)... (C ca) e ca ::= ce ck eq ::= x = ca x = (v fby a) ck (x,..., x) = (f (a,..., a) every x) ck D ::= D and D eq

18 Example is rewritten into: ck on True(c) z = ((((4 fby o) 3) when True(c)) + k) and o = (merge c (True (5 fby (z + 1)) + 2) (False ((6 fby x)) when False(c))) ck ck on True(c) z = (((t 1 3) when True(c)) + k) and o = (merge c (True t 2 + 2) and t 1 = (4 fby o) ck (False t 3 when False(c))) ck ck on True(c) and t 2 = (5 fby (z + 1)) and t 3 = (6 fby x) ck

19 Intermediate Language d ::= class f = memory m, instances j, reset() returns () = S, step (p) returns (p) = var p in S S ::= x := c state (x) := c S ; S skip o.reset (x,..., x) = o.step (c,..., c) case (x) {C : S;...; C : S} c ::= x v state (x) op(c,..., c) v ::= C i j ::= o : f,..., o : f p, m ::= x : t,..., x : t

20 Intermediate Language the minimal need to represent transition functions we introduce an ad-hoc intermediate language to represent them it has an object-based flavor (with minimal expressiveness nonetheless) static allocation of states only it can be trivially translated into a imperative language we only need a subset set of C (functions and static allocation of structures, very simple pointer manipulation)

21 Principles of the translation hierarchical memory model which corresponds to the call graph: one local memory for each function call every function defines a machine (a class ) the translation is made by a linear traversal of the sequence of normalized and scheduled equations control-structure (invariant): a computation on clock ck is executed when ck is true a guarded equations x = e ck translates into a control-structure E.g., the equation: x = (y + 2) base on C 1(x 1 ) on C 2 (x 2 ) is translated into a piece of control-structure: case (x 1 ) {C 1 : case (x 2 ) {C 2 : x = y + 2}}

22 Clock-directed code generation local generation of a control-structure from a clock merge them locally Control(base, S) = S Control(ck on C(x), S) = Control(ck, case (x) {C : S}) Join( case (x) {C 1 : S 1 ;...; C n : S n }, case (x) {C 1 : S 1 ;...; C n : S n}) = case (x) {C 1 : Join(S 1, S 1 );...; C n : Join(S n, S n)} Join(S 1, S 2 ) = S 1 ; S 2 control-optimization: find a static schedule of the equations which gather equations guarded by related clocks

23 Example (modularity) each function is compiled separately a function call needs a local memory node count(x:int) returns (o:int) let o = 0 fby o + x; tel node condact(c:bool;input:int) returns (o:int) var v: int; let v = count(input when true(c)); o = merge c (true -> v) (false -> (0 fby o) when false(c)); tel

24 class condact { x_2 : int; x_4 : count; reset() { x_4.reset(); mem x_2 = 0; } step(c : bool; input : int) returns (o : int) { x_3 : int; switch (c) { case true : (x_3) = x_4.step(input); o = x_3; case false : o = mem(x_2); }; mem x_2 = o; }

25 MiniLustre in Numbers Administrative code Abstract syntax + printers 546 Lexer&Parser 335 main (misc, symbol tables, loader) 285 Basis graph 74 scheduling 67 type checking 269 clock checking 190 causality check 30 normalization 95 translate (to ob) 136 Emitters to concrete languages (C, Java and Caml) arround 300 each Optimizations Inline + reset 250 Dead-code Removal 42 Data-flow network minimization 162

26 Extensions (towards a full language) Purely DataFlow Language + automata, signals, loop iteration, richer clocks, etc. Clocked DataFlow Language extend the source language with new programming constructs translation semantics into the basic data-flow language this is essentially the approach taken in Lucid Synchrone and the ReLuC compiler of SCADE clocks play a central role simple and gives very good code reuse of the existing code generator (adequate in the context of a certification process)

27 Formal Certification (Coq development) In parallel, we have implemented MiniLustre in the programming language of Coq + parts in Caml some part are directly programmed in Coq (2000 loc): E.g., translation function, checking clock/type annotation others (administrative) or less structural (e.g., scheduling) kept in Caml type and clock inference also done (4000 loc of Coq) The semantics of the source and target with proof of equivalence between the two are done on paper (one year to find the adequate combination). Proofs in Coq are under way

28 Conclusion Results a minimal description of modular code-generation formalize and simplifies the compilation techniques we have developped for the ReLuC compiler (with Esterel-Technologies) used for SCADE 6 Current Coq development of the semantics preservation finish the Coq programming of the reference compiler (combines translation validation, e.g., for the clever scheduling of equations) and certified compilation) Future (longer term) mixed systems (data-flow systems + mode-automata) source-to-source transformation into the data-flow system translation semantics (as done in ReLuC [EMSOFT 05, EMSOFT 06])