static bool getFragmentToCompile(Core &core, uint32_t startAddress, std::vector<InstructionOpcode> &opcode, std::vector<Operands> &operands, bool &endOfBlock, uint32_t &nextAddress) { uint32_t address = startAddress; opcode.clear(); operands.clear(); endOfBlock = false; nextAddress = address; InstructionOpcode opc; Operands ops; InstructionProperties *properties; do { if (!getInstruction(core, address, opc, ops)) { endOfBlock = true; break; } instructionTransform(opc, ops, core, address); properties = &instructionProperties[opc]; nextAddress = address + properties->size; if (properties->mayBranch()) endOfBlock = true; if (!properties->function) break; opcode.push_back(opc); operands.push_back(ops); address = nextAddress; } while (!properties->mayBranch()); return !opcode.empty(); }
static Register::Reg getOperandRegister(const InstructionProperties &properties, const Operands &ops, unsigned i) { if (i >= properties.getNumExplicitOperands()) return properties.getImplicitOperand(i - properties.getNumExplicitOperands()); if (properties.getNumExplicitOperands() > 3) return static_cast<Register::Reg>(ops.lops[i]); return static_cast<Register::Reg>(ops.ops[i]); }
static uint32_t getOperand(const InstructionProperties &properties, const Operands &operands, unsigned i) { if (properties.getNumExplicitOperands() > 3) { return operands.lops[i]; } return operands.ops[i]; }
/// Try and compile a fragment starting at the specified address. Returns /// true if successful setting \a nextAddress to the first instruction after /// the fragment. If unsuccessful returns false and sets \a nextAddress to the /// address after the current function. \a endOfBlock is set to true if the /// next address is in a new basic block. bool JITImpl:: compileOneFragment(Core &core, JITCoreInfo &coreInfo, uint32_t startPc, bool &endOfBlock, uint32_t &pcAfterFragment) { assert(initialized); resetPerFunctionState(); auto infoIt = coreInfo.functionMap.find(startPc); JITFunctionInfo *info = (infoIt == coreInfo.functionMap.end()) ? 0 : infoIt->second; if (info && !info->isStub) { endOfBlock = true; return false; } std::vector<InstructionOpcode> opcode; std::vector<Operands> operands; if (!getFragmentToCompile(core, core.fromRamPc(startPc), opcode, operands, endOfBlock, pcAfterFragment)) { pcAfterFragment = core.toRamPc(pcAfterFragment); return false; } std::queue<std::pair<uint32_t,MemoryCheck*> > checks; placeMemoryChecks(opcode, operands, checks); LLVMValueRef f; if (info) { f = info->value; info->func = 0; info->isStub = false; deleteFunctionBody(f); } else { info = new JITFunctionInfo(startPc); coreInfo.functionMap.insert(std::make_pair(startPc, info)); // Create function to contain the code we are about to add. info->value = f = LLVMAddFunction(module, "", jitFunctionType); LLVMSetFunctionCallConv(f, LLVMFastCallConv); } threadParam = LLVMGetParam(f, 0); LLVMValueRef ramBase = LLVMConstInt(LLVMInt32TypeInContext(context), core.getRamBase(), false); ramSizeLog2Param = LLVMConstInt(LLVMInt32TypeInContext(context), core.getRamSizeLog2(), false); LLVMBasicBlockRef entryBB = LLVMAppendBasicBlockInContext(context, f, "entry"); LLVMPositionBuilderAtEnd(builder, entryBB); uint32_t pc = startPc; bool needsReturn = true; for (unsigned i = 0, e = opcode.size(); i != e; ++i) { InstructionOpcode opc = opcode[i]; const Operands &ops = operands[i]; InstructionProperties *properties = &instructionProperties[opc]; uint32_t nextPc = pc + properties->size / 2; emitMemoryChecks(i, checks); // Lookup function to call. LLVMValueRef callee = LLVMGetNamedFunction(module, properties->function); assert(callee && "Function for instruction not found in module"); LLVMTypeRef calleeType = LLVMGetElementType(LLVMTypeOf(callee)); const unsigned fixedArgs = 4; const unsigned maxOperands = 6; unsigned numArgs = properties->getNumExplicitOperands() + fixedArgs; assert(LLVMCountParamTypes(calleeType) == numArgs); LLVMTypeRef paramTypes[fixedArgs + maxOperands]; assert(numArgs <= (fixedArgs + maxOperands)); LLVMGetParamTypes(calleeType, paramTypes); // Build call. LLVMValueRef args[fixedArgs + maxOperands]; args[0] = threadParam; args[1] = LLVMConstInt(paramTypes[1], nextPc, false); args[2] = ramBase; args[3] = ramSizeLog2Param; for (unsigned i = fixedArgs; i < numArgs; i++) { uint32_t value = properties->getNumExplicitOperands() <= 3 ? ops.ops[i - fixedArgs] : ops.lops[i - fixedArgs]; args[i] = LLVMConstInt(paramTypes[i], value, false); } LLVMValueRef call = emitCallToBeInlined(callee, args, numArgs); checkReturnValue(call, *properties); if (properties->mayBranch() && properties->function && emitJumpToNextFragment(opc, ops, coreInfo, nextPc, info)) { needsReturn = false; } pc = nextPc; } assert(checks.empty() && "Not all checks emitted"); if (needsReturn) { LLVMValueRef args[] = { threadParam }; emitCallToBeInlined(functions.jitUpdateExecutionFrequency, args, 1); // Build return. LLVMBuildRet(builder, LLVMConstInt(LLVMGetReturnType(jitFunctionType), JIT_RETURN_CONTINUE, 0)); } // Add incoming phi values. if (earlyReturnBB) { LLVMAddIncoming(earlyReturnPhi, &earlyReturnIncomingValues[0], &earlyReturnIncomingBlocks[0], earlyReturnIncomingValues.size()); } if (DEBUG_JIT) { LLVMDumpValue(f); LLVMVerifyFunction(f, LLVMAbortProcessAction); } // Optimize. for (LLVMValueRef call : calls) { LLVMExtraInlineFunction(call); } LLVMRunFunctionPassManager(FPM, f); if (DEBUG_JIT) { LLVMDumpValue(f); } // Compile. JITInstructionFunction_t compiledFunction = reinterpret_cast<JITInstructionFunction_t>( LLVMRecompileAndRelinkFunction(executionEngine, f)); info->isStub = false; info->func = compiledFunction; core.setOpcode(startPc, getFunctionThunk(*info), (pc - startPc) * 2); return true; }
static bool mayReturnEarly(InstructionProperties &properties) { return properties.mayYield() || properties.mayEndTrace() || properties.mayDeschedule(); }