Programming Languages and Compiler Design

Similar documents
Compiler Construction

Compiler Construction

G Programming Languages - Fall 2012

CS 480 Fall Runtime Environments. Mike Lam, Professor. (a.k.a. procedure calls and heap management)

System Software Assignment 1 Runtime Support for Procedures

CS 314 Principles of Programming Languages. Lecture 13

CprE 288 Introduction to Embedded Systems Course Review for Exam 3. Instructors: Dr. Phillip Jones

Compilers and computer architecture: A realistic compiler to MIPS

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

CSE 504: Compiler Design. Runtime Environments

CS356: Discussion #6 Assembly Procedures and Arrays. Marco Paolieri

Principles of Programming Languages Topic: Scope and Memory Professor Louis Steinberg Fall 2004

Principles of Programming Languages

Compiler Construction

Today's Topics. CISC 458 Winter J.R. Cordy

7 Translation to Intermediate Code

Module 27 Switch-case statements and Run-time storage management

Compiler Construction

CSE 504. Expression evaluation. Expression Evaluation, Runtime Environments. One possible semantics: Problem:

CIT Week13 Lecture

Lecture 9: Procedures & Functions. CS 540 George Mason University

Mosig M1 - PLSCD Written exam

Compiler Construction

CprE 288 Introduction to Embedded Systems ARM Assembly Programming: Translating C Control Statements and Function Calls

Course Administration

Chapter 2. Computer Abstractions and Technology. Lesson 4: MIPS (cont )

! Those values must be stored somewhere! Therefore, variables must somehow be bound. ! How?

Code Generation. The Main Idea of Today s Lecture. We can emit stack-machine-style code for expressions via recursion. Lecture Outline.

We can emit stack-machine-style code for expressions via recursion

COMP 303 Computer Architecture Lecture 3. Comp 303 Computer Architecture

THEORY OF COMPILATION

Scope, Functions, and Storage Management

Code Generation and Optimisation. Local variables and procedures

Lecture 7: Procedures

Compiler Construction

Compiler Construction I

Compiler Construction

CMa simple C Abstract Machine

CSE443 Compilers. Dr. Carl Alphonce 343 Davis Hall

Compilation /15a Lecture 7. Activation Records Noam Rinetzky

Procedure and Object- Oriented Abstraction

Instructions: Assembly Language

CS61C : Machine Structures

Compiler Theory. (Semantic Analysis and Run-Time Environments)

Review of Activation Frames. FP of caller Y X Return value A B C

Separate compilation. Topic 6: Runtime Environments p.1/21. CS 526 Topic 6: Runtime Environments The linkage convention

CSCI 402: Computer Architectures. Instructions: Language of the Computer (3) Fengguang Song Department of Computer & Information Science IUPUI.

Topic 7: Activation Records

Branch Addressing. Jump Addressing. Target Addressing Example. The University of Adelaide, School of Computer Science 28 September 2015

Control Instructions. Computer Organization Architectures for Embedded Computing. Thursday, 26 September Summary

Control Instructions

Lecture08: Scope and Lexical Address

Code Generation. Lecture 19

Plan. Regression testing: Demo of how to use the regress.sh script.

Winter Compiler Construction T11 Activation records + Introduction to x86 assembly. Today. Tips for PA4. Today:

Run-Time Environments

G53CMP: Lecture 14. Run-Time Organisation I. Henrik Nilsson. University of Nottingham, UK. G53CMP: Lecture 14 p.1/37

CS558 Programming Languages Winter 2018 Lecture 4a. Andrew Tolmach Portland State University

This Lecture. G53CMP: Lecture 14 Run-Time Organisation I. Example: Lifetime (1) Storage Areas

COMP3221: Microprocessors and. and Embedded Systems. Overview. Variable Types and Memory Sections. Types of Variables in C

Concepts Introduced in Chapter 7

Midterm II CS164, Spring 2006

The Activation Record (AR)

CENG3420 Lecture 03 Review

231 Spring Final Exam Name:

CSE443 Compilers. Dr. Carl Alphonce 343 Davis Hall

Functional Programming. Pure Functional Programming

Chapter 3 Machine Instructions & Programs. Jin-Fu Li Department of Electrical Engineering National Central University Jungli, Taiwan

UCB CS61C : Machine Structures

