Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r73619 - trunk/libs/spirit/example/qi/compiler_tutorial/conjure3
From: joel_at_[hidden]
Date: 2011-08-09 04:29:47


Author: djowel
Date: 2011-08-09 04:29:45 EDT (Tue, 09 Aug 2011)
New Revision: 73619
URL: http://svn.boost.org/trac/boost/changeset/73619

Log:
refactoring: moving low-level llvm stuff into separate classes
Text files modified:
   trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp | 258 ++++++++++++++++++++++++---------------
   trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp | 91 +++++++++++++
   2 files changed, 244 insertions(+), 105 deletions(-)

Modified: trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp
==============================================================================
--- trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp (original)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp 2011-08-09 04:29:45 EDT (Tue, 09 Aug 2011)
@@ -227,6 +227,11 @@
         );
     }
 
+ void function::add(basic_block const& b)
+ {
+ f->getBasicBlockList().push_back(b);
+ }
+
     value llvm_compiler::val(unsigned int x)
     {
         return value(
@@ -258,7 +263,7 @@
         // Create an alloca instruction in the entry block of
         // the function. This is used for mutable variables etc.
         llvm::AllocaInst*
- create_entry_block_alloca(
+ make_entry_block_alloca(
             llvm::Function* f,
             char const* name,
             llvm::LLVMContext& context)
@@ -347,6 +352,34 @@
         return llvm_builder.GetInsertBlock()->getParent();
     }
 
+ function llvm_compiler::declare_function(
+ bool void_return
+ , std::string const& name
+ , std::size_t nargs)
+ {
+ llvm::Type* int_type =
+ llvm::Type::getIntNTy(context(), int_size);
+ llvm::Type* void_type = llvm::Type::getVoidTy(context());
+
+ std::vector<llvm::Type*> ints(nargs, int_type);
+ llvm::Type* return_type = void_return ? void_type : int_type;
+
+ llvm::FunctionType* function_type =
+ llvm::FunctionType::get(void_return ? void_type : int_type, ints, false);
+
+ return llvm::Function::Create(
+ function_type, llvm::Function::ExternalLinkage,
+ name, vm.module());
+ }
+
+ basic_block llvm_compiler::make_basic_block(
+ char const* name
+ , function parent
+ , basic_block before)
+ {
+ return llvm::BasicBlock::Create(context(), name, parent, before);
+ }
+
     void llvm_compiler::init_fpm()
     {
         // Set up the optimizer pipeline. Start with registering info about how the
@@ -386,12 +419,12 @@
     value compiler::operator()(ast::identifier const& x)
     {
         // Look this variable up in the function.
- if (named_values.find(x.name) == named_values.end())
+ if (locals.find(x.name) == locals.end())
         {
             error_handler(x.id, "Undeclared variable: " + x.name);
             return val();
         }
- return named_values[x.name];
+ return locals[x.name];
     }
 
     value compiler::operator()(ast::unary_expr const& x)
@@ -608,13 +641,13 @@
 
     value compiler::operator()(ast::assignment const& x)
     {
- if (named_values.find(x.lhs.name) == named_values.end())
+ if (locals.find(x.lhs.name) == locals.end())
         {
             error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name);
             return val();
         }
 
- value lhs = named_values[x.lhs.name];
+ value lhs = locals[x.lhs.name];
         value rhs = (*this)(x.rhs);
         if (!rhs.is_valid())
             return val();
@@ -647,7 +680,7 @@
 
     bool compiler::operator()(ast::variable_declaration const& x)
     {
- if (named_values.find(x.lhs.name) != named_values.end())
+ if (locals.find(x.lhs.name) != locals.end())
         {
             error_handler(x.lhs.id, "Duplicate variable: " + x.lhs.name);
             return false;
@@ -664,11 +697,11 @@
         }
 
         value var_ = var(name.c_str());
- if (init)
+ if (init.is_valid())
             var_.assign(init);
 
         // Remember this binding.
- named_values[name] = var_;
+ locals[name] = var_;
         return true;
     }
 
@@ -705,90 +738,90 @@
         if (!condition.is_valid())
             return false;
 
- llvm::Function* f = get_current_function();
+ function f = get_current_function();
 
         // Create blocks for the then and else cases. Insert the 'then' block at the
         // end of the function.
- llvm::BasicBlock* then_block = llvm::BasicBlock::Create(context(), "if.then", f);
- llvm::BasicBlock* else_block = 0;
- llvm::BasicBlock* exit_block = 0;
+ basic_block then_block = make_basic_block("if.then", f);
+ basic_block else_block;
+ basic_block exit_block;
 
         if (x.else_)
         {
- else_block = llvm::BasicBlock::Create(context(), "if.else");
- builder().CreateCondBr(condition, then_block, else_block);
+ else_block = make_basic_block("if.else");
+ conditional_branch(condition, then_block, else_block);
         }
         else
         {
- exit_block = llvm::BasicBlock::Create(context(), "if.end");
- builder().CreateCondBr(condition, then_block, exit_block);
+ exit_block = make_basic_block("if.end");
+ conditional_branch(condition, then_block, exit_block);
         }
 
         // Emit then value.
- builder().SetInsertPoint(then_block);
+ set_insert_point(then_block);
         if (!(*this)(x.then))
             return false;
- if (then_block->getTerminator() == 0)
+ if (!then_block.has_terminator())
         {
- if (exit_block == 0)
- exit_block = llvm::BasicBlock::Create(context(), "if.end");
- builder().CreateBr(exit_block);
+ if (!exit_block.is_valid())
+ exit_block = make_basic_block("if.end");
+ branch(exit_block);
         }
         // Codegen of 'then' can change the current block, update then_block
- then_block = builder().GetInsertBlock();
+ then_block = get_insert_block();
 
         if (x.else_)
         {
             // Emit else block.
- f->getBasicBlockList().push_back(else_block);
- builder().SetInsertPoint(else_block);
+ f.add(else_block);
+ set_insert_point(else_block);
             if (!(*this)(*x.else_))
                 return false;
- if (else_block->getTerminator() == 0)
+ if (!else_block.has_terminator())
             {
- if (exit_block == 0)
- exit_block = llvm::BasicBlock::Create(context(), "if.end");
- builder().CreateBr(exit_block);
+ if (!exit_block.is_valid())
+ exit_block = make_basic_block("if.end");
+ branch(exit_block);
             }
             // Codegen of 'else' can change the current block, update else_block
- else_block = builder().GetInsertBlock();
+ else_block = get_insert_block();
         }
 
- if (exit_block != 0)
+ if (exit_block.is_valid())
         {
             // Emit exit block
- f->getBasicBlockList().push_back(exit_block);
- builder().SetInsertPoint(exit_block);
+ f.add(exit_block);
+ set_insert_point(exit_block);
         }
         return true;
     }
 
     bool compiler::operator()(ast::while_statement const& x)
     {
- llvm::Function* f = get_current_function();
+ function f = get_current_function();
 
- llvm::BasicBlock* cond_block = llvm::BasicBlock::Create(context(), "while.cond", f);
- llvm::BasicBlock* body_block = llvm::BasicBlock::Create(context(), "while.body");
- llvm::BasicBlock* exit_block = llvm::BasicBlock::Create(context(), "while.end");
+ basic_block cond_block = make_basic_block("while.cond", f);
+ basic_block body_block = make_basic_block("while.body");
+ basic_block exit_block = make_basic_block("while.end");
 
- builder().CreateBr(cond_block);
- builder().SetInsertPoint(cond_block);
+ branch(cond_block);
+ set_insert_point(cond_block);
         value condition = (*this)(x.condition);
         if (!condition.is_valid())
             return false;
- builder().CreateCondBr(condition, body_block, exit_block);
- f->getBasicBlockList().push_back(body_block);
- builder().SetInsertPoint(body_block);
+ conditional_branch(condition, body_block, exit_block);
+ f.add(body_block);
+ set_insert_point(body_block);
 
         if (!(*this)(x.body))
             return false;
 
- if (body_block->getTerminator() == 0)
- builder().CreateBr(cond_block); // loop back
+ if (!body_block.has_terminator())
+ branch(cond_block); // loop back
 
         // Emit exit block
- f->getBasicBlockList().push_back(exit_block);
- builder().SetInsertPoint(exit_block);
+ f.add(exit_block);
+ set_insert_point(exit_block);
 
         return true;
     }
@@ -823,85 +856,110 @@
             return_var.assign(return_val);
         }
 
- builder().CreateBr(return_block);
+ branch(return_block);
         return true;
     }
 
