summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/llvmtranslate
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /tiger-compiler/src/llvmtranslate
add: added projectsHEADmain
Diffstat (limited to 'tiger-compiler/src/llvmtranslate')
-rw-r--r--tiger-compiler/src/llvmtranslate/escapes-collector.cc151
-rw-r--r--tiger-compiler/src/llvmtranslate/escapes-collector.hh13
-rw-r--r--tiger-compiler/src/llvmtranslate/fwd.hh31
-rwxr-xr-xtiger-compiler/src/llvmtranslate/generate-runtime.sh32
-rw-r--r--tiger-compiler/src/llvmtranslate/libllvmtranslate.cc47
-rw-r--r--tiger-compiler/src/llvmtranslate/libllvmtranslate.hh31
-rw-r--r--tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc110
-rw-r--r--tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh50
-rw-r--r--tiger-compiler/src/llvmtranslate/local.am39
-rw-r--r--tiger-compiler/src/llvmtranslate/tasks.cc64
-rw-r--r--tiger-compiler/src/llvmtranslate/tasks.hh34
-rw-r--r--tiger-compiler/src/llvmtranslate/tiger-runtime.c304
-rw-r--r--tiger-compiler/src/llvmtranslate/translator.cc737
-rw-r--r--tiger-compiler/src/llvmtranslate/translator.hh145
-rw-r--r--tiger-compiler/src/llvmtranslate/translator.hxx30
15 files changed, 1818 insertions, 0 deletions
diff --git a/tiger-compiler/src/llvmtranslate/escapes-collector.cc b/tiger-compiler/src/llvmtranslate/escapes-collector.cc
new file mode 100644
index 0000000..4984805
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/escapes-collector.cc
@@ -0,0 +1,151 @@
+#include <ast/all.hh>
+#include <ast/default-visitor.hh>
+#include <ast/non-assert-visitor.hh>
+#include <ast/non-object-visitor.hh>
+#include <llvmtranslate/escapes-collector.hh>
+
+namespace llvmtranslate
+{
+ /// LLVM IR doesn't support static link and nested functions.
+ /// In order to translate those functions to LLVM IR, we use a technique
+ /// called Lambda Lifting, which consists in passing a pointer to
+ /// the escaped variables to the nested function using that variable.
+ ///
+ /// In order to do that, we need a visitor to collect these kind of
+ /// variables and associate them to each function.
+
+ class EscapesCollector
+ : public ast::DefaultConstVisitor
+ , public ast::NonObjectConstVisitor
+ , public ast::NonAssertConstVisitor
+ {
+ public:
+ /// Super class.
+ using super_type = ast::DefaultConstVisitor;
+ /// Import overloaded operator() methods.
+ using super_type::operator();
+
+ EscapesCollector()
+ : did_modify_{false}
+ , escaped_{}
+ {}
+
+ escaped_map_type& escaped_get() { return escaped_; }
+
+ void operator()(const ast::FunctionChunk& e) override
+ {
+ const bool saved_did_modify = did_modify_;
+
+ // Iterate on the chunk in order to iteratively collect all the callee
+ // functions' escaped variables.
+ did_modify_ = !e.empty();
+ while (did_modify_)
+ {
+ did_modify_ = false;
+ super_type::operator()(e);
+ }
+
+ did_modify_ = saved_did_modify;
+ }
+
+ void operator()(const ast::FunctionDec& e) override
+ {
+ // Keep track of the current function
+ // FIXME DONE: Some code was deleted here.
+ const auto function_type = \
+ dynamic_cast<const type::Function*>(&e.type_get()->actual());
+
+ precondition(function_type != nullptr);
+
+ // Last visited function
+ const type::Function* previous_scope = current_;
+
+ if (previous_scope == nullptr)
+ {
+ // Then we are at the first depth of the program, hence _main
+ main_function_ = function_type;
+ }
+
+ current_ = function_type;
+
+ if (!escaped_.contains(current_))
+ {
+ escaped_[current_] = misc::set<const ast::VarDec*>{};
+ }
+
+ super_type::operator()(e);
+
+ // Guess we only really need to go back one step before
+ current_ = previous_scope;
+ }
+
+ void operator()(const ast::CallExp& e) override
+ {
+ super_type::operator()(e);
+
+ // FIXME DONE: Some code was deleted here.
+ const auto escaped_reference = escaped_.find(current_);
+ precondition(escaped_reference != escaped_.end());
+ const auto escaped_state = escaped_reference->second;
+
+ const size_t before_size = escaped_state.size();
+
+ super_type::operator()(e.def_get());
+
+ // Check whether there are any newly collected escaped variables.
+ // If there are, mark the iteration as modified.
+ // FIXME DONE: Some code was deleted here.
+ const size_t after_size = escaped_state.size();
+
+ did_modify_ = before_size != after_size;
+ }
+
+ void operator()(const ast::SimpleVar& e) override
+ {
+ // Associate escaped variables declared in parent frames with their
+ // functions
+ // FIXME DONE: Some code was deleted here.
+ if (current_ == main_function_ || !e.def_get()->escape_get())
+ {
+ super_type::operator()(e);
+ return;
+ }
+
+ // Checking that the variable is escaped because not all variables are
+ auto attached_to = escaped_.find(current_);
+
+ // We have a function to attach this to
+ if (attached_to != escaped_.end()
+ && !attached_to->second.contains(e.def_get()))
+ {
+ attached_to->second.insert(e.def_get());
+ did_modify_ = true;
+ }
+
+ super_type::operator()(e);
+ }
+
+ private:
+ /// Whether any modification was done during the iteration.
+ bool did_modify_ = false;
+
+ /// Associate a set of variables with their function.
+ escaped_map_type escaped_;
+
+ /// Current visiting function.
+ // FIXME DONE: Some code was deleted here.
+ const type::Function* current_ = nullptr;
+
+ /// The reference to the main function
+ const type::Function* main_function_ = nullptr;
+ };
+
+ escaped_map_type collect_escapes(const ast::Ast& ast)
+ {
+ EscapesCollector collect;
+ collect(ast);
+
+ return std::move(collect.escaped_get());
+ }
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/escapes-collector.hh b/tiger-compiler/src/llvmtranslate/escapes-collector.hh
new file mode 100644
index 0000000..dab93c6
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/escapes-collector.hh
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <ast/fwd.hh>
+#include <llvmtranslate/fwd.hh>
+
+namespace llvmtranslate
+{
+
+ /// Collect all escapes for every function in the ast, and store it in a map.
+ /// This is used for Lambda Lifting.
+ escaped_map_type collect_escapes(const ast::Ast& ast);
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/fwd.hh b/tiger-compiler/src/llvmtranslate/fwd.hh
new file mode 100644
index 0000000..1eef45e
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/fwd.hh
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <map>
+
+#include <ast/fwd.hh>
+#include <misc/set.hh>
+#include <type/fwd.hh>
+
+namespace llvm
+{
+ // From llvm/IR/Module.h.
+ class Module;
+
+ // From llvm/IR/DerivedTypes.h
+ class StructType;
+
+ // From llvm/IR/Type.h
+ class Type;
+
+ // From llvm/IR/LLVMContext.h
+ class LLVMContext;
+
+} // namespace llvm
+
+namespace llvmtranslate
+{
+ using escaped_map_type =
+ std::map<const type::Function*, misc::set<const ast::VarDec*>>;
+ using frame_map_type = escaped_map_type;
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/generate-runtime.sh b/tiger-compiler/src/llvmtranslate/generate-runtime.sh
new file mode 100755
index 0000000..2e05445
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/generate-runtime.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+[ -z "$1" ] && echo "$0: Missing input file" && exit
+[ -z "$2" ] && echo "$0: Missing output file" && exit
+
+input=$1
+output=$2
+
+awk 'BEGIN {
+ print("#include <string>");
+ print("#include <llvmtranslate/libllvmtranslate.hh>");
+ print("");
+ print("namespace llvmtranslate");
+ print("{");
+ print("const char* runtime_string()");
+ print("{");
+ print(" return");
+ printf("R\"0("); # The zero here only serves as an identifier for the raw string, it does not changes the code behavior at all.
+ }
+ /^#(<<|>>)/ {
+ next;
+ }
+ {
+ print($0);
+ }
+ END {
+ print(")0\";");
+ print("}");
+ print("} // namespace llvmtranslate");
+ }' "$input" > "$output".tmp
+
+mv "$output".tmp "$output"
diff --git a/tiger-compiler/src/llvmtranslate/libllvmtranslate.cc b/tiger-compiler/src/llvmtranslate/libllvmtranslate.cc
new file mode 100644
index 0000000..b86b03a
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/libllvmtranslate.cc
@@ -0,0 +1,47 @@
+/**
+ ** \file llvmtranslate/libllvmtranslate.hh
+ ** \brief Public llvmtranslate module interface implementation.
+ **/
+
+#include <ast/default-visitor.hh>
+#include <ast/non-object-visitor.hh>
+#include <common.hh> // program_name
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/AsmParser/Parser.h>
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/Verifier.h>
+#include <llvm/Support/SourceMgr.h> // llvm::SMDiagnostic
+#include <llvmtranslate/escapes-collector.hh>
+#include <llvmtranslate/fwd.hh>
+#include <llvmtranslate/libllvmtranslate.hh>
+#include <llvmtranslate/translator.hh>
+
+#pragma GCC diagnostic pop
+
+namespace llvmtranslate
+{
+ std::pair<std::unique_ptr<llvm::LLVMContext>, std::unique_ptr<llvm::Module>>
+ translate(const ast::Ast& the_program)
+ {
+ auto ctx = std::make_unique<llvm::LLVMContext>();
+ auto module = std::make_unique<llvm::Module>(program_name, *ctx);
+
+ Translator translate{*module, collect_escapes(the_program)};
+ translate(the_program);
+
+ llvm::verifyModule(*module);
+
+ return {std::move(ctx), std::move(module)};
+ }
+
+ std::unique_ptr<llvm::Module> runtime_get(llvm::LLVMContext& ctx)
+ {
+ llvm::SMDiagnostic diag;
+ return llvm::parseAssemblyString(runtime_string(), diag, ctx);
+ }
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/libllvmtranslate.hh b/tiger-compiler/src/llvmtranslate/libllvmtranslate.hh
new file mode 100644
index 0000000..cf431e9
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/libllvmtranslate.hh
@@ -0,0 +1,31 @@
+/**
+ ** \file llvmtranslate/libllvmtranslate.hh
+ ** \brief Public llvmtranslate module interface declaration.
+ */
+
+#pragma once
+
+#include <memory>
+#include <utility>
+
+#include <llvm/IR/LLVMContext.h>
+
+#include <ast/fwd.hh>
+#include <llvmtranslate/fwd.hh>
+
+/// Translation from ast::Ast to llvm::Value.
+namespace llvmtranslate
+{
+ /// Translate the file into a llvm::Module.
+ std::pair<std::unique_ptr<llvm::LLVMContext>, std::unique_ptr<llvm::Module>>
+ translate(const ast::Ast& the_program);
+
+ /// Load the runtime as a llvm::Module.
+ std::unique_ptr<llvm::Module> runtime_get(llvm::LLVMContext& ctx);
+
+ /// The LLVM runtime as a string, loaded from the generated file.
+ /// This function is implemented in $(build_dir)/src/llvmtranslate/runtime.cc
+ /// For more information take a look at `local.am`.
+ const char* runtime_string();
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc
new file mode 100644
index 0000000..a2130c9
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.cc
@@ -0,0 +1,110 @@
+/**
+ ** \file llvmtranslate/llvm-type-visitor.cc
+ ** \brief Implementation of llvmtranslator::LLVMTypeVisitor.
+ */
+
+#include <llvm/IR/DerivedTypes.h>
+
+#include <llvmtranslate/llvm-type-visitor.hh>
+#include <type/types.hh>
+
+namespace llvmtranslate
+{
+ LLVMTypeVisitor::LLVMTypeVisitor(llvm::LLVMContext& ctx)
+ : ctx_{ctx}
+ {}
+
+ llvm::Type* LLVMTypeVisitor::llvm_type_get() { return type_; }
+
+ llvm::Type* LLVMTypeVisitor::get_record_ltype(const type::Record* e)
+ {
+ return structs_[e];
+ }
+
+ llvm::Type* LLVMTypeVisitor::llvm_type(const type::Type& type)
+ {
+ operator()(type);
+ return type_;
+ }
+
+ void LLVMTypeVisitor::operator()(const type::Nil& e)
+ {
+ if (auto record_type = e.record_type_get())
+ type_ = llvm_type(*record_type);
+ else
+ unreachable();
+ }
+
+ void LLVMTypeVisitor::operator()(const type::Void&)
+ {
+ // FIXME DONE: Some code was deleted here (Void types can be interpreted as int or void type).
+ // Je cite : "a variable can hold a void value (that are represented using an integer in LLVM)"
+ // Mais du coup, je sais pas ce qui détermine quel type on lui donne.
+ // En attendant je mets void tout le temps jusqu'à ce qu'on trouve pourquoi on le mettrait à int
+ // Y a un monde où ça se gère au dessus (genre les vardecs et les assignexp ?)
+ // Source : Un YAKA qui dit que en gros on a le choix mais qu'il vaut mieux le représenter comme int
+ // Du coup pour le distinguer c'est un int sur 0 bits
+ type_ = llvm::Type::getIntNTy(ctx_,0);
+ }
+
+ void LLVMTypeVisitor::operator()(const type::Int&)
+ {
+ type_ = llvm::Type::getInt32Ty(ctx_);
+ }
+
+ void LLVMTypeVisitor::operator()(const type::String&)
+ {
+ // Strings are pointers to characters in LLVM.
+ // FIXME DONE: Some code was deleted here.
+ // Bon vu qu'on a pas accès à la taille de la string on y va yolo.
+ // Du coup c'est un PointerType de Int8 (du coup 8 bits pour un char)
+ type_ = llvm::PointerType::get(llvm::Type::getInt8Ty(ctx_), 0);
+ }
+
+ void LLVMTypeVisitor::operator()(const type::Named& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ llvm_type(e.actual());
+
+ const std::string reference_name = e.name_get().get();
+
+ if (const auto record_ref = dynamic_cast<const type::Record*>(&e.type_get()->actual());
+ record_ref != nullptr && structs_[record_ref]->getName() != reference_name)
+ {
+ structs_[record_ref]->setName(reference_name);
+ }
+ }
+
+ void LLVMTypeVisitor::operator()(const type::Record& e)
+ {
+ // If the record was never translated, translate it
+ if (!structs_[&e])
+ {
+ // We need to create the struct in two passes to support recursive
+ // types, like 'type a = { next : a }
+ // So, first we create an empty struct
+ structs_[&e] = llvm::StructType::create(ctx_);
+ // Then set the body of the structure
+ std::vector<llvm::Type*> field_types;
+ field_types.reserve(e.fields_get().size());
+ // FIXME DONE: Some code was deleted here.
+ //Martial: Copy and convert the fields ig
+ for (const auto& field : e.fields_get())
+ {
+ super_type::operator()(field.type_get());
+ field_types.push_back(type_);
+ }
+ //Martial: Set the body with the converted types
+ structs_[&e]->setBody(std::move(field_types), false);
+ }
+ type_ = llvm::PointerType::getUnqual(structs_[&e]);
+ }
+
+ void LLVMTypeVisitor::operator()(const type::Array& e)
+ {
+ // Arrays are pointers to the array elements, like in C.
+ // FIXME DONE: Some code was deleted here.
+ llvm_type(*e.get_element_type());
+ type_ = llvm::PointerType::getUnqual(type_);
+ }
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh
new file mode 100644
index 0000000..d19ea9d
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/llvm-type-visitor.hh
@@ -0,0 +1,50 @@
+/**
+ ** \file llvmtranslate/llvm-type-visitor.hh
+ ** \brief Definition of llvmtranslator::LLVMTypeVisitor.
+ */
+
+#pragma once
+
+#include <llvm/IR/LLVMContext.h>
+
+#include <llvmtranslate/fwd.hh>
+#include <type/default-visitor.hh>
+
+namespace llvmtranslate
+{
+ /// Visit a Type and return the corresponding llvm::Type.
+ class LLVMTypeVisitor : public type::DefaultConstVisitor
+ {
+ public:
+ using super_type = type::DefaultConstVisitor;
+ // Import overloaded virtual functions.
+ using super_type::operator();
+
+ LLVMTypeVisitor(llvm::LLVMContext& ctx);
+
+ llvm::Type* llvm_type_get();
+ llvm::Type* get_record_ltype(const type::Record* e);
+
+ /// Visit methods.
+ /// \{
+ void operator()(const type::Nil& e) override;
+ void operator()(const type::Void& e) override;
+ void operator()(const type::Int& e) override;
+ void operator()(const type::String& e) override;
+ void operator()(const type::Named& e) override;
+ void operator()(const type::Record& e) override;
+ void operator()(const type::Array& e) override;
+ /// \}
+
+ private:
+ /// The global context.
+ llvm::LLVMContext& ctx_;
+ /// The type of the last visited node.
+ llvm::Type* type_ = nullptr;
+ /// llvm::StructTypes for each type::Record
+ std::map<const type::Record*, llvm::StructType*> structs_;
+ /// Recurse and return the type_.
+ llvm::Type* llvm_type(const type::Type& type);
+ };
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/local.am b/tiger-compiler/src/llvmtranslate/local.am
new file mode 100644
index 0000000..d5a752b
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/local.am
@@ -0,0 +1,39 @@
+# Compile the LLVM Tiger runtime
+EXTRA_DIST += %D%/tiger-runtime.c
+CLEANFILES += %D%/runtime.ll
+%D%/runtime.ll: %D%/tiger-runtime.c
+ $(AM_V_CC)$(CLANG) -c -m32 -std=c99 -emit-llvm -S -o $@ $^
+
+LLVM_RUNTIME_GENERATION = %D%/generate-runtime.sh
+EXTRA_DIST += $(LLVM_RUNTIME_GENERATION)
+CLEANFILES += %D%/runtime.cc
+%D%/runtime.cc: %D%/runtime.ll
+ $(AM_V_GEN)$(srcdir)/$(LLVM_RUNTIME_GENERATION) $< $@
+
+## llvmtranslate module.
+src_libtc_la_SOURCES += \
+ %D%/escapes-collector.cc %D%/escapes-collector.hh \
+ %D%/translator.hh %D%/translator.hxx %D%/translator.cc \
+ %D%/libllvmtranslate.cc %D%/libllvmtranslate.hh \
+ %D%/llvm-type-visitor.cc %D%/llvm-type-visitor.hh \
+ %D%/fwd.hh
+
+nodist_src_libtc_la_SOURCES += %D%/runtime.cc
+
+## FIXME: Add SWIG support for this module
+
+## LLVM flags are found using `llvm-config`.
+
+if STATIC_LLVM
+EXTRA_LLVM_CONFIG_FLAGS = --link-static
+else
+EXTRA_LLVM_CONFIG_FLAGS =
+endif
+
+AM_CXXFLAGS += `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --cppflags`
+src_libtc_la_LDFLAGS += \
+ `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --ldflags` \
+ `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --libs core linker asmparser irprinter` \
+ `$(LLVM_CONFIG) $(EXTRA_LLVM_CONFIG_FLAGS) --system-libs`
+
+TASKS += %D%/tasks.hh %D%/tasks.cc
diff --git a/tiger-compiler/src/llvmtranslate/tasks.cc b/tiger-compiler/src/llvmtranslate/tasks.cc
new file mode 100644
index 0000000..09f865c
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/tasks.cc
@@ -0,0 +1,64 @@
+/**
+ ** \file llvmtranslate/tasks.cc
+ ** \brief LLVM Translate tasks.
+ */
+
+#include <memory>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/Config/llvm-config.h> // LLVM_VERSION_*
+#include <llvm/IR/Module.h>
+#include <llvm/IR/PassManager.h>
+#include <llvm/IRPrinter/IRPrintingPasses.h>
+#include <llvm/Linker/Linker.h>
+#include <llvm/Support/raw_ostream.h> // llvm::outs()
+
+#pragma GCC diagnostic pop
+
+#include <ast/tasks.hh>
+#include <llvmtranslate/fwd.hh>
+#include <llvmtranslate/libllvmtranslate.hh>
+#define DEFINE_TASKS 1
+#include <llvmtranslate/tasks.hh>
+#undef DEFINE_TASKS
+
+namespace llvmtranslate::tasks
+{
+ std::pair<std::unique_ptr<llvm::LLVMContext>, std::unique_ptr<llvm::Module>>
+ module = {nullptr, nullptr};
+
+ /// Translate the AST to LLVM IR.
+ void llvm_compute() { module = translate(*ast::tasks::the_program); }
+
+ /// Display the LLVM IR.
+ void llvm_display()
+ {
+ // If the runtime has to be displayed, get the runtime module,
+ // link it with the program module and print it.
+ if (llvm_runtime_display_p)
+ {
+ auto runtime = runtime_get(*module.first);
+#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 7
+ auto link =
+ llvm::Linker::LinkModules(module.second.get(), runtime.get());
+#else
+ auto link =
+ llvm::Linker::linkModules(*module.second, std::move(runtime));
+#endif
+ (void)link;
+ postcondition(!link); // Returns true on error
+ }
+
+ auto& out = llvm::outs();
+ llvm::PrintModulePass printer{out};
+#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 8
+ printer.run(*module.second);
+#else
+ llvm::ModuleAnalysisManager dummy_mam;
+ printer.run(*module.second, dummy_mam);
+#endif
+ }
+
+} // namespace llvmtranslate::tasks
diff --git a/tiger-compiler/src/llvmtranslate/tasks.hh b/tiger-compiler/src/llvmtranslate/tasks.hh
new file mode 100644
index 0000000..ade231a
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/tasks.hh
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <llvmtranslate/fwd.hh>
+#include <task/libtask.hh>
+
+namespace llvmtranslate::tasks
+{
+ /// The global translation module of ast;:tasks::the_program.
+ extern std::pair<std::unique_ptr<llvm::LLVMContext>,
+ std::unique_ptr<llvm::Module>>
+ module;
+
+ TASK_GROUP("5.5. Translation to LLVM Intermediate Representation");
+
+ /// Translate the AST to LLVM IR.
+ TASK_DECLARE("llvm-compute",
+ "translate to LLVM IR",
+ llvm_compute,
+ "typed desugar-for desugar-string-cmp desugar escapes-compute");
+
+ /// Activate displaying the runtime along with the LLVM IR.
+ BOOLEAN_TASK_DECLARE("llvm-runtime-display",
+ "enable runtime displaying"
+ "along with the LLVM IR",
+ llvm_runtime_display_p,
+ "");
+
+ /// Display the LLVM IR.
+ TASK_DECLARE("llvm-display",
+ "display the LLVM IR",
+ llvm_display,
+ "llvm-compute");
+
+} // namespace llvmtranslate::tasks
diff --git a/tiger-compiler/src/llvmtranslate/tiger-runtime.c b/tiger-compiler/src/llvmtranslate/tiger-runtime.c
new file mode 100644
index 0000000..f952e0f
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/tiger-runtime.c
@@ -0,0 +1,304 @@
+/**
+ \file llvm/tiger-runtime.c
+ \brief C Implementation of the Tiger runtime.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define EXIT_RUNTIME_FAILURE 120
+
+// FWD, declared by tc
+void tc_main(int);
+
+/** \name Internal functions (calls generated by the compiler only). */
+/** \{ */
+
+/** \brief Allocate an array and fill it with a default value.
+ \param size The size of the array.
+ \param elt The default element.
+
+ An element size is always the size of a word on 32-bit systems.
+*/
+int *tc_init_array(int size, int elt)
+{
+ int *arr = (int *)malloc(size * sizeof (elt));
+ for (size_t i = 0; i < size; ++i)
+ arr[i] = elt;
+ return arr;
+}
+/** \} */
+
+/** \name Miscellaneous */
+/** \{ */
+
+/** \brief Equivalent to operator! in C.
+ \param i The integer to be inversed.
+*/
+int tc_not(int i)
+{
+ return !i;
+}
+
+/** \brief Exit the program with the desired status.
+ \param status The status code.
+*/
+void tc_exit(int status)
+{
+ exit(status);
+}
+/** \} */
+
+/** \name Operations on strings. */
+/** \{ */
+
+// Small trick. Add a \0 after each char in the consts
+// This is filled in tc_main
+static char consts[512] = { 0 };
+
+/** \brief Get a string containing the character represented by the ascii value
+ * of \a i.
+ \param i The ascii value of the desired character.
+*/
+const char *tc_chr(int i)
+{
+ if (!(0 <= i && i <= 255))
+ {
+ fputs("chr: character out of range\n", stderr);
+ exit(EXIT_RUNTIME_FAILURE);
+ }
+ return consts + i * 2;
+}
+
+/** \brief Concatenate two strings.
+ \param a The first string.
+ \param b The second string.
+*/
+const char *tc_concat(const char *a, const char *b)
+{
+ size_t len_a = strlen(a);
+ size_t len_b = strlen(b);
+ if (len_a == 0)
+ return b;
+ else if (len_b == 0)
+ return a;
+ else
+ {
+ int i = 0;
+ int n = len_a + len_b;
+ char *t = (char *) malloc(n + 1);
+ for (i = 0; i < len_a; i++)
+ t[i] = a[i];
+ for (i = 0; i < len_b; i++)
+ t[i + len_a] = b[i];
+ t[n] = '\0';
+ return t;
+ }
+}
+
+/** \brief Get the ascii value of a character.
+ \param s The string representing the desired character to be converted.
+
+ Inverse of `tc_chr`.
+*/
+int tc_ord(const char *s)
+{
+ size_t len = strlen(s);
+ if (len == 0)
+ return -1;
+ else
+ return s[0];
+}
+
+/** \brief Get the size of a string.
+ \param s The string.
+*/
+int tc_size(const char *s)
+{
+ return strlen(s);
+}
+
+/** \brief Return a part of the string \a s.
+ \param s The source string
+ \param first The first character of the extraction (starting at 0)
+ \param n The number of characters to get.
+
+ \a first and \a n must be positive, and one shall not go beyond the size
+ of \a s. Otherwise, exit with runtime failure.
+*/
+const char *tc_substring(const char *s, int first, int n)
+{
+ size_t len = strlen(s);
+ if (!(0 <= first
+ && 0 <= n
+ && first + n <= len))
+ {
+ fputs("substring: arguments out of bounds\n", stderr);
+ exit(EXIT_RUNTIME_FAILURE);
+ }
+
+ if (n == 1)
+ return consts + s[first] * 2;
+ else
+ {
+ char *t = (char *) malloc(n + 1);
+ for (int i = 0; i < n; i++)
+ t[i] = s[first + i];
+ t[n] = '\0';
+ return t;
+ }
+}
+
+/** \brief Compare two strings.
+ \param lhs The first string.
+ \param rhs The second string.
+*/
+int tc_strcmp(const char *lhs, const char *rhs)
+{
+ return strcmp(lhs, rhs);
+}
+
+/** \brief Return a part of the string \a s.
+ \param s The source string
+ \param first The first character of the extraction (starting at 0)
+ \param n The number of characters to get.
+
+ \a first and \a n must be positive, and one shall not go beyond the size
+ of \a s. Otherwise, exit with runtime failure.
+*/
+int tc_streq(const char *lhs, const char *rhs)
+{
+ return strcmp(lhs, rhs) == 0;
+}
+/** \} */
+
+/** \name Input/Output. */
+/** \{ */
+
+/** \brief Get a character from the standard input.
+*/
+const char *tc_getchar()
+{
+ int i = getc(stdin);
+ if (i == EOF)
+ return "";
+ else
+ return consts + i * 2;
+}
+
+/** \brief Print a string on the standard output.
+ \param s The string to be printed.
+*/
+void tc_print(const char *s)
+{
+ printf("%s", s);
+}
+
+/** \brief Print a string on the standard error.
+ \param s The string to be printed.
+*/
+void tc_print_err(const char *s)
+{
+ fprintf(stderr, "%s", s);
+}
+
+/** \brief Print an int on the standard error.
+ \param i The int to be printed.
+*/
+void tc_print_int(int i)
+{
+ printf("%d", i);
+}
+
+/** \brief Flush the standard output.
+*/
+void tc_flush()
+{
+ fflush(stdout);
+}
+
+/** \} */
+
+/** \name Custom primitives. */
+/** \{ */
+
+/** \brief Terminate the program with an error if condition if false
+ \param condition Condition to assert, represented as a Tiger 32 bits integer
+ \param instruction_str The condition represented as a string, used for
+ pretty-printing. Can be NULL
+ \param filename File in which the assertion comes from. Can be NULL
+*/
+void tc_assertion(const int condition, const char* instruction_str,
+ const char* filename)
+{
+ if (condition)
+ {
+ return;
+ }
+
+ if (instruction_str == NULL)
+ {
+ instruction_str = "";
+ }
+
+ if (filename == NULL)
+ {
+ filename = "";
+ }
+
+ fprintf(stderr, "%s: %s assertion is false. aborting\n", filename,
+ instruction_str);
+ abort();
+}
+
+/**
+ * \brief Proceed to a fork syscall and return the resulting process id.
+ * \return the resulting pid for the parent, 0 for the child, or -1 if the
+ * process forking failed
+ */
+int tc_fork()
+ {
+ return fork();
+ }
+
+/**
+ * \brief Wait for the process specified by the provided id to end and return
+ * the resulting status code.
+ * \param pid a process, referenced by its identifier
+ * \return the process' status code
+ */
+int tc_wait_pid(int pid)
+ {
+ int status_code = -1;
+ waitpid(pid, &status_code, WUNTRACED | WCONTINUED);
+ return status_code;
+ }
+
+/**
+ * \brief Determine the exit code specified by a status code and return it.
+ * \param status_code any process status code
+ * \return the implied exit code
+ */
+int tc_get_exit_code(int status_code)
+ {
+ return WEXITSTATUS(status_code);
+ }
+
+/** \} */
+
+int main()
+{
+ // Fill the `consts` array with every character in the ascii table, followed
+ // by a null character.
+ for (int i = 0; i < 512; i += 2)
+ {
+ consts[i] = i / 2;
+ consts[i + 1] = 0;
+ }
+
+ tc_main(0);
+ return 0;
+}
diff --git a/tiger-compiler/src/llvmtranslate/translator.cc b/tiger-compiler/src/llvmtranslate/translator.cc
new file mode 100644
index 0000000..2c27db5
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/translator.cc
@@ -0,0 +1,737 @@
+/**
+ ** \file llvmtranslate/translator.cc
+ ** \brief Implementation of llvmtranslate::Translator
+ */
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/Config/llvm-config.h> // LLVM_VERSION_*
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/IR/Verifier.h> // llvm::verifyFunction
+#include <llvm/Support/Casting.h>
+#include <llvm/TargetParser/Host.h> // llvm::sys
+#include <llvm/TargetParser/Triple.h>
+
+#pragma GCC diagnostic pop
+
+#include <ast/all.hh>
+#include <llvmtranslate/translator.hh>
+
+#include "llvm-type-visitor.hh"
+
+namespace llvmtranslate
+{
+ using namespace std::string_literals;
+
+ namespace
+ {
+ // Shorthands for integer type and pointer to integer type.
+ inline llvm::IntegerType* i32_t(llvm::LLVMContext& ctx)
+ {
+ return llvm::Type::getInt32Ty(ctx);
+ }
+
+ inline llvm::PointerType* i32p_t(llvm::LLVMContext& ctx)
+ {
+ return llvm::PointerType::get(llvm::Type::getInt32Ty(ctx), 0);
+ }
+
+ llvm::AllocaInst* create_alloca(llvm::Function* ll_function,
+ llvm::Type* ll_type,
+ const std::string& name)
+ {
+ // Create an IRBuilder starting at the beginning of the current entry
+ // block. LLVM treats allocas as local variables only if they occur at the
+ // beginning of a function.
+ llvm::IRBuilder<> tmp(&ll_function->getEntryBlock(),
+ ll_function->getEntryBlock().begin());
+ return tmp.CreateAlloca(ll_type, nullptr, name);
+ }
+
+ // Set default attributes to the functions
+ void set_default_attributes(llvm::Function& the_function,
+ const ast::FunctionDec& e)
+ {
+ the_function.addFnAttr(llvm::Attribute::NoUnwind); // No exceptions in TC
+ if (!e.body_get()) // Inline primitives
+ the_function.addFnAttr(llvm::Attribute::InlineHint);
+ }
+
+ std::string function_dec_name(const ast::FunctionDec& e)
+ {
+ // Rename "_main" to "tc_main"
+ if (e.name_get() == "_main")
+ return "tc_main";
+ // Prefix all the primitives with "tc_"
+ if (!e.body_get())
+ return "tc_" + e.name_get().get();
+ return e.name_get().get();
+ }
+ } // namespace
+
+ Translator::Translator(llvm::Module& module, escaped_map_type&& escaped)
+ : module_{module}
+ , ctx_{module_.getContext()}
+ , builder_{ctx_}
+ , escaped_{std::move(escaped)}
+ , type_visitor_{ctx_}
+ {
+ // The current process triple.
+ auto process_triple = llvm::Triple(llvm::sys::getProcessTriple());
+ // Set the 32-bit version of the triple.
+ module_.setTargetTriple(process_triple.get32BitArchVariant().str());
+ }
+
+ void Translator::operator()(const ast::Ast& e)
+ {
+ translate(e);
+ value_ = nullptr;
+ }
+
+ llvm::Value* Translator::translate(const ast::Ast& node)
+ {
+ node.accept(*this);
+ return value_;
+ }
+
+ llvm::Value* Translator::access_var(const ast::Var& e)
+ {
+ if (auto var_ast = dynamic_cast<const ast::SimpleVar*>(&e))
+ {
+ // FIXME DONE: Some code was deleted here.
+ // Chopper la VarDec
+ const ast::VarDec* dec = var_ast->def_get();
+ if (dec == nullptr)
+ {
+ // This is probably a binder error
+ unreachable();
+ }
+ // getFunction --> getValueSymbolTable --> lookup
+ auto all_vars = locals_[current_function_];
+ // Ou si c'est un argument, juste iterer sur les arguments jusqu'a trouver la bonne
+ return all_vars[dec];
+
+ }
+ else if (auto arr_ast = dynamic_cast<const ast::SubscriptVar*>(&e))
+ {
+ // FIXME DONE: Some code was deleted here.
+ // Here we have the case of an access in an array (var[index])
+ // Faire un call recursif sur la variable parent afin d'obtenir une reference
+ llvm::Value* mother_val = translate(arr_ast->var_get());
+
+ // Utiliser cette ref pour trouver le type associe
+ llvm::Type* mother_ty = llvm_type(*(arr_ast->var_get().type_get()));
+
+ // Creer une array ref afin de pouvoir stocker les indexs
+ // Ici, l'index en question est index_ de la variable e
+ llvm::Value* index_exp = translate(arr_ast->index_get());
+ //std::vector<llvm::Value*> =
+ const auto index = llvm::ArrayRef(index_exp);
+ // Et il n'y a plus qu'a cree tout ca en utilisant un llvm::IRBuilderBase::CreateGEP
+
+ return builder_.CreateGEP(mother_ty, mother_val, index, "subscriptptr_"s + "some_index");
+ }
+ else if (auto field_ast = dynamic_cast<const ast::FieldVar*>(&e))
+ {
+ const ast::Var* var = nullptr;
+ // FIXME DONE: Some code was deleted here.
+ // Wait, isn't this going to recurse and create many instances of the thing?
+ // Or it is just a small piece of the path towards the full path to this var?
+ var = &(field_ast->var_get());
+
+ auto var_val = translate(*var);
+
+ const type::Record* record_type = nullptr;
+ // FIXME DONE: Some code was deleted here.
+ record_type = dynamic_cast<const type::Record*>(&var->type_get()->actual());
+
+ if (record_type == nullptr)
+ {
+ // This could be a typing error (we do not have a var pointing to a record?)
+ unreachable();
+ }
+
+ misc::symbol field_name;
+ // FIXME DONE: Some code was deleted here.
+ field_name = field_ast->name_get();
+
+ int index = -1;
+ // FIXME DONE: Some code was deleted here (Get the index of the field).
+ index = record_type->field_index(field_name);
+
+ // The GEP instruction provides us with safe pointer arithmetics,
+ // usually used with records or arrays.
+ llvm::Type* record_ltype = nullptr;
+ // FIXME DONE: Some code was deleted here (Get record's corresponding LLVM type).
+ llvm_type(*record_type);
+ record_ltype = type_visitor_.get_record_ltype(record_type);
+
+ return builder_.CreateStructGEP(record_ltype, var_val, index,
+ "fieldptr_"s + field_name.get());
+ }
+ else
+ unreachable();
+ }
+
+ llvm::Value* Translator::init_array(llvm::Value* count_val,
+ llvm::Value* init_val)
+ {
+ // Cast everything so that it is conform to the signature of init_array
+ // int *init_array(int, int)
+
+ // We need to separate the pointers and the ints.
+ // LLVM requires a ptrtoint instruction for pointers
+ // and a bitcast for others.
+ auto init_val_cast = init_val->getType()->isPointerTy()
+ ? builder_.CreatePtrToInt(init_val, i32_t(ctx_), "init_array_ptrtoint")
+ : builder_.CreateBitCast(init_val, i32_t(ctx_), "init_array_bitcast");
+
+ // Create the init_array function:
+ // First, the arguments (int*, int, int)
+ std::vector<llvm::Type*> arg_type{i32_t(ctx_), init_val_cast->getType()};
+
+ // Then, create the FunctionType.
+ auto init_array_ltype =
+ llvm::FunctionType::get(i32p_t(ctx_), arg_type, false);
+
+ // Get the llvm::Function from the module related to the name and type
+ auto init_array_function =
+ module_.getOrInsertFunction("tc_init_array", init_array_ltype);
+
+ // Prepare the arguments.
+ std::vector<llvm::Value*> arg_vals{count_val, init_val_cast};
+
+ // Create the call.
+ auto init_array_call =
+ builder_.CreateCall(init_array_function, arg_vals, "init_array_call");
+
+ // Cast the result of the call in the desired type.
+ return builder_.CreateBitCast(init_array_call,
+ init_val->getType()->getPointerTo(),
+ "init_array_call_cast");
+ }
+
+ llvm::Type* Translator::llvm_type(const type::Type& type)
+ {
+ type_visitor_(type);
+ return type_visitor_.llvm_type_get();
+ }
+
+ llvm::FunctionType*
+ Translator::llvm_function_type(const type::Function& function_type)
+ {
+ // Prepare the arguments
+ std::vector<llvm::Type*> args_types;
+ // First, if there are any escaped vars, create ptr arguments for it
+ // (Lambda lifting)
+
+ if (auto escapes_it = escaped_.find(&function_type);
+ escapes_it != std::end(escaped_))
+ {
+ auto& escapes = escapes_it->second;
+ args_types.reserve(escapes.size()
+ + function_type.formals_get().fields_get().size());
+ for (const auto dec : escapes)
+ {
+ llvm::Type* var_ltype = nullptr;
+ // FIXME DONE: Some code was deleted here (Get the llvm type of the VarDec).
+ var_ltype = llvm_type(*dec->type_get());
+
+ args_types.emplace_back(llvm::PointerType::getUnqual(var_ltype));
+ }
+ }
+ else
+ args_types.reserve(function_type.formals_get().fields_get().size());
+
+ // Then, the actual arguments
+ for (const auto& field : function_type.formals_get())
+ args_types.emplace_back(llvm_type(field.type_get()));
+
+ llvm::Type* result_ltype = nullptr;
+ // FIXME DONE: Some code was deleted here (If the result is void typed, we assign llvm void type to result_ltype).
+ result_ltype = dynamic_cast<const type::Void*>(&function_type.result_get().actual()) != nullptr
+ ? llvm::Type::getVoidTy(ctx_)
+ : llvm_type(function_type.result_get());
+
+ return llvm::FunctionType::get(result_ltype, args_types, false);
+ }
+
+ void Translator::operator()(const ast::SimpleVar& e)
+ {
+ // Void var types are actually Ints represented by a 0
+ // FIXME DONE: Some code was deleted here.
+ value_ = access_var(e);
+ }
+
+ void Translator::operator()(const ast::FieldVar& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ value_ = access_var(e);
+ }
+
+ void Translator::operator()(const ast::SubscriptVar& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ value_ = access_var(e);
+ }
+
+ void Translator::operator()(const ast::NilExp& e)
+ {
+ // FIXME DONE: Some code was deleted here (Get the record_type of the Nil type, and create a null pointer).
+ const llvm::Type* pointer_type = llvm_type(*e.type_get());
+ value_ = llvm::ConstantPointerNull::get(pointer_type->getPointerTo());
+ }
+
+ void Translator::operator()(const ast::IntExp& e)
+ {
+ // FIXME DONE: Some code was deleted here (Integers in Tiger are all 32bit signed).
+ value_ = builder_.getInt32(e.value_get());
+ }
+
+ void Translator::operator()(const ast::StringExp& e)
+ {
+ // FIXME DONE: Some code was deleted here (Strings are translated as `i8*` values, like C's `char*`).
+ value_ = builder_.CreateGlobalStringPtr(e.value_get(), "str_exp");
+ }
+
+ void Translator::operator()(const ast::RecordExp& e)
+ {
+ // Get the record type
+ const type::Record* record_type = nullptr;
+ // FIXME DONE: Some code was deleted here.
+ record_type = dynamic_cast<const type::Record*>(&e.type_get()->actual());
+
+ // Type the record and use get_record_ltype() to get its LLVM type
+ llvm_type(*record_type);
+ auto struct_ltype = type_visitor_.get_record_ltype(record_type);
+
+ // The size of the structure and cast it to int
+ auto sizeof_val = llvm::ConstantExpr::getSizeOf(struct_ltype);
+ sizeof_val = llvm::ConstantExpr::getTruncOrBitCast(sizeof_val, i32_t(ctx_));
+
+ // Generate the instruction calling Malloc
+ auto malloc_val = builder_.CreateMalloc(
+ i32_t(ctx_), struct_ltype, sizeof_val, nullptr, nullptr, "malloccall");
+
+ // Init the fields
+ // FIXME DONE: Some code was deleted here.
+ auto& fields = e.fields_get();
+
+ assertion(fields.size() == record_type->fields_get().size());
+
+ for (const auto field : fields)
+ {
+ const auto field_value = translate(field->init_get());
+ const auto field_index = record_type->field_index(field->name_get());
+
+ assertion(field_index >= 0);
+
+ const auto field_llvm_address = builder_.CreateStructGEP(
+ struct_ltype, malloc_val, field_index, "field_" + field->name_get().get()
+ );
+
+ value_ = builder_.CreateStore(field_value, field_llvm_address);
+ }
+
+ value_ = malloc_val;
+ }
+
+ void Translator::operator()(const ast::OpExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ // The comparison instructions returns an i1, and we need an i32, since everything
+ // is an i32 in Tiger. Use a zero-extension to avoid this.
+ const auto left_operand = \
+ get_dereferenced(translate(e.left_get()), e.left_get().type_get());
+ const auto right_operand = \
+ get_dereferenced(translate(e.right_get()), e.right_get().type_get());
+
+ switch (e.oper_get())
+ {
+ case ast::OpExp::Oper::add:
+ value_ = builder_.CreateAdd(left_operand, right_operand, "op_add");
+ break;
+ case ast::OpExp::Oper::sub:
+ value_ = builder_.CreateSub(left_operand, right_operand, "op_sub");
+ break;
+ case ast::OpExp::Oper::mul:
+ value_ = builder_.CreateMul(left_operand, right_operand, "op_mul");
+ break;
+ case ast::OpExp::Oper::div:
+ value_ = builder_.CreateSDiv(left_operand, right_operand, "op_div");
+ break;
+ case ast::OpExp::Oper::eq:
+ value_ = builder_.CreateICmpEQ(left_operand, right_operand, "op_eq");
+ break;
+ case ast::OpExp::Oper::ne:
+ value_ = builder_.CreateICmpNE(left_operand, right_operand, "op_ne");
+ break;
+ case ast::OpExp::Oper::gt:
+ value_ = builder_.CreateICmpSGT(left_operand, right_operand, "op_gt");
+ break;
+ case ast::OpExp::Oper::ge:
+ value_ = builder_.CreateICmpSGE(left_operand, right_operand, "op_ge");
+ break;
+ case ast::OpExp::Oper::lt:
+ value_ = builder_.CreateICmpSLT(left_operand, right_operand, "op_lt");
+ break;
+ case ast::OpExp::Oper::le:
+ value_ = builder_.CreateICmpSLE(left_operand, right_operand, "op_le");
+ break;
+ }
+
+ value_ = builder_.CreateZExtOrTrunc(value_, i32_t(ctx_), "op_zext");
+ }
+
+ void Translator::operator()(const ast::SeqExp& e)
+ {
+ // An empty SeqExp is an empty expression, so we should return an int
+ // containing 0, since its type is void.
+ // FIXME DONE: Some code was deleted here.
+ if (e.exps_get().empty())
+ {
+ value_ = get_void_value();
+ return;
+ }
+
+ for (const auto& exp : e.exps_get())
+ {
+ translate(*exp);
+
+ if (dynamic_cast<const ast::Var*>(exp) != nullptr)
+ {
+ value_ = get_dereferenced(value_, exp->type_get());
+ }
+ }
+ }
+
+ void Translator::operator()(const ast::AssignExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ llvm::Value* value = \
+ get_dereferenced(translate(e.exp_get()), e.exp_get().type_get());
+ llvm::Value* variable = translate(e.var_get());
+
+ value_ = builder_.CreateStore(value, variable);
+ }
+
+ void Translator::operator()(const ast::IfExp& e)
+ {
+ // FIXME: Some code was deleted here (IfExps are handled in a similar way to Kaleidoscope (see LangImpl5.html)).
+ llvm::Value* cond = translate(e.test_get());
+
+ auto zero_val = llvm::ConstantInt::getSigned(cond->getType(), 0);
+
+ // The condition may not be correct we are checking that this is different than 0
+ cond = builder_.CreateICmpNE(cond, zero_val, "ifcond");
+
+ // We create the blocks
+ auto then_block = llvm::BasicBlock::Create(ctx_, "then_body", current_function_);
+ auto else_block = llvm::BasicBlock::Create(ctx_, "else_body", current_function_);
+ auto after_if = llvm::BasicBlock::Create(ctx_, "after_if", current_function_);
+
+ // Explicitely create the if statement
+ builder_.CreateCondBr(cond, then_block, else_block);
+
+ // We now tell the builder to insert newly added code to this block
+ builder_.SetInsertPoint(then_block);
+
+ llvm::Value* then_body = translate(e.thenclause_get());
+
+ // Create an unconditional jump to the end of the if statement
+ builder_.CreateBr(after_if);
+
+ // Very akward case where we have an if inside another if
+ // Apparently this is done to not disturb the PHI node
+ then_block = builder_.GetInsertBlock();
+
+ // We add the else block to the current function
+ // Function is private so we directly add the block to the function
+ // current_function_->getBasicBlockList().push_back(else_block);
+
+ // We are now adding stuff in the else block
+ builder_.SetInsertPoint(else_block);
+
+ llvm::Value* else_body = translate(e.elseclause_get());
+
+ builder_.CreateBr(after_if);
+
+ else_block = builder_.GetInsertBlock();
+
+ // Finally we add the remaining block, the one to be executer after the if
+ // current_function_->getBasicBlockList().push_back(after_if);
+
+ builder_.SetInsertPoint(after_if);
+
+ if (dynamic_cast<const type::Void*>(e.type_get()) == nullptr)
+ {
+ // And now for the star of the show: The PHI node
+ // This is a value that can take multiple values depending on the path
+ llvm::PHINode* phi = builder_.CreatePHI(llvm_type(*e.elseclause_get().type_get()), 2, "phinode");
+
+ phi->addIncoming(then_body, then_block);
+ phi->addIncoming(else_body, else_block);
+
+ value_ = phi;
+ }
+ }
+
+ void Translator::operator()(const ast::WhileExp& e)
+ {
+ // Bb containing the test and the branching
+ auto test_bb = llvm::BasicBlock::Create(ctx_, "test", current_function_);
+ auto body_bb = llvm::BasicBlock::Create(ctx_, "body", current_function_);
+ auto after_bb =
+ llvm::BasicBlock::Create(ctx_, "afterloop", current_function_);
+
+ // Save the after block for breaks
+ loop_end_[&e] = after_bb;
+
+ // Explicitly fall through from the current block
+ builder_.CreateBr(test_bb);
+
+ // Start inside the test BasicBlock
+ builder_.SetInsertPoint(test_bb);
+
+ auto cond_val = translate(e.test_get());
+ auto zero_val = llvm::ConstantInt::getSigned(cond_val->getType(), 0);
+ auto cmp_val = builder_.CreateICmpNE(cond_val, zero_val, "loopcond");
+
+ // Create the branching
+ builder_.CreateCondBr(cmp_val, body_bb, after_bb);
+
+ // Translate the body inside the body BasicBlock
+ builder_.SetInsertPoint(body_bb);
+ // Don't store the return value, is should be void.
+ translate(e.body_get());
+
+ // Go back to the Test BasicBlock
+ builder_.CreateBr(test_bb);
+
+ // Continue after the loop BasicBlock
+ builder_.SetInsertPoint(after_bb);
+ }
+
+ void Translator::operator()(const ast::BreakExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ const auto while_node = dynamic_cast<const ast::WhileExp*>(e.def_get());
+
+ precondition(loop_end_.contains(while_node));
+
+ value_ = builder_.CreateBr(loop_end_.at(while_node));
+ }
+
+ void Translator::operator()(const ast::ArrayExp& e)
+ {
+ // Translate the number of elements,
+ // fill the array with the default value, then
+ // return the pointer to the allocated zone.
+ // FIXME DONE: Some code was deleted here (Use `init_array`).
+ llvm::Value* size = translate(e.size_get());
+ llvm::Value* init_value = translate(e.init_get());
+ value_ = init_array(size, init_value);
+ }
+
+ void Translator::operator()(const ast::CastExp& e)
+ {
+ auto exp_val = translate(e.exp_get());
+ llvm::Type* ltype = nullptr;
+ // FIXME DONE: Some code was deleted here (Destination llvm type).
+ ltype = llvm_type(*e.type_get());
+ value_ = builder_.CreateBitCast(exp_val, ltype, "cast_exp");
+ }
+
+ void Translator::operator()(const ast::FunctionChunk& e)
+ {
+ for (const auto& fdec : e)
+ visit_function_dec_header(*fdec);
+
+ for (const auto& fdec : e)
+ // There is nothing to translate for primitives.
+ if (fdec->body_get())
+ visit_function_dec_body(*fdec);
+ }
+
+ void Translator::visit_function_dec_header(const ast::FunctionDec& e)
+ {
+ bool is_main = e.name_get() == "_main";
+ bool is_primitive = e.body_get() == nullptr;
+ auto name = function_dec_name(e);
+
+ const type::Type* node_type = nullptr;
+ // FIXME DONE: Some code was deleted here.
+ node_type = &e.type_get()->actual();
+
+ auto& function_type = static_cast<const type::Function&>(*node_type);
+ auto function_ltype = llvm_function_type(function_type);
+
+ // Main and primitives have External linkage.
+ // Other Tiger functions are treated as "static" functions in C.
+ auto linkage = is_main || is_primitive ? llvm::Function::ExternalLinkage
+ : llvm::Function::InternalLinkage;
+
+ auto the_function =
+ llvm::Function::Create(function_ltype, linkage, name, &module_);
+ set_default_attributes(*the_function, e);
+
+ auto& escaped = escaped_[&function_type];
+
+ // Name each argument of the function
+ for (auto arg_it = the_function->arg_begin();
+ arg_it != the_function->arg_end(); ++arg_it)
+ {
+ auto i = std::distance(the_function->arg_begin(), arg_it);
+ auto var = escaped.size() && static_cast<size_t>(i) < escaped.size()
+ ? *std::next(escaped_[&function_type].begin(), i)
+ : e.formals_get()[i - escaped.size()];
+
+ arg_it->setName(var->name_get().get());
+ }
+ }
+
+ void Translator::visit_function_dec_body(const ast::FunctionDec& e)
+ {
+ auto the_function = module_.getFunction(function_dec_name(e));
+
+ // Save the old function in case a nested function occurs.
+ auto old_insert_point = builder_.saveIP();
+ auto old_function = current_function_;
+ current_function_ = the_function;
+
+ // Create a new basic block to start the function.
+ auto bb = llvm::BasicBlock::Create(ctx_, "entry_"s + e.name_get().get(),
+ the_function);
+ builder_.SetInsertPoint(bb);
+
+ const type::Type* node_type = nullptr;
+ // FIXME DONE: Some code was deleted here.
+ node_type = &e.type_get()->actual();
+
+ auto& function_type = static_cast<const type::Function&>(*node_type);
+ auto& escaped = escaped_[&function_type];
+ auto& formals = e.formals_get();
+
+ auto arg_it = the_function->arg_begin();
+
+ for (const auto var : escaped)
+ {
+ locals_[current_function_][var] = &*arg_it;
+ ++arg_it;
+ }
+
+ // FIXME DONE: Some code was deleted here (Create alloca instructions for each variable).
+ for (size_t i = 0; i < formals.decs_get().size(); i++)
+ {
+ const ast::VarDec* const parameter = formals.decs_get().at(i);
+ locals_[current_function_][parameter] = arg_it + i;
+ }
+
+ translate(*e.body_get());
+
+ // FIXME DONE: Some code was deleted here (Create a return instruction).
+ if (dynamic_cast<const type::Void*>(&function_type.result_get().actual()) != nullptr)
+ {
+ value_ = builder_.CreateRetVoid();
+ }
+ else
+ {
+ if (dynamic_cast<const ast::Var*>(e.body_get()) != nullptr)
+ {
+ value_ = get_dereferenced(value_, e.body_get()->type_get());
+ }
+
+ value_ = builder_.CreateRet(value_);
+ }
+
+ // Validate the generated code, checking for consistency.
+ llvm::verifyFunction(*the_function);
+
+ // Restore the context of the old function.
+ current_function_ = old_function;
+ builder_.restoreIP(old_insert_point);
+ }
+
+ void Translator::operator()(const ast::CallExp& e)
+ {
+ // Look up the name in the global module table.
+ // If it's a primitive, rename the call to tc_name.
+ //
+ // Then, add the escaped variables and the rest of the arguments to the
+ // list of arguments, and return the correct value.
+ // FIXME DONE: Some code was deleted here.
+ const auto function_type = dynamic_cast<const type::Function*>(e.def_get()->type_get());
+
+ precondition(function_type != nullptr);
+
+ std::string function_name = e.name_get().get();
+
+ if (e.def_get()->body_get() == nullptr)
+ {
+ // then the call references a primitive
+ function_name = "tc_" + function_name;
+ }
+
+ llvm::Function* function = module_.getFunction(function_name);
+ std::vector<llvm::Value*> args;
+ std::string twine = "call_" + e.name_get().get();
+
+ assertion(function != nullptr);
+
+ for (auto& var : escaped_[function_type])
+ {
+ args.push_back(translate(*var));
+ }
+
+ for (auto& parameter : e.args_get())
+ {
+ llvm::Value* variable = translate(*parameter);
+ llvm::Value* value = dynamic_cast<ast::Var*>(parameter) != nullptr
+ ? get_dereferenced(variable, parameter->type_get())
+ : variable;
+ args.push_back(value);
+ }
+
+ if (dynamic_cast<const type::Void*>(&function_type->result_get().actual()) != nullptr)
+ {
+ twine = "";
+ }
+
+ value_ = builder_.CreateCall(function, args, twine);
+ }
+
+ void Translator::operator()(const ast::VarDec& e)
+ {
+ // Void var types are actually Ints represented by a 0
+ // FIXME DONE: Some code was deleted here.
+ if (const auto existing_ref = declaration_in_current_scope(e))
+ {
+ value_ = existing_ref;
+ return;
+ }
+
+ llvm::Type* variable_type = llvm_type(*e.type_get());
+
+ llvm::Value* init_value = translate(*e.init_get());
+
+ llvm::Value* variable = create_alloca(current_function_, variable_type,
+ "var_" + e.name_get().get());
+
+ locals_[current_function_][&e] = variable;
+ value_ = builder_.CreateStore(init_value, variable);
+ }
+
+ llvm::Value* Translator::declaration_in_current_scope(const ast::VarDec& e) const
+ {
+ if (!locals_.contains(current_function_))
+ {
+ return nullptr;
+ }
+
+ const auto current_scope = locals_.at(current_function_);
+
+ return current_scope.contains(&e) ? current_scope.at(&e) : nullptr;
+ }
+
+} // namespace llvmtranslate
diff --git a/tiger-compiler/src/llvmtranslate/translator.hh b/tiger-compiler/src/llvmtranslate/translator.hh
new file mode 100644
index 0000000..3ddb625
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/translator.hh
@@ -0,0 +1,145 @@
+/**
+ ** \file llvmtranslate/translator.hh
+ ** \brief Declaration of llvmtranslate::Translator
+ */
+
+#pragma once
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/IR/IRBuilder.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/Value.h>
+
+#pragma GCC diagnostic pop
+
+#include <ast/default-visitor.hh>
+#include <ast/non-assert-visitor.hh>
+#include <ast/non-object-visitor.hh>
+#include <llvmtranslate/fwd.hh>
+#include <llvmtranslate/llvm-type-visitor.hh>
+
+namespace llvmtranslate
+{
+ class Translator
+ : public ast::DefaultConstVisitor
+ , public ast::NonObjectConstVisitor
+ , public ast::NonAssertConstVisitor
+ {
+ public:
+ /// Super class.
+ using super_type = ast::DefaultConstVisitor;
+ /// Import overloaded operator() methods.
+ using super_type::operator();
+
+ Translator(llvm::Module& module, escaped_map_type&& escaped);
+
+ /// Run the translation.
+ void operator()(const ast::Ast& e) override;
+
+ /// \brief Run this visitor on \a node, and return its translation.
+ ///
+ /// It is also guaranteed that \a value_ is set to it.
+ /// More generally, this routine saves from direct calls to \a accept.
+ llvm::Value* translate(const ast::Ast& node);
+
+ /// \name Lvalues
+ /// \{
+ void operator()(const ast::SimpleVar& e) override;
+ void operator()(const ast::FieldVar& e) override;
+ void operator()(const ast::SubscriptVar& e) override;
+ /// \}
+
+ /// \name Expressions
+ /// \{
+ void operator()(const ast::NilExp&) override;
+ void operator()(const ast::IntExp& e) override;
+ void operator()(const ast::StringExp& e) override;
+ void operator()(const ast::RecordExp& e) override;
+ void operator()(const ast::CallExp& e) override;
+ void operator()(const ast::OpExp& e) override;
+ void operator()(const ast::SeqExp& e) override;
+ void operator()(const ast::AssignExp& e) override;
+ void operator()(const ast::IfExp& e) override;
+ void operator()(const ast::WhileExp& e) override;
+ void operator()(const ast::BreakExp&) override;
+ void operator()(const ast::ArrayExp& e) override;
+ void operator()(const ast::CastExp& e) override;
+ /// \}
+
+ /// \name Declarations
+ /// \{
+ void operator()(const ast::FunctionChunk& e) override;
+ void visit_function_dec_header(const ast::FunctionDec& e);
+ void visit_function_dec_body(const ast::FunctionDec& e);
+ void operator()(const ast::VarDec& e) override;
+
+ /**
+ * Check for the existence of a VarDec node within the current function
+ * scoped (locals_[current_function_]).
+ *
+ * If the node does exist within the scope, its LLVM reference is returned
+ * afterward. If not, nullptr is returned instead.
+ *
+ * @param e The node to look for
+ * @return The associated LLVM value if e does exist within the scope.
+ * nullptr otherwise
+ */
+ llvm::Value* declaration_in_current_scope(const ast::VarDec& e) const;
+
+ /// \}
+
+ protected:
+ /// The translation of the last visited node.
+ llvm::Value* value_ = nullptr;
+
+ /// The current function.
+ llvm::Function* current_function_ = nullptr;
+
+ /// The module containing the metadata and this file's AST.
+ llvm::Module& module_;
+
+ /// The global context of the translator.
+ llvm::LLVMContext& ctx_;
+
+ /// IR builder to simplify building nodes and instructions.
+ llvm::IRBuilder<> builder_;
+
+ /// Access for each "variable".
+ /// Since the AST doesn't contain the arguments added
+ /// for the lambda lifting, we need to identify them by their declaration.
+ std::map<const llvm::Function*, std::map<const ast::VarDec*, llvm::Value*>>
+ locals_;
+
+ /// For each loop, the basic block immediately after it.
+ std::map<const ast::WhileExp*, llvm::BasicBlock*> loop_end_;
+
+ /// Access every escaped variable for each function.
+ escaped_map_type escaped_;
+
+ /// The llvm type translator.
+ LLVMTypeVisitor type_visitor_;
+
+ /// Get a Tiger void value.
+ inline llvm::Value* get_void_value();
+
+ inline llvm::Value* get_dereferenced(llvm::Value* ptr, const type::Type* type);
+
+ private:
+ /// Get a LLVM access to a variable, usually to be loaded right after.
+ llvm::Value* access_var(const ast::Var& e);
+
+ /// Call the init_array function that allocates and initialize the array.
+ llvm::Value* init_array(llvm::Value* count_val, llvm::Value* init_val);
+
+ /// Get a llvm::Type from a type::Type using the type_visitor_.
+ llvm::Type* llvm_type(const type::Type& type);
+
+ /// Create a llvm function from a function type.
+ llvm::FunctionType* llvm_function_type(const type::Function& function_t);
+ };
+
+} // namespace llvmtranslate
+
+#include <llvmtranslate/translator.hxx>
diff --git a/tiger-compiler/src/llvmtranslate/translator.hxx b/tiger-compiler/src/llvmtranslate/translator.hxx
new file mode 100644
index 0000000..a509d4d
--- /dev/null
+++ b/tiger-compiler/src/llvmtranslate/translator.hxx
@@ -0,0 +1,30 @@
+/**
+ ** \file llvmtranslate/translator.hxx
+ ** \brief Inline methods for llvmtranslate::Translator.
+ */
+
+#pragma once
+
+#include <llvmtranslate/translator.hh>
+
+namespace llvmtranslate
+{
+
+ inline llvm::Value* Translator::get_void_value()
+ {
+ return builder_.getIntN(0, 0);
+ }
+
+ inline llvm::Value* Translator::get_dereferenced(llvm::Value* ptr,
+ const type::Type* type)
+ {
+ if (!ptr->getType()->isPointerTy())
+ {
+ return ptr;
+ }
+
+ return builder_.CreateLoad(llvm_type(*type), ptr, ptr->getName() + "_dereferenced");
+ }
+
+
+} // namespace llvmtranslate