blob: 750e4ca5ae25d3e30336914bb79b87c49ee837d8 [file] [log] [blame]
//===-- Compiler.cpp - Java bytecode compiler -------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains Java bytecode to LLVM bytecode compiler.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "javacompiler"
#include <llvm/Java/Compiler.h>
#include "BasicBlockBuilder.h"
#include "Locals.h"
#include "OperandStack.h"
#include "Resolver.h"
#include <llvm/Java/Bytecode.h>
#include <llvm/Java/BytecodeParser.h>
#include <llvm/Java/ClassFile.h>
#include <llvm/Constants.h>
#include <llvm/DerivedTypes.h>
#include <llvm/Value.h>
#include <llvm/Type.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/SetVector.h>
#include <llvm/ADT/StringExtras.h>
#include <llvm/Support/CFG.h>
#include <llvm/Support/Debug.h>
#include <list>
#include <vector>
using namespace llvm;
using namespace llvm::Java;
namespace llvm { namespace Java { namespace {
const std::string TMP("tmp");
llvm::Constant* ONE = ConstantSInt::get(Type::IntTy, 1);
llvm::Constant* ZERO = ConstantSInt::get(Type::IntTy, 0);
llvm::Constant* MINUS_ONE = ConstantSInt::get(Type::IntTy, -1);
llvm::Constant* INT_SHIFT_MASK = ConstantUInt::get(Type::UByteTy, 0x1f);
llvm::Constant* LONG_SHIFT_MASK = ConstantUInt::get(Type::UByteTy, 0x3f);
class Compiler : public BytecodeParser<Compiler> {
Module* module_;
std::auto_ptr<Resolver> resolver_;
GlobalVariable* JNIEnvPtr_;
const VMClass* class_;
std::auto_ptr<BasicBlockBuilder> bbBuilder_;
std::list<BasicBlock*> bbWorkList_;
typedef std::map<BasicBlock*, unsigned> OpStackDepthMap;
OpStackDepthMap opStackDepthMap_;
typedef std::map<std::string, GlobalVariable*> StringMap;
StringMap stringMap_;
BasicBlock* currentBB_;
Locals locals_;
OperandStack opStack_;
Function *getClassRecord_, *setClassRecord_, *throw_, *isInstanceOf_,
*memcpy_, *memset_;
std::vector<llvm::Constant*> classInitializers_;
SetVector<const VMMethod*> toCompileMethods_;
public:
Compiler(Module* m)
: module_(m),
resolver_(new Resolver(module_)),
locals_(resolver_.get(), 0),
opStack_(resolver_.get(), 0) {
Type* JNIEnvTy = OpaqueType::get();
module_->addTypeName("JNIEnv", JNIEnvTy);
JNIEnvPtr_ = new GlobalVariable(JNIEnvTy,
true,
GlobalVariable::ExternalLinkage,
NULL,
"llvm_java_JNIEnv",
module_);
const Type* classRecordPtrType = resolver_->getClassRecordPtrType();
const Type* objectBaseType = resolver_->getObjectBaseType();
getClassRecord_ = module_->getOrInsertFunction(
"llvm_java_get_class_record", classRecordPtrType,
objectBaseType, NULL);
setClassRecord_ = module_->getOrInsertFunction(
"llvm_java_set_class_record", Type::VoidTy,
objectBaseType, classRecordPtrType, NULL);
throw_ = module_->getOrInsertFunction(
"llvm_java_throw", Type::IntTy,
objectBaseType, NULL);
isInstanceOf_ = module_->getOrInsertFunction(
"llvm_java_is_instance_of", Type::IntTy,
objectBaseType, classRecordPtrType, NULL);
memcpy_ = module_->getOrInsertFunction(
"llvm.memcpy", Type::VoidTy,
PointerType::get(Type::SByteTy),
PointerType::get(Type::SByteTy),
Type::ULongTy, Type::UIntTy, NULL);
memset_ = module_->getOrInsertFunction(
"llvm.memset", Type::VoidTy,
PointerType::get(Type::SByteTy),
Type::UByteTy, Type::ULongTy, Type::UIntTy, NULL);
}
private:
void push(Value* value) {
opStack_.push(value, currentBB_);
}
Value* pop(const Type* type) {
return opStack_.pop(type, currentBB_);
}
/// Schedule a method for compilation. Returns true if this is the
/// first time this function was scheduled.
bool scheduleMethod(const VMMethod* method) {
if (toCompileMethods_.insert(method)) {
DEBUG(std::cerr << "Scheduling function: "
<< method->getFunction()->getName() << " for compilation\n");
return true;
}
return false;
}
template <typename InsertionPointTy>
void initializeString(Value* globalString,
const std::string& str,
InsertionPointTy* ip) {
// Create a new byte[] object and initialize it with the
// contents of this string constant.
Value* count = ConstantUInt::get(Type::UIntTy, str.size());
Value* arrayRef = allocateArray(resolver_->getClass("[B"), count, ip);
// Copy string data.
std::vector<Value*> indices;
indices.reserve(3);
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
indices.push_back(ConstantUInt::get(Type::UIntTy, 2));
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
Value* arrayData = new GetElementPtrInst(arrayRef, indices, TMP, ip);
llvm::Constant* init = ConstantArray::get(str);
GlobalVariable* chars = new GlobalVariable(
init->getType(),
true,
GlobalVariable::InternalLinkage,
init,
str + ".str",
module_);
std::vector<Value*> params;
params.reserve(4);
params.clear();
params.push_back(arrayData);
params.push_back(ConstantExpr::getPtrPtrFromArrayPtr(chars));
params.push_back(new CastInst(count, Type::ULongTy, TMP, ip));
params.push_back(ConstantUInt::get(Type::UIntTy, 0));
new CallInst(memcpy_, params, "", ip);
// Get class information for java/lang/String.
const VMClass* clazz = resolver_->getClass("java/lang/String");
emitClassInitializers(clazz);
// Install the class record.
Value* objBase =
new CastInst(globalString, resolver_->getObjectBaseType(), TMP, ip);
const Type* classRecordPtrType = resolver_->getClassRecordPtrType();
Value* classRecord =
new CastInst(clazz->getClassRecord(), classRecordPtrType, TMP, ip);
new CallInst(setClassRecord_, objBase, classRecord, "", ip);
// Initialize it: call java/lang/String/<init>(byte[],int)
const VMMethod* method = clazz->getMethod("<init>([BI)V");
params.reserve(3);
params.clear();
params.push_back(objBase);
params.push_back(
new CastInst(arrayRef, resolver_->getObjectBaseType(), TMP, ip));
params.push_back(ConstantSInt::get(Type::IntTy, 0));
new CallInst(method->getFunction(), params, "", ip);
}
/// Returns the type of the Java string descriptor for JNI.
const Type* getJNIType(const std::string& descr) {
unsigned i = 0;
return getJNITypeHelper(descr, i);
}
const Type* getJNITypeHelper(const std::string& descr, unsigned& i) {
assert(i < descr.size());
switch (descr[i++]) {
case 'B': return Type::SByteTy;
case 'C': return Type::UShortTy;
case 'D': return Type::DoubleTy;
case 'F': return Type::FloatTy;
case 'I': return Type::IntTy;
case 'J': return Type::LongTy;
case 'S': return Type::ShortTy;
case 'Z': return Type::BoolTy;
case 'V': return Type::VoidTy;
// Both array and object types are pointers to llvm_object_base
case 'L': {
unsigned e = descr.find(';', i);
i = e + 1;
return resolver_->getObjectBaseType();
}
case '[':
// Skip '['s.
if (descr[i] == '[')
do { ++i; } while (descr[i] == '[');
// Consume the element type
getJNITypeHelper(descr, i);
return resolver_->getObjectBaseType();
case '(': {
std::vector<const Type*> params;
// JNIEnv*
params.push_back(JNIEnvPtr_->getType());
params.push_back(resolver_->getObjectBaseType());
while (descr[i] != ')')
params.push_back(getJNITypeHelper(descr, i));
return FunctionType::get(getJNITypeHelper(descr, ++i), params, false);
}
// FIXME: Throw something
default: assert(0 && "Cannot parse type descriptor!");
}
return 0; // not reached
}
std::string getMangledString(const std::string& str) {
std::string mangledStr;
for (unsigned i = 0, e = str.size(); i != e; ++i) {
if (str[i] == '/')
mangledStr += '_';
else if (str[i] == '_')
mangledStr += "_1";
else if (str[i] == ';')
mangledStr += "_2";
else if (str[i] == '[')
mangledStr += "_3";
else
mangledStr += str[i];
}
return mangledStr;
}
/// Compiles the passed method only (it does not compile any
/// callers or methods of objects it creates).
void compileMethodOnly(const VMMethod* method) {
class_ = method->getParent();
Function* function = method->getFunction();
if (!function->empty()) {
DEBUG(std::cerr << "Function: " << function->getName()
<< " is already compiled!\n");
return;
}
if (method->isNative()) {
DEBUG(std::cerr << "Adding stub for natively implemented method: "
<< function->getName() << '\n');
const FunctionType* jniFuncTy =
cast<FunctionType>(getJNIType(method->getDescriptor()));
std::string funcName =
"Java_" +
getMangledString(class_->getName()) + '_' +
getMangledString(method->getName());
if (class_->getClassFile()->isNativeMethodOverloaded(*method->getMethod())) {
// We need to add two underscores and a mangled argument signature
funcName += "__";
const std::string descr = method->getDescriptor();
funcName += getMangledString(
std::string(descr.begin() + descr.find('(') + 1,
descr.begin() + descr.find(')')));
}
Function* jniFunction =
module_->getOrInsertFunction(funcName, jniFuncTy);
BasicBlock* bb = new BasicBlock("entry", function);
std::vector<Value*> params;
params.push_back(JNIEnvPtr_);
if (method->isStatic())
params.push_back(
new CastInst(method->getParent()->getClassRecord(),
resolver_->getObjectBaseType(), TMP, bb));
for (Function::arg_iterator A = function->arg_begin(),
E = function->arg_end(); A != E; ++A) {
params.push_back(
new CastInst(A, jniFuncTy->getParamType(params.size()), TMP, bb));
}
Value* result = new CallInst(jniFunction, params, "", bb);
if (result->getType() != Type::VoidTy)
result = new CastInst(result, function->getReturnType(), TMP,bb);
new ReturnInst(result, bb);
return;
}
assert (!method->isAbstract() && "Trying to compile an abstract method!");
// HACK: skip most of the class libraries.
const std::string& funcName = function->getName();
if ((funcName.find("java/") == 0 &&
funcName.find("java/lang/Object") != 0 &&
(funcName.find("java/lang/Throwable") != 0 ||
funcName.find("java/lang/Throwable$StaticData/<cl") == 0) &&
funcName.find("java/lang/Exception") != 0 &&
funcName.find("java/lang/IllegalArgumentException") != 0 &&
funcName.find("java/lang/IllegalStateException") != 0 &&
funcName.find("java/lang/IndexOutOfBoundsException") != 0 &&
funcName.find("java/lang/RuntimeException") != 0 &&
(funcName.find("java/lang/Math") != 0 ||
funcName.find("java/lang/Math/<cl") == 0) &&
funcName.find("java/lang/Number") != 0 &&
funcName.find("java/lang/Byte") != 0 &&
funcName.find("java/lang/Float") != 0 &&
funcName.find("java/lang/Integer") != 0 &&
funcName.find("java/lang/Long") != 0 &&
funcName.find("java/lang/Short") != 0 &&
(funcName.find("java/lang/String") != 0 ||
funcName.find("java/lang/String/<cl") == 0) &&
funcName.find("java/lang/StringBuffer") != 0 &&
(funcName.find("java/lang/System") != 0 ||
funcName.find("java/lang/System/loadLibrary") == 0) &&
funcName.find("java/lang/VMSystem") != 0 &&
(funcName.find("java/util/") != 0 ||
funcName.find("java/util/Locale/<cl") == 0 ||
funcName.find("java/util/ResourceBundle/<cl") == 0 ||
funcName.find("java/util/Calendar/<cl") == 0) ||
funcName.find("java/util/PropertyPermission/<cl") == 0) ||
(funcName.find("gnu/") == 0)) {
DEBUG(std::cerr << "Skipping compilation of method: "
<< funcName << '\n');
return;
}
DEBUG(std::cerr << "Compiling method: " << funcName << '\n');
Java::CodeAttribute* codeAttr = method->getMethod()->getCodeAttribute();
opStackDepthMap_.clear();
bbBuilder_.reset(new BasicBlockBuilder(function, codeAttr));
// Put arguments into locals.
locals_ = Locals(resolver_.get(), codeAttr->getMaxLocals());
unsigned index = 0;
for (Function::arg_iterator a = function->arg_begin(),
ae = function->arg_end(); a != ae; ++a) {
locals_.store(index, a, &function->getEntryBlock());
index += resolver_->isTwoSlotType(a->getType()) ? 2 : 1;
}
BasicBlock* bb0 = bbBuilder_->getBasicBlock(0);
// For bb0 the operand stack is empty and the locals contain the
// arguments to the function.
//
// NOTE: We create an operand stack one size too big because we
// push extra values on the stack to simplify code generation
// (see implementation of ifne).
opStack_ = OperandStack(resolver_.get(), codeAttr->getMaxStack()+2);
opStackDepthMap_.insert(std::make_pair(bb0, 0));
// Insert bb0 in the work list.
bbWorkList_.push_back(bb0);
// Process the work list until we compile the whole function.
while (!bbWorkList_.empty()) {
currentBB_ = bbWorkList_.front();
bbWorkList_.pop_front();
OpStackDepthMap::iterator opStackDepth =
opStackDepthMap_.find(currentBB_);
assert(opStackDepth != opStackDepthMap_.end() &&
"Unknown operand stack depth for basic block in work list!");
opStack_.setDepth(opStackDepth->second);
unsigned start, end;
tie(start, end) = bbBuilder_->getBytecodeIndices(currentBB_);
// Compile this basic block.
parse(codeAttr->getCode(), start, end);
// If this basic block does not have a terminator, it should
// have an unconditional branch to the next basic block
// (fallthrough).
if (!currentBB_->getTerminator())
new BranchInst(bbBuilder_->getBasicBlock(end), currentBB_);
// For each successor of this basic block we can compute its
// entry operand stack depth. We do so, and add it to the work
// list. If a successor already has an entry operand stack and
// locals we assume the computation was correct and do not add
// it to the work list.
for (succ_iterator
SI = succ_begin(currentBB_), SE = succ_end(currentBB_);
SI != SE; ++SI) {
BasicBlock* Succ = *SI;
OpStackDepthMap::iterator succOpStackDepth =
opStackDepthMap_.lower_bound(Succ);
if (succOpStackDepth == opStackDepthMap_.end() ||
succOpStackDepth->first != Succ) {
opStackDepthMap_.insert(succOpStackDepth,
std::make_pair(Succ, opStack_.getDepth()));
bbWorkList_.push_back(Succ);
}
}
}
// Add an unconditional branch from the entry block to bb0.
new BranchInst(bb0, &function->getEntryBlock());
// FIXME: remove empty basic blocks (we have empty basic blocks
// because of our lack of exception support).
for (Function::iterator bb = function->begin(), be = function->end();
bb != be; )
if (bb->empty())
bb = function->getBasicBlockList().erase(bb);
else
++bb;
DEBUG(std::cerr << "Finished compilation of method: "<< funcName << '\n');
// DEBUG(function->dump());
}
/// Emits static initializers for this class if not done already.
void emitClassInitializers(const VMClass* clazz) {
static SetVector<const VMClass*> toInitClasses;
// If this is a primitive class we are done.
if (clazz->isPrimitive())
return;
// If this class is already initialized, we are done.
if (!toInitClasses.insert(clazz))
return;
// If this class has a super class, initialize that first.
if (const VMClass* superClass = clazz->getSuperClass())
emitClassInitializers(superClass);
// If this class is an array, initialize its component class now.
if (const VMClass* componentClass = clazz->getComponentClass())
emitClassInitializers(componentClass);
// Schedule all its dynamically bound non abstract methods for
// compilation.
for (unsigned i = 0, e = clazz->getNumDynamicMethods(); i != e; ++i) {
const VMMethod* method = clazz->getDynamicMethod(i);
if (!method->isAbstract())
scheduleMethod(method);
}
// Schedule all its statically bound non abstract methods for
// compilation.
for (unsigned i = 0, e = clazz->getNumStaticMethods(); i != e; ++i) {
const VMMethod* method = clazz->getStaticMethod(i);
if (!method->isAbstract())
scheduleMethod(method);
}
// If this class has a constant pool (was loaded from a
// classfile), create constant strings for it.
if (const ClassFile* classfile = clazz->getClassFile()) {
Function* stringConstructors = module_->getOrInsertFunction(
clazz->getName() + "<strinit>",
FunctionType::get(Type::VoidTy, std::vector<const Type*>(), false));
Instruction* I =
new ReturnInst(NULL, new BasicBlock("entry", stringConstructors));
for (unsigned i = 0, e = classfile->getNumConstants(); i != e; ++i)
if (ConstantString* s = dynamic_cast<ConstantString*>(classfile->getConstant(i)))
initializeString(clazz->getConstant(i), s->getValue()->str(), I);
// Insert string constructors method in class initializers array.
classInitializers_.push_back(stringConstructors);
// Call its class initialization method if it exists.
if (const VMMethod* method = clazz->getMethod("<clinit>()V"))
classInitializers_.push_back(method->getFunction());
}
}
public:
/// Compiles the specified method given a <class,method>
/// descriptor and the transitive closure of all methods
/// (possibly) called by it.
const VMMethod* compileMethod(const std::string& className,
const std::string& methodDesc) {
// Load the class.
const VMClass* clazz = resolver_->getClass(className);
emitClassInitializers(clazz);
// Find the method.
const VMMethod* method = clazz->getMethod(methodDesc);
// Compile the transitive closure of methods called by this method.
for (unsigned i = 0; i != toCompileMethods_.size(); ++i) {
const VMMethod* m = toCompileMethods_[i];
compileMethodOnly(m);
DEBUG(std::cerr << i+1 << '/' << toCompileMethods_.size()
<< " functions compiled\n");
}
// Null terminate the static initializers array and add the
// global to the module.
Type* classInitializerType = PointerType::get(
FunctionType::get(Type::VoidTy, std::vector<const Type*>(), false));
classInitializers_.push_back(
llvm::Constant::getNullValue(classInitializerType));
ArrayType* classInitializersType =
ArrayType::get(classInitializerType, classInitializers_.size());
new GlobalVariable(classInitializersType,
true,
GlobalVariable::ExternalLinkage,
ConstantArray::get(classInitializersType,
classInitializers_),
"llvm_java_class_initializers",
module_);
// Emit the array of all class records.
resolver_->emitClassRecordsArray();
return method;
}
void do_aconst_null() {
push(llvm::Constant::getNullValue(resolver_->getObjectBaseType()));
}
void do_iconst(int value) {
push(ConstantSInt::get(Type::IntTy, value));
}
void do_lconst(long long value) {
push(ConstantSInt::get(Type::LongTy, value));
}
void do_fconst(float value) {
push(ConstantFP::get(Type::FloatTy, value));
}
void do_dconst(double value) {
push(ConstantFP::get(Type::DoubleTy, value));
}
void do_ldc(unsigned index) {
llvm::Constant* c = class_->getConstant(index);
assert(c && "Java constant not handled!");
push(c);
}
void do_ldc2(unsigned index) {
do_ldc(index);
}
void do_iload(unsigned index) { do_load_common(index, Type::IntTy); }
void do_lload(unsigned index) { do_load_common(index, Type::LongTy); }
void do_fload(unsigned index) { do_load_common(index, Type::FloatTy); }
void do_dload(unsigned index) { do_load_common(index, Type::DoubleTy); }
void do_aload(unsigned index) { do_load_common(index, resolver_->getObjectBaseType()); }
void do_load_common(unsigned index, const Type* type) {
Value* val = locals_.load(index, type, currentBB_);
push(val);
}
void do_iaload() { do_aload_common("[I"); }
void do_laload() { do_aload_common("[J"); }
void do_faload() { do_aload_common("[F"); }
void do_daload() { do_aload_common("[D"); }
void do_aaload() { do_aload_common("[Ljava/lang/Object;"); }
void do_baload() { do_aload_common("[B"); }
void do_caload() { do_aload_common("[C"); }
void do_saload() { do_aload_common("[S"); }
void do_aload_common(const std::string& className) {
const VMClass* arrayClass = resolver_->getClass(className);
assert(arrayClass->isArray() && "Not an array class!");
Value* index = pop(Type::IntTy);
Value* arrayRef = pop(arrayClass->getType());
std::vector<Value*> indices;
indices.reserve(3);
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
indices.push_back(ConstantUInt::get(Type::UIntTy, 2));
indices.push_back(index);
Value* elementPtr =
new GetElementPtrInst(arrayRef, indices, TMP, currentBB_);
Value* result = new LoadInst(elementPtr, TMP, currentBB_);
push(result);
}
void do_istore(unsigned index) { do_store_common(index, Type::IntTy); }
void do_lstore(unsigned index) { do_store_common(index, Type::LongTy); }
void do_fstore(unsigned index) { do_store_common(index, Type::FloatTy); }
void do_dstore(unsigned index) { do_store_common(index, Type::DoubleTy); }
void do_astore(unsigned index) { do_store_common(index, resolver_->getObjectBaseType()); }
void do_store_common(unsigned index, const Type* type) {
Value* val = pop(type);
locals_.store(index, val, currentBB_);
}
void do_iastore() { do_astore_common("[I"); }
void do_lastore() { do_astore_common("[J"); }
void do_fastore() { do_astore_common("[F"); }
void do_dastore() { do_astore_common("[D"); }
void do_aastore() { do_astore_common("[Ljava/lang/Object;"); }
void do_bastore() { do_astore_common("[B"); }
void do_castore() { do_astore_common("[C"); }
void do_sastore() { do_astore_common("[S"); }
void do_astore_common(const std::string& className) {
const VMClass* arrayClass = resolver_->getClass(className);
assert(arrayClass->isArray() && "Not an array class!");
const VMClass* componentClass = arrayClass->getComponentClass();
Value* value = pop(componentClass->getType());
Value* index = pop(Type::IntTy);
Value* arrayRef = pop(arrayClass->getType());
std::vector<Value*> indices;
indices.reserve(3);
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
indices.push_back(ConstantUInt::get(Type::UIntTy, 2));
indices.push_back(index);
Value* elementPtr =
new GetElementPtrInst(arrayRef, indices, TMP, currentBB_);
new StoreInst(value, elementPtr, currentBB_);
}
void do_pop() {
opStack_.do_pop(currentBB_);
}
void do_pop2() {
opStack_.do_pop2(currentBB_);
}
void do_dup() {
opStack_.do_dup(currentBB_);
}
void do_dup_x1() {
opStack_.do_dup_x1(currentBB_);
}
void do_dup_x2() {
opStack_.do_dup_x2(currentBB_);
}
void do_dup2() {
opStack_.do_dup2(currentBB_);
}
void do_dup2_x1() {
opStack_.do_dup2_x1(currentBB_);
}
void do_dup2_x2() {
opStack_.do_dup2_x2(currentBB_);
}
void do_swap() {
opStack_.do_swap(currentBB_);
}
void do_iadd() { do_binary_op_common(Instruction::Add, Type::IntTy); }
void do_ladd() { do_binary_op_common(Instruction::Add, Type::LongTy); }
void do_fadd() { do_binary_op_common(Instruction::Add, Type::FloatTy); }
void do_dadd() { do_binary_op_common(Instruction::Add, Type::DoubleTy); }
void do_isub() { do_binary_op_common(Instruction::Sub, Type::IntTy); }
void do_lsub() { do_binary_op_common(Instruction::Sub, Type::LongTy); }
void do_fsub() { do_binary_op_common(Instruction::Sub, Type::FloatTy); }
void do_dsub() { do_binary_op_common(Instruction::Sub, Type::DoubleTy); }
void do_imul() { do_binary_op_common(Instruction::Mul, Type::IntTy); }
void do_lmul() { do_binary_op_common(Instruction::Mul, Type::LongTy); }
void do_fmul() { do_binary_op_common(Instruction::Mul, Type::FloatTy); }
void do_dmul() { do_binary_op_common(Instruction::Mul, Type::DoubleTy); }
void do_idiv() { do_binary_op_common(Instruction::Div, Type::IntTy); }
void do_ldiv() { do_binary_op_common(Instruction::Div, Type::LongTy); }
void do_fdiv() { do_binary_op_common(Instruction::Div, Type::FloatTy); }
void do_ddiv() { do_binary_op_common(Instruction::Div, Type::DoubleTy); }
void do_irem() { do_binary_op_common(Instruction::Rem, Type::IntTy); }
void do_lrem() { do_binary_op_common(Instruction::Rem, Type::LongTy); }
void do_frem() { do_binary_op_common(Instruction::Rem, Type::FloatTy); }
void do_drem() { do_binary_op_common(Instruction::Rem, Type::DoubleTy); }
void do_ineg() { do_neg_common(Type::IntTy); }
void do_lneg() { do_neg_common(Type::LongTy); }
void do_fneg() { do_neg_common(Type::FloatTy); }
void do_dneg() { do_neg_common(Type::DoubleTy); }
void do_neg_common(const Type* type) {
Value* v1 = pop(type);
Value* r = BinaryOperator::createNeg(v1, TMP, currentBB_);
push(r);
}
void do_ishl() { do_shift_common(Instruction::Shl, Type::IntTy); }
void do_lshl() { do_shift_common(Instruction::Shl, Type::LongTy); }
void do_ishr() { do_shift_common(Instruction::Shr, Type::IntTy); }
void do_lshr() { do_shift_common(Instruction::Shr, Type::LongTy); }
void do_iushr() { do_shift_common(Instruction::Shr, Type::UIntTy); }
void do_lushr() { do_shift_common(Instruction::Shr, Type::ULongTy); }
void do_shift_common(Instruction::OtherOps op, const Type* type) {
llvm::Constant* mask =
type == Type::IntTy ? INT_SHIFT_MASK : LONG_SHIFT_MASK;
Value* a = pop(Type::UByteTy);
a = BinaryOperator::create(Instruction::And, a, mask, TMP, currentBB_);
Value* v = pop(type);
Value* r = new ShiftInst(op, v, a, TMP, currentBB_);
push(r);
}
void do_iand() { do_binary_op_common(Instruction::And, Type::IntTy); }
void do_land() { do_binary_op_common(Instruction::And, Type::LongTy); }
void do_ior() { do_binary_op_common(Instruction::Or, Type::IntTy); }
void do_lor() { do_binary_op_common(Instruction::Or, Type::LongTy); }
void do_ixor() { do_binary_op_common(Instruction::Xor, Type::IntTy); }
void do_lxor() { do_binary_op_common(Instruction::Xor, Type::LongTy); }
void do_binary_op_common(Instruction::BinaryOps op, const Type* type) {
Value* v2 = pop(type);
Value* v1 = pop(type);
Value* r = BinaryOperator::create(op, v1, v2, TMP, currentBB_);
push(r);
}
void do_iinc(unsigned index, int amount) {
Value* v = locals_.load(index, Type::IntTy, currentBB_);
Value* a = ConstantSInt::get(Type::IntTy, amount);
v = BinaryOperator::createAdd(v, a, TMP, currentBB_);
locals_.store(index, v, currentBB_);
}
void do_i2l() { do_cast_common(Type::IntTy, Type::LongTy); }
void do_i2f() { do_cast_common(Type::IntTy, Type::FloatTy); }
void do_i2d() { do_cast_common(Type::IntTy, Type::DoubleTy); }
void do_l2i() { do_cast_common(Type::LongTy, Type::IntTy); }
void do_l2f() { do_cast_common(Type::LongTy, Type::FloatTy); }
void do_l2d() { do_cast_common(Type::LongTy, Type::DoubleTy); }
void do_f2i() { do_cast_common(Type::FloatTy, Type::IntTy); }
void do_f2l() { do_cast_common(Type::FloatTy, Type::LongTy); }
void do_f2d() { do_cast_common(Type::FloatTy, Type::DoubleTy); }
void do_d2i() { do_cast_common(Type::DoubleTy, Type::IntTy); }
void do_d2l() { do_cast_common(Type::DoubleTy, Type::LongTy); }
void do_d2f() { do_cast_common(Type::DoubleTy, Type::FloatTy); }
void do_i2b() { do_cast_common(Type::IntTy, Type::SByteTy); }
void do_i2c() { do_cast_common(Type::IntTy, Type::UShortTy); }
void do_i2s() { do_cast_common(Type::IntTy, Type::ShortTy); }
void do_cast_common(const Type* from, const Type* to) {
Value* v1 = pop(from);
push(new CastInst(v1, to, TMP, currentBB_));
}
void do_lcmp() {
Value* v2 = pop(Type::LongTy);
Value* v1 = pop(Type::LongTy);
Value* c = BinaryOperator::createSetGT(v1, v2, TMP, currentBB_);
Value* r = new SelectInst(c, ONE, ZERO, TMP, currentBB_);
c = BinaryOperator::createSetLT(v1, v2, TMP, currentBB_);
r = new SelectInst(c, MINUS_ONE, r, TMP, currentBB_);
push(r);
}
void do_fcmpl() { do_cmp_common(Type::FloatTy, false); }
void do_dcmpl() { do_cmp_common(Type::DoubleTy, false); }
void do_fcmpg() { do_cmp_common(Type::FloatTy, true); }
void do_dcmpg() { do_cmp_common(Type::DoubleTy, true); }
void do_cmp_common(const Type* type, bool pushOne) {
Value* v2 = pop(type);
Value* v1 = pop(type);
Value* c = BinaryOperator::createSetGT(v1, v2, TMP, currentBB_);
Value* r = new SelectInst(c, ONE, ZERO, TMP, currentBB_);
c = BinaryOperator::createSetLT(v1, v2, TMP, currentBB_);
r = new SelectInst(c, MINUS_ONE, r, TMP, currentBB_);
c = new CallInst(module_->getOrInsertFunction
("llvm.isunordered",
Type::BoolTy, v1->getType(), v2->getType(), 0),
v1, v2, TMP, currentBB_);
r = new SelectInst(c, pushOne ? ONE : MINUS_ONE, r, TMP, currentBB_);
push(r);
}
void do_ifeq(unsigned t, unsigned f) {
do_iconst(0);
do_if_common(Instruction::SetEQ, Type::IntTy, t, f);
}
void do_ifne(unsigned t, unsigned f) {
do_iconst(0);
do_if_common(Instruction::SetNE, Type::IntTy, t, f);
}
void do_iflt(unsigned t, unsigned f) {
do_iconst(0);
do_if_common(Instruction::SetLT, Type::IntTy, t, f);
}
void do_ifge(unsigned t, unsigned f) {
do_iconst(0);
do_if_common(Instruction::SetGE, Type::IntTy, t, f);
}
void do_ifgt(unsigned t, unsigned f) {
do_iconst(0);
do_if_common(Instruction::SetGT, Type::IntTy, t, f);
}
void do_ifle(unsigned t, unsigned f) {
do_iconst(0);
do_if_common(Instruction::SetLE, Type::IntTy, t, f);
}
void do_if_icmpeq(unsigned t, unsigned f) {
do_if_common(Instruction::SetEQ, Type::IntTy, t, f);
}
void do_if_icmpne(unsigned t, unsigned f) {
do_if_common(Instruction::SetNE, Type::IntTy, t, f);
}
void do_if_icmplt(unsigned t, unsigned f) {
do_if_common(Instruction::SetLT, Type::IntTy, t, f);
}
void do_if_icmpge(unsigned t, unsigned f) {
do_if_common(Instruction::SetGE, Type::IntTy, t, f);
}
void do_if_icmpgt(unsigned t, unsigned f) {
do_if_common(Instruction::SetGT, Type::IntTy, t, f);
}
void do_if_icmple(unsigned t, unsigned f) {
do_if_common(Instruction::SetLE, Type::IntTy, t, f);
}
void do_if_acmpeq(unsigned t, unsigned f) {
do_if_common(Instruction::SetEQ, resolver_->getObjectBaseType(), t, f);
}
void do_if_acmpne(unsigned t, unsigned f) {
do_if_common(Instruction::SetNE, resolver_->getObjectBaseType(), t, f);
}
void do_ifnull(unsigned t, unsigned f) {
do_aconst_null();
do_if_common(Instruction::SetEQ, resolver_->getObjectBaseType(), t, f);
}
void do_ifnonnull(unsigned t, unsigned f) {
do_aconst_null();
do_if_common(Instruction::SetNE, resolver_->getObjectBaseType(), t, f);
}
void do_if_common(Instruction::BinaryOps cc, const Type* type,
unsigned t, unsigned f) {
Value* v2 = pop(type);
Value* v1 = pop(type);
Value* c = new SetCondInst(cc, v1, v2, TMP, currentBB_);
new BranchInst(bbBuilder_->getBasicBlock(t),
bbBuilder_->getBasicBlock(f),
c, currentBB_);
}
void do_goto(unsigned target) {
new BranchInst(bbBuilder_->getBasicBlock(target), currentBB_);
}
void do_ireturn() { do_return_common(Type::IntTy); }
void do_lreturn() { do_return_common(Type::LongTy); }
void do_freturn() { do_return_common(Type::FloatTy); }
void do_dreturn() { do_return_common(Type::DoubleTy); }
void do_areturn() { do_return_common(resolver_->getObjectBaseType()); }
void do_return_common(const Type* type) {
Value* r = pop(type);
const Type* retTy = currentBB_->getParent()->getReturnType();
if (retTy != r->getType())
r = new CastInst(r, retTy, TMP, currentBB_);
new ReturnInst(r, currentBB_);
}
void do_return() {
new ReturnInst(NULL, currentBB_);
}
void do_jsr(unsigned target, unsigned retAddress) {
// FIXME: this is currently a noop.
push(llvm::Constant::getNullValue(Type::IntTy));
}
void do_ret(unsigned index) {
// FIXME: this is currently a noop.
}
void do_tableswitch(unsigned defTarget, const SwitchCases& sw) {
do_switch_common(defTarget, sw);
}
void do_lookupswitch(unsigned defTarget, const SwitchCases& sw) {
do_switch_common(defTarget, sw);
}
void do_switch_common(unsigned defTarget, const SwitchCases& sw) {
Value* v = pop(Type::IntTy);
SwitchInst* in =
new SwitchInst(v, bbBuilder_->getBasicBlock(defTarget), sw.size(),
currentBB_);
for (unsigned i = 0, e = sw.size(); i != e; ++i)
in->addCase(ConstantSInt::get(Type::IntTy, sw[i].first),
bbBuilder_->getBasicBlock(sw[i].second));
}
void do_getstatic(unsigned index) {
const VMField* field = class_->getField(index);
emitClassInitializers(field->getParent());
Value* v = new LoadInst(field->getGlobal(), TMP, currentBB_);
push(v);
}
void do_putstatic(unsigned index) {
const VMField* field = class_->getField(index);
emitClassInitializers(field->getParent());
Value* v = pop(field->getClass()->getType());
new StoreInst(v, field->getGlobal(), currentBB_);
}
void do_getfield(unsigned index) {
const VMField* field = class_->getField(index);
Value* p = pop(field->getParent()->getType());
std::vector<Value*> indices(2);
indices[0] = ConstantUInt::get(Type::UIntTy, 0);
indices[1] = ConstantUInt::get(Type::UIntTy, field->getMemberIndex());
Value* fieldPtr =
new GetElementPtrInst(p, indices, field->getName()+'*', currentBB_);
Value* v = new LoadInst(fieldPtr, field->getName(), currentBB_);
push(v);
}
void do_putfield(unsigned index) {
const VMField* field = class_->getField(index);
Value* v = pop(field->getClass()->getType());
Value* p = pop(field->getParent()->getType());
std::vector<Value*> indices(2);
indices[0] = ConstantUInt::get(Type::UIntTy, 0);
indices[1] = ConstantUInt::get(Type::UIntTy, field->getMemberIndex());
Value* fieldPtr =
new GetElementPtrInst(p, indices, field->getName()+'*', currentBB_);
new StoreInst(v, fieldPtr, currentBB_);
}
void makeCall(Value* fun, const std::vector<Value*> params) {
const PointerType* funPtrTy = cast<PointerType>(fun->getType());
const FunctionType* funTy =
cast<FunctionType>(funPtrTy->getElementType());
if (funTy->getReturnType() == Type::VoidTy)
new CallInst(fun, params, "", currentBB_);
else {
Value* r = new CallInst(fun, params, TMP, currentBB_);
push(r);
}
}
std::vector<Value*> getParams(const FunctionType* funTy) {
unsigned numParams = funTy->getNumParams();
std::vector<Value*> params(numParams);
while (numParams--)
params[numParams] = pop(funTy->getParamType(numParams));
return params;
}
void do_invokevirtual(unsigned index) {
const VMMethod* method = class_->getMethod(index);
const VMClass* clazz = method->getParent();
Function* function = method->getFunction();
std::vector<Value*> params(getParams(function->getFunctionType()));
Value* objRef = params.front();
objRef = new CastInst(objRef, clazz->getType(), "this", currentBB_);
Value* objBase =
new CastInst(objRef, resolver_->getObjectBaseType(), TMP, currentBB_);
Value* classRecord =
new CallInst(getClassRecord_, objBase, TMP, currentBB_);
classRecord = new CastInst(classRecord,
clazz->getClassRecord()->getType(),
clazz->getName() + ".classRecord", currentBB_);
std::vector<Value*> indices(1, ConstantUInt::get(Type::UIntTy, 0));
assert(method->getMethodIndex() != -1 &&
"Method index not found for dynamically bound method!");
indices.push_back(
ConstantUInt::get(Type::UIntTy, method->getMethodIndex()+1));
Value* funPtr =
new GetElementPtrInst(classRecord, indices, TMP, currentBB_);
Value* fun = new LoadInst(funPtr, function->getName(), currentBB_);
makeCall(fun, params);
}
void do_invokespecial(unsigned index) {
const VMMethod* method = class_->getMethod(index);
Function* function = method->getFunction();
makeCall(function, getParams(function->getFunctionType()));
}
void do_invokestatic(unsigned index) {
const VMMethod* method = class_->getMethod(index);
emitClassInitializers(method->getParent());
Function* function = method->getFunction();
makeCall(function, getParams(function->getFunctionType()));
}
void do_invokeinterface(unsigned index) {
const VMMethod* method = class_->getMethod(index);
const VMClass* clazz = method->getParent();
assert(clazz->isInterface() && "Class must be an interface!");
Function* function = method->getFunction();
std::vector<Value*> params(getParams(function->getFunctionType()));
Value* objRef = params.front();
objRef = new CastInst(objRef, clazz->getType(), "this", currentBB_);
Value* objBase =
new CastInst(objRef, resolver_->getObjectBaseType(), TMP, currentBB_);
Value* classRecord =
new CallInst(getClassRecord_, objBase, TMP, currentBB_);
classRecord = new CastInst(classRecord,
resolver_->getClassRecordPtrType(),
TMP, currentBB_);
// get the interfaces array of class records
std::vector<Value*> indices(2, ConstantUInt::get(Type::UIntTy, 0));
indices.push_back(ConstantUInt::get(Type::UIntTy, 4));
Value* interfaceClassRecords =
new GetElementPtrInst(classRecord, indices, TMP, currentBB_);
interfaceClassRecords =
new LoadInst(interfaceClassRecords, TMP, currentBB_);
// Get the actual interface class record.
indices.clear();
indices.push_back(
ConstantUInt::get(Type::UIntTy, clazz->getInterfaceIndex()));
Value* interfaceClassRecord =
new GetElementPtrInst(interfaceClassRecords, indices, TMP, currentBB_);
interfaceClassRecord =
new LoadInst(interfaceClassRecord,
clazz->getName() + ".classRecord", currentBB_);
interfaceClassRecord =
new CastInst(interfaceClassRecord,
clazz->getClassRecord()->getType(), TMP, currentBB_);
// Get the function pointer.
assert(method->getMethodIndex() != -1 &&
"Method index not found for dynamically bound method!");
indices.resize(2);
indices[0] = ConstantUInt::get(Type::UIntTy, 0);
indices[1] = ConstantUInt::get(Type::UIntTy, method->getMethodIndex()+1);
Value* funPtr =
new GetElementPtrInst(interfaceClassRecord, indices, TMP, currentBB_);
Value* fun = new LoadInst(funPtr, function->getName(), currentBB_);
makeCall(fun, params);
}
template<typename InsertionPointTy>
Value* allocateObject(const VMClass& clazz, InsertionPointTy* ip) {
static std::vector<Value*> params(4);
Value* objRef = new MallocInst(clazz.getLayoutType(), NULL, TMP, ip);
params[0] =
new CastInst(objRef, PointerType::get(Type::SByteTy), TMP, ip); // dest
params[1] = ConstantUInt::get(Type::UByteTy, 0); // value
params[2] = ConstantExpr::getSizeOf(clazz.getLayoutType()); // size
params[3] = ConstantUInt::get(Type::UIntTy, 0); // alignment
new CallInst(memset_, params, "", ip);
// Install the class record.
Value* objBase =
new CastInst(objRef, resolver_->getObjectBaseType(), TMP, ip);
const Type* classRecordPtrType = resolver_->getClassRecordPtrType();
Value* classRecord =
new CastInst(clazz.getClassRecord(), classRecordPtrType, TMP, ip);
new CallInst(setClassRecord_, objBase, classRecord, "", ip);
return objRef;
}
void do_new(unsigned index) {
const VMClass* clazz = class_->getClass(index);
emitClassInitializers(clazz);
push(allocateObject(*clazz, currentBB_));
}
template <typename InsertionPointTy>
Value* getArrayLengthPtr(Value* arrayRef, InsertionPointTy* ip) const {
std::vector<Value*> indices;
indices.reserve(2);
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
indices.push_back(ConstantUInt::get(Type::UIntTy, 1));
return new GetElementPtrInst(arrayRef, indices, TMP, ip);
}
template <typename InsertionPointTy>
Value* getArrayObjectBasePtr(Value* arrayRef, InsertionPointTy* ip) const {
std::vector<Value*> indices;
indices.reserve(2);
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
indices.push_back(ConstantUInt::get(Type::UIntTy, 0));
return new GetElementPtrInst(arrayRef, indices, TMP, ip);
}
template<typename InsertionPointTy>
Value* allocateArray(const VMClass* clazz,
Value* count,
InsertionPointTy* ip) {
static std::vector<Value*> params(4);
assert(clazz->isArray() && "Not an array class!");
const VMClass* componentClass = clazz->getComponentClass();
const Type* elementTy = componentClass->getType();
// The size of the element.
llvm::Constant* elementSize =
ConstantExpr::getCast(ConstantExpr::getSizeOf(elementTy), Type::UIntTy);
// The size of the array part of the struct.
Value* size = BinaryOperator::create(
Instruction::Mul, count, elementSize, TMP, ip);
// The size of the rest of the array object.
llvm::Constant* arrayObjectSize =
ConstantExpr::getCast(ConstantExpr::getSizeOf(clazz->getLayoutType()),
Type::UIntTy);
// Add the array part plus the object part together.
size = BinaryOperator::create(
Instruction::Add, size, arrayObjectSize, TMP, ip);
// Allocate memory for the object.
Value* objRef = new MallocInst(Type::SByteTy, size, TMP, ip);
params[0] = objRef; // dest
params[1] = ConstantUInt::get(Type::UByteTy, 0); // value
params[2] = new CastInst(size, Type::ULongTy, TMP, ip); // size
params[3] = ConstantUInt::get(Type::UIntTy, 0); // alignment
new CallInst(memset_, params, "", ip);
// Cast back to array type.
objRef = new CastInst(objRef, clazz->getType(), TMP, ip);
// Store the size.
Value* lengthPtr = getArrayLengthPtr(objRef, ip);
new StoreInst(count, lengthPtr, ip);
// Install the class record.
Value* objBase = new CastInst(objRef, resolver_->getObjectBaseType(), TMP, ip);
const Type* classRecordPtrType = resolver_->getClassRecordPtrType();
Value* classRecord =
new CastInst(clazz->getClassRecord(), classRecordPtrType, TMP, ip);
new CallInst(setClassRecord_, objBase, classRecord, "", ip);
return objRef;
}
void do_newarray(JType type) {
Value* count = pop(Type::UIntTy);
const VMClass* clazz = resolver_->getClass(type);
const VMClass* arrayClass = resolver_->getArrayClass(clazz);
emitClassInitializers(arrayClass);
push(allocateArray(arrayClass, count, currentBB_));
}
void do_anewarray(unsigned index) {
Value* count = pop(Type::UIntTy);
const VMClass* clazz = class_->getClass(index);
const VMClass* arrayClass = resolver_->getArrayClass(clazz);
emitClassInitializers(arrayClass);
push(allocateArray(arrayClass, count, currentBB_));
}
void do_arraylength() {
const VMClass* clazz = resolver_->getClass("[Ljava/lang/Object;");
Value* arrayRef = pop(clazz->getType());
Value* lengthPtr = getArrayLengthPtr(arrayRef, currentBB_);
Value* length = new LoadInst(lengthPtr, TMP, currentBB_);
push(length);
}
void do_athrow() {
Value* objRef = pop(resolver_->getObjectBaseType());
new CallInst(throw_, objRef, "", currentBB_);
new UnreachableInst(currentBB_);
}
void do_checkcast(unsigned index) {
const VMClass* clazz = class_->getClass(index);
Value* objRef = pop(resolver_->getObjectBaseType());
const Type* classRecordPtrType = resolver_->getClassRecordPtrType();
Value* classRecord = new CastInst(clazz->getClassRecord(),
classRecordPtrType, TMP, currentBB_);
Value* r =
new CallInst(isInstanceOf_, objRef, classRecord, TMP, currentBB_);
Value* b = new SetCondInst(Instruction::SetEQ,
r, ConstantSInt::get(Type::IntTy, 1),
TMP, currentBB_);
// FIXME: if b is false we must throw a ClassCast exception
push(objRef);
}
void do_instanceof(unsigned index) {
const VMClass* clazz = class_->getClass(index);
Value* objRef = pop(resolver_->getObjectBaseType());
const Type* classRecordPtrType = resolver_->getClassRecordPtrType();
Value* classRecord = new CastInst(clazz->getClassRecord(),
classRecordPtrType, TMP, currentBB_);
Value* r =
new CallInst(isInstanceOf_, objRef, classRecord, TMP, currentBB_);
push(r);
}
void do_monitorenter() {
// FIXME: This is currently a noop.
pop(resolver_->getObjectBaseType());
}
void do_monitorexit() {
// FIXME: This is currently a noop.
pop(resolver_->getObjectBaseType());
}
void do_multianewarray(unsigned index, unsigned dims) {
assert(0 && "not implemented");
}
};
} } } // namespace llvm::Java::
std::auto_ptr<Module> llvm::Java::compile(const std::string& className)
{
DEBUG(std::cerr << "Compiling class: " << className << '\n');
std::auto_ptr<Module> m(new Module(className));
// Require the Java runtime.
m->addLibrary("jrt");
Compiler c(m.get());
const VMMethod* main =
c.compileMethod(className, "main([Ljava/lang/String;)V");
Function* javaMain = m->getOrInsertFunction
("llvm_java_main", Type::VoidTy,
Type::IntTy, PointerType::get(PointerType::get(Type::SByteTy)), NULL);
BasicBlock* bb = new BasicBlock("entry", javaMain);
const FunctionType* mainTy = main->getFunction()->getFunctionType();
new CallInst(main->getFunction(),
// FIXME: Forward correct params from llvm_java_main
llvm::Constant::getNullValue(mainTy->getParamType(0)),
"",
bb);
new ReturnInst(NULL, bb);
return m;
}