TC-L FAQ

What is a PHI node?

LLVM instructions are represented in the SSA (Static Single Assignment) form.

Let’s take an example:

let
  var v := 10
  var a := 1
  var b := 0
in
  if (v < 10) then
    a := 2;
  b := a
end

The whole point of The SSA form is to create a variable each time a variable is assigned more than once to enforce the single static assignment, so we cannot assign 2 to a.

In that case, LLVM is going to create two a’s, and the assignment has to pick the desired version.

Using a PHI node, the assignment will depend on the original path of the code, and using that information, it can decide which version of a should be picked.

You can use the opt tool in order to display the control-flow graph. For example:

fact.tig
let
   function fact(n : int) : int =
      if (n = 0)
         then 1
         else n * fact((n - 1))
in
   fact(10)
end
tc --llvm-runtime-display --llvm-display fact.tig > fact.ll
$ tc --llvm-runtime-display --llvm-display fact.tig > fact.ll

$ echo $?
0
opt-18 -passes=dot-cfg -disable-output fact.ll
$ opt-18 -passes=dot-cfg -disable-output fact.ll
Writing '.tc_main.dot'...
Writing '.fact_18.dot'...
Writing '.tc_init_array.dot'...
Writing '.tc_not.dot'...
Writing '.tc_exit.dot'...
Writing '.tc_chr.dot'...
Writing '.tc_concat.dot'...
Writing '.tc_ord.dot'...
Writing '.tc_size.dot'...
Writing '.tc_substring.dot'...
Writing '.tc_strcmp.dot'...
Writing '.tc_streq.dot'...
Writing '.tc_getchar.dot'...
Writing '.tc_print.dot'...
Writing '.tc_print_err.dot'...
Writing '.tc_print_int.dot'...
Writing '.tc_flush.dot'...
Writing '.main.dot'...
$ echo $?
0

This generates two files: .tc_main.dot and .fact_18.dot, corresponding to the main function and the fact function:

I don’t understand all the acronyms used in LLVM.

Where can I find their meaning? You can find it in The LLVM Lexicon.

Can I output the LLVM IR of a C/C++ program?

Yes, you can. Clang, A C language family front end for LLVM allows you to do it using the flags -S -emit-llvm.

clang-example.c
int main(void)
{
  int a = 1 + 2 * 3;
  return a;
}
clang -m32 -S -emit-llvm -o - clang-example.c
$ clang -m32 -S -emit-llvm -o - clang-example.c
; ModuleID = 'clang-example.c'
source_filename = "clang-example.c"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i386-pc-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, ptr %1, align 4
  store i32 7, ptr %2, align 4
  %3 = load i32, ptr %2, align 4
  ret i32 %3
}

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
!llvm.ident = !{!6}

!0 = !{i32 1, !"NumRegisterParameters", i32 0}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 8, !"PIC Level", i32 2}
!3 = !{i32 7, !"PIE Level", i32 2}
!4 = !{i32 7, !"uwtable", i32 2}
!5 = !{i32 7, !"frame-pointer", i32 2}
!6 = !{!"Debian clang version 18.1.8 (17)"}
$ echo $?
0
How should Tiger functions linked?

_main & primitives should have an external linkage. They are to be exposed.

Otherwise, other Tiger functions are treated as ‘static’ C functions and then should have internal linkage. They are not to be exposed outside of the program.

Legacy errors…

LLVM is an ever-changing project. As such, some errors which used to be common no longer happen with more up-to-date versions. If you are interested in knowing more, you can take a look at Legacy LLVM.