Example. program sort; var a : array[0..10] of integer; procedure readarray; : function partition (y, z :integer) :integer; var i, j,x, v :integer; :

CS 314 Principles of Programming Languages

Typical Runtime Layout. Tiger Runtime Environments. Example: Nested Functions. Activation Trees. code. Memory Layout

Chapter 10. Implementing Subprograms

CS 432 Fall Mike Lam, Professor. Code Generation

Code Generation. Lecture 12

Run-time Environments. Lecture 13. Prof. Alex Aiken Original Slides (Modified by Prof. Vijay Ganesh) Lecture 13

Static Program Analysis

Intermediate Code Generation

Virtual Machine Tutorial

Special Topics: Programming Languages

Lexical Considerations

Implementing Procedure Calls

CS61C : Machine Structures

Parsing Scheme (+ (* 2 3) 1) * 1

ARM Assembly Programming

Lecture Outline. Topic 1: Basic Code Generation. Code Generation. Lecture 12. Topic 2: Code Generation for Objects. Simulating a Stack Machine

Application: Programming Language Semantics

CVO103: Programming Languages. Lecture 5 Design and Implementation of PLs (1) Expressions

ECE 15B Computer Organization Spring 2010

Instruction Set Architectures Part I: From C to MIPS. Readings:

Chapter 9 :: Subroutines and Control Abstraction

Implementing Subprograms

CSc 520 Principles of Programming Languages. Questions. rocedures as Control Abstractions... 30: Procedures Introduction

Compiling Code, Procedures and Stacks

CS558 Programming Languages

Run-time Environments - 2

2. ADDRESSING METHODS

Lecture 04: Machine Instructions

CS153: Compilers Lecture 8: Compiling Calls

Computer Architecture. Chapter 2-2. Instructions: Language of the Computer

Transcription:

Programming Languages and Compiler Design Programming Language Semantics Compiler Design Techniques Yassine Lakhnech & Laurent Mounier {lakhnech,mounier}@imag.fr http://www-verimag.imag.fr/ lakhnech http://www-verimag.imag.fr/ mounier. Master of Sciences in Informatics at Grenoble (MoSIG) Grenoble Universités (Université Joseph Fourier, Grenoble INP) Yassine Lakhnech, Sémantique Start C3 C4 p.1/50

Code Generation Yassine Lakhnech, Sémantique Start C3 C4 p.2/50

Overview 1. Introduction 2. The M Machine 3. Code generation for basic while 4. Extension 1: blocks and procedures 5. Extension 2: some OO features Yassine Lakhnech, Sémantique Start C3 C4 p.3/50

Main issues for code generation input : (well-typed) source pgm AST output : machine level code Expected properties for the output: compliance with the target machine instruction set, architecture, memory access, OS,... correctness of the generated code semantically equivalent to the source pgm optimality w.r.t. non-functional criteria execution time, memory size, energy comsumption,... Yassine Lakhnech, Sémantique Start C3 C4 p.4/50

A pragmatic approach AST intermediate code generation Intermediate Representation 1... Intermediate Representation n (final) code generation target machine code optimization(s) optimization(s) Yassine Lakhnech, Sémantique Start C3 C4 p.5/50

Intermediate Representations Abstractions of a real target machine generic code level instruction set simple addressing modes simple memory hierarchy Examples a stack machine a register machine etc. Rk: other intermediate representations are used in the optimization phases... Yassine Lakhnech, Sémantique Start C3 C4 p.6/50

The M Machine Machine with (unlimited) registers Ri special registers: program counter PC, frame pointer FP, stack pointer SP, register R0 (contains always 0) Instructions, addresses, and integers take 4 bytes in memory Address of variable x is E - offx where: E = address of the environment definition of x offx = offset of x within this environment (staticaly computed, stored in the symbol table) Addressing modes: Ri, val (immediate), Ri +/- Rj, Ri +/- offset usual arithmetic instructions OPER: ADD, SUB, AND, etc. usual (conditional) branch instructions BRANCH: BA, BEQ, BGT, etc. Yassine Lakhnech, Sémantique Start C3 C4 p.7/50

