zapashcanon 10 months ago
parent
commit
1b61d69639
Signed by: zapashcanon GPG Key ID: 8981C3C62D1D28F1
  1. 45
      src/article.tex
  2. 21
      src/dune
  3. 29
      src/figures.ml
  4. 18
      src/layout.java
  5. 37
      src/layout.wast
  6. 26
      src/ral.fsx

45
src/article.tex

@ -6,9 +6,11 @@
\section{By hand: meta-programming}
\paragraph{Source code duplication by hand} TODO.
In this section, we'll discuss what we are \emph{not} talking about. That is to say, techniques used when a language do not properly supports polymorphism.
\paragraph{Source code generation} TODO.
\paragraph{Source code duplication by hand} When a programming language do not offer any facility to write polymorphic code, it is always possible to duplicate code by hand to handle different types.
\paragraph{Source code generation} It is possible to avoid doing this duplication by hand for each different type. This is done by writing a first program that will generate source code for a given type.
\paragraph{Source code transformation} It is possible to generate source code using a preprocessor, this is described in~\cite{SY74} under the name \emph{syntax-macro extension}. Its is used~\cite{EBN02} in the C programming language through it's preprocessor~\cite{SW87}. The technique is described in~\cite{Reb17}. For instance, given the \commandbox{list.c} file:
@ -90,6 +92,13 @@ For instance, this implementation of \emph{Revisited Binary Random-Access Lists}
The Rust compiler loops indefinitely on this file as it tries to generate an infinite number of specialised versions of the \texttt{len} function. Note that without the \texttt{main} function, the compiler succeeds. That's because monomorphization can only happen at linking. Without a main function, the \texttt{len} function can't be specialised as its call sites are still partly unknown.
TODO: cutoff C++
It may be possible to statically detect that a function is not monomorphizable. To the best of our knowledge this is an open problem.
TODO: si typage dans HM -> monomorphisation possible ?
TODO: demander à JHJ s'il a réoslu le pb avec la réduction vers le système de réécriture
\subsection{Optimisations}
\paragraph{Avoid useless specialisation} When a type parameter is not used by a function, it not necessary to specialise the function for this parameter. This optimisation is performed by the Rust compiler. It's not very common for a type parameter not to be used (i.e. not to appear in arguments or result).
@ -200,7 +209,7 @@ If $b_{1} = 1$, then the $n - 1$ most significant bits are a small scalar and $b
In the second case, we talk about \emph{small scalars} instead of scalars because we can only represents $2^{n - 1}$ values instead of the $2^n$ that are representable when all bits are available. For pointers, we do not loose anything as they need to be \emph{aligned} anyway and the last bit is always unused.
This technique is used by OCaml:
This technique is used by SML/NJ and OCaml:
\begin{tcolorbox}[breakable]
\input{layout.ml.tex}
@ -242,14 +251,44 @@ But it's possible to use a special tag in the array's block metadatas indicating
TODO: explain that Java could have i31 in generics
TODO: explain the Wasm way
\begin{tcolorbox}[breakable]
\input{layout.wast.tex}
\end{tcolorbox}
\begin{center}
\includegraphics[width=13em]{figure_wasm_layout.mps}
\end{center}
\section{Tagged union}
\section{Type-passing}
TODO: CLOS (Common Lisp Object System)
xo.m(x1, x2, ....)
m(x0, x1, x2)
TO, t1, t2 ---
int, int, int, ===== f1
int, int, float ==== f2
\subsection{Disadvantages}
TODO: it's really slow
\section{Runtime monomorphization}
Prevent empty bib~\cite{KS01}
\subsection{Polymorphic recursion}
Runtime monomorphization supports polymorphic recursion. Even if a function may statically be called with an unbounded number of types, this number is bounded at runtime. Each time a function is called with a new type, the JIT will monomorphize it.
\begin{tcolorbox}[breakable]
\input{ral.fsx.tex}
\end{tcolorbox}
When running the above code with \commandbox{fsharpc ral.fsx && chmod +x ral.exe && ./ral.exe}, the JIT is observable. Each time the length of the list is doubling, its type is getting bigger. This triggers the JIT and pause the execution. This is noticeable only on the first call to \commandbox{loop Nil}. On the second call, the functions have already been monomorphized and the execution is fast and no pause is happening.
\section{Comparison}
\section{Conclusion}

21
src/dune

