// Copyright 2017 the Monicelli project authors. All rights reserved. // Use of this source code is governed by a GPLv3 license, see LICENSE.txt. #include "codegen.def" #include "ast-visitor.h" #include "parser.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/TypeBuilder.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Transforms/Scalar.h" #include using namespace monicelli; namespace { class NestedScopes final { public: class Guard final { public: Guard(NestedScopes& context) : context_(context) { context_.enterScope(); } ~Guard() { context_.leaveScope(); } private: NestedScopes& context_; }; NestedScopes() {} NestedScopes(NestedScopes&) = delete; NestedScopes& operator=(NestedScopes&) = delete; llvm::Value* lookup(const std::string& name); bool define(const std::string& name, llvm::Value* def) { assert(!scopes_.empty() && "Trying to define outside any scope"); auto result = scopes_.back().insert({name, def}); return result.second; } void enterScope() { scopes_.emplace_back(); } void leaveScope() { assert(!scopes_.empty() && "Trying to leave a scope, but there is none"); scopes_.pop_back(); } void reset() { scopes_.clear(); } bool empty() const { return scopes_.empty(); } private: std::vector> scopes_; }; class IRGenerator; class ResultTypeCalculator : public ConstAstVisitor, public ErrorReportingMixin { public: ResultTypeCalculator(IRGenerator* codegen, const std::string& source_filename) : ErrorReportingMixin(source_filename), codegen_(codegen) {} llvm::Type* visitBinaryExpression(const BinaryExpression* e); llvm::Type* visitAtomicExpression(const AtomicExpression* e); llvm::Type* visitFunctionCallExpression(const FunctionCallExpression* e); private: IRGenerator* codegen_; }; class IRGenerator final : public ConstAstVisitor, public ErrorReportingMixin { public: IRGenerator(llvm::LLVMContext& context, const std::string& source_filename) : ErrorReportingMixin(source_filename), context_(context), builder_(context), exit_block_(nullptr), return_var_(nullptr), type_calculator_(this, source_filename) {} std::unique_ptr releaseModule() { return std::move(module_); } llvm::Module* getModule() { return module_.get(); } llvm::Value* visitModule(const Module* m); llvm::Value* visitFunction(const Function* f); llvm::Value* visitVardeclStatement(const VardeclStatement* s); llvm::Value* visitReturnStatement(const ReturnStatement* r); llvm::Value* visitAssignStatement(const AssignStatement* a); llvm::Value* visitBranchStatement(const BranchStatement* b); llvm::Value* visitLoopStatement(const LoopStatement* l); llvm::Value* visitInputStatement(const InputStatement* s); llvm::Value* visitPrintStatement(const PrintStatement* p); llvm::Value* visitExpressionStatement(const ExpressionStatement* s) { visit(s->getExpression()); return nullptr; } llvm::Value* visitBinaryExpression(const BinaryExpression* e); llvm::Value* visitAtomicExpression(const AtomicExpression* e); llvm::Value* visitFunctionCallExpression(const FunctionCallExpression* f); private: llvm::Function* declareFunction(const Function* f); std::string getFunctionName(const Function* f) { return f->isEntryPoint() ? "main" : f->getName(); } void declareBuiltins(); template const char* getFormatSpecifier(llvm::Type* type); template llvm::Value* getFormatString(llvm::Type* type); template void callIOBuiltin(llvm::Type* type, llvm::Value* value); llvm::Type* getIRType(const VarType& type) { auto base_type = getIRBaseType(type.getBaseType()); if (type.isPointer()) { return base_type->getPointerTo(); } return base_type; } llvm::Type* getIRBaseType(VarType::BaseType type); llvm::Value* ensureType(llvm::Value* value, llvm::Type* type); const char* getSourceBaseType(llvm::Type* type); std::string getSourceType(llvm::Type* type); llvm::Value* evalTruthiness(llvm::Value* val); llvm::Function* current_function() { return builder_.GetInsertBlock()->getParent(); } llvm::LLVMContext& context_; llvm::IRBuilder<> builder_; std::unique_ptr module_; NestedScopes var_scopes_; llvm::DenseMap input_format_strings_cache_; llvm::DenseMap output_format_strings_cache_; llvm::BasicBlock* exit_block_; llvm::AllocaInst* return_var_; ResultTypeCalculator type_calculator_; friend class ResultTypeCalculator; }; } // namespace llvm::Value* NestedScopes::lookup(const std::string& name) { for (auto c = scopes_.crbegin(), end = scopes_.crend(); c != end; ++c) { auto result = c->find(name); if (result != c->end()) return result->second; } return nullptr; } void IRGenerator::declareBuiltins() { llvm::FunctionType* printf_type = llvm::TypeBuilder::get(context_); auto no_alias = llvm::AttributeSet().addAttribute(context_, 1, llvm::Attribute::NoAlias); module_->getOrInsertFunction("printf", printf_type, no_alias); module_->getOrInsertFunction("scanf", printf_type, no_alias); } llvm::Value* IRGenerator::visitModule(const Module* m) { module_ = llvm::make_unique("antani", context_); declareBuiltins(); for (const Function* f : m->functions()) { declareFunction(f); } if (m->hasEntryPoint()) declareFunction(m->getEntryPoint()); for (const Function* f : m->functions()) { visit(f); } if (m->hasEntryPoint()) visit(m->getEntryPoint()); llvm::verifyModule(*module_); return nullptr; } llvm::Function* IRGenerator::declareFunction(const Function* ast_f) { std::vector param_types; param_types.reserve(ast_f->params_size()); for (const FunctionParam& param : ast_f->params()) { param_types.push_back(getIRType(param.getType())); } auto type = llvm::FunctionType::get(getIRType(ast_f->getReturnType()), param_types, false); llvm::Function* f = llvm::Function::Create(type, llvm::Function::ExternalLinkage, getFunctionName(ast_f), module_.get()); auto ast_arg = ast_f->begin_params(); for (auto& arg : f->args()) { arg.setName(ast_arg->getArg().getName()); ++ast_arg; } assert(ast_arg == ast_f->end_params()); return f; } llvm::Value* IRGenerator::visitFunction(const Function* ast_f) { llvm::Function* f = module_->getFunction(getFunctionName(ast_f)); assert(f && "This function should have had a prototype defined"); if (ast_f->body_empty()) return f; NestedScopes::Guard scopes_guard{var_scopes_}; llvm::BasicBlock* entry = llvm::BasicBlock::Create(context_, "entry", f); builder_.SetInsertPoint(entry); if (!f->getReturnType()->isVoidTy()) { return_var_ = builder_.CreateAlloca(f->getReturnType(), nullptr, "result"); if (ast_f->isEntryPoint()) { assert(f->getReturnType()->isIntegerTy()); builder_.CreateStore(ensureType(builder_.getInt64(0), f->getReturnType()), return_var_); } } else { return_var_ = nullptr; } for (auto& arg : f->args()) { auto arg_ptr = builder_.CreateAlloca(arg.getType(), nullptr, arg.getName()); builder_.CreateStore(&arg, arg_ptr); var_scopes_.define(arg.getName(), arg_ptr); } exit_block_ = llvm::BasicBlock::Create(context_, "exit"); for (const Statement* s : ast_f->body()) { visit(s); } builder_.CreateBr(exit_block_); f->getBasicBlockList().push_back(exit_block_); builder_.SetInsertPoint(exit_block_); if (return_var_) { builder_.CreateRet(builder_.CreateLoad(return_var_)); } else { builder_.CreateRetVoid(); } llvm::verifyFunction(*f); exit_block_ = nullptr; return_var_ = nullptr; return f; } llvm::Value* IRGenerator::visitVardeclStatement(const VardeclStatement* s) { const auto& name = s->getVariable().getName(); llvm::AllocaInst* var = builder_.CreateAlloca(getIRType(s->getType()), nullptr, name); if (!var_scopes_.define(name, var)) { error(&s->getVariable(), "redefining an existing variable"); } if (s->hasInitializer()) { llvm::Value* init = visit(s->getInitializer()); auto original_init_type = init->getType(); auto target_type = var->getType()->getPointerElementType(); init = ensureType(init, target_type); if (!init) { error(s->getInitializer(), "cannot initialize variable of type", getSourceType(target_type), "with expression of type", getSourceType(original_init_type)); } builder_.CreateStore(init, var); } return var; } llvm::Value* IRGenerator::visitReturnStatement(const ReturnStatement* r) { if (r->hasExpression()) { auto return_value = visit(r->getExpression()); auto original_return_type = return_value->getType(); auto return_type = return_var_->getType()->getPointerElementType(); return_value = ensureType(return_value, return_type); if (!return_value) { error(r->getExpression(), "cannot return expression of type", original_return_type, "from function of type", return_type); } builder_.CreateStore(return_value, return_var_); } builder_.CreateBr(exit_block_); // Code after the return will end up in this unreachable BB and DCE will // take care of it. llvm::BasicBlock* after = llvm::BasicBlock::Create(context_, "return.after", current_function()); builder_.SetInsertPoint(after); // This one is not necessary, but will help catch codegen errors. builder_.CreateUnreachable(); return nullptr; } llvm::Value* IRGenerator::visitAssignStatement(const AssignStatement* a) { auto val = visit(a->getExpression()); assert(val && "unhandled error while building expression"); auto var = var_scopes_.lookup(a->getVariable().getName()); if (!var) { error(&a->getVariable(), "assigning to undefined variable", a->getVariable().getName()); } auto original_val_type = val->getType(); auto target_type = var->getType()->getPointerElementType(); val = ensureType(val, target_type); if (!val) { error(a->getExpression(), "cannot assign expression of type", getSourceType(original_val_type), "to variable of type", getSourceType(target_type)); } builder_.CreateStore(val, var); return nullptr; } llvm::Value* IRGenerator::evalTruthiness(llvm::Value* val) { if (llvm::isa(val)) return val; auto val_type = val->getType(); if (!val_type->isIntegerTy() && !val_type->isFloatingPointTy()) { return nullptr; } auto zero = ensureType(builder_.getInt64(0), val_type); return builder_.CreateICmpNE(val, zero, "cond"); } llvm::Value* IRGenerator::visitBranchStatement(const BranchStatement* b) { llvm::BasicBlock* entry_bb = llvm::BasicBlock::Create(context_, "branch.head", current_function()); llvm::BasicBlock* exit_bb = llvm::BasicBlock::Create(context_, "branch.after"); builder_.CreateBr(entry_bb); builder_.SetInsertPoint(entry_bb); llvm::BasicBlock* case_cond_bb = llvm::BasicBlock::Create(context_, "branch.case.cond", current_function()); builder_.CreateBr(case_cond_bb); builder_.SetInsertPoint(case_cond_bb); for (const BranchCase& branch_case : b->cases()) { llvm::Value* condition = visit(branch_case.getExpression()); auto condition_type = condition->getType(); condition = evalTruthiness(condition); if (!condition) { error(branch_case.getExpression(), "cannot convert expression of type", getSourceType(condition_type), "to boolean."); } case_cond_bb = llvm::BasicBlock::Create(context_, "branch.case.cond"); llvm::BasicBlock* case_body_bb = llvm::BasicBlock::Create(context_, "branch.case.body", current_function()); builder_.CreateCondBr(condition, case_body_bb, case_cond_bb); builder_.SetInsertPoint(case_body_bb); for (const Statement* s : branch_case.body()) { visit(s); } builder_.CreateBr(exit_bb); current_function()->getBasicBlockList().push_back(case_cond_bb); builder_.SetInsertPoint(case_cond_bb); } if (b->hasBranchElse()) { NestedScopes::Guard scope_guard{var_scopes_}; llvm::BasicBlock* else_bb = llvm::BasicBlock::Create(context_, "branch.else", current_function()); builder_.CreateBr(else_bb); builder_.SetInsertPoint(else_bb); for (const Statement* s : b->getBranchElse()->body()) { visit(s); } } builder_.CreateBr(exit_bb); current_function()->getBasicBlockList().push_back(exit_bb); builder_.SetInsertPoint(exit_bb); return nullptr; } llvm::Value* IRGenerator::visitLoopStatement(const LoopStatement* l) { llvm::BasicBlock* body_bb = llvm::BasicBlock::Create(context_, "loop.body", current_function()); builder_.CreateBr(body_bb); builder_.SetInsertPoint(body_bb); { NestedScopes::Guard scope_guard{var_scopes_}; for (const Statement* s : l->body()) { visit(s); } } llvm::BasicBlock* condition_bb = llvm::BasicBlock::Create(context_, "loop.condition", current_function()); llvm::BasicBlock* after_bb = llvm::BasicBlock::Create(context_, "loop.after"); builder_.CreateBr(condition_bb); builder_.SetInsertPoint(condition_bb); auto condition = visit(l->getCondition()); auto condition_type = condition->getType(); condition = evalTruthiness(condition); if (!condition) { error(l->getCondition(), "cannot convert expression of type", getSourceType(condition_type), "to boolean"); } builder_.CreateCondBr(condition, body_bb, after_bb); current_function()->getBasicBlockList().push_back(after_bb); builder_.SetInsertPoint(after_bb); return nullptr; } const char* IRGenerator::getSourceBaseType(llvm::Type* type) { assert(type->isIntegerTy() || type->isFloatingPointTy()); #define RETURN_BASE_NAME(_1, TYPE, _2, _3, SOURCE_NAME, _4) \ if (type == builder_.get##TYPE##Ty()) { \ return SOURCE_NAME; \ } BUILTIN_TYPES(RETURN_BASE_NAME) #undef RETURN_BASE_NAME UNREACHABLE("Unhandled base type."); } std::string IRGenerator::getSourceType(llvm::Type* type) { std::string name; while (type->isPointerTy()) { name += "conte "; type = type->getPointerElementType(); } name += getSourceBaseType(type); return name; } namespace { template<> const char* IRGenerator::getFormatSpecifier(llvm::Type* type) { assert(type->isIntegerTy() || type->isFloatingPointTy()); #define RETURN_SPECIFIER(_1, TYPE, SPEC, _2, _3, _4) \ if (type == builder_.get##TYPE##Ty()) { \ return SPEC; \ } BUILTIN_TYPES(RETURN_SPECIFIER) #undef RETURN_SPECIFIER UNREACHABLE("Unhandled input format specifier"); } template<> const char* IRGenerator::getFormatSpecifier(llvm::Type* type) { assert(type->isIntegerTy() || type->isFloatingPointTy()); #define RETURN_SPECIFIER(_1, TYPE, _2, SPEC, _3, _4) \ if (type == builder_.get##TYPE##Ty()) { \ return SPEC; \ } BUILTIN_TYPES(RETURN_SPECIFIER) #undef RETURN_SPECIFIER UNREACHABLE("Unhandled output format specifier"); } } // namespace template llvm::Value* IRGenerator::getFormatString(llvm::Type* type) { auto& cache = output ? output_format_strings_cache_ : input_format_strings_cache_; auto hit = cache.find(type); if (hit != cache.end()) return hit->second; auto specifier = getFormatSpecifier(type); auto format = builder_.CreateGlobalStringPtr(specifier, "format"); cache.insert({type, format}); return format; } template void IRGenerator::callIOBuiltin(llvm::Type* type, llvm::Value* value) { auto builtin = module_->getFunction(output ? "printf" : "scanf"); assert(builtin && "Builtin was not declared"); llvm::Value* args[] = {getFormatString(type), value}; builder_.CreateCall(builtin, args); } llvm::Value* IRGenerator::visitInputStatement(const InputStatement* s) { auto var = var_scopes_.lookup(s->getVariable().getName()); if (!var) { error(&s->getVariable(), "reading an undefined variable"); } assert(var->getType()->isPointerTy()); auto target = var; auto target_type = target->getType()->getPointerElementType(); bool reading_bool = target_type == builder_.getInt1Ty(); if (!target_type->isIntegerTy() && !target_type->isFloatingPointTy()) { error(&s->getVariable(), "can only read integers and floating point"); } if (reading_bool) { target = builder_.CreateAlloca(builder_.getInt32Ty()); } callIOBuiltin(target_type, target); if (reading_bool) { builder_.CreateStore(evalTruthiness(builder_.CreateLoad(target)), var); } return nullptr; } llvm::Value* IRGenerator::visitPrintStatement(const PrintStatement* p) { auto value = visit(p->getExpression()); auto type = value->getType(); if (!type->isIntegerTy() && !type->isFloatingPointTy()) { error(p->getExpression(), "only integer and float valued expressions may be printed"); } // Integer promotion for variadic call. if (type->isIntegerTy() && type->getIntegerBitWidth() < 32) { value = builder_.CreateZExt(value, builder_.getInt32Ty()); } // Same, for floating point. if (type == builder_.getFloatTy()) { value = builder_.CreateFPCast(value, builder_.getDoubleTy()); } callIOBuiltin(type, value); return nullptr; } llvm::Value* IRGenerator::visitBinaryExpression(const BinaryExpression* e) { auto lhs = visit(e->getLeft()); auto rhs = visit(e->getRight()); if (lhs->getType()->isPointerTy() || rhs->getType()->isPointerTy()) { error(e, "pointer arithmetic is not supported"); } llvm::Type* result_type = type_calculator_.visit(e); auto original_lhs_type = lhs->getType(); auto original_rhs_type = rhs->getType(); lhs = ensureType(lhs, result_type); rhs = ensureType(rhs, result_type); if (!lhs || !rhs) { auto expression = !lhs ? e->getLeft() : e->getRight(); error(expression, "cannot cast expression of type", getSourceType(original_lhs_type), "to type", getSourceType(original_rhs_type)); } if (result_type->isIntegerTy()) { switch (e->getType()) { #define RETURN_INT_BINOP(NAME, OP) \ case BinaryExpression::NAME: \ return builder_.CreateBinOp(llvm::Instruction::OP, lhs, rhs); IR_INT_BINARY_OPS(RETURN_INT_BINOP) #undef RETURN_INT_BINOP #define RETURN_INT_CMPOP(NAME, OP) \ case BinaryExpression::NAME: \ return builder_.CreateICmp(llvm::CmpInst::ICMP_##OP, lhs, rhs); IR_INT_CMP_OPS(RETURN_INT_CMPOP) #undef RETURN_INT_CMPOP default: error(e, "this operation cannot be applied to integers"); } return nullptr; } if (result_type->isFloatingPointTy()) { switch (e->getType()) { #define RETURN_FLOAT_BINOP(NAME, OP) \ case BinaryExpression::NAME: \ return builder_.CreateBinOp(llvm::Instruction::OP, lhs, rhs); IR_FLOAT_BINARY_OPS(RETURN_FLOAT_BINOP) #undef RETURN_FLOAT_BINOP #define RETURN_FLOAT_CMPOP(NAME, OP) \ case BinaryExpression::NAME: \ return builder_.CreateFCmp(llvm::CmpInst::FCMP_##OP, lhs, rhs); IR_FLOAT_CMP_OPS(RETURN_FLOAT_CMPOP) #undef RETURN_FLOAT_CMPOP default: error(e, "this operation cannot be applied to floats"); } return nullptr; } UNREACHABLE("Unimplemented operand conversion"); } llvm::Value* IRGenerator::visitAtomicExpression(const AtomicExpression* e) { switch (e->getType()) { case AtomicExpression::INTEGER: return builder_.getInt32(e->getIntValue()); case AtomicExpression::FLOAT: return llvm::ConstantFP::get(builder_.getDoubleTy(), e->getFloatValue()); case AtomicExpression::IDENTIFIER: { auto var = var_scopes_.lookup(e->getIdentifierValue().getName()); if (!var) { error(&e->getIdentifierValue(), "undefined variable", e->getIdentifierValue().getName()); } assert(llvm::isa(var)); return builder_.CreateLoad(var); } default: UNREACHABLE("Unhandled AtomicExpression type"); } } llvm::Value* IRGenerator::visitFunctionCallExpression(const FunctionCallExpression* ast_f) { llvm::Function* f = module_->getFunction(ast_f->getFunctionName()); if (!f) { error(ast_f, "call to undefined function", ast_f->getFunctionName()); } std::vector call_args; auto ir_arg = f->arg_begin(); for (const Expression* ast_arg : ast_f->args()) { auto arg = visit(ast_arg); auto original_arg_type = arg->getType(); arg = ensureType(arg, ir_arg->getType()); if (!arg) { error(ast_arg, "cannot pass expression of type", getSourceType(original_arg_type), "as argument of type", getSourceType(ir_arg->getType()), "in call to", ast_f->getFunctionName()); } call_args.push_back(arg); ++ir_arg; } assert(ir_arg == f->arg_end()); return builder_.CreateCall(f, call_args); } llvm::Type* IRGenerator::getIRBaseType(VarType::BaseType type) { switch (type) { #define RETURN_IR_TYPE(NAME, IR_TYPE, _1, _2, _3, _4) \ case VarType::NAME: \ return builder_.get##IR_TYPE##Ty(); BUILTIN_TYPES(RETURN_IR_TYPE) #undef RETURN_IR_TYPE default: UNREACHABLE("Unhandled VarType::BaseType on getIRBaseType"); } } llvm::Value* IRGenerator::ensureType(llvm::Value* value, llvm::Type* type) { if (value->getType()->isPointerTy() != type->isPointerTy()) { // Cannot cast pointer to int or viceversa. return nullptr; } if (value->getType()->isPointerTy()) { // Pointers are not castable. return value->getType() == type ? value : nullptr; } if (value->getType()->isIntegerTy() && type->isIntegerTy()) { return builder_.CreateSExtOrTrunc(value, type); } if (value->getType()->isFloatingPointTy() && type->isIntegerTy()) { return builder_.CreateFPToSI(value, type); } if (value->getType()->isIntegerTy() && type->isFloatingPointTy()) { return builder_.CreateSIToFP(value, type); } if (value->getType()->isFloatingPointTy() && type->isFloatingPointTy()) { return builder_.CreateFPCast(value, type); } UNREACHABLE("Unhandled IR type conversion"); } llvm::Type* ResultTypeCalculator::visitBinaryExpression(const BinaryExpression* e) { llvm::Type* ltype = visit(e->getLeft()); llvm::Type* rtype = visit(e->getRight()); // Void should not be here at all. if (ltype->isVoidTy() || rtype->isVoidTy()) { error(e, "cannot operate on void"); } // Same type, job done. if (ltype == rtype) return ltype; // Pointers are not castable. if (ltype->isPointerTy() != rtype->isPointerTy()) { error(e, "cannot cast pointer to int"); } if (ltype->isPointerTy() && rtype->isPointerTy()) { // implied: different pointer types. error(e, "cannot cast between pointer types"); } // Double (floating point) always wins. if (ltype->isFloatingPointTy() || rtype->isFloatingPointTy()) { return codegen_->builder_.getDoubleTy(); } // Integers always upcast. if (ltype->isIntegerTy() && rtype->isIntegerTy()) { int lsize = ltype->getPrimitiveSizeInBits(); int rsize = rtype->getPrimitiveSizeInBits(); return lsize > rsize ? ltype : rtype; } UNREACHABLE("Unhandled BinaryExpression type"); } llvm::Type* ResultTypeCalculator::visitAtomicExpression(const AtomicExpression* e) { switch (e->getType()) { case AtomicExpression::INTEGER: return codegen_->builder_.getInt32Ty(); case AtomicExpression::FLOAT: return codegen_->builder_.getDoubleTy(); case AtomicExpression::IDENTIFIER: { auto var = codegen_->var_scopes_.lookup(e->getIdentifierValue().getName()); assert(var); return var->getType()->getPointerElementType(); } default: UNREACHABLE("Unhandled AtomicExpression type"); } } llvm::Type* ResultTypeCalculator::visitFunctionCallExpression(const FunctionCallExpression* e) { auto f = codegen_->module_->getFunction(e->getFunctionName()); assert(f); return f->getReturnType(); } namespace monicelli { std::unique_ptr generateIR(llvm::LLVMContext& context, Module* ast) { IRGenerator codegen{context, ast->getSourceFilename()}; codegen.visit(ast); return codegen.releaseModule(); } void runFunctionOptimizer(llvm::Module* module) { llvm::legacy::FunctionPassManager pass_manager{module}; pass_manager.add(llvm::createInstructionCombiningPass()); pass_manager.add(llvm::createReassociatePass()); pass_manager.add(llvm::createGVNPass()); pass_manager.add(llvm::createCFGSimplificationPass()); pass_manager.add(llvm::createDeadCodeEliminationPass()); pass_manager.add(llvm::createPromoteMemoryToRegisterPass()); pass_manager.doInitialization(); for (llvm::Function& f : module->functions()) { pass_manager.run(f); } } void printIR(std::ostream& stream, llvm::Module* module) { llvm::raw_os_ostream llvm_stream{stream}; module->print(llvm_stream, nullptr); } } // namespace monicelli