Noeud « Next »: , Noeud « Up »: T6 Samples



4.8.2.1 T6 Canonicalization Samples

The first task in T6 is getting rid of all the eseq. To do this, you have to move the statement part of an eseq at the end of the current sequence point, and keeping the expression part in place.

Compare for instance the HIR to the LIR in the following case:

     let function print_ints (a: int, b: int) =
         (print_int (a); print (", "); print_int (b); print ("\n"))
         var a := 0
     in
       print_ints (1,  (a := a + 1; a))
     end
     File 96: preincr-1.tig

One possible HIR translation is:

     $ tc -eH preincr-1.tig
     /* == High Level Intermediate representation. == */

     label l1
             ", "

     label l2
             "\n"
     # Routine: print_ints
     label l0
     # Prologue
     move
         temp t2
         temp fp
     move
         temp fp
         temp sp
     move
         temp sp
         binop (-)
             temp sp
             const 4
     move
         mem
             temp $fp
         temp i0
     move
         temp t0
         temp i1
     move
         temp t1
         temp i2
     # Body
     seq
         sxp
             call
                 name print_int
                 temp t0
             call end
         sxp
             call
                 name print
                 name l1
             call end
         sxp
             call
                 name print_int
                 temp t1
             call end
         sxp
             call
                 name print
                 name l2
             call end
     seq end
     # Epilogue
     move
         temp sp
         temp fp
     move
         temp fp
         temp t2
     label end

     # Routine: Main
     label Main
     # Prologue
     # Body
     seq
         move
             temp t3
             const 0
         sxp
             call
                 name l0
                 temp $fp
                 const 1
                 eseq
                     move
                         temp t3
                         binop (+)
                             temp t3
                             const 1
                     temp t3
             call end
     seq end
     # Epilogue
     label end
     Example 97: tc -eH preincr-1.tig

A possible canonicalization is then:

     $ tc -eL preincr-1.tig
     /* == Low Level Intermediate representation. == */

     label l1
             ", "

     label l2
             "\n"
     # Routine: print_ints
     label l0
     # Prologue
     move
         temp t2
         temp fp
     move
         temp fp
         temp sp
     move
         temp sp
         binop (-)
             temp sp
             const 4
     move
         mem
             temp $fp
         temp i0
     move
         temp t0
         temp i1
     move
         temp t1
         temp i2
     # Body
     seq
         label l3
         sxp
             call
                 name print_int
                 temp t0
             call end
         sxp
             call
                 name print
                 name l1
             call end
         sxp
             call
                 name print_int
                 temp t1
             call end
         sxp
             call
                 name print
                 name l2
             call end
         label l4
     seq end
     # Epilogue
     move
         temp sp
         temp fp
     move
         temp fp
         temp t2
     label end

     # Routine: Main
     label Main
     # Prologue
     # Body
     seq
         label l5
         move
             temp t3
             const 0
         move
             temp t5
             temp $fp
         move
             temp t3
             binop (+)
                 temp t3
                 const 1
         sxp
             call
                 name l0
                 temp t5
                 const 1
                 temp t3
             call end
         label l6
     seq end
     # Epilogue
     label end
     Example 98: tc -eL preincr-1.tig

But please note the example above is simple because 1 commutes with (a := a + 1; a): the order does not matter. But if you change the 1 into a, then you cannot exchange a and (a := a + 1; a), so the translation is different. Compare the previous LIR with the following, and pay attention to

     let function print_ints (a: int, b: int) =
         (print_int (a); print (", "); print_int (b); print ("\n"))
         var a := 0
     in
       print_ints (a,  (a := a + 1; a))
     end
     File 99: preincr-2.tig
     $ tc -eL preincr-2.tig
     /* == Low Level Intermediate representation. == */

     label l1
             ", "

     label l2
             "\n"
     # Routine: print_ints
     label l0
     # Prologue
     move
         temp t2
         temp fp
     move
         temp fp
         temp sp
     move
         temp sp
         binop (-)
             temp sp
             const 4
     move
         mem
             temp $fp
         temp i0
     move
         temp t0
         temp i1
     move
         temp t1
         temp i2
     # Body
     seq
         label l3
         sxp
             call
                 name print_int
                 temp t0
             call end
         sxp
             call
                 name print
                 name l1
             call end
         sxp
             call
                 name print_int
                 temp t1
             call end
         sxp
             call
                 name print
                 name l2
             call end
         label l4
     seq end
     # Epilogue
     move
         temp sp
         temp fp
     move
         temp fp
         temp t2
     label end

     # Routine: Main
     label Main
     # Prologue
     # Body
     seq
         label l5
         move
             temp t3
             const 0
         move
             temp t5
             temp $fp
         move
             temp t6
             temp t3
         move
             temp t3
             binop (+)
                 temp t3
                 const 1
         sxp
             call
                 name l0
                 temp t5
                 temp t6
                 temp t3
             call end
         label l6
     seq end
     # Epilogue
     label end
     Example 100: tc -eL preincr-2.tig