@ -9,6 +9,7 @@
figure_ocaml_layout.mps
figure_python_layout.mps
figure_python_layout2.mps
figure_wasm_layout.mps
figure_python_block1.mps
figure_python_block2.mps
figure_ocaml_float1.mps
@ -17,6 +18,7 @@
layout.java.tex
layout.ml.tex
layout.py.tex
layout.wast.tex
list.c.tex
list.preprocessed.c.tex
list.d.tex
@ -24,6 +26,7 @@
main.bbl
main.tex
packages.tex
ral.fsx.tex
ral.rs.tex
styledef.tex)
(action
@ -60,18 +63,21 @@
figure_ocaml_layout.mps
figure_python_layout.mps
figure_python_layout2.mps
figure_wasm_layout.mps
figure_python_block1.mps
figure_python_block2.mps
forbidden.java.tex
layout.java.tex
layout.ml.tex
layout.py.tex
layout.wast.tex
list.c.tex
list.preprocessed.c.tex
list.d.tex
list_int.d.tex
main.tex
packages.tex
ral.fsx.tex
ral.rs.tex
styledef.tex)
(action
@ -141,6 +147,12 @@
(action
(run pygmentize -O envname=BVerbatim ral.rs -o %{targets})))
(rule
(targets ral.fsx.tex)
(deps styledef.tex ral.fsx)
(action
(run pygmentize -O envname=BVerbatim ral.fsx -o %{targets})))
(mdx
(deps list.c list.d)
(files CODE.md))
@ -168,7 +180,8 @@
figure_java_layout.mps
figure_ocaml_layout.mps
figure_python_layout.mps
figure_python_layout2.mps)
figure_python_layout2.mps
figure_wasm_layout.mps)
(deps figures.ml)
(action
(run mlpost %{deps})))
@ -189,6 +202,12 @@
(action
(run pygmentize -O envname=BVerbatim layout.java -o %{targets})))
(rule
(targets layout.wast.tex)
(deps styledef.tex layout.wast)
(action
(run pygmentize -O envname=BVerbatim layout.wast -o %{targets})))
(rule
(targets forbidden.java.tex)
(deps styledef.tex forbidden.java)

29
src/figures.ml

@ -303,6 +303,34 @@ let figure_ocaml_float2 =
in
Command.seq [ draw figure ]
let figure_wasm_layout =
let stack =
stack
[ stack_ptr "array1"
; stack_ptr "array2"
; stack_ptr "pair"
; stack_scalar ~name:"x" "42"
]
in
let heap =
vbox ~padding:(Num.em 2.)
[ tex "heap:"
; scalar_block "array1_meta" [ "1"; "2"; "3" ]
; ptr_block "pair_meta" [ "pair_1"; "pair_2" ]
; scalar_block "array2_meta" [ "42"; "99"; "666" ]
]
in
let figure = hbox ~padding:(Num.em 2.) [ stack; heap ] in
let ptr a b = Helpers.box_pointer_arrow (get a figure) (get b figure) in
Command.seq
[ draw figure
; ptr "array1" "array1_meta"
; ptr "array2" "array2_meta"
; ptr "pair" "pair_meta"
; ptr "pair_1" "array1_meta"
; ptr "pair_2" "array2_meta"
]
let () =
Array.iter
(fun (name, figure) -> Metapost.emit name (Picture.scale (Num.em 1.) figure))
@ -317,4 +345,5 @@ let () =
; ("figure_python_block2", figure_python_block2)
; ("figure_ocaml_float1", figure_ocaml_float1)
; ("figure_ocaml_float2", figure_ocaml_float2)
; ("figure_wasm_layout", figure_wasm_layout)
|]

18
src/layout.java

@ -1,10 +1,20 @@
import java.util.ArrayList;
import java.util.AbstractMap.SimpleEntry;
public class main {
public class layout {
public static void main(String[] args) {
int[] array1 = [ 1, 2, 3 ];
ArrayList<T> array2 = new ArrayList([ 42, 99, 666 ]);
Pair<ArrayList<T>> pair = new Pair(array1, array2);
int[] array1 = { 1, 2, 3 };
ArrayList<Integer> array2 = new ArrayList<Integer>();
array2.add(42);
array2.add(99);
array2.add(666);
SimpleEntry<ArrayList<Integer>, ArrayList<Integer>> pair =
new SimpleEntry<ArrayList<Integer>, ArrayList<Integer>>(
new ArrayList<Integer>(),
array2);
pair.getKey().add(1);
pair.getKey().add(2);
pair.getKey().add(3);
int x = 42;
}
}

37
src/layout.wast

@ -0,0 +1,37 @@
(module
(type $t1 (array (mut i32)))
(type $t2 (array (mut (ref i31))))
(type $tpair (struct
(field $left (ref $t1))
(field $right (ref $t2))))
(func $f
(local $array1 (ref $t1)) (local $array2 (ref $t2))
(local $pair (ref $tpair)) (local $x i32)
(array.new_canon $t1 (i32.const 0) (i32.const 3))
(local.set $array1)
(array.set $t1 (local.get $array1) (i32.const 0) (i32.const 1))
(array.set $t1 (local.get $array1) (i32.const 1) (i32.const 2))
(array.set $t1 (local.get $array1) (i32.const 2) (i32.const 3))
(array.new_canon $t2 (i31.new (i32.const 0)) (i32.const 3))
(local.set $array2)
(array.set $t2 (local.get $array2) (i32.const 0)
(i31.new (i32.const 42)))
(array.set $t2 (local.get $array2) (i32.const 1)
(i31.new (i32.const 99)))
(array.set $t2 (local.get $array2) (i32.const 2)
(i31.new (i32.const 666)))
(struct.new_canon $tpair
(local.get $array1) (local.get $array2))
(local.set $pair)
(i32.const 42)
(local.set $x)
)
(start $f)
)

26
src/ral.fsx

@ -0,0 +1,26 @@
type ral<'a> =
| Nil
| Zero of ral<'a * 'a>
| One of 'a * ral<'a * 'a>
let rec length<'a> (l : ral<'a>) : int =
match l with
| Nil -> 0
| Zero s -> 2 * length s
| One (_v, s) -> 1 + 2 * length s
let rec cons<'a> (x : 'a) (l : ral<'a>) : ral<'a> =
match l with
| Nil -> One (x, Nil)
| Zero s -> One (x, s)
| One (y, s) -> Zero (cons (x, y) s)
let rec loop l =
let len = length l
if len < 3000000 then
let l = cons 42 l
printfn "len = %d" (length l)
loop l
loop Nil
loop Nil
Loading…
Cancel
Save