Instruction Set instruction OPER Ri, Rj, Rk OPER Ri, Rk, val CMP Ri, Rj LD Ri, [adr] ST Ri, [adr] BRANCH label CALL label RET informal semantics Ri Rj oper Rk Ri Rj oper val Ri-Rj (set cond flags) Ri Mem[adr] Mem[adr] Ri if cond then PC label else PC PC + 4 branch to the procedure labelled with label end of procedure Yassine Lakhnech, Sémantique Start C3 C4 p.8/50

The while language p ::= d ; c d ::= var x d ; d s ::= x := a s ; s if b then s else s while b s a ::= n x a + a a * a... b ::= a = a b and b not b... Rk: terms are well-typed distinction between boolean and arithmetic expr. Exo: Give the M Machine code for the following terms: 1. y := x+42 * (3+y) 2. if (not x=1) then x := x+1 else x := x-1 ; y := x ; Yassine Lakhnech, Sémantique Start C3 C4 p.9/50

Functions for Code Generation GCStm : Stm Code GCStm(s) computes the code C corresponding to statement s. GCAExp : Exp Code Reg GCAExp(e) returns a pair (C, i) where C is the code allowing to 1. compute the value of e, 2. store it in Ri. GCBExp : BExp Label Label Code GCBExp(b, ltrue, lfalse) produces code C allowing to compute the value of b and branch to label ltrue when this value is true and to lfalse otherwise. Yassine Lakhnech, Sémantique Start C3 C4 p.10/50

Auxilliary functions AllocRegister : Reg allocate a new register Ri newlabel : Labels produce a new label GetOffset : Var IN returns the offset corresponding to the specified name denotes concatenation for Code sequences. Yassine Lakhnech, Sémantique Start C3 C4 p.11/50

GCStm GCStm (x := e) = Let (C,i)=GCAExp(e), k=getoffset(x) in C ST Ri, [FP-k] GCStm (c 1 ; c 2 ) = Let C 1 = GCStm(c 1 ), C 2 = GCStm(c 2 ) in C 1 C 2 Yassine Lakhnech, Sémantique Start C3 C4 p.12/50

GCStm (2) GCStm (while e c) = Let lb=newlabel(), ltrue=newlabel(), in lfalse=newlabel() lb: GCBExp(e,ltrue,lfalse) ltrue: GCStm(c) BA lb lfalse: Yassine Lakhnech, Sémantique Start C3 C4 p.13/50

