Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

467 lines (382 sloc) 16.705 kb
#include "llvm.hpp"
#include "message.hpp"
#include "vm/object_utils.hpp"
#include "builtin/compiledmethod.hpp"
#include "builtin/contexts.hpp"
#include "builtin/iseq.hpp"
#include "builtin/task.hpp"
#include "builtin/tuple.hpp"
#include "builtin/symbol.hpp"
#include "builtin/string.hpp"
#include <llvm/Module.h>
#include <llvm/DerivedTypes.h>
#include <llvm/Constants.h>
#include <llvm/GlobalVariable.h>
#include <llvm/Function.h>
#include <llvm/CallingConv.h>
#include <llvm/BasicBlock.h>
#include <llvm/Instructions.h>
#include <llvm/InlineAsm.h>
#include <llvm/Support/MathExtras.h>
#include <llvm/Pass.h>
#include <llvm/PassManager.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/TypeSymbolTable.h>
#include "llvm/Analysis/Verifier.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/ModuleProvider.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include <llvm/Support/CommandLine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/Transforms/Utils/Cloning.h>
#include <llvm/Analysis/Verifier.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <fstream>
namespace rubinius {
using namespace llvm;
static llvm::Module* operations = NULL;
static llvm::ExistingModuleProvider* mp = NULL;
static llvm::ExecutionEngine* engine = NULL;
// static llvm::Function* puts = NULL;
llvm::Module* VM::llvm_module() {
return operations;
/* Clean up the memory in the statics.
* TODO remove the statics entirely. */
void VM::llvm_cleanup() {
/* We can't also delete mp and operations, since deleting engine
* cascade deletes mp and operations internally. */
delete engine;
operations = NULL;
mp = NULL;
engine = NULL;
void VMLLVMMethod::init(const char* path) {
llvm::ExceptionHandling = true;
llvm::UnwindTablesMandatory = true;
std::string error;
if(!operations) {
if(MemoryBuffer* buffer = MemoryBuffer::getFile(path, &error)) {
operations = ParseBitcodeFile(buffer, &error);
delete buffer;
} else {
operations = NULL;
throw std::runtime_error(std::string("Unable to open LLVM operations file") + error);
mp = new ExistingModuleProvider(operations);
engine = ExecutionEngine::create(mp);
#if 0
llvm::PointerType* PointerTy_5 = llvm::PointerType::get(llvm::IntegerType::get(8), 0);
std::vector<const Type*>FuncTy_8_args;
FunctionType* FuncTy_8 = FunctionType::get(
/*Result=*/ llvm::IntegerType::get(32),
/*Params=*/ FuncTy_8_args,
/*isVarArg=*/ false);
puts = Function::Create(
/*Type=*/ FuncTy_8,
/*Linkage=*/ GlobalValue::ExternalLinkage,
/*Name=*/ "puts", operations); // (external, no body)
static CallInst* call_function(std::string name, Value* task,
Value* js, BasicBlock* block) {
Function* func = operations->getFunction(name);
if(!func) {
std::string str = std::string("Unable to find: ");
str += name;
throw std::runtime_error(str);
std::vector<Value*> args(0);
return CallInst::Create(func, args.begin(), args.end(), "", block);
CallInst* VMLLVMMethod::call_operation(Opcode* op, Value* task,
Value* js, BasicBlock* block) {
const char* name = InstructionSequence::get_instruction_name(op->op);
Function* func = operations->getFunction(std::string(name));
if(!func) {
std::string str = std::string("Unable to find: ");
str += name;
throw std::runtime_error(str);
std::vector<Value*> args(0);
switch(op->args) {
case 2:
args.push_back(ConstantInt::get(Type::Int32Ty, op->arg1));
args.push_back(ConstantInt::get(Type::Int32Ty, op->arg2));
case 1:
args.push_back(ConstantInt::get(Type::Int32Ty, op->arg1));
return CallInst::Create(func, args.begin(), args.end(), "", block);
#if 0
void show(const char* data, BasicBlock* block) {
llvm::ArrayType* ArrayTy_0 = llvm::ArrayType::get(llvm::IntegerType::get(8), strlen(data) + 1);
GlobalVariable* glob = new GlobalVariable(ArrayTy_0,
true, GlobalValue::InternalLinkage, 0, data, operations);
Constant* str = ConstantArray::get(data, true);
std::vector<Constant*> const_ptr_7_indices;
Constant* const_int32_8 = Constant::getNullValue(llvm::IntegerType::get(32));
Constant* const_ptr_7 = ConstantExpr::getGetElementPtr(glob, &const_ptr_7_indices[0], const_ptr_7_indices.size() );
std::vector<Value*> args(0);
CallInst::Create(puts, args.begin(), args.end(), "tmp", block);
#if 0
/* Copied originally from opt */
static void add_passes(PassManager& PM) {
PM.add(createVerifierPass()); // Verify that input is correct
PM.add(createRaiseAllocationsPass()); // call %malloc -> malloc inst
PM.add(createCFGSimplificationPass()); // Clean up disgusting code
PM.add(createPromoteMemoryToRegisterPass());// Kill useless allocas
PM.add(createGlobalOptimizerPass()); // Optimize out global vars
PM.add(createGlobalDCEPass()); // Remove unused fns and globs
PM.add(createIPConstantPropagationPass());// IP Constant Propagation
PM.add(createDeadArgEliminationPass()); // Dead argument elimination
PM.add(createInstructionCombiningPass()); // Clean up after IPCP & DAE
PM.add(createCFGSimplificationPass()); // Clean up after IPCP & DAE
PM.add(createPruneEHPass()); // Remove dead EH info
PM.add(createFunctionInliningPass(INLINE_THRESHOLD)); // Inline small functions
PM.add(createArgumentPromotionPass()); // Scalarize uninlined fn args
PM.add(createTailDuplicationPass()); // Simplify cfg by copying code
PM.add(createSimplifyLibCallsPass()); // Library Call Optimizations
PM.add(createInstructionCombiningPass()); // Cleanup for scalarrepl.
PM.add(createJumpThreadingPass()); // Thread jumps.
PM.add(createCFGSimplificationPass()); // Merge & remove BBs
PM.add(createScalarReplAggregatesPass()); // Break up aggregate allocas
PM.add(createInstructionCombiningPass()); // Combine silly seq's
PM.add(createCondPropagationPass()); // Propagate conditionals
PM.add(createTailCallEliminationPass()); // Eliminate tail calls
PM.add(createCFGSimplificationPass()); // Merge & remove BBs
PM.add(createReassociatePass()); // Reassociate expressions
PM.add(createLICMPass()); // Hoist loop invariants
PM.add(createLoopUnswitchPass()); // Unswitch loops.
PM.add(createLoopIndexSplitPass()); // Index split loops.
// FIXME : Removing instcombine causes nestedloop regression.
PM.add(createIndVarSimplifyPass()); // Canonicalize indvars
PM.add(createLoopDeletionPass()); // Delete dead loops
PM.add(createLoopUnrollPass()); // Unroll small loops
PM.add(createInstructionCombiningPass()); // Clean up after the unroller
PM.add(createGVNPass()); // Remove redundancies
PM.add(createMemCpyOptPass()); // Remove memcpy / form memset
PM.add(createSCCPPass()); // Constant prop with SCCP
// Run instcombine after redundancy elimination to exploit opportunities
// opened up by them.
PM.add(createCondPropagationPass()); // Propagate conditionals
PM.add(createDeadStoreEliminationPass()); // Delete dead stores
PM.add(createAggressiveDCEPass()); // Delete dead instructions
PM.add(createCFGSimplificationPass()); // Merge & remove BBs
PM.add(createStripDeadPrototypesPass()); // Get rid of dead prototypes
PM.add(createConstantMergePass()); // Merge dup global constants
static Function* create_function(const char* name) {
std::stringstream stream;
stream << "_XJIT_" << strlen(name) << name << "_" << operations->size();
const Type* task_type = operations->getTypeByName(std::string("struct.rubinius::Task"));
std::string js("struct.jit_state");
const Type* obj_type = operations->getTypeByName(js);
Type* stack_type = PointerType::get(obj_type, 0);
Function* func = cast<Function>(
operations->getOrInsertFunction(stream.str(), Type::VoidTy,
PointerType::getUnqual(task_type), stack_type,
PointerType::getUnqual(Type::Int32Ty), (void *)NULL));
return func;
static BasicBlock** construct_blocks(Function* func,
std::vector<Opcode*> ops, Value* next_pos) {
BasicBlock* top = BasicBlock::Create("top", func);
size_t num_blocks = ops[ops.size() - 1]->block + 1;
/* if we have to bail, set next_pos to -1, so the caller knows. */
BasicBlock* def = BasicBlock::Create("error", func);
new StoreInst(ConstantInt::get(Type::Int32Ty, (uint64_t)-1), next_pos, def);
/* load in the current value of next_pos, and branch to it. */
Value* pos = new LoadInst(next_pos, "get_pos", top);
SwitchInst* sw = SwitchInst::Create(pos, def, num_blocks, top);
/* Create all the blocks and initialize them in the switch */
BasicBlock** blocks = (BasicBlock**)calloc(num_blocks, sizeof(BasicBlock*));
for(size_t i = 0; i < num_blocks; i++) {
std::stringstream stream;
stream << "block" << i;
blocks[i] = BasicBlock::Create(stream.str(), func);
sw->addCase(ConstantInt::get(Type::Int32Ty, i), blocks[i]);
return blocks;
void VMLLVMMethod::compile(STATE) {
const char* name = original->name()->c_str(state);
Function* func = create_function(name);
Function::arg_iterator args = func->arg_begin();
Value* task = args++;
Value* js = args++;
Value* next_pos = args++;
std::vector<Opcode*> ops = create_opcodes();
BasicBlock** blocks = construct_blocks(func, ops, next_pos);
BasicBlock* last = NULL;
BasicBlock* cur = NULL;
int cur_block = -1;
/* We collect up all the calls to the operation functions so we can
* inline them later. */
std::vector<CallInst*> calls(0);
for(std::vector<Opcode*>::iterator i = ops.begin(); i != ops.end(); i++) {
Opcode* op = *i;
/* Having this be conditional lets us change +cur+ for the rest of a block.
* This is used to implement branch's 2nd position without allocating
* continuation style slots. */
if(cur_block < 0 || (int)op->block != cur_block) {
cur = blocks[op->block];
cur_block = op->block;
if(op->start_block) {
/* Setup a branch from the last block to this one if it doesn't have a
* terminator on the end. */
if(!isa<TerminatorInst>(last->back())) {
BranchInst::Create(cur, last);
last = cur;
// show(InstructionSequence::get_instruction_name(op->op), cur);
CallInst* call = NULL;
switch(op->op) {
case InstructionSequence::insn_goto:
BranchInst::Create(blocks[ops[op->arg1]->block], cur);
case InstructionSequence::insn_goto_if_true:
case InstructionSequence::insn_goto_if_false:
case InstructionSequence::insn_goto_if_defined: {
/* Remove the argument from being used, since we used it directly
* here. */
std::string func_name;
switch(op->op) {
case InstructionSequence::insn_goto_if_true:
func_name = std::string("jit_goto_if_true");
case InstructionSequence::insn_goto_if_false:
func_name = std::string("jit_goto_if_false");
case InstructionSequence::insn_goto_if_defined:
func_name = std::string("jit_goto_if_defined");
call = call_function(func_name, task, js, cur);
ICmpInst* cmp = new ICmpInst(ICmpInst::ICMP_EQ, call,
ConstantInt::get(Type::Int8Ty, 1), "cmp", cur);
BasicBlock* bb = BasicBlock::Create("span", func);
BranchInst::Create(blocks[ops[op->arg1]->block], bb, cmp, cur);
cur = bb;
case InstructionSequence::insn_halt:
new StoreInst(ConstantInt::get(Type::Int32Ty, (uint64_t)-1), next_pos, cur);
call = call_operation(op, task, js, cur);
if(op->is_send()) {
// Set up a comparison for the upcoming return value from the send
ICmpInst* cmp = new ICmpInst(ICmpInst::ICMP_EQ, call,
ConstantInt::get(Type::Int8Ty, 1), "cmp", cur);
// If the send returns true, then we will reset the execution state.
// Specifically, it will cause VMMethod::resume to return
BasicBlock* send = BasicBlock::Create("send", func);
// If the send returns false, then we will 'stay'
BasicBlock* stay = blocks[cur_block + 1];
// Perform the comparison and go to stay or send as above
// 'cur' is where the branch instruction is stored
BranchInst::Create(send, stay, cmp, cur);
// Add 'store' and 'return' instructions to the 'send' basic block
new StoreInst(ConstantInt::get(Type::Int32Ty, op->block + 1), next_pos, send);
// Make this flow look sane in the LLVM debugging output
// The current block is now the one after 'send'
cur = stay;
} else if(op->op == InstructionSequence::insn_ret) {
/* -2 signals that we're done for reals now. */
new StoreInst(ConstantInt::get(Type::Int32Ty, (uint64_t)-2), next_pos, cur);
} else if(op->is_terminator()) {
/* Setup where to return to when we're re-entered. */
new StoreInst(ConstantInt::get(Type::Int32Ty, op->block + 1), next_pos, cur);
/* Now, inline it! We don't hinge on the optimizer to do this anymore. */
if(call) {
/* stick a return on the end if there isn't already one. */
if(!isa<ReturnInst>(cur->back())) {
/* Now, manually inline all those calls to operation functions. */
for(std::vector<CallInst*>::iterator i = calls.begin(); i != calls.end(); i++) {
CallInst* ci = *i;
FunctionPassManager fpm(new ExistingModuleProvider(func->getParent()));
fpm.add(new TargetData(func->getParent()));
function = func;
c_func = (CompiledFunction)engine->getPointerToFunction(func);
ExecuteStatus VMLLVMMethod::uncompiled_execute(STATE,
Task* task, Message& msg) {
CompiledMethod* cm = as<CompiledMethod>(msg.method);
VMLLVMMethod* real = new VMLLVMMethod(state, cm);
cm->backend_method_ = real;
return VMMethod::execute(state, task, msg);
void VMLLVMMethod::resume(Task* task, MethodContext* ctx) {
c_func(task, &ctx->js, &ctx->ip);
if(ctx->ip == -1) {
throw Task::Halt("Task halted");
Jump to Line
Something went wrong with that request. Please try again.