- llvm::Function* compiler::function_decl(ast::function const& x)
+ namespace
     {
- void_return = x.return_type == "void";
- current_function_name = x.function_name.name;
+ struct set_arg_name
+ {
+ typedef std::list<ast::identifier>::const_iterator ast_iter;
+ typedef void result_type;
 
- llvm::Type* int_type =
- llvm::Type::getIntNTy(context(), int_size);
- llvm::Type* void_type = llvm::Type::getVoidTy(context());
+ mutable ast_iter i;
+ set_arg_name(ast_iter i)
+ : i(i) {}
 
- std::vector<llvm::Type*> ints(x.args.size(), int_type);
- llvm::Type* return_type = void_return ? void_type : int_type;
+ void operator()(value arg) const
+ {
+ arg.name(i->name.c_str());
+ ++i;
+ }
+ };
+ }
 
- llvm::FunctionType* function_type =
- llvm::FunctionType::get(void_return ? void_type : int_type, ints, false);
+ function compiler::function_decl(ast::function const& x)
+ {
+ void_return = x.return_type == "void";
+ current_function_name = x.function_name.name;
 
- llvm::Function* f =
- llvm::Function::Create(
- function_type, llvm::Function::ExternalLinkage,
- current_function_name, vm.module());
+ function f =
+ declare_function(
+ void_return
+ , current_function_name
+ , x.args.size());
 
         // If function conflicted, the function already exixts. If it has a
         // body, don't allow redefinition or reextern.
- if (f->getName() != current_function_name)
+ if (f.name() != current_function_name)
         {
             // Delete the one we just made and get the existing one.
- f->eraseFromParent();
+ f.erase_from_parent();
             f = get_function(current_function_name);
 
             // If function already has a body, reject this.
- if (!f->empty())
+ if (!f.empty())
             {
                 error_handler(
                     x.function_name.id,
                     "Duplicate function: " + x.function_name.name);
- return 0;
+ return function();
             }
 
             // If function took a different number of args, reject.
- if (f->arg_size() != x.args.size())
+ if (f.arg_size() != x.args.size())
             {
                 error_handler(
                     x.function_name.id,
                     "Redefinition of function with different # args: "
                         + x.function_name.name);
- return 0;
+ return function();
             }
 
             // Set names for all arguments.
- llvm::Function::arg_iterator iter = f->arg_begin();
- BOOST_FOREACH(ast::identifier const& arg, x.args)
- {
- iter->setName(arg.name);
- ++iter;
- }
+ for_each_arg(f, set_arg_name(x.args.begin()));
         }
         return f;
     }
 
