Initial checkin of virtual machine implementation.

We can now run very trivial test cases


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@4894 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Chris Lattner 2002-12-03 22:48:59 +00:00
parent f815aebd20
commit 6710121b6b
4 changed files with 199 additions and 54 deletions

74
tools/jello/Emitter.cpp Normal file
View File

@ -0,0 +1,74 @@
//===-- Emitter.cpp - Write machine code to executable memory -------------===//
//
// This file defines a MachineCodeEmitter object that is used by Jello to write
// machine code to memory and remember where relocatable values lie.
//
//===----------------------------------------------------------------------===//
#include "VM.h"
#include "llvm/CodeGen/MachineCodeEmitter.h"
#include "llvm/CodeGen/MachineFunction.h"
namespace {
class Emitter : public MachineCodeEmitter {
VM &TheVM;
unsigned char *CurBlock;
unsigned char *CurByte;
public:
Emitter(VM &vm) : TheVM(vm) {}
virtual void startFunction(MachineFunction &F);
virtual void finishFunction(MachineFunction &F);
virtual void startBasicBlock(MachineBasicBlock &BB) {}
virtual void emitByte(unsigned char B);
virtual void emitPCRelativeDisp(Value *V);
};
}
MachineCodeEmitter *VM::createEmitter(VM &V) {
return new Emitter(V);
}
#define _POSIX_MAPPED_FILES
#include <unistd.h>
#include <sys/mman.h>
static void *getMemory() {
return mmap(0, 4096*2, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
}
void Emitter::startFunction(MachineFunction &F) {
CurBlock = (unsigned char *)getMemory();
CurByte = CurBlock; // Start writing at the beginning of the fn.
}
#include <iostream>
#include "llvm/Function.h"
void Emitter::finishFunction(MachineFunction &F) {
std::cerr << "Finished Code Generation of Function: "
<< F.getFunction()->getName() << ": " << CurByte-CurBlock
<< " bytes of text\n";
TheVM.addGlobalMapping(F.getFunction(), CurBlock);
}
void Emitter::emitByte(unsigned char B) {
*CurByte++ = B; // Write the byte to memory
}
// emitPCRelativeDisp - Just output a displacement that will cause a reference
// to the zero page, which will cause a seg-fault, causing things to get
// resolved on demand. Keep track of these markers.
//
void Emitter::emitPCRelativeDisp(Value *V) {
unsigned ZeroAddr = -(unsigned)CurByte; // Calculate displacement to null
*(unsigned*)CurByte = ZeroAddr; // 4 byte offset
CurByte += 4;
}

65
tools/jello/VM.cpp Normal file
View File

@ -0,0 +1,65 @@
//===-- jello.cpp - LLVM Just in Time Compiler ----------------------------===//
//
// This tool implements a just-in-time compiler for LLVM, allowing direct
// execution of LLVM bytecode in an efficient manner.
//
//===----------------------------------------------------------------------===//
#include "VM.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/CodeGen/MachineCodeEmitter.h"
#include "llvm/Function.h"
#include <iostream>
VM::~VM() {
delete MCE;
}
/// setupPassManager - Initialize the VM PassManager object with all of the
/// passes needed for the target to generate code.
///
void VM::setupPassManager() {
// Compile LLVM Code down to machine code in the intermediate representation
if (TM.addPassesToJITCompile(PM)) {
std::cerr << ExeName << ": target '" << TM.getName()
<< "' doesn't support JIT compilation!\n";
abort();
}
// Turn the machine code intermediate representation into bytes in memory that
// may be executed.
//
if (TM.addPassesToEmitMachineCode(PM, *MCE)) {
std::cerr << ExeName << ": target '" << TM.getName()
<< "' doesn't support machine code emission!\n";
abort();
}
}
int VM::run(Function *F) {
int(*PF)() = (int(*)())getPointerToFunction(F);
assert(PF != 0 && "Null pointer to function?");
return PF();
}
/// getPointerToFunction - This method is used to get the address of the
/// specified function, compiling it if neccesary.
///
void *VM::getPointerToFunction(Function *F) {
void *&Addr = GlobalAddress[F]; // Function already code gen'd
if (Addr) return Addr;
if (F->isExternal()) {
assert(0 && "VM::getPointerToFunction: Doesn't handle external fn's yet!");
}
// JIT all of the functions in the module. Eventually this will JIT functions
// on demand. This has the effect of populating all of the non-external
// functions into the GlobalAddress table.
PM.run(M);
assert(Addr && "Code generation didn't add function to GlobalAddress table!");
return Addr;
}

50
tools/jello/VM.h Normal file
View File

@ -0,0 +1,50 @@
//===-- VM.h - Definitions for Virtual Machine ------------------*- C++ -*-===//
//
// This file defines the top level Virtual Machine data structure.
//
//===----------------------------------------------------------------------===//
#ifndef VM_H
#define VM_H
#include "llvm/PassManager.h"
#include <string>
#include <map>
class TargetMachine;
class Function;
class GlobalValue;
class MachineCodeEmitter;
class VM {
std::string ExeName;
Module &M; // The LLVM program we are running
TargetMachine &TM; // The current target we are compiling to
PassManager PM; // Passes to compile a function
MachineCodeEmitter *MCE; // MCE object
std::map<const GlobalValue*, void *> GlobalAddress;
public:
VM(const std::string &name, Module &m, TargetMachine &tm)
: ExeName(name), M(m), TM(tm) {
MCE = createEmitter(*this); // Initialize MCE
setupPassManager();
}
~VM();
int run(Function *F);
void addGlobalMapping(const Function *F, void *Addr) {
void *&CurVal = GlobalAddress[(const GlobalValue*)F];
assert(CurVal == 0 && "GlobalMapping already established!");
CurVal = Addr;
}
private:
static MachineCodeEmitter *createEmitter(VM &V);
void setupPassManager();
void *getPointerToFunction(Function *F);
};
#endif

View File

@ -3,42 +3,14 @@
// This tool implements a just-in-time compiler for LLVM, allowing direct
// execution of LLVM bytecode in an efficient manner.
//
// FIXME: This code will get more object oriented as we get the call back
// intercept stuff implemented.
//
//===----------------------------------------------------------------------===//
#include "llvm/Module.h"
#include "llvm/PassManager.h"
#include "llvm/Bytecode/Reader.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetMachineImpls.h"
#include "Support/CommandLine.h"
#include "Support/Statistic.h"
#include "llvm/CodeGen/MachineCodeEmitter.h"
#include "llvm/CodeGen/MachineFunction.h"
struct JelloMachineCodeEmitter : public MachineCodeEmitter {
void startFunction(MachineFunction &F) {
std::cout << "\n**** Writing machine code for function: "
<< F.getFunction()->getName() << "\n";
}
void finishFunction(MachineFunction &F) {
std::cout << "\n";
}
void startBasicBlock(MachineBasicBlock &BB) {
std::cout << "\n--- Basic Block: " << BB.getBasicBlock()->getName() << "\n";
}
void emitByte(unsigned char B) {
std::cout << "0x" << std::hex << (unsigned int)B << std::dec << " ";
}
void emitPCRelativeDisp(Value *V) {
std::cout << "<" << V->getName() << ": 0x00 0x00 0x00 0x00> ";
}
};
#include "VM.h"
namespace {
cl::opt<std::string>
@ -57,10 +29,8 @@ int main(int argc, char **argv) {
// Allocate a target... in the future this will be controllable on the
// command line.
std::auto_ptr<TargetMachine> target(allocateX86TargetMachine());
assert(target.get() && "Could not allocate target machine!");
TargetMachine &Target = *target.get();
std::auto_ptr<TargetMachine> Target(allocateX86TargetMachine());
assert(Target.get() && "Could not allocate target machine!");
// Parse the input bytecode file...
std::string ErrorMsg;
@ -71,29 +41,15 @@ int main(int argc, char **argv) {
return 1;
}
PassManager Passes;
// Create the virtual machine object...
VM TheVM(argv[0], *M.get(), *Target.get());
// Compile LLVM Code down to machine code in the intermediate representation
if (Target.addPassesToJITCompile(Passes)) {
std::cerr << argv[0] << ": target '" << Target.getName()
<< "' doesn't support JIT compilation!\n";
Function *F = M.get()->getNamedFunction(MainFunction);
if (F == 0) {
std::cerr << "Could not find function '" << MainFunction <<"' in module!\n";
return 1;
}
// Turn the machine code intermediate representation into bytes in memory that
// may be executed.
//
JelloMachineCodeEmitter MCE;
if (Target.addPassesToEmitMachineCode(Passes, MCE)) {
std::cerr << argv[0] << ": target '" << Target.getName()
<< "' doesn't support machine code emission!\n";
return 1;
}
// JIT all of the methods in the module. Eventually this will JIT functions
// on demand.
Passes.run(*M.get());
return 0;
// Run the virtual machine...
return TheVM.run(F);
}