diff --git a/include/dusk/AST/Pattern.h b/include/dusk/AST/Pattern.h
index c0ebc47a92ee8ab1ff76cd36936bb32e3e80fa34..4ab8409e9f258f820bb1b5d8175e60d674a31a80 100644
--- a/include/dusk/AST/Pattern.h
+++ b/include/dusk/AST/Pattern.h
@@ -1,6 +1,4 @@
-
-
-  //===--- Pattern.h - Dusk patterns ------------------------------*- C++ -*-===//
+//===--- Pattern.h - Dusk patterns ------------------------------*- C++ -*-===//
 //
 //                                 dusk-lang
 // This source file is part of a dusk-lang project, which is a semestral
diff --git a/include/dusk/AST/Stmt.h b/include/dusk/AST/Stmt.h
index a811718b10bf5d2317c3aff269fc378eb37fba7d..46b6b6fe228d43e8c9ef36be8a7da56dbfebda78 100644
--- a/include/dusk/AST/Stmt.h
+++ b/include/dusk/AST/Stmt.h
@@ -26,7 +26,17 @@ class IdentifierExpr;
 class ASTWalker;
 
 /// Describes statement type.
-enum struct StmtKind { Break, Return, Range, Block, Func, For, While, If, Subscript };
+enum struct StmtKind {
+  Break,
+  Return,
+  Range,
+  Block,
+  Func,
+  For,
+  While,
+  If,
+  Subscript
+};
 
 class Stmt : public ASTNode {
   /// Statement type
@@ -66,26 +76,26 @@ public:
 };
 
 /// Subscript statement.
