$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r73570 - trunk/libs/spirit/example/qi/compiler_tutorial/conjure3
From: joel_at_[hidden]
Date: 2011-08-06 08:05:24
Author: djowel
Date: 2011-08-06 08:05:23 EDT (Sat, 06 Aug 2011)
New Revision: 73570
URL: http://svn.boost.org/trac/boost/changeset/73570
Log:
refactoring the compiler
Text files modified: 
   trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp |   224 +++++++++++++++++++++++++-------------- 
   trunk/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp |   105 +++++++++++++++---                      
   2 files changed, 229 insertions(+), 100 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-06 08:05:23 EDT (Sat, 06 Aug 2011)
@@ -17,6 +17,94 @@
 
 namespace client { namespace code_gen
 {
+    value::value(
+        llvm::Value* v,
+        bool is_lvalue_,
+        llvm::IRBuilder<>* builder)
+      : v(v),
+        is_lvalue_(is_lvalue_),
+        builder(builder)
+    {}
+
+    value::value(value const& rhs)
+      : v(rhs.v),
+        is_lvalue_(rhs.is_lvalue_),
+        builder(rhs.builder)
+    {}
+
+    value::value(unsigned int x)
+      : v(llvm::ConstantInt::get(context(), llvm::APInt(int_size, x))),
+        is_lvalue_(false),
+        builder(0)
+    {}
+
+    value::value(int x)
+      : v(llvm::ConstantInt::get(context(), llvm::APInt(int_size, x))),
+        is_lvalue_(false),
+        builder(0)
+    {}
+
+    value::value(bool x)
+      : v(llvm::ConstantInt::get(context(), llvm::APInt(1, x))),
+        is_lvalue_(false),
+        builder(0)
+    {}
+
+    value::operator llvm::Value*() const
+    {
+        if (is_lvalue_)
+        {
+            BOOST_ASSERT(builder != 0);
+            return builder->CreateLoad(v, v->getName());
+        }
+        return v;
+    }
+
+    value& value::operator=(value const& rhs)
+    {
+        v = rhs.v;
+        is_lvalue_ = rhs.is_lvalue_;
+        builder = rhs.builder;
+        return *this;
+    }
+
+    value& value::assign(value const& rhs)
+    {
+        BOOST_ASSERT(is_lvalue());
+        BOOST_ASSERT(builder != 0);
+        builder->CreateStore(rhs, v);
+        return *this;
+    }
+
+    namespace
+    {
+        //  Create an alloca instruction in the entry block of
+        //  the function. This is used for mutable variables etc.
+        llvm::AllocaInst*
+        create_entry_block_alloca(
+            llvm::Function* function,
+            char const* name,
+            llvm::LLVMContext& context)
+        {
+            llvm::IRBuilder<> builder(
+                &function->getEntryBlock(),
+                function->getEntryBlock().begin());
+
+            return builder.CreateAlloca(
+                llvm::Type::getIntNTy(context, int_size), 0, name);
+        }
+    }
+
+    lvalue::lvalue(llvm::IRBuilder<>& builder, char const* name)
+      : value(
+            create_entry_block_alloca(
+                builder.GetInsertBlock()->getParent(),
+                name,
+                context()),
+        true, &builder)
+    {
+    }
+
     void compiler::init_fpm()
     {
         // Set up the optimizer pipeline.  Start with registering info about how the
@@ -38,26 +126,26 @@
         fpm.doInitialization();
     }
 
-    llvm::Value* compiler::operator()(unsigned int x)
+    value compiler::operator()(unsigned int x)
     {
-        return llvm::ConstantInt::get(context(), llvm::APInt(int_size, x));
+        return value(x);
     }
 
-    llvm::Value* compiler::operator()(bool x)
+    value compiler::operator()(bool x)
     {
-        return llvm::ConstantInt::get(context(), llvm::APInt(1, x));
+        return value(x);
     }
 
-    llvm::Value* compiler::operator()(ast::literal const& x)
+    value compiler::operator()(ast::literal const& x)
     {
         return boost::apply_visitor(*this, x.get());
     }
 
-    llvm::Value* compiler::operator()(ast::identifier const& x)
+    value compiler::operator()(ast::identifier const& x)
     {
         // Look this variable up in the function.
-        llvm::Value* value = named_values[x.name];
-        if (value == 0)
+        value value = named_values[x.name];
+        if (!value)
         {
             error_handler(x.id, "Undeclared variable: " + x.name);
             return 0;
@@ -67,28 +155,17 @@
         return builder.CreateLoad(value, x.name.c_str());
     }
 
-    namespace
-    {
-        llvm::Value*
-        neg1(llvm::LLVMContext& context, llvm::IRBuilder<>& builder)
-        {
-            llvm::Value* one = llvm::ConstantInt::get(context,
-                llvm::APInt(int_size, 1));
-            return builder.CreateNeg(one, "neg1");
-        }
-    }
-
-    llvm::Value* compiler::operator()(ast::unary const& x)
+    value compiler::operator()(ast::unary const& x)
     {
-        llvm::Value* operand = boost::apply_visitor(*this, x.operand_);
-        if (operand == 0)
+        value operand = boost::apply_visitor(*this, x.operand_);
+        if (!operand)
             return 0;
 
         switch (x.operator_)
         {
             case token_ids::compl_:
                 return builder.CreateXor(
-                    operand, neg1(context(), builder), "compltmp");
+                    operand, value(-1), "compltmp");
             case token_ids::minus:
                 return builder.CreateNeg(operand, "negtmp");
             case token_ids::not_:
@@ -99,10 +176,10 @@
         }
     }
 
-    llvm::Value* compiler::operator()(ast::function_call const& x)
+    value compiler::operator()(ast::function_call const& x)
     {
         llvm::Function* callee = vm.module()->getFunction(x.function_name.name);
-        if (callee == 0)
+        if (!callee)
         {
             error_handler(x.function_name.id, "Function not found: " + x.function_name.name);
             return false;
@@ -189,8 +266,8 @@
         return precedence[op & 0xFF];
     }
 
-    llvm::Value* compiler::compile_binary_expression(
-        llvm::Value* lhs, llvm::Value* rhs, token_ids::type op)
+    value compiler::compile_binary_expression(
+        value lhs, value rhs, token_ids::type op)
     {
         switch (op)
         {
@@ -220,9 +297,9 @@
     }
 
     // The Shunting-yard algorithm
-    llvm::Value* compiler::compile_expression(
+    value compiler::compile_expression(
         int min_precedence,
-        llvm::Value* lhs,
+        value lhs,
         std::list<ast::operation>::const_iterator& rest_begin,
         std::list<ast::operation>::const_iterator rest_end)
     {
@@ -230,8 +307,8 @@
             (precedence_of(rest_begin->operator_) >= min_precedence))
         {
             token_ids::type op = rest_begin->operator_;
-            llvm::Value* rhs = boost::apply_visitor(*this, rest_begin->operand_);
-            if (rhs == 0)
+            value rhs = boost::apply_visitor(*this, rest_begin->operand_);
+            if (!rhs)
                 return 0;
             ++rest_begin;
 
@@ -248,94 +325,82 @@
         return lhs;
     }
 
-    llvm::Value* compiler::operator()(ast::expression const& x)
+    value compiler::operator()(ast::expression const& x)
     {
-        llvm::Value* lhs = boost::apply_visitor(*this, x.first);
-        if (lhs == 0)
+        value lhs = boost::apply_visitor(*this, x.first);
+        if (!lhs)
             return 0;
         std::list<ast::operation>::const_iterator rest_begin = x.rest.begin();
         return compile_expression(0, lhs, rest_begin, x.rest.end());
     }
 
-    llvm::Value* compiler::operator()(ast::assignment const& x)
+    value compiler::operator()(ast::assignment const& x)
     {
-        llvm::Value* lhs = named_values[x.lhs.name];
-        if (lhs == 0)
+        lvalue lhs(named_values[x.lhs.name], builder);
+        if (!lhs)
         {
             error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name);
             return 0;
         }
 
-        llvm::Value* rhs = (*this)(x.rhs);
-        if (rhs == 0)
+        value rhs = (*this)(x.rhs);
+        if (!rhs)
             return 0;
 
         if (x.operator_ == token_ids::assign)
         {
-            builder.CreateStore(rhs, lhs);
-            return rhs;
+            lhs.assign(rhs);
+            return lhs;
         }
 
-        llvm::Value* result = builder.CreateLoad(lhs, x.lhs.name.c_str());
+        value result;
         switch (x.operator_)
         {
             case token_ids::plus_assign:
-                result = builder.CreateAdd(result, rhs, "addtmp");
+                result = builder.CreateAdd(lhs, rhs, "addtmp");
                 break;
 
             case token_ids::minus_assign:
-                result = builder.CreateSub(result, rhs, "subtmp");
+                result = builder.CreateSub(lhs, rhs, "subtmp");
                 break;
 
             case token_ids::times_assign:
-                result = builder.CreateMul(result, rhs, "multmp");
+                result = builder.CreateMul(lhs, rhs, "multmp");
                 break;
 
             case token_ids::divide_assign:
-                result = builder.CreateSDiv(result, rhs, "divtmp");
+                result = builder.CreateSDiv(lhs, rhs, "divtmp");
                 break;
 
             case token_ids::mod_assign:
-                result = builder.CreateSRem(result, rhs, "modtmp");
+                result = builder.CreateSRem(lhs, rhs, "modtmp");
                 break;
 
             case token_ids::bit_and_assign:
-                result = builder.CreateAnd(result, rhs, "andtmp");
+                result = builder.CreateAnd(lhs, rhs, "andtmp");
                 break;
 
             case token_ids::bit_xor_assign:
-                result = builder.CreateXor(result, rhs, "xortmp");
+                result = builder.CreateXor(lhs, rhs, "xortmp");
                 break;
 
             case token_ids::bit_or_assign:
-                result = builder.CreateOr(result, rhs, "ortmp");
+                result = builder.CreateOr(lhs, rhs, "ortmp");
                 break;
 
             case token_ids::shift_left_assign:
-                result = builder.CreateShl(result, rhs, "shltmp");
+                result = builder.CreateShl(lhs, rhs, "shltmp");
                 break;
 
             case token_ids::shift_right_assign:
-                result = builder.CreateLShr(result, rhs, "shrtmp");
+                result = builder.CreateLShr(lhs, rhs, "shrtmp");
                 break;
 
             default: BOOST_ASSERT(0); return 0;
         }
 
-        builder.CreateStore(result, lhs);
-        return result;
-    }
-
-    //  Create an alloca instruction in the entry block of
-    //  the function. This is used for mutable variables etc.
-    llvm::AllocaInst*
-    create_entry_block_alloca(
-        llvm::Function* function, std::string const& var, llvm::LLVMContext& context)
-    {
-        llvm::IRBuilder<> builder(
-            &function->getEntryBlock(), function->getEntryBlock().begin());
-        return builder.CreateAlloca(
-            llvm::Type::getIntNTy(context, int_size), 0, var.c_str());
+        lhs.assign(result);
+        return lhs;
     }
 
     bool compiler::operator()(ast::variable_declaration const& x)
@@ -346,21 +411,19 @@
             return false;
         }
 
-        llvm::Function* function = builder.GetInsertBlock()->getParent();
-        llvm::Value* init = 0;
+        value init;
         std::string const& var = x.lhs.name;
 
         if (x.rhs) // if there's an RHS initializer
         {
             init = (*this)(*x.rhs);
-            if (init == 0) // don't add the variable if the RHS fails
+            if (!init) // don't add the variable if the RHS fails
                 return false;
         }
 
-        llvm::AllocaInst* alloca
-            = create_entry_block_alloca(function, var, context());
-        if (init != 0)
-            builder.CreateStore(init, alloca);
+        lvalue alloca(builder, var.c_str());
+        if (init)
+            alloca.assign(init);
 
         // Remember this binding.
         named_values[var] = alloca;
@@ -396,8 +459,8 @@
 
     bool compiler::operator()(ast::if_statement const& x)
     {
-        llvm::Value* condition = (*this)(x.condition);
-        if (condition == 0)
+        value condition = (*this)(x.condition);
+        if (!condition)
             return 0;
 
         llvm::Function* function = builder.GetInsertBlock()->getParent();
@@ -468,8 +531,8 @@
 
         builder.CreateBr(cond_block);
         builder.SetInsertPoint(cond_block);
-        llvm::Value* condition = (*this)(x.condition);
-        if (condition == 0)
+        value condition = (*this)(x.condition);
+        if (!condition)
             return false;
         builder.CreateCondBr(condition, body_block, exit_block);
         function->getBasicBlockList().push_back(body_block);
@@ -512,8 +575,8 @@
 
         if (x.expr)
         {
-            llvm::Value* return_val = (*this)(*x.expr);
-            if (return_val == 0)
+            value return_val = (*this)(*x.expr);
+            if (!return_val)
                 return false;
             builder.CreateStore(return_val, return_alloca);
         }
@@ -589,7 +652,8 @@
         {
             // Create an alloca for this variable.
             llvm::AllocaInst* alloca =
-                create_entry_block_alloca(function, arg.name, context());
+                create_entry_block_alloca(
+                    function, arg.name.c_str(), context());
 
             // Store the initial value into the alloca.
             builder.CreateStore(iter, alloca);
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-06 08:05:23 EDT (Sat, 06 Aug 2011)
@@ -30,20 +30,86 @@
 
 namespace client { namespace code_gen
 {
+    unsigned const int_size = 32;
+
     ///////////////////////////////////////////////////////////////////////////
-    //  The Compiler
+    //  The Value (light abstraction of an LLVM::Value
     ///////////////////////////////////////////////////////////////////////////
-    unsigned const int_size = 32;
+    struct value
+    {
+        value(
+            llvm::Value* v = 0,
+            bool is_lvalue_ = false,
+            llvm::IRBuilder<>* builder = 0);
+
+        value(value const& rhs);
+
+        explicit value(unsigned int x);
+        explicit value(int x);
+        explicit value(bool x);
+
+        operator llvm::Value*() const;
+        bool operator!() const { return v == 0; }
+
+        value& operator=(value const& rhs);
+        bool is_lvalue() const { return is_lvalue_; }
+
+        value& assign(value const& rhs);
+
+    protected:
+
+        llvm::LLVMContext& context() const
+        { return llvm::getGlobalContext(); }
+
+        llvm::Value* v;
+        bool is_lvalue_;
+        llvm::IRBuilder<>* builder;
+    };
+
+    struct lvalue : value
+    {
+        lvalue(
+            llvm::AllocaInst* var,
+            llvm::IRBuilder<>& builder)
+          : value(var, true, &builder)
+        {}
+
+        lvalue(llvm::IRBuilder<>& builder, char const* name);
 
-    struct compiler
+        operator llvm::AllocaInst*() const
+        {
+            return dynamic_cast<llvm::AllocaInst*>(v);
+        }
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+    //  The Expression Compiler
+    ///////////////////////////////////////////////////////////////////////////
+    struct expression_compiler
     {
-        typedef llvm::Value* result_type;
+        expression_compiler()
+          : builder(context())
+        {}
+
+    protected:
+
+        llvm::LLVMContext& context() const
+        { return llvm::getGlobalContext(); }
+
+        llvm::IRBuilder<> builder;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+    //  The Compiler
+    ///////////////////////////////////////////////////////////////////////////
+    struct compiler : expression_compiler
+    {
+        typedef value result_type;
 
         template <typename ErrorHandler>
         compiler(vmachine& vm, ErrorHandler& error_handler_)
           : vm(vm),
-            fpm(vm.module()),
-            builder(context())
+            fpm(vm.module())
         {
             using namespace boost::phoenix::arg_names;
             namespace phx = boost::phoenix;
@@ -55,15 +121,15 @@
             init_fpm();
         }
 
-        llvm::Value* operator()(ast::nil) { BOOST_ASSERT(0); return 0; }
-        llvm::Value* operator()(unsigned int x);
-        llvm::Value* operator()(bool x);
-        llvm::Value* operator()(ast::literal const& x);
-        llvm::Value* operator()(ast::identifier const& x);
-        llvm::Value* operator()(ast::unary const& x);
-        llvm::Value* operator()(ast::function_call const& x);
-        llvm::Value* operator()(ast::expression const& x);
-        llvm::Value* operator()(ast::assignment const& x);
+        value operator()(ast::nil) { BOOST_ASSERT(0); return 0; }
+        value operator()(unsigned int x);
+        value operator()(bool x);
+        value operator()(ast::literal const& x);
+        value operator()(ast::identifier const& x);
+        value operator()(ast::unary const& x);
+        value operator()(ast::function_call const& x);
+        value operator()(ast::expression const& x);
+        value operator()(ast::assignment const& x);
 
         bool operator()(ast::variable_declaration const& x);
         bool operator()(ast::statement_list const& x);
@@ -87,7 +153,6 @@
         llvm::AllocaInst* return_alloca;
 
         vmachine& vm;
-        llvm::IRBuilder<> builder;
         llvm::FunctionPassManager fpm;
 
         llvm::LLVMContext& context() const
@@ -96,12 +161,12 @@
         llvm::Module* init_llvm();
         void init_fpm();
 
-        llvm::Value* compile_binary_expression(
-            llvm::Value* lhs, llvm::Value* rhs, token_ids::type op);
+        value compile_binary_expression(
+            value lhs, value rhs, token_ids::type op);
 
-        llvm::Value* compile_expression(
+        value compile_expression(
             int min_precedence,
-            llvm::Value* lhs,
+            value lhs,
             std::list<ast::operation>::const_iterator& rest_begin,
             std::list<ast::operation>::const_iterator rest_end);