GCStm (3) GCStm (if e then c 1 else c 2 ) = Let lnext=newlabel(), ltrue=newlabel(), in lfalse=newlabel() GCBExp(e,ltrue,lfalse ltrue: GCStm(c 1 ) BA lnext lfalse: GCStm(c 2 ) lnext: Yassine Lakhnech, Sémantique Start C3 C4 p.14/50

GCAexp GCAExp(x) = Let i=allocregister() k=getoffset(x) in ((LD Ri,[FP-k]),i) GCAExp(n) = Let i=allocregister() in ((ADD Ri,R0,n),i) GCAExp(e 1 + e 2 ) = Let (C 1,i 1 )=GCAExp(e 1 ), (C 2,i 2 )=GCAExp(e 2 ), k=allocregister() in ((C 1 C 2 ADD Rk, Ri 1,Ri 2 ),k) Yassine Lakhnech, Sémantique Start C3 C4 p.15/50

GCBexp GCBExp (e 1 = e 2,ltrue,lfalse) = Let (C 1,i 1 )=GCAExp(e 1 ), (C 2,i 2 )=GCAExp(e 2 ), in C 1 C 2 CMP Ri 1, Ri 2 BEQ ltrue BA lfalse GCBExp (e 1 et e 2,ltrue,lfalse) = Let l=newlabel() in GCBExp(e 1,l,lfalse) l: GCBExp(e 2,ltrue,lfalse) GCBExp(NOT e,ltrue,lfalse) = GCBExp(e,lfalse,ltrue) Yassine Lakhnech, Sémantique Start C3 C4 p.16/50

Exercises code obtained for y := x+42 * (3+y) if (not x=1) then x := x+1 else x := x-1 ; y := x ; add new statements (e.g, repeat) add new operators (e.g, b? e1 : e2) Yassine Lakhnech, Sémantique Start C3 C4 p.17/50

Extension 1: blocks Yassine Lakhnech, Sémantique Start C3 C4 p.18/50

Blocks Syntax S ::= begin D V ; S end D V ::= var x D V ; D V Rk: variables are unitialized and assumed to be of type Int Problems raised for code generation to preserve scoping rules: local variables should be visible inside the block their lifetime should be limited to block execution Possible locations to store local variables registers vs memory Yassine Lakhnech, Sémantique Start C3 C4 p.19/50

Storing local variables in memory - Example 1 begin var x ; var y ; var z ;... end FP x y z offy a memory environment is associated to each declaration Dv register FP contains the address of the current environment (static) offsets are associated to each local variables Yassine Lakhnech, Sémantique Start C3 C4 p.20/50

Storing local variables in memory - Example 2 begin var x ; var y ; <s1> begin var x ; var z ; <s2> end ; <s3> end FP <s1> x y... <s2> x y... FP <s3> x y... FP x z entering/leaving a block allocate/de-allocate a mem. env. nested block env. have to be linked together: Ariane link a stack of memory environments... ( operational semantics) Yassine Lakhnech, Sémantique Start C3 C4 p.21/50

Structure of the memory code data... 1 2... 3 SP 1: global variables 2: execution stack, SP = last occupied address 3: heap (for dynamic allocation) Yassine Lakhnech, Sémantique Start C3 C4 p.22/50

Code generation for variable declarations SizeDecl : D V IN SizeDecl(d) computes the size of declarations d SizeDecl (var x) = 4 (x of type Int) SizeDecl (d 1 ; d 2 ) = Let v 1 = SizeDecl(d 1 ), v 2 = SizeDecl(d 2 ) in v 1 + v 2 Yassine Lakhnech, Sémantique Start C3 C4 p.23/50

Code Generation for blocks GCStm (begin d ; s ; end) = Let size =SizeDecl(d), C=GCStm(s) in ADD, SP, SP, -4 ST FP, [SP] ADD FP, SP, 0 ADD SP, SP, size C ADD SP, FP, 0 LD FP, [SP] ADD SP, SP, 4 Yassine Lakhnech, Sémantique Start C3 C4 p.24/50

With the help of some auxilliary functions... prologue(size) epilogue push register(ri) ADD SP, SP, -4 ADD SP, FP, 0 ST FP, [SP] ADD SP, SP, -4 LD FP, [SP] ADD FP, SP, 0 ST Ri, [SP] ADD SP, SP, +4 ADD SP, SP, size GCStm (begin d ; s ; end) = Let size =SizeDecl(d), C=GCStm(s) in Prologue(size) C Epilogue Yassine Lakhnech, Sémantique Start C3 C4 p.25/50

Access to variables from a block?... begin var... x :=... end What is the memory address of x? if x is a local variable (w.r.t the current block) adr(x) = FP + GetOffset(x) if x is a non local variable it is defined in a nesting memory env. E adr(x) = adr(e) + GetOffset(x) adr(e) can be accessed through the Ariane link... Yassine Lakhnech, Sémantique Start C3 C4 p.26/50

Access to non local variables The number n of indirections to perform on the Ariane link depends on the distance between: the nesting level of the current block : p the nesting level of the target environment : r More precisely: r p n = p r n can be staticaly computed... Yassine Lakhnech, Sémantique Start C3 C4 p.27/50

Example begin var x ; /* env. E1, nesting level = 1 */ begin var y ; /* env. E2, nesting level = 2 */ begin var z ; /* env. E3, nesting level = 3 */ x := y + z /* s, nesting level = 3 */ end end end From statement s: no indirection to access to z 1 indirection to access to y 2 indirections to access to x Yassine Lakhnech, Sémantique Start C3 C4 p.28/50

Code generation for variable access 1. the nesting level r of each identifier x is computed during type-checking; 2. it is associated to each occurrence of x in the AST (via the symbol table) 3. function GCStm keeps track of the current nesting level p (incremented/decremented at each block entry/exit) adr(x) is obtained by executing the following code: if r = p: if r < p: FP + GetOffset(x) LD Ri, [FP] LD Ri, [Ri]} Ri + GetOffset(x) (p r 1) times Yassine Lakhnech, Sémantique Start C3 C4 p.29/50