- void compiler::function_allocas(ast::function const& x, llvm::Function* f)
+ namespace
     {
- // Create an variables for each argument and register the
- // argument in the symbol table so that references to it will succeed.
- llvm::Function::arg_iterator iter = f->arg_begin();
- BOOST_FOREACH(ast::identifier const& arg, x.args)
+ struct make_arg
         {
- // Create an arg_ for this variable.
- value arg_ = var(arg.name.c_str());
+ typedef std::list<ast::identifier>::const_iterator ast_iter;
+ typedef void result_type;
 
- // Store the initial value into the arg_.
- arg_.assign(val(iter));
+ llvm_compiler& c;
+ std::map<std::string, value>& locals;
+ mutable ast_iter i;
 
- // Add arguments to variable symbol table.
- named_values[arg.name] = arg_;
- ++iter;
- }
+ make_arg(
+ llvm_compiler& c
+ , std::map<std::string, value>& locals
+ , ast_iter i)
+ : c(c), locals(locals), i(i) {}
+
+ void operator()(value arg) const
+ {
+ // Create an arg_ for this variable.
+ value arg_ = c.var(i->name.c_str());
+
+ // Store the initial value into the arg_.
+ arg_.assign(arg);
+
+ // Add arguments to variable symbol table.
+ locals[i->name] = arg_;
+ ++i;
+ }
+ };
+ }
+
+ void compiler::function_allocas(ast::function const& x, function f)
+ {
+ // Create variables for each argument and register the
+ // argument in the symbol table so that references to it will succeed.
+ for_each_arg(f, make_arg(*this, locals, x.args.begin()));
 
         if (!void_return)
         {
@@ -914,7 +972,7 @@
     {
         ///////////////////////////////////////////////////////////////////////
         // the signature:
- llvm::Function* f = function_decl(x);
+ function f = function_decl(x);
         if (f == 0)
             return false;
 
@@ -923,37 +981,35 @@
         if (x.body) // compile the body if this is not a prototype
         {
             // Create a new basic block to start insertion into.
- llvm::BasicBlock* block =
- llvm::BasicBlock::Create(context(), "entry", f);
- builder().SetInsertPoint(block);
+ basic_block block = make_basic_block("entry", f);
+ set_insert_point(block);
 
             function_allocas(x, f);
- return_block = llvm::BasicBlock::Create(context(), "return");
+ return_block = make_basic_block("return");
 
             if (!(*this)(*x.body))
             {
                 // Error reading body, remove function.
- f->eraseFromParent();
+ f.erase_from_parent();
                 return false;
             }
 
- llvm::BasicBlock* last_block =
- &f->getBasicBlockList().back();
+ basic_block last_block = f.last_block();
 
             // If the last block is unterminated, connect it to return_block
- if (last_block->getTerminator() == 0)
+ if (!last_block.has_terminator())
             {
- builder().SetInsertPoint(last_block);
- builder().CreateBr(return_block);
+ set_insert_point(last_block);
+ branch(return_block);
             }
 
- f->getBasicBlockList().push_back(return_block);
- builder().SetInsertPoint(return_block);
+ f.add(return_block);
+ set_insert_point(return_block);
 
             if (void_return)
- builder().CreateRetVoid();
+ return_();
             else
- builder().CreateRet(return_var);
+ return_(return_var);
 
             //~ vm.module()->dump();
 
@@ -971,7 +1027,7 @@
     {
         BOOST_FOREACH(ast::function const& f, x)
         {
- named_values.clear(); // clear the variables
+ locals.clear(); // clear the variables
             if (!(*this)(f))
                 return false;
         }

Modified: trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp
==============================================================================
--- trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp (original)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp 2011-08-09 04:29:45 EDT (Tue, 09 Aug 2011)
@@ -49,6 +49,9 @@
 
         value& assign(value const& rhs);
 
+ void name(char const* id)
+ { v->setName(id); }
+
         friend value operator-(value a);
         friend value operator!(value a);
         friend value operator+(value a, value b);
@@ -90,6 +93,32 @@
     };
 
     ///////////////////////////////////////////////////////////////////////////
+ struct function;
+
+ struct basic_block
+ {
+ basic_block()
+ : b(0) {}
+
+ bool has_terminator() const
+ { return b->getTerminator() != 0; }
+
+ bool is_valid() const { return b != 0; }
+
+ private:
+
+ basic_block(llvm::BasicBlock* b)
+ : b(b) {}
+
+ operator llvm::BasicBlock*() const
+ { return b; }
+
+ friend struct llvm_compiler;
+ friend struct function;
+ llvm::BasicBlock* b;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
     struct function
     {
         function()
@@ -101,6 +130,20 @@
         std::size_t arg_size() const
         { return f->arg_size(); }
 
+ void add(basic_block const& b);
+
+ void erase_from_parent()
+ { f->eraseFromParent(); }
+
+ basic_block last_block()
+ { return &f->getBasicBlockList().back(); }
+
+ bool empty() const
+ { return f->empty(); }
+
+ std::string name() const
+ { return f->getName(); }
+
     private:
 
         function(llvm::Function* f)
@@ -110,6 +153,7 @@
         llvm::Function* f;
     };
 
+
     ///////////////////////////////////////////////////////////////////////////
     // The LLVM Compiler. Lower level compiler (does not deal with ASTs)
     ///////////////////////////////////////////////////////////////////////////
@@ -142,6 +186,45 @@
 
         function get_current_function() const;
 
+ template <typename F>
+ void for_each_arg(function f, F do_)
+ {
+ typedef typename llvm::Function::arg_iterator iter;
+ for (iter i = f.f->arg_begin(); i != f.f->arg_end(); ++i)
+ do_(val(i));
+ }
+
+ function declare_function(
+ bool void_return
+ , std::string const& name
+ , std::size_t nargs);
+
+ basic_block make_basic_block(
+ char const* name
+ , function parent = function()
+ , basic_block before = basic_block());
+
+ basic_block get_insert_block()
+ { return llvm_builder.GetInsertBlock(); }
+
+ void set_insert_point(basic_block b)
+ { llvm_builder.SetInsertPoint(b); }
+
+ void conditional_branch(
+ value cond, basic_block true_br, basic_block false_br)
+ { llvm_builder.CreateCondBr(cond, true_br, false_br); }
+
+ void branch(basic_block b)
+ { llvm_builder.CreateBr(b); }
+
+ void return_()
+ { llvm_builder.CreateRetVoid(); }
+
+ void return_(value v)
+ { llvm_builder.CreateRet(v); }
+
+ struct get_llvm_value;
+
     protected:
 
         llvm::LLVMContext& context() const
@@ -211,8 +294,8 @@
         struct statement_compiler;
         statement_compiler& as_statement();
 
- llvm::Function* function_decl(ast::function const& x);
- void function_allocas(ast::function const& x, llvm::Function* function);
+ function function_decl(ast::function const& x);
+ void function_allocas(ast::function const& x, function function);
 
         boost::function<
             void(int tag, std::string const& what)>
@@ -220,8 +303,8 @@
 
         bool void_return;
         std::string current_function_name;
- std::map<std::string, value> named_values;
- llvm::BasicBlock* return_block;
+ std::map<std::string, value> locals;
+ basic_block return_block;
         value return_var;
     };
 }}


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk