| /*============================================================================= |
| Copyright (c) 2001-2011 Joel de Guzman |
| |
| Distributed under the Boost Software License, Version 1.0. (See accompanying |
| file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| =============================================================================*/ |
| #include "config.hpp" |
| #include "compiler.hpp" |
| #include "annotation.hpp" |
| #include "vm.hpp" |
| |
| #include <boost/foreach.hpp> |
| #include <boost/variant/apply_visitor.hpp> |
| #include <boost/range/adaptor/transformed.hpp> |
| #include <boost/assert.hpp> |
| #include <boost/lexical_cast.hpp> |
| #include <set> |
| |
| namespace client { namespace code_gen |
| { |
| value::value() |
| : v(0), |
| is_lvalue_(false), |
| builder(0) |
| {} |
| |
| 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) |
| {} |
| |
| bool value::is_lvalue() const |
| { |
| return is_lvalue_; |
| } |
| |
| bool value::is_valid() const |
| { |
| return v != 0; |
| } |
| |
| value::operator bool() const |
| { |
| return v != 0; |
| } |
| |
| void value::name(char const* id) |
| { |
| v->setName(id); |
| } |
| |
| void value::name(std::string const& id) |
| { |
| v->setName(id); |
| } |
| |
| 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; |
| } |
| |
| value operator-(value a) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateNeg(a, "neg_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator!(value a) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateNot(a, "not_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator+(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateAdd(a, b, "add_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator-(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateSub(a, b, "sub_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator*(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateMul(a, b, "mul_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator/(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateSDiv(a, b, "div_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator%(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateSRem(a, b, "mod_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator&(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateAnd(a, b, "and_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator|(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateOr(a, b, "or_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator^(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateXor(a, b, "xor_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator<<(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateShl(a, b, "shl_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator>>(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateLShr(a, b, "shr_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator==(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateICmpEQ(a, b, "eq_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator!=(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateICmpNE(a, b, "ne_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator<(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateICmpSLT(a, b, "slt_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator<=(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateICmpSLE(a, b, "sle_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator>(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateICmpSGT(a, b, "sgt_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| value operator>=(value a, value b) |
| { |
| BOOST_ASSERT(a.builder != 0); |
| return value( |
| a.builder->CreateICmpSGE(a, b, "sge_tmp"), |
| false, a.builder |
| ); |
| } |
| |
| struct function::to_value |
| { |
| typedef value result_type; |
| llvm_compiler* c; |
| |
| to_value(llvm_compiler* c = 0) : c(c) {} |
| |
| value operator()(llvm::Value& v) const |
| { |
| return c->val(&v); |
| } |
| }; |
| |
| bool basic_block::has_terminator() const |
| { |
| return b->getTerminator() != 0; |
| } |
| |
| bool basic_block::is_valid() const |
| { |
| return b != 0; |
| } |
| |
| function::operator llvm::Function*() const |
| { |
| return f; |
| } |
| |
| std::size_t function::arg_size() const |
| { |
| return f->arg_size(); |
| } |
| |
| void function::add(basic_block const& b) |
| { |
| f->getBasicBlockList().push_back(b); |
| } |
| |
| void function::erase_from_parent() |
| { |
| f->eraseFromParent(); |
| } |
| |
| basic_block function::last_block() |
| { |
| return &f->getBasicBlockList().back(); |
| } |
| |
| bool function::empty() const |
| { |
| return f->empty(); |
| } |
| |
| std::string function::name() const |
| { |
| return f->getName(); |
| } |
| |
| function::arg_range function::args() const |
| { |
| BOOST_ASSERT(c != 0); |
| arg_val_iterator first(f->arg_begin(), to_value()); |
| arg_val_iterator last(f->arg_end(), to_value()); |
| return arg_range(first, last); |
| } |
| |
| bool function::is_valid() const |
| { |
| return f != 0; |
| } |
| |
| void function::verify() const |
| { |
| llvm::verifyFunction(*f); |
| } |
| |
| value llvm_compiler::val(unsigned int x) |
| { |
| return value( |
| llvm::ConstantInt::get(context(), llvm::APInt(int_size, x)), |
| false, &llvm_builder); |
| } |
| |
| value llvm_compiler::val(int x) |
| { |
| return value( |
| llvm::ConstantInt::get(context(), llvm::APInt(int_size, x)), |
| false, &llvm_builder); |
| } |
| |
| value llvm_compiler::val(bool x) |
| { |
| return value( |
| llvm::ConstantInt::get(context(), llvm::APInt(1, x)), |
| false, &llvm_builder); |
| } |
| |
| value llvm_compiler::val(llvm::Value* v) |
| { |
| return value(v, false, &llvm_builder); |
| } |
| |
| namespace |
| { |
| // Create an alloca instruction in the entry block of |
| // the function. This is used for mutable variables etc. |
| llvm::AllocaInst* |
| make_entry_block_alloca( |
| llvm::Function* f, |
| char const* name, |
| llvm::LLVMContext& context) |
| { |
| llvm::IRBuilder<> builder( |
| &f->getEntryBlock(), |
| f->getEntryBlock().begin()); |
| |
| return builder.CreateAlloca( |
| llvm::Type::getIntNTy(context, int_size), 0, name); |
| } |
| } |
| |
| value llvm_compiler::var(char const* name) |
| { |
| llvm::Function* f = llvm_builder.GetInsertBlock()->getParent(); |
| llvm::IRBuilder<> builder( |
| &f->getEntryBlock(), |
| f->getEntryBlock().begin()); |
| |
| llvm::AllocaInst* alloca = builder.CreateAlloca( |
| llvm::Type::getIntNTy(context(), int_size), 0, name); |
| |
| return value(alloca, true, &llvm_builder); |
| } |
| |
| struct value::to_llvm_value |
| { |
| typedef llvm::Value* result_type; |
| llvm::Value* operator()(value const& x) const |
| { |
| return x; |
| } |
| }; |
| |
| template <typename C> |
| llvm::Value* llvm_compiler::call_impl( |
| function callee, |
| C const& args_) |
| { |
| // Sigh. LLVM requires CreateCall arguments to be random access. |
| // It would have been better if it can accept forward iterators. |
| // I guess it needs the arguments to be in contiguous memory. |
| // So, we have to put the args into a temporary std::vector. |
| std::vector<llvm::Value*> args( |
| args_.begin(), args_.end()); |
| |
| // Check the args for null values. We can't have null values. |
| // Return 0 if we find one to flag error. |
| BOOST_FOREACH(llvm::Value* arg, args) |
| { |
| if (arg == 0) |
| return 0; |
| } |
| |
| return llvm_builder.CreateCall( |
| callee, args.begin(), args.end(), "call_tmp"); |
| } |
| |
| template <typename Container> |
| value llvm_compiler::call( |
| function callee, |
| Container const& args) |
| { |
| llvm::Value* call = call_impl( |
| callee, |
| args | boost::adaptors::transformed(value::to_llvm_value())); |
| |
| if (call == 0) |
| return val(); |
| return value(call, false, &llvm_builder); |
| } |
| |
| function llvm_compiler::get_function(char const* name) |
| { |
| return function(vm.module()->getFunction(name), this); |
| } |
| |
| function llvm_compiler::get_current_function() |
| { |
| // get the current function |
| return function(llvm_builder.GetInsertBlock()->getParent(), this); |
| } |
| |
| function llvm_compiler::declare_function( |
| bool void_return |
| , std::string const& name |
| , std::size_t nargs) |
| { |
| llvm::Type const* int_type = |
| llvm::Type::getIntNTy(context(), int_size); |
| llvm::Type const* void_type = llvm::Type::getVoidTy(context()); |
| |
| std::vector<llvm::Type const*> ints(nargs, int_type); |
| llvm::Type const* return_type = void_return ? void_type : int_type; |
| |
| llvm::FunctionType* function_type = |
| llvm::FunctionType::get(void_return ? void_type : int_type, ints, false); |
| |
| return function(llvm::Function::Create( |
| function_type, llvm::Function::ExternalLinkage, |
| name, vm.module()), this); |
| } |
| |
| basic_block llvm_compiler::make_basic_block( |
| char const* name |
| , function parent |
| , basic_block before) |
| { |
| return llvm::BasicBlock::Create(context(), name, parent, before); |
| } |
| |
| value llvm_compiler::var(std::string const& name) |
| { |
| return var(name.c_str()); |
| } |
| |
| function llvm_compiler::get_function(std::string const& name) |
| { |
| return get_function(name.c_str()); |
| } |
| |
| basic_block llvm_compiler::get_insert_block() |
| { |
| return llvm_builder.GetInsertBlock(); |
| } |
| |
| void llvm_compiler::set_insert_point(basic_block b) |
| { |
| llvm_builder.SetInsertPoint(b); |
| } |
| |
| void llvm_compiler::conditional_branch( |
| value cond, basic_block true_br, basic_block false_br) |
| { |
| llvm_builder.CreateCondBr(cond, true_br, false_br); |
| } |
| |
| void llvm_compiler::branch(basic_block b) |
| { |
| llvm_builder.CreateBr(b); |
| } |
| |
| void llvm_compiler::return_() |
| { |
| llvm_builder.CreateRetVoid(); |
| } |
| |
| void llvm_compiler::return_(value v) |
| { |
| llvm_builder.CreateRet(v); |
| } |
| |
| void llvm_compiler::optimize_function(function f) |
| { |
| // Optimize the function. |
| fpm.run(*f); |
| } |
| |
| void llvm_compiler::init_fpm() |
| { |
| // Set up the optimizer pipeline. Start with registering info about how the |
| // target lays out data structures. |
| fpm.add(new llvm::TargetData(*vm.execution_engine()->getTargetData())); |
| // Provide basic AliasAnalysis support for GVN. |
| fpm.add(llvm::createBasicAliasAnalysisPass()); |
| // Promote allocas to registers. |
| fpm.add(llvm::createPromoteMemoryToRegisterPass()); |
| // Do simple "peephole" optimizations and bit-twiddling optzns. |
| fpm.add(llvm::createInstructionCombiningPass()); |
| // Reassociate expressions. |
| fpm.add(llvm::createReassociatePass()); |
| // Eliminate Common SubExpressions. |
| fpm.add(llvm::createGVNPass()); |
| // Simplify the control flow graph (deleting unreachable blocks, etc). |
| fpm.add(llvm::createCFGSimplificationPass()); |
| |
| fpm.doInitialization(); |
| } |
| |
| value compiler::operator()(unsigned int x) |
| { |
| return val(x); |
| } |
| |
| value compiler::operator()(bool x) |
| { |
| return val(x); |
| } |
| |
| value compiler::operator()(ast::primary_expr const& x) |
| { |
| return boost::apply_visitor(*this, x.get()); |
| } |
| |
| value compiler::operator()(ast::identifier const& x) |
| { |
| // Look this variable up in the function. |
| if (locals.find(x.name) == locals.end()) |
| { |
| error_handler(x.id, "Undeclared variable: " + x.name); |
| return val(); |
| } |
| return locals[x.name]; |
| } |
| |
| value compiler::operator()(ast::unary_expr const& x) |
| { |
| value operand = boost::apply_visitor(*this, x.operand_); |
| if (!operand.is_valid()) |
| return val(); |
| |
| switch (x.operator_) |
| { |
| case token_ids::compl_: return operand ^ val(-1); |
| case token_ids::minus: return -operand; |
| case token_ids::not_: return !operand; |
| case token_ids::plus: return operand; |
| case token_ids::plus_plus: |
| { |
| if (!operand.is_lvalue()) |
| { |
| error_handler(x.id, "++ needs an var"); |
| return val(); |
| } |
| |
| value r = operand + val(1); |
| operand.assign(r); |
| return operand; |
| } |
| case token_ids::minus_minus: |
| { |
| if (!operand.is_lvalue()) |
| { |
| error_handler(x.id, "-- needs an var"); |
| return val(); |
| } |
| |
| value r = operand - val(1); |
| operand.assign(r); |
| return operand; |
| } |
| default: |
| BOOST_ASSERT(0); |
| return val(); |
| } |
| } |
| |
| namespace |
| { |
| struct compile_args |
| { |
| compiler& c; |
| compile_args(compiler& c) : c(c) {} |
| |
| typedef value result_type; |
| value operator()(ast::expression const& expr) const |
| { |
| return c(expr); |
| } |
| }; |
| } |
| |
| value compiler::operator()(ast::function_call const& x) |
| { |
| function callee = get_function(x.function_name.name); |
| if (!callee.is_valid()) |
| { |
| error_handler(x.function_name.id, |
| "Function not found: " + x.function_name.name); |
| return val(); |
| } |
| |
| if (callee.arg_size() != x.args.size()) |
| { |
| error_handler(x.function_name.id, |
| "Wrong number of arguments: " + x.function_name.name); |
| return val(); |
| } |
| |
| return call(callee, |
| x.args | boost::adaptors::transformed(compile_args(*this))); |
| } |
| |
| namespace |
| { |
| int precedence[] = { |
| // precedence 1 |
| 1, // op_comma |
| |
| // precedence 2 |
| 2, // op_assign |
| 2, // op_plus_assign |
| 2, // op_minus_assign |
| 2, // op_times_assign |
| 2, // op_divide_assign |
| 2, // op_mod_assign |
| 2, // op_bit_and_assign |
| 2, // op_bit_xor_assign |
| 2, // op_bitor_assign |
| 2, // op_shift_left_assign |
| 2, // op_shift_right_assign |
| |
| // precedence 3 |
| 3, // op_logical_or |
| |
| // precedence 4 |
| 4, // op_logical_and |
| |
| // precedence 5 |
| 5, // op_bit_or |
| |
| // precedence 6 |
| 6, // op_bit_xor |
| |
| // precedence 7 |
| 7, // op_bit_and |
| |
| // precedence 8 |
| 8, // op_equal |
| 8, // op_not_equal |
| |
| // precedence 9 |
| 9, // op_less |
| 9, // op_less_equal |
| 9, // op_greater |
| 9, // op_greater_equal |
| |
| // precedence 10 |
| 10, // op_shift_left |
| 10, // op_shift_right |
| |
| // precedence 11 |
| 11, // op_plus |
| 11, // op_minus |
| |
| // precedence 12 |
| 12, // op_times |
| 12, // op_divide |
| 12 // op_mod |
| }; |
| } |
| |
| inline int precedence_of(token_ids::type op) |
| { |
| return precedence[op & 0xFF]; |
| } |
| |
| value compiler::compile_binary_expression( |
| value lhs, value rhs, token_ids::type op) |
| { |
| switch (op) |
| { |
| case token_ids::plus: return lhs + rhs; |
| case token_ids::minus: return lhs - rhs; |
| case token_ids::times: return lhs * rhs; |
| case token_ids::divide: return lhs / rhs; |
| case token_ids::mod: return lhs % rhs; |
| |
| case token_ids::logical_or: |
| case token_ids::bit_or: return lhs | rhs; |
| |
| case token_ids::logical_and: |
| case token_ids::bit_and: return lhs & rhs; |
| |
| case token_ids::bit_xor: return lhs ^ rhs; |
| case token_ids::shift_left: return lhs << rhs; |
| case token_ids::shift_right: return lhs >> rhs; |
| |
| case token_ids::equal: return lhs == rhs; |
| case token_ids::not_equal: return lhs != rhs; |
| case token_ids::less: return lhs < rhs; |
| case token_ids::less_equal: return lhs <= rhs; |
| case token_ids::greater: return lhs > rhs; |
| case token_ids::greater_equal: return lhs >= rhs; |
| |
| default: BOOST_ASSERT(0); return val(); |
| } |
| } |
| |
| // The Shunting-yard algorithm |
| value compiler::compile_expression( |
| int min_precedence, |
| value lhs, |
| std::list<ast::operation>::const_iterator& rest_begin, |
| std::list<ast::operation>::const_iterator rest_end) |
| { |
| while ((rest_begin != rest_end) && |
| (precedence_of(rest_begin->operator_) >= min_precedence)) |
| { |
| token_ids::type op = rest_begin->operator_; |
| value rhs = boost::apply_visitor(*this, rest_begin->operand_); |
| if (!rhs.is_valid()) |
| return val(); |
| ++rest_begin; |
| |
| while ((rest_begin != rest_end) && |
| (precedence_of(rest_begin->operator_) > precedence_of(op))) |
| { |
| token_ids::type next_op = rest_begin->operator_; |
| rhs = compile_expression( |
| precedence_of(next_op), rhs, rest_begin, rest_end); |
| } |
| |
| lhs = compile_binary_expression(lhs, rhs, op); |
| } |
| return lhs; |
| } |
| |
| value compiler::operator()(ast::expression const& x) |
| { |
| value lhs = boost::apply_visitor(*this, x.first); |
| if (!lhs.is_valid()) |
| return val(); |
| std::list<ast::operation>::const_iterator rest_begin = x.rest.begin(); |
| return compile_expression(0, lhs, rest_begin, x.rest.end()); |
| } |
| |
| value compiler::operator()(ast::assignment const& x) |
| { |
| if (locals.find(x.lhs.name) == locals.end()) |
| { |
| error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name); |
| return val(); |
| } |
| |
| value lhs = locals[x.lhs.name]; |
| value rhs = (*this)(x.rhs); |
| if (!rhs.is_valid()) |
| return val(); |
| |
| if (x.operator_ == token_ids::assign) |
| { |
| lhs.assign(rhs); |
| return lhs; |
| } |
| |
| value result; |
| switch (x.operator_) |
| { |
| case token_ids::plus_assign: result = lhs + rhs; break; |
| case token_ids::minus_assign: result = lhs - rhs; break; |
| case token_ids::times_assign: result = lhs * rhs; break; |
| case token_ids::divide_assign: result = lhs / rhs; break; |
| case token_ids::mod_assign: result = lhs % rhs; break; |
| case token_ids::bit_and_assign: result = lhs & rhs; break; |
| case token_ids::bit_xor_assign: result = lhs ^ rhs; break; |
| case token_ids::bit_or_assign: result = lhs | rhs; break; |
| case token_ids::shift_left_assign: result = lhs << rhs; break; |
| case token_ids::shift_right_assign: result = lhs >> rhs; break; |
| default: BOOST_ASSERT(0); return val(); |
| } |
| |
| lhs.assign(result); |
| return lhs; |
| } |
| |
| bool compiler::operator()(ast::variable_declaration const& x) |
| { |
| if (locals.find(x.lhs.name) != locals.end()) |
| { |
| error_handler(x.lhs.id, "Duplicate variable: " + x.lhs.name); |
| return false; |
| } |
| |
| value init; |
| std::string const& name = x.lhs.name; |
| |
| if (x.rhs) // if there's an RHS initializer |
| { |
| init = (*this)(*x.rhs); |
| if (!init.is_valid()) // don't add the variable if the RHS fails |
| return false; |
| } |
| |
| value var_ = var(name.c_str()); |
| if (init.is_valid()) |
| var_.assign(init); |
| |
| // Remember this binding. |
| locals[name] = var_; |
| return true; |
| } |
| |
| struct compiler::statement_compiler : compiler |
| { |
| typedef bool result_type; |
| }; |
| |
| compiler::statement_compiler& compiler::as_statement() |
| { |
| return *static_cast<statement_compiler*>(this); |
| } |
| |
| bool compiler::operator()(ast::statement const& x) |
| { |
| if (boost::get<ast::nil>(&x) != 0) // empty statement |
| return true; |
| return boost::apply_visitor(as_statement(), x); |
| } |
| |
| bool compiler::operator()(ast::statement_list const& x) |
| { |
| BOOST_FOREACH(ast::statement const& s, x) |
| { |
| if (!(*this)(s)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool compiler::operator()(ast::if_statement const& x) |
| { |
| value condition = (*this)(x.condition); |
| if (!condition.is_valid()) |
| return false; |
| |
| function f = get_current_function(); |
| |
| // Create blocks for the then and else cases. Insert the 'then' block at the |
| // end of the function. |
| basic_block then_block = make_basic_block("if.then", f); |
| basic_block else_block; |
| basic_block exit_block; |
| |
| if (x.else_) |
| { |
| else_block = make_basic_block("if.else"); |
| conditional_branch(condition, then_block, else_block); |
| } |
| else |
| { |
| exit_block = make_basic_block("if.end"); |
| conditional_branch(condition, then_block, exit_block); |
| } |
| |
| // Emit then value. |
| set_insert_point(then_block); |
| if (!(*this)(x.then)) |
| return false; |
| if (!then_block.has_terminator()) |
| { |
| 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 = get_insert_block(); |
| |
| if (x.else_) |
| { |
| // Emit else block. |
| f.add(else_block); |
| set_insert_point(else_block); |
| if (!(*this)(*x.else_)) |
| return false; |
| if (!else_block.has_terminator()) |
| { |
| 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 = get_insert_block(); |
| } |
| |
| if (exit_block.is_valid()) |
| { |
| // Emit exit block |
| f.add(exit_block); |
| set_insert_point(exit_block); |
| } |
| return true; |
| } |
| |
| bool compiler::operator()(ast::while_statement const& x) |
| { |
| function f = get_current_function(); |
| |
| 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"); |
| |
| branch(cond_block); |
| set_insert_point(cond_block); |
| value condition = (*this)(x.condition); |
| if (!condition.is_valid()) |
| return false; |
| 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.has_terminator()) |
| branch(cond_block); // loop back |
| |
| // Emit exit block |
| f.add(exit_block); |
| set_insert_point(exit_block); |
| |
| return true; |
| } |
| |
| bool compiler::operator()(ast::return_statement const& x) |
| { |
| if (void_return) |
| { |
| if (x.expr) |
| { |
| error_handler( |
| x.id, "'void' function returning a value: "); |
| return false; |
| } |
| } |
| else |
| { |
| if (!x.expr) |
| { |
| error_handler( |
| x.id, current_function_name |
| + " function must return a value: "); |
| return false; |
| } |
| } |
| |
| if (x.expr) |
| { |
| value return_val = (*this)(*x.expr); |
| if (!return_val.is_valid()) |
| return false; |
| return_var.assign(return_val); |
| } |
| |
| branch(return_block); |
| return true; |
| } |
| |
| function compiler::function_decl(ast::function const& x) |
| { |
| void_return = x.return_type == "void"; |
| current_function_name = x.function_name.name; |
| |
| 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.name() != current_function_name) |
| { |
| // Delete the one we just made and get the existing one. |
| f.erase_from_parent(); |
| f = get_function(current_function_name); |
| |
| // If function already has a body, reject this. |
| if (!f.empty()) |
| { |
| error_handler( |
| x.function_name.id, |
| "Duplicate function: " + x.function_name.name); |
| return function(); |
| } |
| |
| // If function took a different number of args, reject. |
| if (f.arg_size() != x.args.size()) |
| { |
| error_handler( |
| x.function_name.id, |
| "Redefinition of function with different # args: " |
| + x.function_name.name); |
| return function(); |
| } |
| |
| // Set names for all arguments. |
| function::arg_range rng = f.args(); |
| function::arg_range::const_iterator iter = rng.begin(); |
| BOOST_FOREACH(ast::identifier const& arg, x.args) |
| { |
| iter->name(arg.name); |
| ++iter; |
| } |
| } |
| return f; |
| } |
| |
| 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. |
| function::arg_range rng = f.args(); |
| function::arg_range::const_iterator iter = rng.begin(); |
| BOOST_FOREACH(ast::identifier const& arg, x.args) |
| { |
| // Create an arg_ for this variable. |
| value arg_ = var(arg.name); |
| |
| // Store the initial value into the arg_. |
| arg_.assign(*iter); |
| |
| // Add arguments to variable symbol table. |
| locals[arg.name] = arg_; |
| ++iter; |
| } |
| |
| if (!void_return) |
| { |
| // Create an alloca for the return value |
| return_var = var("return.val"); |
| } |
| } |
| |
| bool compiler::operator()(ast::function const& x) |
| { |
| /////////////////////////////////////////////////////////////////////// |
| // the signature: |
| function f = function_decl(x); |
| if (!f.is_valid()) |
| return false; |
| |
| /////////////////////////////////////////////////////////////////////// |
| // the body: |
| if (x.body) // compile the body if this is not a prototype |
| { |
| // Create a new basic block to start insertion into. |
| basic_block block = make_basic_block("entry", f); |
| set_insert_point(block); |
| |
| function_allocas(x, f); |
| return_block = make_basic_block("return"); |
| |
| if (!(*this)(*x.body)) |
| { |
| // Error reading body, remove function. |
| f.erase_from_parent(); |
| return false; |
| } |
| |
| basic_block last_block = f.last_block(); |
| |
| // If the last block is unterminated, connect it to return_block |
| if (!last_block.has_terminator()) |
| { |
| set_insert_point(last_block); |
| branch(return_block); |
| } |
| |
| f.add(return_block); |
| set_insert_point(return_block); |
| |
| if (void_return) |
| return_(); |
| else |
| return_(return_var); |
| |
| // Validate the generated code, checking for consistency. |
| f.verify(); |
| |
| // Optimize the function. |
| optimize_function(f); |
| } |
| |
| return true; |
| } |
| |
| bool compiler::operator()(ast::function_list const& x) |
| { |
| BOOST_FOREACH(ast::function const& f, x) |
| { |
| locals.clear(); // clear the variables |
| if (!(*this)(f)) |
| return false; |
| } |
| return true; |
| } |
| }} |
| |