-class SubscriptStmt: public Stmt {
+class SubscriptStmt : public Stmt {
   /// Subcript value
   Expr *Value;
-  
+
   /// Location of left bracket
   SMLoc LBracket;
-  
+
   /// Location of right bracket
   SMLoc RBracket;
-  
+
 public:
   SubscriptStmt(Expr *V, SMLoc L, SMLoc R);
-  
+
   Expr *getValue() const { return Value; }
   SMLoc getLBracket() const { return LBracket; }
   SMLoc getRBracket() const { return RBracket; }
-  
+
   virtual SMRange getSourceRange() const override;
 };
-  
+
 /// Represents a range.
 class RangeStmt : public Stmt {
   /// Start of the range
@@ -209,4 +219,3 @@ public:
 
 #endif /* DUSK_STMT_H */
 
-
diff --git a/include/dusk/IRGen/Context.h b/include/dusk/IRGen/Context.h
index c21c347d558117afd5e1f736cc1ebea5ea741a13..85672bcde19d9a529e402276456b2b13f9b7489b 100644
--- a/include/dusk/IRGen/Context.h
+++ b/include/dusk/IRGen/Context.h
@@ -23,12 +23,14 @@
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/GlobalVariable.h"
 #include <memory>
 
 namespace dusk {
 class VarDecl;
 class ConstDecl;
 class FuncDecl;
+class ParamDecl;
 
 namespace irgen {
 
@@ -89,20 +91,18 @@ private:
 /// Holds declaration of variables, constatnts and functions.
 class Context {
   llvm::LLVMContext &Ctx;
-  llvm::Module *Module;
-  llvm::IRBuilder<> &Builder;
   llvm::StringMap<llvm::FunctionType *> Funcs;
   ContextVals *Vals;
-
-  unsigned Depth;
+  unsigned Depth = 0;
 
 public:
+  llvm::Module *Module;
+  llvm::IRBuilder<> &Builder;
+  
   Context(llvm::LLVMContext &C, llvm::Module *M, llvm::IRBuilder<> &B);
   ~Context();
 
   operator llvm::LLVMContext &() { return Ctx; }
-
-  llvm::IRBuilder<> &getBuilder() { return Builder; }
   
   /// Returns current depth of the context.
   unsigned getDepth() const { return Depth; }
@@ -112,6 +112,12 @@ public:
   /// \return \c true on success, \c false if the current scope is already
   /// a variable, constatnt or function with the same identifier.
   bool declare(const VarDecl *);
+  
+  /// \brief Declares a param in current scope.
+  ///
+  /// \return \c true on success, \c false if the current scope is already
+  /// a variable, constatnt or function with the same identifier.
+  bool declare(const ParamDecl *);
 
   /// \brief Declares a variable in current scope.
   ///
diff --git a/include/dusk/IRGen/IRGenerator.h b/include/dusk/IRGen/IRGenerator.h
index 9368eadd99458c4f223875ed4d1ac17469b41ea8..01c50ca251fb5ac911ac4547963bf3e64b8154d2 100644
--- a/include/dusk/IRGen/IRGenerator.h
+++ b/include/dusk/IRGen/IRGenerator.h
@@ -39,7 +39,7 @@ namespace irgen {
 class IRGenerator : public ASTWalker {
   llvm::StringMap<llvm::AllocaInst *> NamedValues;
   DiagnosticEngine &Diag;
-  llvm::LLVMContext Context;
+  llvm::LLVMContext Ctx;
   llvm::IRBuilder<> Builder;
   std::unique_ptr<llvm::Module> Module;
   
@@ -49,7 +49,7 @@ public:
   IRGenerator(DiagnosticEngine &Diag);
   ~IRGenerator();
   
-  void gen(ModuleDecl *Module) {}
+  bool gen(ModuleDecl *M);
   
 private:
     
diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt
index 6b115e3f8579fe89c634904a1fbf6a70c81b6e2d..e54a233f8c7a910682ca83ab572fac6690d74edb 100644
--- a/lib/IRGen/CMakeLists.txt
+++ b/lib/IRGen/CMakeLists.txt
@@ -1,7 +1,11 @@
 set(SOURCE
     ${CMAKE_CURRENT_SOURCE_DIR}/Context.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/GenDecl.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/GenDecl.h
     ${CMAKE_CURRENT_SOURCE_DIR}/GenExpr.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/GenExpr.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/GenStmt.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/GenStmt.h
     ${CMAKE_CURRENT_SOURCE_DIR}/IRGenerator.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/TopLevelExpr.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/TopLevelExpr.h
diff --git a/lib/IRGen/Context.cpp b/lib/IRGen/Context.cpp
index 8d0dbd25ae6fd3180e37bf0a01c235aa27209647..7ac61c403b465a8b5461abde42bb1991b3d07d3b 100644
--- a/lib/IRGen/Context.cpp
+++ b/lib/IRGen/Context.cpp
@@ -1,4 +1,5 @@
 
+
 //===--- Context.cpp - Dusck context implementation -----------------------===//
 //
 //                                 dusk-lang
@@ -17,6 +18,8 @@
 #include "dusk/AST/Pattern.h"
 #include <vector>
 
+#include "GenExpr.h"
+
 using namespace dusk;
 using namespace irgen;
 
@@ -72,29 +75,57 @@ llvm::Value *ContextVals::get(StringRef Str) const {
 // MARK: - Context
 
 Context::Context(llvm::LLVMContext &C, llvm::Module *M, llvm::IRBuilder<> &B)
-    : Ctx(C), Module(M), Builder(B), Vals(new ContextVals()) {}
+    : Ctx(C), Vals(new ContextVals()), Module(M), Builder(B) {}
 
 Context::~Context() { delete Vals; }
 
 bool Context::declare(const VarDecl *D) {
   // Check if already declared in current scope
-  if (!Vals->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
+  if (Vals->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
+    return false;
+
+  // Check if global
+  if (Depth == 0) {
+    auto GV =
+        Module->getOrInsertGlobal(D->getName(), llvm::Type::getInt64Ty(Ctx));
+    Vals->Vars[D->getName()] = GV;
+    return true;
+  }
+
+  auto Ty = llvm::Type::getInt64Ty(Ctx);
+  auto Var = Builder.CreateAlloca(Ty, 0, D->getName());
+  Vals->Vars[D->getName()] = Var;
+  return true;
+}
+
+bool Context::declare(const ParamDecl *D) {
+  assert(Depth != 0 && "Param declaration happen only in functions");
+  // Check if already declared in current scope
+  if (Vals->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
     return false;
 
-  auto Ty = llvm::Type::getInt32Ty(Ctx);
-  auto Val = Builder.CreateAlloca(Ty, 0, D->getName());
-  Vals->Vars[D->getName()] = Val;
+  auto Ty = llvm::Type::getInt64Ty(Ctx);
+  auto Par = Builder.CreateAlloca(Ty, 0, D->getName());
+  Vals->Vars[D->getName()] = Par;
   return true;
 }
 
 bool Context::declare(const ConstDecl *D) {
   // Check if already declared in current scope
-  if (!Vals->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
+  if (Vals->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
     return false;
 
-  auto Ty = llvm::Type::getInt32Ty(Ctx);
-  auto Val = Builder.CreateAlloca(Ty, 0, D->getName());
-  Vals->Consts[D->getName()] = Val;
+  // Check if global
+  if (Depth == 0) {
+    auto GV =
+        Module->getOrInsertGlobal(D->getName(), llvm::Type::getInt64Ty(Ctx));
+    Vals->Consts[D->getName()] = GV;
+    return true;
+  }
+
+  auto Ty = llvm::Type::getInt64Ty(Ctx);
+  auto Const = Builder.CreateAlloca(Ty, 0, D->getName());
+  Vals->Consts[D->getName()] = Const;
   return true;
 }
 
@@ -106,7 +137,7 @@ bool Context::declare(const FuncDecl *Fn) {
   if (Funcs[Fn->getName()] != nullptr)
     return false;
 
-  auto Ty = llvm::Type::getInt32Ty(Ctx);
+  auto Ty = llvm::Type::getInt64Ty(Ctx);
   auto Args = std::vector<llvm::Type *>(Fn->getArgs()->count(), Ty);
   auto FT = llvm::FunctionType::get(llvm::Type::getVoidTy(Ctx), Args, false);
   Funcs[Fn->getName()] = FT;
diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..feda6ba09bb0c1a494fa06dcc9d63bda298fe124
--- /dev/null
+++ b/lib/IRGen/GenDecl.cpp
@@ -0,0 +1,86 @@
+//===--- GenDecl.cpp ------------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GenDecl.h"
+#include "GenExpr.h"
+
+using namespace dusk;
+using namespace irgen;
+
+GenDecl::GenDecl(Decl *D, Context &C) : Declaration(D), Ctx(C) {}
+
+bool GenDecl::gen() { return codegen(Declaration); }
+
+bool GenDecl::genArgs() {
+  assert(Declaration->getKind() == DeclKind::Func &&
+         "Invalid declaration type");
+  auto FT = static_cast<FuncDecl *>(Declaration);
+  
+  for (auto &Arg : FT->getArgs()->getVars())
+    if (!codegen(Arg))
+      llvm_unreachable("Error argument declaration");
+  return true;
+}
+
+bool GenDecl::codegen(VarDecl *D) {
+  if (!Ctx.declare(D))
+    llvm_unreachable("Redefinition");
+  
+  auto Addr = Ctx.getVal(D->getName());
+  auto Val = GenExpr(D->getValue(), Ctx).gen();
+  Ctx.Builder.CreateStore(Val, Addr);
+  return true;
+}
+
+bool GenDecl::codegen(ConstDecl *D) {
+  if (!Ctx.declare(D))
+    llvm_unreachable("Redefinition");
+
+  auto Addr = Ctx.getVal(D->getName());
+  auto Val = GenExpr(D->getValue(), Ctx).gen();
+  Ctx.Builder.CreateStore(Val, Addr);
+  return true;
+}
+
+bool GenDecl::codegen(FuncDecl *D) {
+  if (!Ctx.declare(D))
+    llvm_unreachable("Redefinition of function");
+
+  auto P = Ctx.getFuncProto(D->getName());
+  if (!P)
+    llvm_unreachable("Function not found");
+
+  auto F = llvm::Function::Create(P, llvm::Function::ExternalLinkage,
+                                  D->getName(), Ctx.Module);
+  unsigned Idx = 0;
+  auto Args = D->getArgs()->getVars();
+  for (auto &Arg : F->args())
+    Arg.setName(Args[Idx++]->getName());
+  return true;
+}
+
+bool GenDecl::codegen(ParamDecl *D) {
+  return Ctx.declare(D);
+}
+
+bool GenDecl::codegen(Decl *D) {
+  switch (D->getKind()) {
+  case DeclKind::Var:
+    return codegen(static_cast<VarDecl *>(D));
+  case DeclKind::Const:
+    return codegen(static_cast<ConstDecl *>(D));
+  case DeclKind::Func:
+    return codegen(static_cast<FuncDecl *>(D));
+  case DeclKind::Param:
+    return codegen(static_cast<ParamDecl *>(D));
+  default:
+    llvm_unreachable("Invalid declaration");
+  }
+}
+
diff --git a/lib/IRGen/GenDecl.h b/lib/IRGen/GenDecl.h
new file mode 100644
index 0000000000000000000000000000000000000000..e63dff4cc5d175543a3f46fc4a0e9dfaf29518db
--- /dev/null
+++ b/lib/IRGen/GenDecl.h
@@ -0,0 +1,49 @@
+//===--- GenDecl.h - Dusk IR declaration generation -------------*- C++ -*-===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DUSK_GEN_DECL_H
+#define DUSK_GEN_DECL_H
+
+#include "dusk/AST/Decl.h"
+#include "dusk/AST/Expr.h"
+#include "dusk/AST/Stmt.h"
+#include "dusk/AST/Pattern.h"
+#include "dusk/IRGen/Context.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Value.h"
+#include "llvm/IR/LLVMContext.h"
+#include "GenExpr.h"
+
+namespace dusk {
+
+namespace irgen {
+
+class GenDecl {
+  Decl *Declaration;
+  Context &Ctx;
+
+public:
+  GenDecl(Decl *D, Context &C);
+  
+  bool gen();
+  bool genArgs();
+  
+private:
+  bool codegen(Decl *D);
+  bool codegen(VarDecl *D);
+  bool codegen(ConstDecl *D);
+  bool codegen(FuncDecl *D);
+  bool codegen(ParamDecl *D);
+};
+
+} // namespace irgen
+
+} // namespace dusk
+
+#endif /* DUSK_GEN_DECL_H */
diff --git a/lib/IRGen/GenExpr.cpp b/lib/IRGen/GenExpr.cpp
index 7a12278b254f330f6d9dc367720ffad67bdae2d3..c5ebcedcd95dee2d486b64beed26184979ccff91 100644
--- a/lib/IRGen/GenExpr.cpp
+++ b/lib/IRGen/GenExpr.cpp
@@ -20,8 +20,6 @@ GenExpr::GenExpr(Expr *R, Context &C) : Root(R), Ctx(C) {}
 
 llvm::Value *GenExpr::gen() { return codegen(Root); }
 
-llvm::Value *GenExpr::codegen(Expr *E) { return nullptr; }
-
 llvm::Value *GenExpr::codegen(NumberLiteralExpr *E) {
   auto Value = llvm::APSInt::get(E->getValue());
   return llvm::ConstantInt::get(Ctx, Value);
@@ -29,7 +27,7 @@ llvm::Value *GenExpr::codegen(NumberLiteralExpr *E) {
 
 llvm::Value *GenExpr::codegen(IdentifierExpr *E) {
   auto Addr = Ctx.getVal(E->getName());
-  return Ctx.getBuilder().CreateLoad(Addr, E->getName());
+  return Ctx.Builder.CreateLoad(Addr, E->getName());
 }
 
 llvm::Value *GenExpr::codegen(InfixExpr *E) {
@@ -41,27 +39,27 @@ llvm::Value *GenExpr::codegen(InfixExpr *E) {
   switch (E->getOp().getKind()) {
   // Arithemtic operations
   case tok::plus:
-    return Ctx.getBuilder().CreateAdd(LHS, RHS, "addtmp");
+    return Ctx.Builder.CreateAdd(LHS, RHS, "addtmp");
   case tok::minus:
-    return Ctx.getBuilder().CreateSub(LHS, RHS, "subtmp");
+    return Ctx.Builder.CreateSub(LHS, RHS, "subtmp");
   case tok::multipy:
-    return Ctx.getBuilder().CreateMul(LHS, RHS, "multmp");
+    return Ctx.Builder.CreateMul(LHS, RHS, "multmp");
   case tok::divide:
-    return Ctx.getBuilder().CreateSDiv(LHS, RHS, "divtmp");
+    return Ctx.Builder.CreateSDiv(LHS, RHS, "divtmp");
 
   // Logical operations
   case tok::equals:
-    return Ctx.getBuilder().CreateICmpEQ(LHS, RHS, "eqtmp");
+    return Ctx.Builder.CreateICmpEQ(LHS, RHS, "eqtmp");
   case tok::nequals:
-    return Ctx.getBuilder().CreateICmpNE(LHS, RHS, "neqtmp");
+    return Ctx.Builder.CreateICmpNE(LHS, RHS, "neqtmp");
   case tok::greater:
-    return Ctx.getBuilder().CreateICmpSGT(LHS, RHS, "gttmp");
+    return Ctx.Builder.CreateICmpSGT(LHS, RHS, "gttmp");
   case tok::greater_eq:
-    return Ctx.getBuilder().CreateICmpSGE(LHS, RHS, "getmp");
+    return Ctx.Builder.CreateICmpSGE(LHS, RHS, "getmp");
   case tok::less:
-    return Ctx.getBuilder().CreateICmpSLT(LHS, RHS, "lttmp");
+    return Ctx.Builder.CreateICmpSLT(LHS, RHS, "lttmp");
   case tok::less_eq:
-    return Ctx.getBuilder().CreateICmpSLE(LHS, RHS, "letmp");
+    return Ctx.Builder.CreateICmpSLE(LHS, RHS, "letmp");
 
   default:
     llvm_unreachable("Invalid infix operand");
@@ -72,11 +70,11 @@ llvm::Value *GenExpr::codegen(PrefixExpr *E) {
   auto Val = codegen(E->getDest());
   if (!Val)
     return nullptr;
-  
+
   switch (E->getOp().getKind()) {
     case tok::neg:
-      return Ctx.getBuilder().CreateNot(Val);
-      
+      return Ctx.Builder.CreateNot(Val);
+
       // TODO: fix unary minus
     case tok::minus:
     default:
@@ -86,23 +84,23 @@ llvm::Value *GenExpr::codegen(PrefixExpr *E) {
 
 llvm::Value *GenExpr::codegen(AssignExpr *E) {
   // Ensure the LHS is a identifier.
-  auto VarExpr = dynamic_cast<IdentifierExpr *>(E);
+  auto VarExpr = dynamic_cast<IdentifierExpr *>(E->getDest());
   if (!VarExpr)
-    return nullptr;
-  
+    llvm_unreachable("Invalid LHS");
+
   // Get variable address
   auto VarAddr = Ctx.getVar(VarExpr->getName());
   auto Val = codegen(E->getSource());
   if (!VarAddr || !Val)
-    return nullptr;
-  
-  return Ctx.getBuilder().CreateStore(Val, VarAddr, "assign");
+    llvm_unreachable("Invalid val or addr");
+
+  return Ctx.Builder.CreateStore(Val, VarAddr, "assign");
 }
 
 llvm::Value *GenExpr::codegen(CallExpr *E) {
   // Ensure the callee is identifier.
   auto CalleeID = dynamic_cast<IdentifierExpr *>(E->getCalle());
-  
+
   // Ensure args is expression pattern
   auto ArgsPttrn = dynamic_cast<ExprPattern *>(E->getArgs());
   if (!CalleeID || !ArgsPttrn)
@@ -116,9 +114,33 @@ llvm::Value *GenExpr::codegen(CallExpr *E) {
   // Invalid number of arguments
   if (Fn->arg_size() != ArgsPttrn->count())
     return nullptr;
-  
+
   auto Args = std::vector<llvm::Value *>(Fn->arg_size());
   for (auto Arg : ArgsPttrn->getValues())
     Args.push_back(codegen(Arg));
-  return Ctx.getBuilder().CreateCall(Fn, Args);
+  return Ctx.Builder.CreateCall(Fn, Args);
 }
+
+
+llvm::Value *GenExpr::codegen(Expr *E) {
+  switch (E->getKind()) {
+    case ExprKind::NumberLiteral:
+      return codegen(static_cast<NumberLiteralExpr *>(E));
+    case ExprKind::Identifier:
+      return codegen(static_cast<IdentifierExpr *>(E));
+    case ExprKind::Paren:
+      return codegen(static_cast<ParenExpr *>(E)->getExpr());
+    case ExprKind::Assign:
+      return codegen(static_cast<AssignExpr *>(E));
+    case ExprKind::Infix:
+      return codegen(static_cast<InfixExpr *>(E));
+    case ExprKind::Prefix:
+      return codegen(static_cast<PrefixExpr *>(E));
+    case ExprKind::Call:
+      return codegen(static_cast<CallExpr *>(E));
+    case ExprKind::Subscript:
+      return codegen(static_cast<SubscriptExpr *>(E));
+  }
+}
+
+
diff --git a/lib/IRGen/GenStmt.cpp b/lib/IRGen/GenStmt.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1d0b03610a44e73e5bd6b1e652cb9e51be271da
--- /dev/null
+++ b/lib/IRGen/GenStmt.cpp
@@ -0,0 +1,170 @@
+//===--- GenStmt.cpp ------------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GenStmt.h"
+#include "GenDecl.h"
+#include "GenExpr.h"
+
+using namespace dusk;
+using namespace irgen;
+
+GenStmt::GenStmt(Stmt *S, Context &C)
+: Statement(S), Ctx(C)
+{}
+
+bool GenStmt::gen() {
+  return codegen(Statement);
+}
+
+bool GenStmt::codegen(BreakStmt *S) {
+  return false;
+}
+
+bool GenStmt::codegen(ReturnStmt *S) {
+  return false;
+}
+
+bool GenStmt::codegen(RangeStmt *S) {
+  return false;
+}
+
+bool GenStmt::codegen(SubscriptStmt *S) {
+  return false;
+}
+
+bool GenStmt::codegen(BlockStmt *S) {
+  for (auto &N : S->getNodes()) {
+    bool R = false;
+
+    if (auto D = dynamic_cast<Decl *>(N))
+      R = GenDecl(D, Ctx).gen();
+    else if (auto E = dynamic_cast<Expr *>(N))
+      R = GenExpr(E, Ctx).gen() != nullptr;
+    else if (auto S = dynamic_cast<Stmt *>(N))
+      R = GenStmt(S, Ctx).gen();
+    else
+      llvm_unreachable("Unexpected node");
+
+    if (!R)
+      return false;
+  }
+  return true;
+}
+
+bool GenStmt::codegen(ForStmt *S) {
+  return false;
+}
+
+bool GenStmt::codegen(WhileStmt *S) {
+  return false;
+}
+
+bool GenStmt::codegen(IfStmt *S) {
+  auto CondV = GenExpr(S->getCond(), Ctx).gen();
+  if (!CondV)
+    llvm_unreachable("Error generating condition.");
+  
+  auto Zero = llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), 0);
+  // Convert to bool by comparing with 0
+  CondV = Ctx.Builder.CreateICmpNE(CondV, Zero, "ifcond");
+  auto F = Ctx.Builder.GetInsertBlock()->getParent();
+  
+  auto *ThenBB = llvm::BasicBlock::Create(Ctx, "then", F);
+  auto *ElseBB = llvm::BasicBlock::Create(Ctx, "else");
+  auto *MergeBB = llvm::BasicBlock::Create(Ctx, "endif");
+  
+  Ctx.Builder.CreateCondBr(CondV, ThenBB, ElseBB);
+  
+  // Generate then block
+  Ctx.Builder.SetInsertPoint(ThenBB);
+  Ctx.push();
+  if (!GenStmt(S->getThen(), Ctx).gen())
+    llvm_unreachable("Error generating Then");
+  Ctx.pop();
+  // pop back to point after the condition.
+  Ctx.Builder.CreateBr(MergeBB);
+  
+  
+  if (!S->hasElseBlock()) {
+    // Generate merge block
+    F->getBasicBlockList().push_back(MergeBB);
+    Ctx.Builder.SetInsertPoint(MergeBB);
+    return true;
+  }
+    
+  // Generate else block
+  F->getBasicBlockList().push_back(ElseBB);
+  Ctx.push();
+  Ctx.Builder.SetInsertPoint(ElseBB);
+  if (!GenStmt(S->getElse(), Ctx).gen())
+    llvm_unreachable("Error generating else");
+  Ctx.pop();
+  // pop back to point after the condition.
+  Ctx.Builder.CreateBr(MergeBB);
+  
+  // Generate merge block
+  F->getBasicBlockList().push_back(MergeBB);
+  Ctx.Builder.SetInsertPoint(MergeBB);
+  return true;
+}
+
+bool GenStmt::codegen(FuncStmt *S) {
+  GenDecl FD(S->getPrototype(), Ctx);
+  // Declare
+  if (!FD.gen())
+    return false;
+
+  // Get function
+  auto F = Ctx.getFunc(S->getPrototype()->getName());
+  if (!F)
+    return false;
+
+  // Create entry block of the function
+  auto BB = llvm::BasicBlock::Create(Ctx, "entry", F);
+  Ctx.Builder.SetInsertPoint(BB);
+
+  // Push new context scope
+  Ctx.push();
+  if (!FD.genArgs()) {
+    Ctx.pop();
+    return false;
+  }
+
+  if (!codegen(S->getBody())) {
+    Ctx.pop();
+    return false;
+  }
+
+  Ctx.pop();
+  return true;
+}
+
+bool GenStmt::codegen(Stmt *S) {
+  switch (S->getKind()) {
+    case StmtKind::Break:
+      return codegen(static_cast<BreakStmt *>(S));
+    case StmtKind::Return:
+      return codegen(static_cast<ReturnStmt *>(S));
+    case StmtKind::Range:
+      return codegen(static_cast<RangeStmt *>(S));
+    case StmtKind::Block:
+      return codegen(static_cast<BlockStmt *>(S));
+    case StmtKind::Func:
+      return codegen(static_cast<FuncStmt *>(S));
+    case StmtKind::For:
+      return codegen(static_cast<ForStmt *>(S));
+    case StmtKind::While:
+      return codegen(static_cast<WhileStmt *>(S));
+    case StmtKind::If:
+      return codegen(static_cast<IfStmt *>(S));
+    case StmtKind::Subscript:
+      llvm_unreachable("Not implemented yet");
+  }
+}
+
diff --git a/lib/IRGen/GenStmt.h b/lib/IRGen/GenStmt.h
new file mode 100644
index 0000000000000000000000000000000000000000..2db8edae305ec68b624cb60b410832429573dcf4
--- /dev/null
+++ b/lib/IRGen/GenStmt.h
@@ -0,0 +1,58 @@
+//===--- GenStmt.h - Dusk IR statement generation ---------------*- C++ -*-===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DUSK_GEN_STMT_H
+#define DUSK_GEN_STMT_H
+
+#include "dusk/AST/Decl.h"
+#include "dusk/AST/Expr.h"
+#include "dusk/AST/Stmt.h"
+#include "dusk/AST/Pattern.h"
+#include "dusk/IRGen/Context.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Value.h"
+#include "llvm/IR/LLVMContext.h"
+
+
+namespace dusk {
+
+namespace irgen {
+
+class GenDel;
+class GenExpr;
+class GenStmt;
+
+class GenStmt {
+  Stmt *Statement;
+  Context &Ctx;
+
+public:
+  GenStmt(Stmt *S, Context &C);
+
+  bool gen();
+
+private:
+  bool codegen(Stmt *S);
+  bool codegen(BreakStmt *S);
+  bool codegen(ReturnStmt *S);
+  bool codegen(RangeStmt *S);
+  bool codegen(SubscriptStmt *S);
+  bool codegen(BlockStmt *S);
+  bool codegen(ForStmt *S);
+  bool codegen(WhileStmt *S);
+  bool codegen(IfStmt *S);
+  bool codegen(FuncStmt *S);
+};
+
+} // namespace irgen
+
+} // namespace dusk
+
+#endif /* DUSK_GEN_STMT_H */
+
diff --git a/lib/IRGen/IRGenerator.cpp b/lib/IRGen/IRGenerator.cpp
index e9f0529e43c916bb43459dc89987092c8f53c9bb..9862ee6e67840dc7bde1aae634c3070ce8b0fb85 100644
--- a/lib/IRGen/IRGenerator.cpp
+++ b/lib/IRGen/IRGenerator.cpp
@@ -10,12 +10,36 @@
 #include "dusk/IRGen/IRGenerator.h"
 #include "dusk/IRGen/Context.h"
 
+#include "GenDecl.h"
 #include "GenExpr.h"
+#include "GenStmt.h"
 
 using namespace dusk;
 using namespace irgen;
 
 IRGenerator::IRGenerator(DiagnosticEngine &Diag)
-    : Diag(Diag), Builder({Context}) {}
+    : Diag(Diag), Builder({Ctx}) {}
 
 IRGenerator::~IRGenerator() {}
+
+bool IRGenerator::gen(ModuleDecl *M) {
+  Module = std::make_unique<llvm::Module>(M->getName(), Ctx);
+  Context Ctx(this->Ctx, Module.get(), Builder);
+  
+  for (auto &N : M->getContents()) {
+    bool R = false;
+    if (auto D = dynamic_cast<Decl *>(N))
+      R = GenDecl(D, Ctx).gen();
+    else if (auto E = dynamic_cast<Expr *>(N))
+      R = GenExpr(E, Ctx).gen() != nullptr;
+    else if (auto S = dynamic_cast<Stmt *>(N))
+      R = GenStmt(S, Ctx).gen();
+    else
+      llvm_unreachable("Unexpected node");
+    
+    if (!R)
+      return false;
+  }
+  Module->print(llvm::errs(), nullptr);
+  return true;
+}