Example (ctn d) begin var x ; /* env. E1, nesting level = 1 */ begin var y ; /* env. E2, nesting level = 2 */ begin var z ; /* env. E3, nesting level = 3 */ x := y + z /* s, nesting level = 3 */ end end end FP x y z LD R1, [FP]! R1 = adr(e2) LD R2, [R1 + offy]! R2 = y LD R3, [FP + offz]! R3 = z ADD R4, R2, R3! R4 = y+z LD R5, [FP] LD R5, [R5]! R5 = adr(e1) ST R4, [R5 + offx]! x = y + z Code generated for statement s Yassine Lakhnech, Sémantique Start C3 C4 p.30/50

Extension 2: Procedures Yassine Lakhnech, Sémantique Start C3 C4 p.31/50

Syntax Procedure declarations: D P FP L ::= proc p (FP L ) is S ; D P ǫ ::= x, FP L ǫ Statements: S ::= begin D V ;D P ; S end call p(ep L ) EP L ::= AExp, EP L ǫ FP L : formal parameters list ; EP L : effective parameters list Rk: we assume here value-passing of integer parameters... Yassine Lakhnech, Sémantique Start C3 C4 p.32/50

Example var z ; proc p1 () is begin proc p2(x, y) is z := x + y ; z := 0 ; call p2(z+1, 3) ; end proc p3 (x) is begin var z ; call p1() ; z := z+x ; end call p3(42) ; Yassine Lakhnech, Sémantique Start C3 C4 p.33/50

Main issues for code generation Procedure P is calling procedure Q... Before the call: set up the memory environment of Q evaluate and transmit the effective parameters switch to the memory environment of Q branch to first intruction of Q During the call: access to local/non local procedures and variables access to parameter values After the call: switch back to the memory environment of P resume execution to the P instruction following the call Yassine Lakhnech, Sémantique Start C3 C4 p.34/50

Access to non-local variables proc main is begin /* definition env. of p */ var x ; proc p() is x:=3 ; proc q() is begin var x ; proc r() is call p() ; call r() ; end ; call q() ; end Static binding when p is executed: acces to the memory env. of main = definition environment of the callee, static link acces to the memory env. of r memory environment of the caller, dynamic link Yassine Lakhnech, Sémantique Start C3 C4 p.35/50

Information exchanged between callers and callees? parameter values return address address of the caller memory environment (dynamic link) address of the callee environment definition (static link) This information should be stored in a memory zone: dynamically allocated (exact number of procedure calls cannot be foreseen at compile time) accessible from both parties (those address could be computed by the caller and the callee) inside the execution stack, at well defined offsets w.r.t FP Yassine Lakhnech, Sémantique Start C3 C4 p.36/50

A possible protocol between the two parties Before the call, the caller: evaluates the effective parameters pushes their values pushes the static link of the callee pushes the return address, and branch to the callee s 1st instruction when it begins, the callee: pushes FP (dynamic link) assigns SP to FP (memory env. address) allocates its local variables on the stack when it ends, the callee: de-allocates its local variables restores FP to caller s memory env. (dynamic link) branch to the return address, and pops it from the stack After the call, the caller de-allocates the static link and parameters Yassine Lakhnech, Sémantique Start C3 C4 p.37/50

Organization of the execution stack low addresses SP Addresses, from the callee: local variables memory environment of the callee loc. variables: FP+d, d<0 dynamic link: FP FP dynamic link return address static link parameters return address: FP+4 static link: FP+8 parameters: FP+d, d>=12 high addresses memory environment of the caller Yassine Lakhnech, Sémantique Start C3 C4 p.38/50

Memory environment of the callee... 0 Loc. var n SP, FP- 4*n... Loc. var 1 FP Dynamic link FP Return address FP+4 Static link FP+8 Param n FP+12... Param 1 FP+8+4*n Yassine Lakhnech, Sémantique Start C3 C4 p.39/50

Code generation for a procedure declaration GCProc : D P Code GCStm(dp) computes the code C corresponding to procedure declaration dp. GCProc (proc p (FP L ) is s end ) = Let in C=GCStm(s) Prologue(0) C Epilogue GCProc (proc p (FP L ) is begin dv ; dp ; s end ) = Let size =SizeDecl(dv), C=GCStm(s) in Prologue(size) C Epilogue Rk: this function is applied to each procedure declaration Yassine Lakhnech, Sémantique Start C3 C4 p.40/50