As you can see, the output is the same for the HIR and the LIR:

     $ tc -eH preincr-2.tig >preincr-2.hir
     Example 101: tc -eH preincr-2.tig >preincr-2.hir
     $ havm preincr-2.hir
     0, 1
     Example 102: havm preincr-2.hir
     $ tc -eL preincr-2.tig >preincr-2.lir
     Example 103: tc -eL preincr-2.tig >preincr-2.lir
     $ havm preincr-2.lir
     0, 1
     Example 104: havm preincr-2.lir

Be very careful when dealing with mem. For instance, rewriting something like:
     call (foo, eseq (move (temp t, const 51), temp t))

into

     move temp t1, temp t
     move temp t, const 51
     call (foo, temp t)

is dead wrong: temp t is a subexpression: it is being defined here. You should produce:

     move temp t, const 51
     call (foo, temp t)

Another danger is the handling of move (mem, ). For instance:

     move (mem foo, x)

must be rewritten into:

     move (temp t, foo)
     move (mem (temp t), x)

not as:

     move (temp t, mem (foo))
     move (temp t, x)

In other words, the first subexpression of move (mem (foo), ) is foo, not mem (foo). The following example is a good crash test against this problem:

     let type int_array = array of int
         var tab := int_array [2] of 51
     in
       tab[0] := 100;
       tab[1] := 200;
       print_int (tab[0]); print ("\n");
       print_int (tab[1]); print ("\n")
     end
     File 105: move-mem.tig
     $ tc -eL move-mem.tig >move-mem.lir
     Example 106: tc -eL move-mem.tig >move-mem.lir
     $ havm move-mem.lir
     100
     200
     Example 107: havm move-mem.lir

You also ought to get rid of nested calls:
     print (chr (ord ("\n")))
     File 108: nested-calls.tig
     $ tc -L nested-calls.tig
     /* == Low Level Intermediate representation. == */

     label l0
             "\n"
     # Routine: Main
     label Main
     # Prologue
     # Body
     seq
         label l1
         move
             temp t1
             call
                 name ord
                 name l0
             call end
         move
             temp t2
             call
                 name chr
                 temp t1
             call end
         sxp
             call
                 name print
                 temp t2
             call end
         label l2
     seq end
     # Epilogue
     label end
     Example 109: tc -L nested-calls.tig

In fact there are only two valid call forms: sxp (call (...)), and move (temp (...), call (...)).


Note that, contrary to C, the HIR and LIR always denote the same value. For instance the following Tiger code:
     let
       var a := 1
       function a (t: int) : int =
          (a := a + 1;
           print_int (t); print (" -> "); print_int (a); print ("\n");
           a)
       var b := a (1) + a (2) * a (3)
     in
       print_int (b); print ("\n")
     end
     File 110: seq-point.tig

should always produce:

     $ tc -L seq-point.tig >seq-point.lir
     Example 111: tc -L seq-point.tig >seq-point.lir
     $ havm seq-point.lir
     1 -> 2
     2 -> 3
     3 -> 4
     14
     Example 112: havm seq-point.lir

independently of the what IR you ran. Note that it has nothing to do with the precedence of the operators!

In C, you have no such guarantee: the following program can give different results with different compilers and/or on different architectures.

     #include <stdio.h>

     int a_ = 1;
     int
     a (int t)
     {
       ++a_;
       printf ("%d -> %d\n", t, a_);
       return a_;
     }

     int
     main (void)
     {
       int b = a (1) + a (2) * a (3);
       printf ("%d\n", b);
       return 0;
     }