| //===----------- Opcodes.cpp - Reads and compiles opcodes -----------------===// |
| // |
| // N3 |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "N3Debug.h" |
| |
| #include <cstring> |
| |
| #include <llvm/Constants.h> |
| #include <llvm/DerivedTypes.h> |
| #include <llvm/LLVMContext.h> |
| #include <llvm/Module.h> |
| #include <llvm/Type.h> |
| #include <llvm/Function.h> |
| #include <llvm/Instructions.h> |
| #include <llvm/PassManager.h> |
| #include <llvm/Transforms/IPO.h> |
| #include <llvm/Transforms/Scalar.h> |
| #include <llvm/Target/TargetData.h> |
| #include <llvm/Target/TargetMachine.h> |
| #include <llvm/Target/TargetOptions.h> |
| |
| #include "mvm/JIT.h" |
| |
| #include "Assembly.h" |
| #include "CLIJit.h" |
| #include "CLIString.h" |
| #include "MSCorlib.h" |
| #include "N3.h" |
| #include "Reader.h" |
| #include "VMArray.h" |
| #include "VMClass.h" |
| #include "VMObject.h" |
| #include "VMThread.h" |
| |
| #include "OpcodeNames.def" |
| |
| using namespace n3; |
| using namespace llvm; |
| |
| |
| static inline sint8 readS1(uint8* bytecode, uint32& i) { |
| return ((sint8*)bytecode)[++i]; |
| } |
| |
| static inline uint8 readU1(uint8* bytecode, uint32& i) { |
| return bytecode[++i]; |
| } |
| |
| static inline sint16 readS2(uint8* bytecode, uint32& i) { |
| sint16 val = readS1(bytecode, i); |
| return val | (readU1(bytecode, i) << 8); |
| } |
| |
| static inline uint16 readU2(uint8* bytecode, uint32& i) { |
| uint16 val = readU1(bytecode, i); |
| return val | (readU1(bytecode, i) << 8); |
| } |
| |
| static inline sint32 readS4(uint8* bytecode, uint32& i) { |
| sint32 val = readU2(bytecode, i); |
| return val | (readU2(bytecode, i) << 16); |
| } |
| |
| |
| static inline uint32 readU4(uint8* bytecode, uint32& i) { |
| return readS4(bytecode, i); |
| } |
| |
| static inline sint64 readS8(uint8* bytecode, uint32& i) { |
| uint64 val = readU4(bytecode, i); |
| uint64 _val2 = readU4(bytecode, i); |
| uint64 val2 = _val2 << 32; |
| return val | val2; |
| } |
| |
| typedef union ufloat_t { |
| uint32 i; |
| float f; |
| }ufloat_t; |
| |
| typedef union udouble_t { |
| sint64 l; |
| double d; |
| }udouble_t; |
| |
| |
| static inline float readFloat(uint8* bytecode, uint32& i) { |
| ufloat_t tmp; |
| tmp.i = readU4(bytecode, i); |
| return tmp.f; |
| } |
| |
| static inline double readDouble(uint8* bytecode, uint32& i) { |
| udouble_t tmp; |
| tmp.l = readS8(bytecode, i); |
| return tmp.d; |
| } |
| |
| |
| extern "C" void n3PrintExecution(char* opcode, VMMethod* meth) { |
| fprintf(stderr, "executing %s %s\n", mvm::PrintBuffer(meth).cString(), opcode); |
| } |
| |
| |
| static void verifyType(Value*& val1, Value*& val2, BasicBlock* currentBlock) { |
| const Type* t1 = val1->getType(); |
| const Type* t2 = val2->getType(); |
| if (t1 != t2) { |
| if (t1->isIntegerTy() && t2->isIntegerTy()) { |
| if (t1->getPrimitiveSizeInBits() < t2->getPrimitiveSizeInBits()) { |
| val1 = new SExtInst(val1, t2, "", currentBlock); |
| } else { |
| val2 = new SExtInst(val2, t1, "", currentBlock); |
| } |
| } else if (t1->isFloatTy()) { |
| if (t1->getPrimitiveSizeInBits() < t2->getPrimitiveSizeInBits()) { |
| val1 = new FPExtInst(val1, t2, "", currentBlock); |
| } else { |
| val2 = new FPExtInst(val2, t1, "", currentBlock); |
| } |
| } else if (isa<PointerType>(t1) && isa<PointerType>(t2)) { |
| val1 = new BitCastInst(val1, VMObject::llvmType, "", currentBlock); |
| val2 = new BitCastInst(val2, VMObject::llvmType, "", currentBlock); |
| } else if (t1->isIntegerTy() && t2 == PointerType::getUnqual(Type::getInt8Ty(getGlobalContext()))) { |
| // CLI says that this is fine for some operation |
| val2 = new PtrToIntInst(val2, t1, "", currentBlock); |
| } else if (t2->isIntegerTy() && t1 == PointerType::getUnqual(Type::getInt8Ty(getGlobalContext()))) { |
| // CLI says that this is fine for some operation |
| val1 = new PtrToIntInst(val1, t2, "", currentBlock); |
| } |
| } |
| } |
| |
| void convertValue(Value*& val, const Type* t1, BasicBlock* currentBlock) { |
| const Type* t2 = val->getType(); |
| if (t1 != t2) { |
| if (t1->isIntegerTy() && t2->isIntegerTy()) { |
| if (t2->getPrimitiveSizeInBits() < t1->getPrimitiveSizeInBits()) { |
| val = new SExtInst(val, t1, "", currentBlock); |
| } else { |
| val = new TruncInst(val, t1, "", currentBlock); |
| } |
| } else if (t1->isFloatTy() && t2->isFloatTy()) { |
| if (t2->getPrimitiveSizeInBits() < t1->getPrimitiveSizeInBits()) { |
| val = new FPExtInst(val, t1, "", currentBlock); |
| } else { |
| val = new FPTruncInst(val, t1, "", currentBlock); |
| } |
| } else if (isa<PointerType>(t1) && isa<PointerType>(t2)) { |
| val = new BitCastInst(val, t1, "", currentBlock); |
| } |
| } |
| } |
| |
| static void store(Value* val, Value* local, bool vol, |
| BasicBlock* currentBlock, mvm::BaseIntrinsics* module) { |
| const Type* contained = local->getType()->getContainedType(0); |
| if (contained->isSingleValueType()) { |
| if (val->getType() != contained) { |
| convertValue(val, contained, currentBlock); |
| } |
| new StoreInst(val, local, vol, currentBlock); |
| } else if (isa<PointerType>(val->getType())) { |
| uint64 size = mvm::MvmModule::getTypeSize(contained); |
| |
| std::vector<Value*> params; |
| params.push_back(new BitCastInst(local, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size)); |
| params.push_back(module->constantZero); |
| CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock); |
| } else { |
| new StoreInst(val, local, vol, currentBlock); |
| } |
| } |
| |
| static Value* load(Value* val, const char* name, BasicBlock* currentBlock, mvm::BaseIntrinsics* module) { |
| const Type* contained = val->getType()->getContainedType(0); |
| if (contained->isSingleValueType()) { |
| return new LoadInst(val, name, currentBlock); |
| } else { |
| uint64 size = mvm::MvmModule::getTypeSize(contained); |
| Value* ret = new AllocaInst(contained, "", currentBlock); |
| std::vector<Value*> params; |
| params.push_back(new BitCastInst(ret, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size)); |
| params.push_back(module->constantZero); |
| CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock); |
| return ret; |
| } |
| } |
| |
| void CLIJit::compileOpcodes(uint8* bytecodes, uint32 codeLength, VMGenericClass* genClass, VMGenericMethod* genMethod) { |
| uint32 leaveIndex = 0; |
| bool isVolatile = false; |
| for(uint32 i = 0; i < codeLength; ++i) { |
| |
| if (bytecodes[i] != 0xFE) { |
| PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i, |
| bytecodes[i]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "compiling %s::", mvm::PrintBuffer(compilingMethod).cString()); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNames[bytecodes[i]]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n"); |
| } |
| |
| Opinfo* opinfo = &(opcodeInfos[i]); |
| if (opinfo->newBlock) { |
| if (currentBlock->getTerminator() == 0) { |
| branch(opinfo->newBlock, currentBlock); |
| } |
| setCurrentBlock(opinfo->newBlock); |
| } |
| currentExceptionBlock = opinfo->exceptionBlock; |
| if (currentBlock->getTerminator() != 0) { // To prevent a gcj bug with useless goto |
| currentBlock = createBasicBlock("gcj bug"); |
| } |
| |
| #if N3_EXECUTE > 1 |
| if (bytecodes[i] == 0xFE) { |
| std::vector<llvm::Value*> args; |
| args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)OpcodeNamesFE[bytecodes[i + 1]])); |
| args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)compilingMethod)); |
| CallInst::Create(printExecutionLLVM, args.begin(), args.end(), "", currentBlock); |
| } else { |
| std::vector<llvm::Value*> args; |
| args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)OpcodeNames[bytecodes[i]])); |
| args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)compilingMethod)); |
| CallInst::Create(printExecutionLLVM, args.begin(), args.end(), "", currentBlock); |
| } |
| #endif |
| |
| if (opinfo->reqSuppl) { |
| push(new LoadInst(supplLocal, "", currentBlock)); |
| } |
| |
| switch (bytecodes[i]) { |
| |
| case ADD: { |
| Value* val2 = pop(); |
| bool isPointer = (val2->getType() == module->ptrType); |
| Value* val1 = pop(); |
| isPointer |= (val1->getType() == module->ptrType); |
| verifyType(val1, val2, currentBlock); |
| Value* res = BinaryOperator::CreateAdd(val1, val2, "", currentBlock); |
| if (isPointer) { |
| res = new IntToPtrInst(res, module->ptrType, "", currentBlock); |
| } |
| push(res); |
| break; |
| } |
| |
| case ADD_OVF: { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case ADD_OVF_UN: { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case AND: { |
| Value* val2 = pop(); |
| Value* val1 = pop(); |
| push(BinaryOperator::CreateAnd(val1, val2, "", currentBlock)); |
| break; |
| } |
| |
| #define TEST(name, read, cmpf, cmpi, offset) case name : { \ |
| uint32 tmp = i; \ |
| Value* val2 = pop(); \ |
| Value* val1 = pop(); \ |
| BasicBlock* ifTrue = opcodeInfos[tmp + offset + read(bytecodes, i)].newBlock; \ |
| Value* test = 0; \ |
| verifyType(val1, val2, currentBlock); \ |
| if (val1->getType()->isFloatTy()) { \ |
| test = new FCmpInst(*currentBlock, FCmpInst::cmpf, val1, val2, ""); \ |
| } else { \ |
| test = new ICmpInst(*currentBlock, ICmpInst::cmpi, val1, val2, ""); \ |
| } \ |
| BasicBlock* ifFalse = createBasicBlock("false BEQ"); \ |
| branch(test, ifTrue, ifFalse, currentBlock); \ |
| currentBlock = ifFalse; \ |
| break; \ |
| } |
| |
| TEST(BEQ, readS4, FCMP_OEQ, ICMP_EQ, 5); |
| TEST(BEQ_S, readS1, FCMP_OEQ, ICMP_EQ, 2); |
| |
| TEST(BGE, readS4, FCMP_OGE, ICMP_SGE, 5); |
| TEST(BGE_S, readS1, FCMP_OGE, ICMP_SGE, 2); |
| TEST(BGE_UN, readS4, FCMP_UGE, ICMP_UGE, 5); |
| TEST(BGE_UN_S, readS1, FCMP_UGE, ICMP_UGE, 2); |
| |
| TEST(BGT, readS4, FCMP_OGT, ICMP_SGT, 5); |
| TEST(BGT_S, readS1, FCMP_OGT, ICMP_SGT, 2); |
| TEST(BGT_UN, readS4, FCMP_UGT, ICMP_UGT, 5); |
| TEST(BGT_UN_S, readS1, FCMP_UGT, ICMP_UGT, 2); |
| |
| TEST(BLE, readS4, FCMP_OLE, ICMP_SLE, 5); |
| TEST(BLE_S, readS1, FCMP_OLE, ICMP_SLE, 2); |
| TEST(BLE_UN, readS4, FCMP_ULE, ICMP_ULE, 5); |
| TEST(BLE_UN_S, readS1, FCMP_ULE, ICMP_ULE, 2); |
| |
| TEST(BLT, readS4, FCMP_OLT, ICMP_SLT, 5); |
| TEST(BLT_S, readS1, FCMP_OLT, ICMP_SLT, 2); |
| TEST(BLT_UN, readS4, FCMP_ULT, ICMP_ULT, 5); |
| TEST(BLT_UN_S, readS1, FCMP_ULT, ICMP_ULT, 2); |
| |
| TEST(BNE_UN, readS4, FCMP_UNE, ICMP_NE, 5); |
| TEST(BNE_UN_S, readS1, FCMP_UNE, ICMP_NE, 2); |
| |
| #undef TEST |
| |
| case BR : { |
| uint32 tmp = i; |
| BasicBlock* br = opcodeInfos[tmp + 5 + readS4(bytecodes, i)].newBlock; |
| branch(br, currentBlock); |
| break; |
| } |
| |
| case BR_S : { |
| uint32 tmp = i; |
| BasicBlock* br = opcodeInfos[tmp + 2 + readS1(bytecodes, i)].newBlock; |
| branch(br, currentBlock); |
| break; |
| } |
| |
| case BREAK: break; |
| |
| #define TEST(name, read, cmpf, cmpi, offset) case name : { \ |
| uint32 tmp = i; \ |
| Value* val2 = pop(); \ |
| Value* val1 = Constant::getNullValue(val2->getType()); \ |
| BasicBlock* ifTrue = opcodeInfos[tmp + offset + read(bytecodes, i)].newBlock; \ |
| Value* test = 0; \ |
| if (val1->getType()->isFloatTy()) { \ |
| test = new FCmpInst(*currentBlock, FCmpInst::cmpf, val1, val2, ""); \ |
| } else { \ |
| test = new ICmpInst(*currentBlock, ICmpInst::cmpi, val1, val2, ""); \ |
| } \ |
| BasicBlock* ifFalse = createBasicBlock("false BR"); \ |
| branch(test, ifTrue, ifFalse, currentBlock); \ |
| currentBlock = ifFalse; \ |
| break; \ |
| } |
| |
| TEST(BRFALSE, readS4, FCMP_OEQ, ICMP_EQ, 5); |
| TEST(BRFALSE_S, readS1, FCMP_OEQ, ICMP_EQ, 2); |
| TEST(BRTRUE, readS4, FCMP_ONE, ICMP_NE, 5); |
| TEST(BRTRUE_S, readS1, FCMP_ONE, ICMP_NE, 2); |
| |
| #undef TEST |
| |
| case CALL: { |
| uint32 value = readU4(bytecodes, i); |
| invoke(value, genClass, genMethod); |
| break; |
| } |
| |
| case CALLI: { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| break; |
| } |
| |
| case CKFINITE : { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| break; |
| } |
| |
| case CONV_I1 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToSIInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt16Ty(getGlobalContext()) || type == Type::getInt32Ty(getGlobalContext()) || |
| type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_I2 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToSIInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt32Ty(getGlobalContext()) || type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext())) { |
| push(new SExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_I4 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToSIInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext())) { |
| push(new SExtInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt32Ty(getGlobalContext())) { |
| push(val); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_I8 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToSIInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext()) || |
| type == Type::getInt32Ty(getGlobalContext())) { |
| push(new SExtInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_R4 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type == Type::getDoubleTy(getGlobalContext())) { |
| push(new FPTruncInst(val, Type::getFloatTy(getGlobalContext()), "", currentBlock)); |
| } else if (type->isIntegerTy()) { |
| push(new SIToFPInst(val, Type::getFloatTy(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_R8 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type == Type::getFloatTy(getGlobalContext())) { |
| push(new FPExtInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock)); |
| } else if (type->isIntegerTy()) { |
| push(new SIToFPInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getDoubleTy(getGlobalContext())) { |
| push(val); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_U1 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToUIInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt16Ty(getGlobalContext()) || type == Type::getInt32Ty(getGlobalContext()) || |
| type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_U2 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToUIInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt32Ty(getGlobalContext()) || type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext())) { |
| push(new ZExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_U4 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToUIInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext())) { |
| push(new ZExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_U8 : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToUIInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext()) || |
| type == Type::getInt32Ty(getGlobalContext())) { |
| push(new ZExtInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock)); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_I : { |
| Value* val = pop(); |
| Value* res = 0; |
| |
| if (val->getType()->isIntegerTy()) { |
| if (val->getType() != Type::getInt64Ty(getGlobalContext())) { |
| val = new ZExtInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock); |
| } |
| res = new IntToPtrInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock); |
| } else if (!val->getType()->isFloatTy()) { |
| res = new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| |
| push(res); |
| break; |
| } |
| |
| case CONV_U : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_R_UN : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type == Type::getFloatTy(getGlobalContext())) { |
| push(new FPExtInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock)); |
| } else if (type->isIntegerTy()) { |
| push(new UIToFPInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getDoubleTy(getGlobalContext())) { |
| push(val); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_OVF_I1 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I2 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I4 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I8 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U1 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U2 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U4 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U8 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I1_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I2_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_I4_UN : { |
| Value* val = pop(); |
| const Type* type = val->getType(); |
| if (type->isFloatTy()) { |
| push(new FPToUIInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt64Ty(getGlobalContext())) { |
| push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext())) { |
| push(new ZExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock)); |
| } else if (type == Type::getInt32Ty(getGlobalContext())) { |
| push(val); |
| } else { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| } |
| break; |
| } |
| |
| case CONV_OVF_I8_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U1_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U2_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U4_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case CONV_OVF_U8_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| |
| case DIV: { |
| Value* two = pop(); |
| Value* one = pop(); |
| if (one->getType()->isFloatTy()) { |
| convertValue(one, two->getType(), currentBlock); |
| push(BinaryOperator::CreateFDiv(one, two, "", currentBlock)); |
| } else { |
| push(BinaryOperator::CreateSDiv(one, two, "", currentBlock)); |
| } |
| break; |
| } |
| |
| case DIV_UN: { |
| Value* two = pop(); |
| Value* one = pop(); |
| if (one->getType()->isFloatTy()) { |
| push(BinaryOperator::CreateFDiv(one, two, "", currentBlock)); |
| } else { |
| push(BinaryOperator::CreateUDiv(one, two, "", currentBlock)); |
| } |
| break; |
| } |
| |
| case DUP: { |
| push(top()); |
| break; |
| } |
| |
| case ENDFINALLY : { |
| Value* val = new LoadInst(supplLocal, "", currentBlock); |
| val = new PtrToIntInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock); |
| SwitchInst* inst = SwitchInst::Create(val, leaves[0], |
| leaves.size(), currentBlock); |
| |
| uint32 index = 0; |
| for (std::vector<BasicBlock*>::iterator i = leaves.begin(), |
| e = leaves.end(); i!= e; ++i, ++index) { |
| inst->addCase(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), index), *i); |
| } |
| |
| //currentBlock = bb2; |
| |
| break; |
| } |
| |
| case JMP : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDARG_S : { |
| push(new LoadInst(arguments[readU1(bytecodes, i)], "", currentBlock)); |
| break; |
| } |
| |
| case LDARG_0 : { |
| push(new LoadInst(arguments[0], "", currentBlock)); |
| break; |
| } |
| |
| case LDARG_1 : { |
| push(new LoadInst(arguments[1], "", currentBlock)); |
| break; |
| } |
| |
| case LDARG_2 : { |
| push(new LoadInst(arguments[2], "", currentBlock)); |
| break; |
| } |
| |
| case LDARG_3 : { |
| push(new LoadInst(arguments[3], "", currentBlock)); |
| break; |
| } |
| |
| case LDARGA_S : { |
| push(arguments[readU1(bytecodes, i)]); |
| break; |
| } |
| |
| case LDC_I4 : { |
| push(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), readS4(bytecodes, i))); |
| break; |
| } |
| |
| case LDC_I8 : { |
| push(ConstantInt::get(Type::getInt64Ty(getGlobalContext()), readS8(bytecodes, i))); |
| break; |
| } |
| |
| case LDC_R4 : { |
| push(ConstantFP::get(Type::getFloatTy(getGlobalContext()), readFloat(bytecodes, i))); |
| break; |
| } |
| |
| case LDC_R8 : { |
| push(ConstantFP::get(Type::getDoubleTy(getGlobalContext()), readDouble(bytecodes, i))); |
| break; |
| } |
| |
| case LDC_I4_0 : { |
| push(module->constantZero); |
| break; |
| } |
| |
| case LDC_I4_1 : { |
| push(module->constantOne); |
| break; |
| } |
| |
| case LDC_I4_2 : { |
| push(module->constantTwo); |
| break; |
| } |
| |
| case LDC_I4_3 : { |
| push(module->constantThree); |
| break; |
| } |
| |
| case LDC_I4_4 : { |
| push(module->constantFour); |
| break; |
| } |
| |
| case LDC_I4_5 : { |
| push(module->constantFive); |
| break; |
| } |
| |
| case LDC_I4_6 : { |
| push(module->constantSix); |
| break; |
| } |
| |
| case LDC_I4_7 : { |
| push(module->constantSeven); |
| break; |
| } |
| |
| case LDC_I4_8 : { |
| push(module->constantEight); |
| break; |
| } |
| |
| case LDC_I4_M1 : { |
| push(module->constantMinusOne); |
| break; |
| } |
| |
| case LDC_I4_S : { |
| push(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), readS1(bytecodes, i))); |
| break; |
| } |
| |
| case LDIND_U1 : |
| case LDIND_I1 : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_U2 : |
| case LDIND_I2 : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getInt16Ty(getGlobalContext())), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_U4 : |
| case LDIND_I4 : { |
| Value* val = pop(); |
| if (val->getType()->isIntegerTy()) { |
| val = new IntToPtrInst(val, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), "", currentBlock); |
| } else { |
| val = new BitCastInst(val, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), "", currentBlock); |
| } |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_I8 : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getInt64Ty(getGlobalContext())), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_R4 : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getFloatTy(getGlobalContext())), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_R8 : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getDoubleTy(getGlobalContext())), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_I : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual( |
| PointerType::getUnqual(Type::getInt8Ty(getGlobalContext()))), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDIND_REF : { |
| Value* _val = pop(); |
| Value* val = new BitCastInst(_val, PointerType::getUnqual( |
| PointerType::getUnqual(VMObject::llvmType)), "", currentBlock); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDLOC_S : { |
| Value* val = load(locals[readU1(bytecodes, i)], "", currentBlock, module); |
| push(val); |
| break; |
| } |
| |
| case LDLOC_0 : { |
| Value* val = load(locals[0], "", currentBlock, module); |
| push(val); |
| break; |
| } |
| |
| case LDLOC_1 : { |
| Value* val = load(locals[1], "", currentBlock, module); |
| push(val); |
| break; |
| } |
| |
| case LDLOC_2 : { |
| Value* val = load(locals[2], "", currentBlock, module); |
| push(val); |
| break; |
| } |
| |
| case LDLOC_3 : { |
| Value* val = load(locals[3], "", currentBlock, module); |
| push(val); |
| break; |
| } |
| |
| case LDLOCA_S : { |
| push(locals[readU1(bytecodes, i)]); |
| break; |
| } |
| |
| case LDNULL : { |
| push(CLIJit::constantVMObjectNull); |
| break; |
| } |
| |
| case LEAVE : { |
| uint32 tmp = i; |
| uint32 index = tmp + 5 + readS4(bytecodes, i); |
| BasicBlock* bb = opcodeInfos[index].newBlock; |
| assert(bb); |
| stack.clear(); |
| if (finallyHandlers.size()) { |
| ExceptionBlockDesc* res = 0; |
| for (std::vector<ExceptionBlockDesc*>::iterator i = finallyHandlers.begin(), |
| e = finallyHandlers.end(); i!= e; ++i) { |
| ExceptionBlockDesc* ex = (*i); |
| if (tmp >= ex->tryOffset && tmp < ex->tryOffset + ex->tryLength) { |
| res = ex; |
| break; |
| } |
| } |
| if (res) { |
| Value* expr = ConstantExpr::getIntToPtr( |
| ConstantInt::get(Type::getInt64Ty(getGlobalContext()), |
| uint64_t (leaveIndex++)), |
| VMObject::llvmType); |
| |
| new StoreInst(expr, supplLocal, false, currentBlock); |
| branch(res->handler, currentBlock); |
| } else { |
| branch(bb, currentBlock); |
| } |
| } else { |
| branch(bb, currentBlock); |
| } |
| break; |
| } |
| |
| case LEAVE_S : { |
| uint32 tmp = i; |
| BasicBlock* bb = opcodeInfos[tmp + 2 + readS1(bytecodes, i)].newBlock; |
| assert(bb); |
| stack.clear(); |
| if (finallyHandlers.size()) { |
| ExceptionBlockDesc* res = 0; |
| for (std::vector<ExceptionBlockDesc*>::iterator i = finallyHandlers.begin(), |
| e = finallyHandlers.end(); i!= e; ++i) { |
| ExceptionBlockDesc* ex = (*i); |
| if (tmp >= ex->tryOffset && tmp < ex->tryOffset + ex->tryLength) { |
| res = ex; |
| break; |
| } |
| } |
| if (res) { |
| Value* expr = ConstantExpr::getIntToPtr( |
| ConstantInt::get(Type::getInt64Ty(getGlobalContext()), |
| uint64_t (leaveIndex++)), |
| VMObject::llvmType); |
| |
| new StoreInst(expr, supplLocal, false, currentBlock); |
| branch(res->handler, currentBlock); |
| } else { |
| branch(bb, currentBlock); |
| } |
| } else { |
| branch(bb, currentBlock); |
| } |
| break; |
| } |
| |
| case MUL : { |
| Value* val2 = pop(); |
| Value* val1 = pop(); |
| convertValue(val1, val2->getType(), currentBlock); |
| push(BinaryOperator::CreateMul(val1, val2, "", currentBlock)); |
| break; |
| } |
| |
| case MUL_OVF : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case MUL_OVF_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case NEG : { |
| Value* val = pop(); |
| push(BinaryOperator::CreateSub( |
| Constant::getNullValue(val->getType()), |
| val, "", currentBlock)); |
| break; |
| } |
| |
| case NOP : break; |
| |
| case NOT : { |
| push(BinaryOperator::CreateNot(pop(), "", currentBlock)); |
| break; |
| } |
| |
| case OR : { |
| Value* two = pop(); |
| Value* one = pop(); |
| push(BinaryOperator::CreateOr(one, two, "", currentBlock)); |
| break; |
| } |
| |
| case POP : { |
| pop(); |
| break; |
| } |
| |
| case REM : { |
| Value* two = pop(); |
| Value* one = pop(); |
| if (one->getType()->isFloatTy()) { |
| push(BinaryOperator::CreateFRem(one, two, "", currentBlock)); |
| } else { |
| push(BinaryOperator::CreateSRem(one, two, "", currentBlock)); |
| } |
| break; |
| } |
| |
| case REM_UN : { |
| Value* two = pop(); |
| Value* one = pop(); |
| if (one->getType()->isFloatTy()) { |
| push(BinaryOperator::CreateFRem(one, two, "", currentBlock)); |
| } else { |
| push(BinaryOperator::CreateURem(one, two, "", currentBlock)); |
| } |
| break; |
| } |
| |
| case RET : { |
| if (compilingMethod->getSignature(genMethod)->getReturnType() != Type::getVoidTy(getGlobalContext())) { |
| Value* val = pop(); |
| if (val->getType() == PointerType::getUnqual(endNode->getType())) { |
| // In case it's a struct |
| val = new LoadInst(val, "", currentBlock); |
| } else { |
| convertValue(val, endNode->getType(), currentBlock); |
| } |
| endNode->addIncoming(val, currentBlock); |
| } else if (compilingMethod->structReturn) { |
| endNode->addIncoming(pop(), currentBlock); |
| } |
| BranchInst::Create(endBlock, currentBlock); |
| break; |
| } |
| |
| case SHL : { |
| Value* val2 = pop(); |
| Value* val1 = pop(); |
| verifyType(val1, val2, currentBlock); |
| push(BinaryOperator::CreateShl(val1, val2, "", currentBlock)); |
| break; |
| } |
| |
| case SHR : { |
| Value* val2 = pop(); |
| Value* val1 = pop(); |
| verifyType(val1, val2, currentBlock); |
| push(BinaryOperator::CreateAShr(val1, val2, "", currentBlock)); |
| break; |
| } |
| |
| case SHR_UN : { |
| Value* val2 = pop(); |
| Value* val1 = pop(); |
| verifyType(val1, val2, currentBlock); |
| push(BinaryOperator::CreateLShr(val1, val2, "", currentBlock)); |
| break; |
| } |
| |
| case STARG_S : { |
| Value* val = pop(); |
| Value* arg = arguments[readU1(bytecodes, i)]; |
| convertValue(val, arg->getType()->getContainedType(0), currentBlock); |
| new StoreInst(val, arg, false, currentBlock); |
| break; |
| } |
| |
| case STIND_I1 : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", |
| currentBlock); |
| convertValue(val, Type::getInt8Ty(getGlobalContext()), currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_I2 : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt16Ty(getGlobalContext())), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_I4 : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_I8 : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt64Ty(getGlobalContext())), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_R4 : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getFloatTy(getGlobalContext())), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_R8 : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getDoubleTy(getGlobalContext())), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_I : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STIND_REF : { |
| Value* val = pop(); |
| Value* _addr = pop(); |
| Value* addr = new BitCastInst(_addr, PointerType::getUnqual(val->getType()), |
| "", currentBlock); |
| new StoreInst(val, addr, isVolatile, currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case STLOC_S : { |
| Value* val = pop(); |
| Value* local = locals[readU1(bytecodes, i)]; |
| store(val, local, false, currentBlock, module); |
| break; |
| } |
| |
| case STLOC_0 : { |
| Value* val = pop(); |
| Value* local = locals[0]; |
| store(val, local, false, currentBlock, module); |
| break; |
| } |
| |
| case STLOC_1 : { |
| Value* val = pop(); |
| Value* local = locals[1]; |
| store(val, local, false, currentBlock, module); |
| break; |
| } |
| |
| case STLOC_2 : { |
| Value* val = pop(); |
| Value* local = locals[2]; |
| store(val, local, false, currentBlock, module); |
| break; |
| } |
| |
| case STLOC_3 : { |
| Value* val = pop(); |
| Value* local = locals[3]; |
| store(val, local, false, currentBlock, module); |
| break; |
| } |
| |
| case SUB : { |
| Value* val2 = pop(); |
| Value* val1 = pop(); |
| verifyType(val1, val2, currentBlock); |
| push(BinaryOperator::CreateSub(val1, val2, "", currentBlock)); |
| break; |
| } |
| |
| case SUB_OVF : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case SUB_OVF_UN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case SWITCH : { |
| uint32 value = readU4(bytecodes, i); |
| Value* val = pop(); |
| uint32 next = i + value * sizeof(sint32) + 1; |
| BasicBlock* defBB = opcodeInfos[next].newBlock; |
| SwitchInst* SI = SwitchInst::Create(val, defBB, value, currentBlock); |
| for (uint32 t = 0; t < value; t++) { |
| sint32 offset = readS4(bytecodes, i); |
| sint32 index = next + offset; |
| assert(index > 0); |
| BasicBlock* BB = opcodeInfos[index].newBlock; |
| SI->addCase(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), t), BB); |
| } |
| break; |
| } |
| |
| case XOR : { |
| Value* two = pop(); |
| Value* one = pop(); |
| convertValue(two, one->getType(), currentBlock); |
| push(BinaryOperator::CreateXor(one, two, "", currentBlock)); |
| break; |
| } |
| |
| case BOX : { |
| uint32 token = readU4(bytecodes, i); |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| VMCommonClass* type = assembly->loadType(vm, token, true, false, false, |
| true, genClass, genMethod); |
| assert(type); |
| |
| if (!type->isPrimitive) { |
| // the box instruction has no effect on non-primitive types |
| break; |
| } |
| |
| Value* var = new LoadInst(type->llvmVar(), "", currentBlock); |
| Value* obj = CallInst::Create(objConsLLVM, var, "", currentBlock); |
| Value* val = pop(); |
| obj = new BitCastInst(obj, type->virtualType, "", currentBlock); |
| |
| |
| std::vector<Value*> ptrs; |
| ptrs.push_back(module->constantZero); |
| ptrs.push_back(module->constantOne); |
| Value* ptr = GetElementPtrInst::Create(obj, ptrs.begin(), ptrs.end(), "", |
| currentBlock); |
| |
| if (val->getType()->getTypeID() != Type::PointerTyID) { |
| convertValue(val, type->naturalType, currentBlock); |
| Value* tmp = new AllocaInst(type->naturalType, "", currentBlock); |
| new StoreInst(val, tmp, false, currentBlock); |
| val = tmp; |
| } |
| |
| |
| uint64 size = mvm::MvmModule::getTypeSize(type->naturalType); |
| |
| std::vector<Value*> params; |
| params.push_back(new BitCastInst(ptr, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size)); |
| params.push_back(module->constantZero); |
| CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock); |
| |
| |
| push(obj); |
| break; |
| } |
| |
| case CALLVIRT : { |
| uint32 value = readU4(bytecodes, i); |
| invokeInterfaceOrVirtual(value, genClass, genMethod); |
| break; |
| } |
| |
| case CASTCLASS : { |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| uint32 token = readU4(bytecodes, i); |
| VMCommonClass* dcl = assembly->loadType(vm, token, true, false, |
| false, true, genClass, genMethod); |
| Value* obj = new BitCastInst(pop(), VMObject::llvmType, "", currentBlock); |
| |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj, |
| CLIJit::constantVMObjectNull, ""); |
| |
| BasicBlock* ifTrue = createBasicBlock("null checkcast"); |
| BasicBlock* ifFalse = createBasicBlock("non null checkcast"); |
| |
| branch(cmp, ifTrue, ifFalse, currentBlock); |
| |
| Value* clVar = new LoadInst(dcl->llvmVar(), "", ifFalse); |
| std::vector<Value*> args; |
| args.push_back(obj); |
| args.push_back(clVar); |
| Value* call = CallInst::Create(instanceOfLLVM, args.begin(), args.end(), |
| "", ifFalse); |
| |
| cmp = new ICmpInst(*ifFalse, ICmpInst::ICMP_EQ, call, |
| module->constantZero, ""); |
| |
| BasicBlock* ex = createBasicBlock("false checkcast"); |
| branch(cmp, ex, ifTrue, ifFalse); |
| |
| std::vector<Value*> exArgs; |
| if (currentExceptionBlock != endExceptionBlock) { |
| InvokeInst::Create(classCastExceptionLLVM, unifiedUnreachable, currentExceptionBlock, exArgs.begin(), exArgs.end(), "", ex); |
| } else { |
| CallInst::Create(classCastExceptionLLVM, exArgs.begin(), exArgs.end(), "", ex); |
| new UnreachableInst(getGlobalContext(), ex); |
| } |
| |
| currentBlock = ifTrue; |
| push(new BitCastInst(obj, dcl->virtualType, "", currentBlock)); |
| break; |
| } |
| |
| |
| case CPOBJ : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case ISINST : { |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| uint32 token = readU4(bytecodes, i); |
| VMCommonClass* dcl = assembly->loadType(vm, token, true, false, |
| false, true, genClass, genMethod); |
| Value* obj = pop(); |
| |
| Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj, |
| Constant::getNullValue(obj->getType()), ""); |
| Constant* nullVirtual = Constant::getNullValue(dcl->virtualType); |
| |
| |
| BasicBlock* isInstEndBlock = createBasicBlock("end isinst"); |
| PHINode* node = PHINode::Create(dcl->virtualType, "", isInstEndBlock); |
| |
| BasicBlock* ifFalse = createBasicBlock("non null isinst"); |
| BasicBlock* ifTrue = createBasicBlock("null isinst"); |
| |
| BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock); |
| node->addIncoming(nullVirtual, ifTrue); |
| BranchInst::Create(isInstEndBlock, ifTrue); |
| |
| |
| Value* clVar = new LoadInst(dcl->llvmVar(), "", ifFalse); |
| std::vector<Value*> args; |
| args.push_back(new BitCastInst(obj, VMObject::llvmType, "", ifFalse)); |
| args.push_back(clVar); |
| Value* call = CallInst::Create(instanceOfLLVM, args.begin(), args.end(), |
| "", ifFalse); |
| |
| cmp = new ICmpInst(*ifFalse, ICmpInst::ICMP_EQ, call, |
| module->constantZero, ""); |
| |
| BasicBlock* falseInst = createBasicBlock("false isinst"); |
| BasicBlock* trueInst = createBasicBlock("true isinst"); |
| BranchInst::Create(falseInst, trueInst, cmp, ifFalse); |
| |
| node->addIncoming(new BitCastInst(obj, dcl->virtualType, "", trueInst), trueInst); |
| BranchInst::Create(isInstEndBlock, trueInst); |
| |
| node->addIncoming(nullVirtual, falseInst); |
| BranchInst::Create(isInstEndBlock, falseInst); |
| |
| currentBlock = isInstEndBlock; |
| push(node); |
| break; |
| } |
| |
| case LDELEM : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDELEM_I1 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDELEM_I2 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArraySInt16::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_I4 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArraySInt32::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_I8 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt64::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_U1 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt8::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_U2 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt16::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_U4 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt32::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_R4 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayFloat::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_R8 : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayDouble::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_I : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArraySInt32::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEM_REF : { |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayObject::llvmType); |
| push(new LoadInst(ptr, "", currentBlock)); |
| break; |
| } |
| |
| case LDELEMA : { |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| uint32 token = readU4(bytecodes, i); |
| VMCommonClass* cl = assembly->loadType(vm, token, true, false, |
| false, true, genClass, genMethod); |
| VMClassArray* array = assembly->constructArray(cl, 1); |
| array->resolveType(false, false, genMethod); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, array->naturalType); |
| push(ptr); |
| break; |
| } |
| |
| case LDFLD : { |
| uint32 value = readU4(bytecodes, i); |
| Value* val = getVirtualField(value, genClass, genMethod); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDFLDA : { |
| uint32 value = readU4(bytecodes, i); |
| push(getVirtualField(value, genClass, genMethod)); |
| break; |
| } |
| |
| case LDLEN : { |
| push(arraySize(pop())); |
| break; |
| } |
| |
| case LDOBJ : { |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| uint32 token = readU4(bytecodes, i); |
| VMCommonClass* cl = assembly->loadType(vm, token, true, false, |
| false, true, genClass, genMethod); |
| if (!(cl->super == MSCorlib::pValue || cl->super == MSCorlib::pEnum)) { |
| push(new LoadInst(pop(), "", isVolatile, currentBlock)); |
| isVolatile = false; |
| } |
| break; |
| } |
| |
| case LDSFLD : { |
| uint32 value = readU4(bytecodes, i); |
| Value* val = getStaticField(value, genClass, genMethod); |
| push(new LoadInst(val, "", isVolatile, currentBlock)); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDSFLDA : { |
| uint32 value = readU4(bytecodes, i); |
| push(getStaticField(value, genClass, genMethod)); |
| break; |
| } |
| |
| case LDSTR : { |
| uint32 value = readU4(bytecodes, i); |
| uint32 index = value & 0xfffffff; |
| // must modify this opcode |
| declare_gcroot(const ArrayChar*, array) = compilingClass->assembly->readUserString(index); |
| Value* val = ConstantExpr::getIntToPtr(ConstantInt::get(Type::getInt64Ty(getGlobalContext()), (int64_t)array), |
| module->ptrType); |
| Value* res = CallInst::Create(newStringLLVM, val, "", currentBlock); |
| /*CLIString * str = |
| (CLIString*)(((N3*)VMThread::get()->getVM())->UTF8ToStr(utf8)); |
| GlobalVariable* gv = str->llvmVar(); |
| push(new BitCastInst(new LoadInst(gv, "", currentBlock), |
| MSCorlib::pString->naturalType, "", currentBlock));*/ |
| push(new BitCastInst(res, MSCorlib::pString->naturalType, "", currentBlock)); |
| break; |
| } |
| |
| case LDTOKEN : { |
| uint32 token = readU4(bytecodes, i); |
| uint32 table = token >> 24; |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| switch (table) { |
| case CONSTANT_Field : { |
| uint32 typeToken = assembly->getTypedefTokenFromField(token); |
| assembly->loadType(vm, typeToken, true, true, false, true, genClass, genMethod); |
| VMField* field = assembly->lookupFieldFromToken(token); |
| if (!field) { |
| VMThread::get()->getVM()->error("implement me"); |
| } |
| Value* arg = new LoadInst(field->llvmVar(), "", currentBlock); |
| push(arg); |
| break; |
| } |
| |
| case CONSTANT_MethodDef : { |
| uint32 typeToken = assembly->getTypedefTokenFromMethod(token); |
| assembly->loadType(vm, typeToken, true, true, false, true, genClass, genMethod); |
| VMMethod* meth = assembly->lookupMethodFromToken(token); |
| if (!meth) { |
| VMThread::get()->getVM()->error("implement me"); |
| } |
| Value* arg = new LoadInst(meth->llvmVar(), "", currentBlock); |
| push(arg); |
| break; |
| } |
| |
| case CONSTANT_TypeDef : |
| case CONSTANT_TypeRef : { |
| VMCommonClass* cl = assembly->loadType(vm, token, true, false, |
| false, true, genClass, genMethod); |
| Value* arg = new LoadInst(cl->llvmVar(), "", currentBlock); |
| push(arg); |
| break; |
| } |
| |
| default : |
| VMThread::get()->getVM()->error("implement me"); |
| } |
| break; |
| } |
| |
| case MKREFANY : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case NEWARR : { |
| uint32 value = readU4(bytecodes, i); |
| Assembly* ass = compilingClass->assembly; |
| VMCommonClass* baseType = ass->loadType((N3*)(VMThread::get()->getVM()), |
| value, true, false, false, |
| true, genClass, genMethod); |
| |
| VMClassArray* type = ass->constructArray(baseType, 1); |
| type->resolveType(false, false, genMethod); |
| Value* var = new LoadInst(type->llvmVar(), "", currentBlock); |
| std::vector<Value*> args; |
| args.push_back(var); |
| args.push_back(pop()); |
| Value* val = CallInst::Create(arrayConsLLVM, args.begin(), args.end(), "", |
| currentBlock); |
| push(new BitCastInst(val, type->naturalType, "", currentBlock)); |
| break; |
| } |
| |
| case NEWOBJ : { |
| uint32 value = readU4(bytecodes, i); |
| invokeNew(value, genClass, genMethod); |
| break; |
| } |
| |
| case REFANYVAL : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case STELEM : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case STELEM_I1 : { |
| Value* val = pop(); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArraySInt8::llvmType); |
| convertValue(val, Type::getInt8Ty(getGlobalContext()), currentBlock); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STELEM_I2 : { |
| Value* val = pop(); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArraySInt16::llvmType); |
| convertValue(val, Type::getInt16Ty(getGlobalContext()), currentBlock); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STELEM_I4 : { |
| Value* val = pop(); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArraySInt32::llvmType); |
| convertValue(val, Type::getInt32Ty(getGlobalContext()), currentBlock); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STELEM_I8 : { |
| Value* val = pop(); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt64::llvmType); |
| convertValue(val, Type::getInt64Ty(getGlobalContext()), currentBlock); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STELEM_R4 : { |
| Value* val = pop(); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayFloat::llvmType); |
| convertValue(val, Type::getFloatTy(getGlobalContext()), currentBlock); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STELEM_R8 : { |
| Value* val = pop(); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayDouble::llvmType); |
| convertValue(val, Type::getDoubleTy(getGlobalContext()), currentBlock); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STELEM_I : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case STELEM_REF : { |
| Value* val = new BitCastInst(pop(), VMObject::llvmType, "", |
| currentBlock); |
| Value* index = pop(); |
| Value* obj = pop(); |
| Value* ptr = verifyAndComputePtr(obj, index, ArrayObject::llvmType); |
| new StoreInst(val, ptr, false, currentBlock); |
| break; |
| } |
| |
| case STFLD : { |
| uint32 index = readU4(bytecodes, i); |
| setVirtualField(index, isVolatile, genClass, genMethod); |
| isVolatile = false; |
| break; |
| } |
| |
| case STOBJ : { |
| VMThread::get()->getVM()->error("implement me"); |
| isVolatile = false; |
| break; |
| } |
| |
| case STSFLD : { |
| uint32 index = readU4(bytecodes, i); |
| setStaticField(index, isVolatile, genClass, genMethod); |
| isVolatile = false; |
| break; |
| } |
| |
| case THROW : { |
| llvm::Value* arg = pop(); |
| arg = new BitCastInst(arg, VMObject::llvmType, "", currentBlock); |
| std::vector<Value*> args; |
| args.push_back(arg); |
| if (currentExceptionBlock != endExceptionBlock) { |
| InvokeInst::Create(throwExceptionLLVM, unifiedUnreachable, currentExceptionBlock, args.begin(), args.end(), "", currentBlock); |
| } else { |
| CallInst::Create(throwExceptionLLVM, args.begin(), args.end(), "", currentBlock); |
| new UnreachableInst(getGlobalContext(), currentBlock); |
| } |
| break; |
| } |
| |
| case UNBOX : { |
| uint32 token = readU4(bytecodes, i); |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| VMCommonClass* type = assembly->loadType(vm, token, true, false, false, |
| true, genClass, genMethod); |
| assert(type); |
| |
| Value* val = new AllocaInst(type->naturalType, "", currentBlock); |
| Value* obj = pop(); |
| |
| if (obj->getType() != type->virtualType) { |
| obj = new BitCastInst(obj, type->virtualType, "", currentBlock); |
| } |
| |
| std::vector<Value*> ptrs; |
| ptrs.push_back(module->constantZero); |
| ptrs.push_back(module->constantOne); |
| Value* ptr = GetElementPtrInst::Create(obj, ptrs.begin(), ptrs.end(), "", |
| currentBlock); |
| |
| uint64 size = mvm::MvmModule::getTypeSize(type->naturalType); |
| |
| std::vector<Value*> params; |
| params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(new BitCastInst(ptr, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock)); |
| params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size)); |
| params.push_back(module->constantZero); |
| CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock); |
| |
| |
| push(val); |
| break; |
| } |
| |
| case UNBOX_ANY : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case 0xFE : { |
| PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i, |
| bytecodes[i + 1]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "compiling %s::", mvm::PrintBuffer(compilingMethod).cString()); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNamesFE[bytecodes[i + 1]]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n"); |
| |
| switch (bytecodes[++i]) { |
| |
| #define TEST(name, cmpf, cmpi) case name : { \ |
| Value* val2 = pop(); \ |
| Value* val1 = pop(); \ |
| Value* test = 0; \ |
| if (val1->getType()->isFloatTy()) { \ |
| test = new FCmpInst(*currentBlock, FCmpInst::cmpf, val1, val2, ""); \ |
| } else { \ |
| convertValue(val2, val1->getType(), currentBlock); \ |
| test = new ICmpInst(*currentBlock, ICmpInst::cmpi, val1, val2, ""); \ |
| } \ |
| push(test); \ |
| break; \ |
| } |
| |
| TEST(CEQ, FCMP_OEQ, ICMP_EQ); |
| TEST(CGT, FCMP_OGT, ICMP_SGT); |
| TEST(CGT_UN, FCMP_UGT, ICMP_UGT); |
| TEST(CLT, FCMP_OLT, ICMP_SLT); |
| TEST(CLT_UN, FCMP_ULT, ICMP_ULT); |
| |
| #undef TEST |
| |
| case CPBLK : { |
| Value* three = pop(); |
| Value* two = pop(); |
| Value* one = pop(); |
| std::vector<Value*> args; |
| args.push_back(one); |
| args.push_back(two); |
| args.push_back(three); |
| CallInst::Create(module->llvm_memcpy_i32, |
| args.begin(), args.end(), "", currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case ENDFILTER: { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDARG : { |
| push(new LoadInst(arguments[readU2(bytecodes, i)], "", currentBlock)); |
| break; |
| } |
| |
| case LDARGA : { |
| push(arguments[readU2(bytecodes, i)]); |
| break; |
| } |
| |
| case LDFTN : { |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| uint32 token = readU4(bytecodes, i); |
| uint32 table = token >> 24; |
| if (table == CONSTANT_MethodDef) { |
| uint32 typeToken = assembly->getTypedefTokenFromMethod(token); |
| assembly->loadType(vm, typeToken, true, false, false, true, genClass, genMethod); |
| VMMethod* meth = assembly->lookupMethodFromToken(token); |
| if (!meth) VMThread::get()->getVM()->error("implement me"); |
| Value* arg = new LoadInst(meth->llvmVar(), "", currentBlock); |
| push(arg); |
| } else { |
| VMThread::get()->getVM()->error("implement me"); |
| } |
| break; |
| } |
| |
| case INITBLK : { |
| Value* three = pop(); |
| Value* two = pop(); |
| Value* one = pop(); |
| std::vector<Value*> args; |
| args.push_back(one); |
| args.push_back(two); |
| args.push_back(three); |
| CallInst::Create(module->llvm_memset_i32, |
| args.begin(), args.end(), "", currentBlock); |
| isVolatile = false; |
| break; |
| } |
| |
| case LDLOC : { |
| push(new LoadInst(locals[readU2(bytecodes, i)], "", currentBlock)); |
| break; |
| } |
| |
| case LOCALLOC : { |
| push(new AllocaInst(Type::getInt8Ty(getGlobalContext()), pop(), "", currentBlock)); |
| break; |
| } |
| |
| case STARG : { |
| new StoreInst(pop(), arguments[readU2(bytecodes, i)], false, currentBlock); |
| break; |
| } |
| |
| case STLOC : { |
| Value* val = pop(); |
| Value* local = locals[readU2(bytecodes, i)]; |
| store(val, local, false, currentBlock, module); |
| break; |
| } |
| |
| case LDLOCA : { |
| push(locals[readU2(bytecodes, i)]); |
| break; |
| } |
| |
| case ARGLIST : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case INITOBJ : { |
| uint32 token = readU4(bytecodes, i); |
| Assembly* assembly = compilingClass->assembly; |
| N3* vm = (N3*)(VMThread::get()->getVM()); |
| VMCommonClass* type = assembly->loadType(vm, token, true, false, false, |
| true, genClass, genMethod); |
| if (type->super == MSCorlib::pValue) { |
| uint64 size = mvm::MvmModule::getTypeSize(type->naturalType); |
| |
| std::vector<Value*> params; |
| params.push_back(new BitCastInst(pop(), module->ptrType, "", |
| currentBlock)); |
| params.push_back(module->constantInt8Zero); |
| params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size)); |
| params.push_back(module->constantZero); |
| CallInst::Create(module->llvm_memset_i32, params.begin(), |
| params.end(), "", currentBlock); |
| } |
| |
| break; |
| } |
| |
| case LDVIRTFTN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case REFANYTYPE : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case RETHROW : { |
| std::vector<Value*> args; |
| if (opinfo->exception) { |
| args.push_back(opinfo->exception); |
| } else { |
| args.push_back(CLIJit::constantVMObjectNull); |
| } |
| if (currentExceptionBlock != endExceptionBlock) { |
| InvokeInst::Create(throwExceptionLLVM, unifiedUnreachable, currentExceptionBlock, args.begin(), args.end(), "", currentBlock); |
| } else { |
| CallInst::Create(throwExceptionLLVM, args.begin(), args.end(), "", currentBlock); |
| new UnreachableInst(getGlobalContext(), currentBlock); |
| } |
| break; |
| } |
| |
| case SIZEOF : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case VOLATILE_ : { |
| isVolatile = true; |
| break; |
| } |
| default : |
| VMThread::get()->getVM()->unknownError("unknown bytecode"); |
| } |
| break; |
| } |
| |
| default : |
| VMThread::get()->getVM()->unknownError("unknown bytecode"); |
| } |
| } |
| } |
| |
| void CLIJit::exploreOpcodes(uint8* bytecodes, uint32 codeLength) { |
| for(uint32 i = 0; i < codeLength; ++i) { |
| |
| if (bytecodes[i] != 0xFE) { |
| PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i, |
| bytecodes[i]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "exploring %s::", mvm::PrintBuffer(compilingMethod).cString()); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNames[bytecodes[i]]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n"); |
| } |
| |
| switch (bytecodes[i]) { |
| |
| case ADD: |
| case ADD_OVF: |
| case ADD_OVF_UN: |
| case AND: break; |
| |
| #define TEST(name, read, offset) case name : { \ |
| uint32 tmp = i; \ |
| uint16 index = tmp + offset + read(bytecodes, i); \ |
| if (!(opcodeInfos[index].newBlock)) \ |
| opcodeInfos[index].newBlock = createBasicBlock("Branches"); \ |
| break; \ |
| } |
| |
| TEST(BEQ, readS4, 5); |
| TEST(BEQ_S, readS1, 2); |
| |
| TEST(BGE, readS4, 5); |
| TEST(BGE_S, readS1, 2); |
| TEST(BGE_UN, readS4, 5); |
| TEST(BGE_UN_S, readS1, 2); |
| |
| TEST(BGT, readS4, 5); |
| TEST(BGT_S, readS1, 2); |
| TEST(BGT_UN, readS4, 5); |
| TEST(BGT_UN_S, readS1, 2); |
| |
| TEST(BLE, readS4, 5); |
| TEST(BLE_S, readS1, 2); |
| TEST(BLE_UN, readS4, 5); |
| TEST(BLE_UN_S, readS1, 2); |
| |
| TEST(BLT, readS4, 5); |
| TEST(BLT_S, readS1, 2); |
| TEST(BLT_UN, readS4, 5); |
| TEST(BLT_UN_S, readS1, 2); |
| |
| TEST(BNE_UN, readS4, 5); |
| TEST(BNE_UN_S, readS1, 2); |
| |
| case BR : { |
| uint32 tmp = i; |
| uint16 index = tmp + 5 + readS4(bytecodes, i); |
| if (!(opcodeInfos[index].newBlock)) |
| opcodeInfos[index].newBlock = createBasicBlock("BR"); |
| break; |
| } |
| |
| case BR_S : { |
| uint32 tmp = i; |
| uint16 index = tmp + 2 + readS1(bytecodes, i); |
| if (!(opcodeInfos[index].newBlock)) |
| opcodeInfos[index].newBlock = createBasicBlock("BR"); |
| break; |
| } |
| |
| case BREAK: break; |
| |
| TEST(BRFALSE, readS4, 5); |
| TEST(BRFALSE_S, readS1, 2); |
| TEST(BRTRUE, readS4, 5); |
| TEST(BRTRUE_S, readS1, 2); |
| |
| #undef TEST |
| |
| case CALL: { |
| i+= 4; |
| break; |
| } |
| |
| case CALLI: { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| break; |
| } |
| |
| case CKFINITE : { |
| VMThread::get()->getVM()->unknownError("implement me"); |
| break; |
| } |
| |
| case CONV_I1 : |
| case CONV_I2 : |
| case CONV_I4 : |
| case CONV_I8 : |
| case CONV_R4 : |
| case CONV_R8 : |
| case CONV_U1 : |
| case CONV_U2 : |
| case CONV_U4 : |
| case CONV_U8 : |
| case CONV_I : |
| case CONV_U : |
| case CONV_R_UN : |
| case CONV_OVF_I1 : |
| case CONV_OVF_I2 : |
| case CONV_OVF_I4 : |
| case CONV_OVF_I8 : |
| case CONV_OVF_U1 : |
| case CONV_OVF_U2 : |
| case CONV_OVF_U4 : |
| case CONV_OVF_U8 : |
| case CONV_OVF_I : |
| case CONV_OVF_U : |
| case CONV_OVF_I1_UN : |
| case CONV_OVF_I2_UN : |
| case CONV_OVF_I4_UN : |
| case CONV_OVF_I8_UN : |
| case CONV_OVF_U1_UN : |
| case CONV_OVF_U2_UN : |
| case CONV_OVF_U4_UN : |
| case CONV_OVF_U8_UN : |
| case DIV: |
| case DIV_UN: |
| case DUP: |
| case ENDFINALLY : break; |
| |
| case JMP : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDARG_S : { |
| i+= 1; |
| break; |
| } |
| |
| case LDARG_0 : |
| case LDARG_1 : |
| case LDARG_2 : |
| case LDARG_3 : break; |
| |
| case LDARGA_S : { |
| i += 1; |
| break; |
| } |
| |
| case LDC_I4 : { |
| i += 4; |
| break; |
| } |
| |
| case LDC_I8 : { |
| i += 8; |
| break; |
| } |
| |
| case LDC_R4 : { |
| i += 4; |
| break; |
| } |
| |
| case LDC_R8 : { |
| i += 8; |
| break; |
| } |
| |
| case LDC_I4_0 : |
| case LDC_I4_1 : |
| case LDC_I4_2 : |
| case LDC_I4_3 : |
| case LDC_I4_4 : |
| case LDC_I4_5 : |
| case LDC_I4_6 : |
| case LDC_I4_7 : |
| case LDC_I4_8 : |
| case LDC_I4_M1 : break; |
| |
| case LDC_I4_S : { |
| i += 1; |
| break; |
| } |
| |
| case LDIND_U1 : |
| case LDIND_I1 : |
| case LDIND_U2 : |
| case LDIND_I2 : |
| case LDIND_U4 : |
| case LDIND_I4 : |
| case LDIND_I8 : |
| case LDIND_R4 : |
| case LDIND_R8 : |
| case LDIND_I : break; |
| |
| case LDIND_REF : { |
| break; |
| } |
| |
| case LDLOC_S : { |
| i += 1; |
| break; |
| } |
| |
| case LDLOC_0 : |
| case LDLOC_1 : |
| case LDLOC_2 : |
| case LDLOC_3 : break; |
| |
| case LDLOCA_S : { |
| i += 1; |
| break; |
| } |
| |
| case LDNULL : break; |
| |
| case LEAVE : { |
| uint32 tmp = i; |
| uint32 value = readS4(bytecodes, i); |
| uint32 index = tmp + 5 + value; |
| if (!(opcodeInfos[index].newBlock)) |
| opcodeInfos[index].newBlock = createBasicBlock("LEAVE"); |
| leaves.push_back(opcodeInfos[index].newBlock); |
| break; |
| } |
| |
| case LEAVE_S : { |
| uint32 tmp = i; |
| uint8 value = readS1(bytecodes, i); |
| uint32 index = tmp + 2 + value; |
| if (!(opcodeInfos[index].newBlock)) |
| opcodeInfos[index].newBlock = createBasicBlock("LEAVE_S"); |
| leaves.push_back(opcodeInfos[index].newBlock); |
| break; |
| } |
| |
| case MUL : |
| case MUL_OVF : |
| case MUL_OVF_UN : |
| case NEG : |
| case NOP : |
| case NOT : |
| case OR : |
| case POP : |
| case REM : |
| case REM_UN : |
| case RET : |
| case SHL : |
| case SHR : |
| case SHR_UN : break; |
| |
| case STARG_S : { |
| i += 1; |
| break; |
| } |
| |
| case STIND_I1 : |
| case STIND_I2 : |
| case STIND_I4 : |
| case STIND_I8 : |
| case STIND_R4 : |
| case STIND_R8 : |
| case STIND_I : |
| case STIND_REF : break; |
| |
| case STLOC_S : { |
| i += 1; |
| break; |
| } |
| |
| case STLOC_0 : |
| case STLOC_1 : |
| case STLOC_2 : |
| case STLOC_3 : |
| case SUB : |
| case SUB_OVF : |
| case SUB_OVF_UN : break; |
| |
| case SWITCH : { |
| uint32 value = readU4(bytecodes, i); |
| uint32 next = i + value * sizeof(sint32) + 1; |
| for (uint32 t = 0; t < value; t++) { |
| sint32 offset = readS4(bytecodes, i); |
| sint32 index = next + offset; |
| assert(index > 0); |
| if (!(opcodeInfos[index].newBlock)) { |
| BasicBlock* block = createBasicBlock("switch"); |
| opcodeInfos[index].newBlock = block; |
| } |
| } |
| if (!(opcodeInfos[i + 1].newBlock)) { |
| BasicBlock* block = createBasicBlock("switch"); |
| opcodeInfos[i + 1].newBlock = block; |
| } |
| break; |
| } |
| |
| case XOR : break; |
| |
| case BOX : { |
| i += 4; |
| break; |
| } |
| |
| case CALLVIRT : { |
| i += 4; |
| break; |
| } |
| |
| case CASTCLASS : { |
| i += 4; |
| break; |
| } |
| |
| case CPOBJ : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case ISINST : { |
| i += 4; |
| break; |
| } |
| |
| case LDELEM : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDELEM_I1 : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDELEM_I2 : |
| case LDELEM_I4 : |
| case LDELEM_I8 : |
| case LDELEM_U1 : |
| case LDELEM_U2 : |
| case LDELEM_U4 : |
| case LDELEM_R4 : |
| case LDELEM_R8 : |
| case LDELEM_I : |
| case LDELEM_REF : |
| case LDELEMA : break; |
| |
| case LDFLD : { |
| i += 4; |
| break; |
| } |
| |
| case LDFLDA : { |
| i += 4; |
| break; |
| } |
| |
| case LDLEN : break; |
| |
| case LDOBJ : { |
| i += 4; |
| break; |
| } |
| |
| case LDSFLD : { |
| i += 4; |
| break; |
| } |
| |
| case LDSFLDA : { |
| i += 4; |
| break; |
| } |
| |
| case LDSTR : { |
| i += 4; |
| break; |
| } |
| |
| case LDTOKEN : { |
| i += 4; |
| break; |
| } |
| |
| case MKREFANY : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case NEWARR : { |
| i += 4; |
| break; |
| } |
| |
| case NEWOBJ : { |
| i += 4; |
| break; |
| } |
| |
| case REFANYVAL : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case STELEM : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case STELEM_I1 : |
| case STELEM_I2 : |
| case STELEM_I4 : |
| case STELEM_I8 : |
| case STELEM_R4 : |
| case STELEM_R8 : |
| case STELEM_I : |
| case STELEM_REF : break; |
| |
| case STFLD : { |
| i += 4; |
| break; |
| } |
| |
| case STOBJ : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case STSFLD : { |
| i += 4; |
| break; |
| } |
| |
| case THROW : { |
| break; |
| } |
| |
| case UNBOX : { |
| i += 4; |
| break; |
| } |
| |
| case UNBOX_ANY : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case 0xFE : { |
| |
| PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i, |
| bytecodes[i + 1]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "exploring %s::", mvm::PrintBuffer(compilingMethod).cString()); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNamesFE[bytecodes[i + 1]]); |
| PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n"); |
| |
| switch (bytecodes[++i]) { |
| |
| case CEQ: |
| case CGT: |
| case CGT_UN: |
| case CLT: |
| case CLT_UN: |
| case CPBLK : break; |
| |
| case ENDFILTER: { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case LDARG : { |
| i += 2; |
| break; |
| } |
| |
| case LDARGA : { |
| i += 2; |
| break; |
| } |
| |
| case LDFTN : { |
| i += 4; |
| break; |
| } |
| |
| case INITBLK : break; |
| |
| case LDLOC : { |
| i += 2; |
| break; |
| } |
| |
| case LOCALLOC : break; |
| |
| case STARG : { |
| i += 2; |
| break; |
| } |
| |
| case STLOC : { |
| i += 2; |
| break; |
| } |
| |
| case LDLOCA : { |
| i += 2; |
| break; |
| } |
| |
| case ARGLIST : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case INITOBJ : { |
| i += 4; |
| break; |
| } |
| |
| case LDVIRTFTN : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case REFANYTYPE : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case RETHROW : { |
| break; |
| } |
| |
| case SIZEOF : { |
| VMThread::get()->getVM()->error("implement me"); |
| break; |
| } |
| |
| case VOLATILE_ : { |
| break; |
| } |
| default : |
| VMThread::get()->getVM()->unknownError("unknown bytecode"); |
| } |
| break; |
| } |
| |
| default : |
| VMThread::get()->getVM()->unknownError("unknown bytecode"); |
| } |
| } |
| } |