Prologue & Epilogue Prologue (size): push (FP) ADD FP, SP, 0 ADD SP, SP, -size! dynamic link! FP := SP! loc. variables allocation Epilogue: ADD SP, FP, 0 LD FP, [SP] ADD SP, SP, +4 RET! SP := FP, loc. var. de-allocation! restore FP! return to caller RET: LD PC, [SP] // ADD SP, SP, +4 Yassine Lakhnech, Sémantique Start C3 C4 p.41/50

Code Generation for a procedure call Four steps: 1. evaluate and push each effective parameter 2. push the static link of the callee 3. push the return address and branch to the callee 4. de-allocate the parameter zone GCStm (call p (ep)) = Let (C, size) = GCParam(ep) in C Push (StaticLink(p)) CALL p ADD SP, SP, size+4 CALL p: ADD R1, PC, +4 // Push (R1) // BA p Yassine Lakhnech, Sémantique Start C3 C4 p.42/50

Parameters evaluation GCParam : EP L Code IN GCStm(ep)=(c,n) where c is the code to evaluate and push each effective parameter of ep and n is the size of pushed data. GCParam (ε) = (ε, 0) GCParam (a ; ep) = Let in (Ca, i) = GCAexp (a), (C, size) = GCParam (ep) (Ca Push (R i ) C, 4 + size) Yassine Lakhnech, Sémantique Start C3 C4 p.43/50

Static link and non local variable access? A global (unique) name is given to each identifier: proc Main is proc P1 (...) is... proc Pn (...) is begin var x... end This notation induces a partial order: x is named Main.P 1..P n.x (Main.P 1.P n Main.P 1.P n ) (n n and k n.p k = P k ) For an identifier x = Main.P1.P n.x, x = Main.P 1.P n is the definition environment of x For any identifier x (variable or procedure), procedure P can access x iff x P. Yassine Lakhnech, Sémantique Start C3 C4 p.44/50

Examples A variable x declared in P can be accessed from P since x = P (hence x P ). If g and x are declared in f, then x can be accessed from g since x = f and f g. If x and f 1 are declared in Main, f 2 is declared in f 1, then x can be accessed from f 2 since x = Main, f 2 = Main.f 1.f 2 (x f 2 ) If p 1 and p 2 are both declared in Main, x is declared in p 1, then x cannot be accessed from p 2, since x = Main.p 1 and Main.p 1 Main.p 2 Yassine Lakhnech, Sémantique Start C3 C4 p.45/50

Code Generation for accessing (non-) local identifiers d x : offset of x (variables or parameters) in its definition environment (x ) P : current procedure Condition x = variable or parameter x = procedure x = P adr(x) = FP+d x SL(x) = FP x < P n-k-1 indirections n-k-1 indirections x = M.P 1 P k LD R,[FP+8] LD R,[FP+8] P = M.P 1 P k P n LD R,[R+8]} (n k 1) LD R,[R+8]} (n k 1) adr(x) = R+d x SL(x)=R Yassine Lakhnech, Sémantique Start C3 C4 p.46/50

var z ; Back to the 1st example proc p1 () is begin proc p2(x, y) is z := x + y ; z := 0 ; call p2(z+1, 3) ; end proc p3 (x) is begin var z ; call p1() ; z := z+x ; end call p3(42) ; Exercice: give the execution stack when p2 is executed give the code for procedures p1 and p2 Yassine Lakhnech, Sémantique Start C3 C4 p.47/50

Exercice Consider the following extensions functions other parameter modes (by reference, by result) dynamic binding for variables and procedures? Yassine Lakhnech, Sémantique Start C3 C4 p.48/50

Procedures used as variables or parameters var z1 ; var p proc (int) ; /* p is a procedure variable */ proc p1 (x : int) is z1 := x ; proc p2 (q : proc (int)) is call q(2) ; proc q1 is begin var z1 ; proc q2 (y int) is z1 := x ; p := q2 ; call p ; end p := p1 ; call p ; call p2 (p1) ; Q: what code to produce for p :=...? for call p2(p1)? for call p? Yassine Lakhnech, Sémantique Start C3 C4 p.49/50

Information associated to a procedure at code level p := q2... call p To translate a procedure call, we need: the address of its 1st instruction the address of its environment definition Variable p should store both information At code level, a procedure type is a pair (address of code, address of memory environment) Exercice: code produced for the previous example? Yassine Lakhnech, Sémantique Start C3 C4 p.50/50