Fichier source minic
Fichier minic sans directives de préprocesseur
Phase de preprocessing, le préprocesseur ne gère pour l'instant que les déclarations #include
, il s'occupe de chercher le fichier inclus et copier son contenu dans le fichier sortie.
Types utilisés : voir minic_ast.ml ou en annexe
Arbre de syntaxe abstraite issu de l'analyse syntaxique
Le même arbre de syntaxe abstraite, où chaque expression possède un type et une indication sur sa calculabilité à la compilation
- Déduit le type de chaque expression du programme
- Vérifie la cohérence des types
- Indique si une expression est "constante" (définition c d'une expression constante)
- Ajoute des conversions de types implicites
Phase de vérification des types, et 1ère phase de la compilation, à chaque expression est associé un type et un booléen const
, une expression constante est une expression uniquement composée de littéraux et d'opérateurs.
Types utilisés : voir minic.ml ou en annexe
Arbre de syntaxe abstraite typé
Arbre de syntaxe abstraite non typé
- Suppression des types associés aux expressions
- Calcul des expressions constantes
- Remplacement des noms de variables locales par des identifiants uniques (à l'échelle de la fonction)
- Transformation des tableaux en pointeurs
- Ajout d'une fonction
__init
permettant d'initialiser les variables globales - Récupération des données qui devront être placées en mémoire
À la fin de cette étape, les noms de variables locales ont été traduits en des identifiants uniques à l'échelle d'une fonction. On a également connaissance de toutes les données qui devront être placées en mémoire statique, et donc de la taille de la "section data" du programme. Les expressions constantes (ex : taille des tableaux) sont également calculées à cette étape.
On ajoute également une fonction spéciale qui a pour rôle d'initialiser les variables globales, en WebAssembly les variables globales ne peuvent être initialisées qu'avec des constantes (pour l'instant), d'où la nécessité d'ajouter une fonction permettant d'initialiser ces variables avec des expressions impliquant des opérateurs
;; valide
(global $v (mut i32) i32.const 3)
;; invalide
(global $v (mut i32)
i32.const 3
i32.const 2
i32.add
)
(remarque : on pourrait également calculer la valeur finale de ces expressions à la compilation, et utiliser le résultat pour initialiser directement la variable globale en question)
Types utilisés : voir llir.ml ou en annexe
Arbre de syntaxe abstraite non typé
Représentation intermédiaire sous forme de pile
Cette phase traduit le programme sous forme d'AST en un programme équivalent mais ayant une représentation sous forme de pile, plus proche du programme wasm final.
Types utilisés : voir wasm.ml ou en annexe
Programme sous représentation intermédiaire LLIR
Programme WebAssembly
- Traduction directe des fonctions LLIR
- Ajout des nœuds (
start
,memory
, ...) nécessaires à exécution
Cette phase traduit le programme sous représentation LLIR en programme wasm. Un programme WebAssembly est constitué d'un nœud module
suivi du contenu de ce module, lors de cette phase on ajoute les nœuds suivantes au module :
(start $__init)
: permet d'appeler la fonction__init
lorsque le module est instancié(memory n)
: ajoute une zone de mémoire au module, avec une taille initiale den
pages (n*64KiB)(data ...)
: ajoute des données à la sectiondata
du module (pour l'instant sert à stocker les initialisateurs des tableaux)(global ...)
pour chaque variable globale(func ...)
pour chaque fonction du programme
Programme sous représentation wasm (type expr
)
Affichage du code .wat (WebAssembly text) correspondant
(** Représentation des types. *)
type integral_type =
| Char
| Int
type constant =
| CInteger of integral_type * Int64.t
| CBool of bool
| CIList of constant list
(** Types des opérations binaires *)
type binop =
| Add | Sub | Mult | Div | Mod
| Eq | Neq | Lt | Leq | Gt | Geq
| And | Or
| BAnd | BOr | BXor
| Lsl | Asr
(** Types des opérations unaires *)
type unop =
| Minus
| Not
| BNot
type typ =
| Integer of integral_type
| Bool
| Void
| Ptr of typ
| Tab of typ * expr
(** Représentation des expressions. *)
and expr_s =
| Cst of constant
| Cast of expr * typ * typ
| InitList of expr list
| UnaryOperator of unop * expr
| BinaryOperator of binop * expr * expr
| Get of string
| Read of expr * expr (* adresse * offset *)
| Call of string * expr list
(** Représentation d'une expression typée *)
and expr = {
t: typ;
const: bool;
expr: expr_s
}
(** Type d'une déclaration de variable *)
type var_decl = string * typ * expr
(** Représentation des instructions. *)
type instr =
| Decl of var_decl list
| Set of string * expr
| Write of expr * expr * expr
| If of expr * block * block
| While of expr * block
| Return of expr
| Expr of expr
| Block of block
(** Type des blocs de code *)
and block = instr list
(** Représentation des fonctions. *)
type fun_def = {
name: string;
params: (string * typ) list;
return: typ;
body: block;
}
(** Type d'un déclaration dans la portée globale (une déclaration de variable
globale ou de fonction) *)
type global_decl =
| Variable of var_decl
| Function of fun_def
| ForwardDecl of fun_def
(** Représentation des programmes.
Un programme est simplement une suite de déclarations de variables ou de fonctions. *)
type prog = global_decl list
type typ =
| Integer of integral_type
| Bool
| Void
| Ptr of typ
| Tab of typ * int
(** Représentation des expressions, on retire les informations de type de l'AST *)
type expr =
| Cst of const_expr
| Cast of expr * typ * typ
| InitList of bool * expr list
| UnaryOperator of typ * unop * expr
| BinaryOperator of typ * binop * expr * expr
| Get of string
| Read of typ * expr
| Call of string * expr list
(** Type des valeurs connues à la compilation *)
and const_value =
| Integral of Int64.t
| Floating of float
| List of const_expr list
and const_expr = {
t: typ;
value: const_value;
}
(** Représentation des instructions, on n'utilise plus le constructeur de déclaration
de variable dans cette représentation *)
type instr =
| Set of string * expr
| StaticMemcpy of expr * int * int (* dest, id du segment, taille du segment *)
| Write of typ * expr * expr
| If of expr * block * block
| While of expr * block
| Return of expr
| Expr of expr
| Block of block
(** Type des blocs de code *)
and block = instr list
type fun_def = {
name: string;
params: (string * typ) list;
locals: (string * typ) list;
return: typ;
body: block;
}
type prog = {
static: (int * string) list; (* Données mémoire *)
persistent: string list; (* Données persistentes, par ex : initialisateur des tableaux*)
globals: (string * typ) list;
functions: fun_def list;
extern_functions: fun_def list;
}
type var =
| Global of string
| Param of int
| Local of int
type num_op =
| Add | Sub | Mul | Div | Mod
| Eq | Eqz | Neq | Lt | Leq | Gt | Geq
| And | Or | Not
| BAnd | BOr | BXor
| Lsl | Asr
type constant =
| I32Cst of Int32.t
| I64Cst of Int64.t
| F32Cst of float
| F64Cst of float
type datatype =
| Int8
| Int16
| Int32
| Int64
| Float32
| Float64
type instr =
| Cst of constant
| Cast of datatype * datatype
| Op of Wasm.dtype * num_op
| Get of var
| Set of var
| Load of Wasm.dtype
| Store of Wasm.dtype
| MemInit of int
| If of seq * seq
| While of seq * seq (* Liste des instructions de la condition d'arrêt + corps de la boucle *)
| Call of string
| Return
| Drop
and seq = instr list
type fun_def = {
name: string;
params: Wasm.dtype list;
locals: Wasm.dtype list;
return: Wasm.dtype option;
code: seq;
}
type prog = {
static: (int option * string) list;
globals: (string * Wasm.dtype) list;
functions: fun_def list;
extern_functions: fun_def list;
}
type dtype = I8 | I16 | I32 | I64 | F32 | F64
type qualifier = Mut | Const
type expr =
| Instr of string list
| Module of seq
| Block of seq
| Loop of seq
| If of seq * seq
| Function of string * dtype list * dtype option * dtype list * seq
| ImportedFunction of string * dtype list * dtype option
| Global of string * qualifier * dtype * seq
| Start of string
| Memory of int
| Data of int option * string
| Comment of string
and seq = expr list