From 40eb706b75529a81235ff5428d448542932b9555 Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 9 Jul 2016 11:24:20 +0600 Subject: [PATCH] Adds initial inference logic to the MethodCompiler and JitRuntime Issue: #17 Issue: #92 --- include/inference.h | 12 +++ include/jit.h | 54 +++++++---- src/JITRuntime.cpp | 65 ++++++++++--- src/MethodCompiler.cpp | 204 ++++++++++++++++++----------------------- src/TypeAnalyzer.cpp | 1 + 5 files changed, 189 insertions(+), 147 deletions(-) diff --git a/include/inference.h b/include/inference.h index a71f8cf..b274e41 100644 --- a/include/inference.h +++ b/include/inference.h @@ -510,6 +510,18 @@ class TypeAnalyzer { const Type* m_blockType; }; +inline type::Type createArgumentsType(TObjectArray* arguments) { + type::Type result(globals.arrayClass, type::Type::tkArray); + + for (std::size_t i = 0; i < arguments->getSize(); i++) { + TObject* const argument = arguments->getField(i); + TClass* const klass = isSmallInteger(argument) ? globals.smallIntClass : argument->getClass(); + result.pushSubType(type::Type(klass)); + } + + return result; +} + } // namespace type #endif // LLST_INFERENCE_H_INCLUDED diff --git a/include/jit.h b/include/jit.h index d0a0002..50fdea5 100644 --- a/include/jit.h +++ b/include/jit.h @@ -35,6 +35,7 @@ #include #include "vm.h" #include "analysis.h" +#include "inference.h" #include @@ -206,6 +207,7 @@ class MethodCompiler { TPhiList pendingPhiNodes; TMethod* originMethod; // Smalltalk method we're currently processing + type::InferContext& inferContext; llvm::Function* function; // LLVM function that is created based on method llvm::IRBuilder<>* builder; // Builder inserts instructions into basic blocks @@ -228,10 +230,26 @@ class MethodCompiler { llvm::Value* getMethodClass(); llvm::Value* getLiteral(uint32_t index); - TJITContext(MethodCompiler* compiler, TMethod* method, bool parse = true) - : currentNode(0), originMethod(method), function(0), builder(0), - preamble(0), exceptionLandingPad(0), unwindBlockReturn(0), unwindPhi(0), methodHasBlockReturn(false), - methodAllocatesMemory(true), compiler(compiler), contextHolder(0), selfHolder(0) + TJITContext( + MethodCompiler* compiler, + TMethod* method, + type::InferContext& context, + bool parse = true + ) : + currentNode(0), + originMethod(method), + inferContext(context), + function(0), + builder(0), + preamble(0), + exceptionLandingPad(0), + unwindBlockReturn(0), + unwindPhi(0), + methodHasBlockReturn(false), + methodAllocatesMemory(true), + compiler(compiler), + contextHolder(0), + selfHolder(0) { if (parse) { parsedMethod = new st::ParsedMethod(method); @@ -254,7 +272,7 @@ class MethodCompiler { st::ParsedMethod* method, st::ParsedBlock* block ) - : TJITContext(compiler, 0, false), parsedBlock(block) + : TJITContext(compiler, 0, *(type::InferContext*)(0), false), parsedBlock(block) { parsedMethod = method; originMethod = parsedMethod->getOrigin(); @@ -282,6 +300,10 @@ class MethodCompiler { TExceptionAPI m_exceptionAPI; TBaseFunctions m_baseFunctions; +private: + type::TypeSystem m_typeSystem; + +private: llvm::Value* getNodeValue(TJITContext& jit, st::ControlNode* node, llvm::BasicBlock* insertBlock = 0); llvm::Value* getPhiValue(TJITContext& jit, st::PhiNode* phi); void encodePhiIncomings(TJITContext& jit, st::PhiNode* phiNode); @@ -316,7 +338,8 @@ class MethodCompiler { void doSendUnary(TJITContext& jit); void doSendBinary(TJITContext& jit); void doSendMessage(TJITContext& jit); - bool doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode, TClass* receiverClass = 0); + void doSendGenericMessage(TJITContext& jit); + void doSendInferredMessage(TJITContext& jit, type::InferContext& context); void doSpecial(TJITContext& jit); void doPrimitive(TJITContext& jit); @@ -334,7 +357,7 @@ class MethodCompiler { llvm::BasicBlock* primitiveFailedBB); TObjectAndSize createArray(TJITContext& jit, uint32_t elementsCount); - llvm::Function* createFunction(TMethod* method); + llvm::Function* createFunction(TMethod* method, const std::string& functionName); uint16_t getSkipOffset(st::InstructionNode* branch); @@ -349,6 +372,7 @@ class MethodCompiler { llvm::Function* compileMethod( TMethod* method, + const type::Type& arguments, llvm::Function* methodFunction = 0, llvm::Value** contextHolder = 0 ); @@ -371,14 +395,7 @@ class MethodCompiler { llvm::Module* JITModule, TRuntimeAPI runtimeApi, TExceptionAPI exceptionApi - ) - : m_runtime(runtime), m_JITModule(JITModule), - m_runtimeAPI(runtimeApi), m_exceptionAPI(exceptionApi), m_callSiteIndex(1) - { - m_baseTypes.initializeFromModule(JITModule); - m_globals.initializeFromModule(JITModule); - m_baseFunctions.initializeFromModule(JITModule); - } + ); }; @@ -446,9 +463,12 @@ class JITRuntime { friend TReturnValue invokeBlock(TBlock* block, TContext* callingContext); friend void emitBlockReturn(TObject* value, TContext* targetContext); + static const unsigned int ARG_CACHE_SIZE = 8; struct TFunctionCacheEntry { TMethod* method; + TClass* arguments[ARG_CACHE_SIZE]; + TMethodFunction function; }; @@ -472,9 +492,9 @@ class JITRuntime { uint32_t m_blockReturnsEmitted; uint32_t m_objectsAllocated; - TMethodFunction lookupFunctionInCache(TMethod* method); + TMethodFunction lookupFunctionInCache(TMethod* method, TObjectArray* arguments); TBlockFunction lookupBlockFunctionInCache(TMethod* containerMethod, uint32_t blockOffset); - void updateFunctionCache(TMethod* method, TMethodFunction function); + void updateFunctionCache(TMethod* method, TMethodFunction function, TObjectArray* arguments); void updateBlockFunctionCache(TMethod* containerMethod, uint32_t blockOffset, TBlockFunction function); void flushBlockFunctionCache(); diff --git a/src/JITRuntime.cpp b/src/JITRuntime.cpp index 5c92f4f..04bf01c 100644 --- a/src/JITRuntime.cpp +++ b/src/JITRuntime.cpp @@ -234,18 +234,34 @@ TBlock* JITRuntime::createBlock(TContext* callingContext, uint8_t argLocation, u return newBlock; } -JITRuntime::TMethodFunction JITRuntime::lookupFunctionInCache(TMethod* method) +JITRuntime::TMethodFunction JITRuntime::lookupFunctionInCache(TMethod* method, TObjectArray* arguments) { - uint32_t hash = reinterpret_cast(method) ^ reinterpret_cast(method->name); // ^ 0xDEADBEEF; + if (!arguments || arguments->getSize() > ARG_CACHE_SIZE) { + m_cacheMisses++; + return 0; + } + + const uint32_t hash = (reinterpret_cast(method) << 2) + arguments->getSize(); TFunctionCacheEntry& entry = m_functionLookupCache[hash % LOOKUP_CACHE_SIZE]; - if (entry.method == method) { - m_cacheHits++; - return entry.function; - } else { + if (entry.method != method) { m_cacheMisses++; return 0; } + + for (std::size_t i = 0; i < arguments->getSize(); i++) { + TClass* const cachedClass = entry.arguments[i]; + TObject* const field = arguments->getField(i); + TClass* const currentClass = isSmallInteger(field) ? globals.smallIntClass : field->getClass(); + + if (currentClass != cachedClass) { + m_cacheMisses++; + return 0; + } + } + + m_cacheHits++; + return entry.function; } JITRuntime::TBlockFunction JITRuntime::lookupBlockFunctionInCache(TMethod* containerMethod, uint32_t blockOffset) @@ -262,13 +278,27 @@ JITRuntime::TBlockFunction JITRuntime::lookupBlockFunctionInCache(TMethod* conta } } -void JITRuntime::updateFunctionCache(TMethod* method, TMethodFunction function) +void JITRuntime::updateFunctionCache(TMethod* method, TMethodFunction function, TObjectArray* arguments) { - uint32_t hash = reinterpret_cast(method) ^ reinterpret_cast(method->name); // ^ 0xDEADBEEF; + if (!arguments || arguments->getSize() > ARG_CACHE_SIZE) + return; + + const std::size_t argSize = arguments->getSize(); + const uint32_t hash = (reinterpret_cast(method) << 2) + argSize; TFunctionCacheEntry& entry = m_functionLookupCache[hash % LOOKUP_CACHE_SIZE]; entry.method = method; entry.function = function; + + for (std::size_t i = 0; i < argSize; i++) { + TObject* const field = arguments->getField(i); + TClass* const currentClass = isSmallInteger(field) ? globals.smallIntClass : field->getClass(); + + entry.arguments[i] = currentClass; + } + + if (argSize < ARG_CACHE_SIZE) + entry.arguments[argSize + 1] = 0; } void JITRuntime::updateBlockFunctionCache(TMethod* containerMethod, uint32_t blockOffset, TBlockFunction function) @@ -371,16 +401,20 @@ void JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TObject } // Searching for the jit compiled function - compiledMethodFunction = lookupFunctionInCache(method); + compiledMethodFunction = lookupFunctionInCache(method, messageArguments); if (! compiledMethodFunction) { + type::Type argumentsType = type::createArgumentsType(messageArguments); + if (receiverClass) + argumentsType[0].set(receiverClass); + // If function was not found in the cache looking it in the LLVM directly - std::string functionName = method->klass->name->toString() + ">>" + method->name->toString(); + const std::string& functionName = type::getQualifiedMethodName(method, argumentsType); Function* methodFunction = m_JITModule->getFunction(functionName); if (! methodFunction) { // Compiling function and storing it to the table for further use - methodFunction = m_methodCompiler->compileMethod(method); + methodFunction = m_methodCompiler->compileMethod(method, argumentsType); // outs() << *methodFunction << "\n"; @@ -391,7 +425,7 @@ void JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TObject // Calling the method and returning the result compiledMethodFunction = reinterpret_cast(m_executionEngine->getPointerToFunction(methodFunction)); - updateFunctionCache(method, compiledMethodFunction); + updateFunctionCache(method, compiledMethodFunction, messageArguments); // outs() << *methodFunction << "\n"; @@ -401,7 +435,7 @@ void JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TObject } // Updating call site statistics and scheduling method processing - updateHotSites(compiledMethodFunction, previousContext, message, receiverClass, callSiteIndex); + //updateHotSites(compiledMethodFunction, previousContext, message, receiverClass, callSiteIndex); // Preparing the context objects. Because we do not call the software // implementation here, we do not need to allocate the stack object @@ -409,6 +443,7 @@ void JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TObject // initialization of various objects such as stackTop and bytePointer. // Creating context object and temporaries + // TODO Think about stack allocation hptr newTemps = m_softVM->newObject(method->temporarySize); newContext = m_softVM->newObject(); @@ -439,7 +474,7 @@ void JITRuntime::updateHotSites(TMethodFunction methodFunction, TContext* callin if (!callSiteIndex) return; - TMethodFunction callerMethodFunction = lookupFunctionInCache(callingContext->method); + TMethodFunction callerMethodFunction = lookupFunctionInCache(callingContext->method, 0); // TODO reload cache if callerMethodFunction was popped out if (!callerMethodFunction) @@ -494,7 +529,7 @@ void JITRuntime::patchHotMethods() // Compiling function from scratch outs() << "Recompiling method for patching: " << methodFunction->getName().str() << "\n"; Value* contextHolder = 0; - m_methodCompiler->compileMethod(method, methodFunction, &contextHolder); + m_methodCompiler->compileMethod(method, type::Type(), methodFunction, &contextHolder); outs() << "Patching " << hotMethod->methodFunction->getName().str() << " ..."; diff --git a/src/MethodCompiler.cpp b/src/MethodCompiler.cpp index af7ec5b..7f0a982 100644 --- a/src/MethodCompiler.cpp +++ b/src/MethodCompiler.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include using namespace llvm; @@ -55,6 +56,24 @@ std::string to_string(const T& x) { return ss.str(); } +MethodCompiler::MethodCompiler( + JITRuntime& runtime, + llvm::Module* JITModule, + TRuntimeAPI runtimeApi, + TExceptionAPI exceptionApi +) : + m_runtime(runtime), + m_JITModule(JITModule), + m_runtimeAPI(runtimeApi), + m_exceptionAPI(exceptionApi), + m_typeSystem(*runtime.getVM()), + m_callSiteIndex(1) +{ + m_baseTypes.initializeFromModule(JITModule); + m_globals.initializeFromModule(JITModule); + m_baseFunctions.initializeFromModule(JITModule); +} + Value* MethodCompiler::TJITContext::getLiteral(uint32_t index) { Value* const literal = builder->CreateCall2( @@ -78,7 +97,7 @@ Value* MethodCompiler::TJITContext::getMethodClass() return klass; } -Function* MethodCompiler::createFunction(TMethod* method) +Function* MethodCompiler::createFunction(TMethod* method, const std::string& functionName) { Type* const methodParams[] = { m_baseTypes.context->getPointerTo() }; FunctionType* const functionType = FunctionType::get( @@ -87,7 +106,6 @@ Function* MethodCompiler::createFunction(TMethod* method) false // we're not dealing with vararg ); - std::string functionName = method->klass->name->toString() + ">>" + method->name->toString(); Function* const function = cast( m_JITModule->getOrInsertFunction(functionName, functionType)); function->setCallingConv(CallingConv::C); //Anyway C-calling conversion is default function->setGC("shadow-stack"); @@ -395,12 +413,24 @@ TObjectAndSize MethodCompiler::createArray(TJITContext& jit, uint32_t elementsCo return std::make_pair(arrayObject, arraySize); } -Function* MethodCompiler::compileMethod(TMethod* method, llvm::Function* methodFunction /*= 0*/, llvm::Value** contextHolder /*= 0*/) +Function* MethodCompiler::compileMethod(TMethod* method, const type::Type& arguments, llvm::Function* methodFunction /*= 0*/, llvm::Value** contextHolder /*= 0*/) { - TJITContext jit(this, method); + type::InferContext* const inferContext = m_typeSystem.inferMessage(method->name, arguments, 0); + assert(inferContext); + assert(inferContext->getMethod() == method); + + const std::string& methodName = type::getQualifiedMethodName(method, arguments); + printf("compiling method %s\n", methodName.c_str()); + + TJITContext jit(this, method, *inferContext); + + { + ControlGraphVisualizer vis(jit.controlGraph, methodName, "dots/"); + vis.run(); + } // Creating the function named as "Class>>method" or using provided one - jit.function = methodFunction ? methodFunction : createFunction(method); + jit.function = methodFunction ? methodFunction : createFunction(method, methodName); // Creating the preamble basic block and inserting it into the function // It will contain basic initialization code (args, temps and so on) @@ -774,17 +804,22 @@ void MethodCompiler::doPushBlock(TJITContext& jit) llvm::Function* MethodCompiler::compileBlock(TBlock* block) { - TJITContext methodContext(this, block->method); + // TODO + type::Type blockType; + type::Type arguments; + type::InferContext* const inferContext = m_typeSystem.inferBlock(blockType, arguments, 0); + + TJITContext blockContext(this, block->method, *inferContext); const uint16_t blockOffset = block->blockBytePointer; std::ostringstream ss; ss << block->method->klass->name->toString() << ">>" << block->method->name->toString() << "@" << blockOffset; const std::string blockFunctionName = ss.str(); - st::ParsedBlock* const parsedBlock = methodContext.parsedMethod->getParsedBlockByOffset(blockOffset); + st::ParsedBlock* const parsedBlock = blockContext.parsedMethod->getParsedBlockByOffset(blockOffset); assert(parsedBlock); - return compileBlock(methodContext, blockFunctionName, parsedBlock); + return compileBlock(blockContext, blockFunctionName, parsedBlock); } llvm::Function* MethodCompiler::compileBlock(TJITContext& jit, const std::string& blockFunctionName, st::ParsedBlock* parsedBlock) @@ -792,6 +827,11 @@ llvm::Function* MethodCompiler::compileBlock(TJITContext& jit, const std::string const uint16_t blockOffset = parsedBlock->getStartOffset(); TJITBlockContext blockContext(this, jit.parsedMethod, parsedBlock); + { + ControlGraphVisualizer vis(blockContext.controlGraph, blockFunctionName, "dots/"); + vis.run(); + } + std::vector blockParams; blockParams.push_back(m_baseTypes.block->getPointerTo()); // block object with context information @@ -1146,80 +1186,35 @@ void MethodCompiler::doSendBinary(TJITContext& jit) setNodeValue(jit, jit.currentNode, resultHolder); } - -bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNode* receiverNode, TClass* receiverClass /*= 0*/) +void MethodCompiler::doSendInferredMessage(TJITContext& jit, type::InferContext& context) { - // Optimized version of doSendMessage which takes into account that - // pending message should be sent to the literal receiver - // (either constant or a member of method literals). Literal receivers - // are encoded at the time of method compilation, so thier value and - // their class will not change over time. Moreover, actual values - // are known at compile time and may be used to lookup the actual - // method that should be invoked. - - // Locating message selector - TSymbolArray& literals = *jit.originMethod->literals; - TSymbol* const messageSelector = literals[jit.currentNode->getInstruction().getArgument()]; - - // Determining receiver class - if (!receiverClass) { - TObject* literalReceiver = 0; - - const st::TSmalltalkInstruction::TOpcode opcode = receiverNode->getInstruction().getOpcode(); - if (opcode == opcode::pushLiteral) { - literalReceiver = literals[receiverNode->getInstruction().getArgument()]; - } else if (opcode == opcode::pushConstant) { - const uint8_t constant = receiverNode->getInstruction().getArgument(); - switch(constant) { - case 0: case 1: case 2: case 3: case 4: - case 5: case 6: case 7: case 8: case 9: - literalReceiver = TInteger(constant); - break; - - case pushConstants::nil: literalReceiver = globals.nilObject; break; - case pushConstants::trueObject: literalReceiver = globals.trueObject; break; - case pushConstants::falseObject: literalReceiver = globals.falseObject; break; - } - } - - assert(literalReceiver); - receiverClass = isSmallInteger(literalReceiver) ? globals.smallIntClass : literalReceiver->getClass(); - } - - assert(receiverClass); + // Inferred messages are easy to dispatch because we know + // virtually everything about their execution context. + // Therefore we may encode direct method call and optimize + // all stuff like as GC roots or checks that is redundant. - // Locating a method suitable for a direct call - TMethod* const directMethod = m_runtime.getVM()->lookupMethod(messageSelector, receiverClass); + TMethod* const directMethod = context.getMethod(); - if (! directMethod) { - outs() << "Error! Could not lookup method for class " << receiverClass->name->toString() << ", selector " << messageSelector->toString() << "\n"; - return false; - } - - std::string directFunctionName = directMethod->klass->name->toString() + ">>" + messageSelector->toString(); + const std::string& directFunctionName = context.getQualifiedName(); Function* directFunction = m_JITModule->getFunction(directFunctionName); if (!directFunction) { // Compiling function and storing it to the table for further use - directFunction = compileMethod(directMethod); - -// outs() << *directFunction << "\n"; + directFunction = compileMethod(directMethod, context.getArguments()); - verifyFunction(*directFunction , llvm::AbortProcessAction); - - m_runtime.optimizeFunction(directFunction, false); + outs() << *directFunction << "\n"; } // Allocating context object and temporaries on the methodFunction's stack. // This operation does not affect garbage collector, so no pointer protection // is required. Moreover, this is operation is much faster than heap allocation. - const bool hasTemporaries = directMethod->temporarySize > 0; + const bool hasTemporaries = context.getMethod()->temporarySize > 0; const uint32_t contextSize = sizeof(TContext); const uint32_t tempsSize = hasTemporaries ? sizeof(TObjectArray) + sizeof(TObject*) * directMethod->temporarySize : 0; // Allocating stack space for objects and registering GC protection holder - MethodCompiler::TStackObject contextPair = allocateStackObject(*jit.builder, sizeof(TContext), 0); + TStackObject contextPair = allocateStackObject(*jit.builder, sizeof(TContext), 0); Value* const contextSlot = contextPair.objectSlot; Value* tempsSlot = 0; @@ -1237,7 +1232,7 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod false // volatile operation ); - if (hasTemporaries) + if (hasTemporaries) { jit.builder->CreateMemSet( tempsSlot, // destination address jit.builder->getInt8(0), // fill with zeroes @@ -1245,13 +1240,14 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod 0, // no alignment false // volatile operation ); + } // Initializing object fields // TODO Move the init sequence out of the block or check that it is correctly optimized in loops - Value* const newContextObject = jit.builder->CreateBitCast(contextSlot, m_baseTypes.object->getPointerTo(), "newContext."); - Value* const newTempsObject = hasTemporaries ? jit.builder->CreateBitCast(tempsSlot, m_baseTypes.object->getPointerTo(), "newTemps.") : 0; - Function* const setObjectSize = getBaseFunctions().setObjectSize; - Function* const setObjectClass = getBaseFunctions().setObjectClass; + Value* const newContextObject = jit.builder->CreateBitCast(contextSlot, m_baseTypes.object->getPointerTo(), "newContext."); + Value* const newTempsObject = hasTemporaries ? jit.builder->CreateBitCast(tempsSlot, m_baseTypes.object->getPointerTo(), "newTemps.") : 0; + Function* const setObjectSize = getBaseFunctions().setObjectSize; + Function* const setObjectClass = getBaseFunctions().setObjectClass; // Object size stored in the TSize field of any ordinary object contains // number of pointers except for the first two fields @@ -1285,27 +1281,12 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod jit.builder->CreateCall3(setObjectField, newContextObject, jit.builder->getInt32(3), contextObject); Value* const newContext = jit.builder->CreateBitCast(newContextObject, m_baseTypes.context->getPointerTo()); - Value* result = 0; - - if (jit.methodHasBlockReturn) { - // Creating basic block that will be branched to on normal invoke - BasicBlock* const nextBlock = BasicBlock::Create(m_JITModule->getContext(), "next.", jit.function); - jit.currentNode->getDomain()->getBasicBlock()->setEndValue(nextBlock); + Value* const result = jit.builder->CreateCall(directFunction, newContext); - // Performing a function invoke - result = jit.builder->CreateInvoke(directFunction, nextBlock, jit.exceptionLandingPad, newContext); - - // Switching builder to a new block - jit.builder->SetInsertPoint(nextBlock); - } else { - // Just calling the function. No block switching is required - result = jit.builder->CreateCall(directFunction, newContext); - } - - AllocaInst* const allocaInst = dyn_cast(jit.currentNode->getArgument()->getValue()->stripPointerCasts()); - Function* const gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::lifetime_end); - Value* const argumentsPointer = jit.builder->CreateBitCast(arguments, jit.builder->getInt8PtrTy()); - jit.builder->CreateCall2(gcrootIntrinsic, jit.builder->CreateZExt(allocaInst->getArraySize(), jit.builder->getInt64Ty()), argumentsPointer); +// AllocaInst* const allocaInst = dyn_cast(jit.currentNode->getArgument()->getValue()->stripPointerCasts()); +// Function* const gcrootIntrinsic = getDeclaration(m_JITModule, Intrinsic::lifetime_end); +// Value* const argumentsPointer = jit.builder->CreateBitCast(arguments, jit.builder->getInt8PtrTy()); +// jit.builder->CreateCall2(gcrootIntrinsic, jit.builder->CreateZExt(allocaInst->getArraySize(), jit.builder->getInt64Ty()), argumentsPointer); Value* const targetContext = jit.builder->CreateExtractValue(result, 1, "targetContext"); Value* const isBlockReturn = jit.builder->CreateIsNotNull(targetContext); @@ -1318,43 +1299,36 @@ bool MethodCompiler::doSendMessageToLiteral(TJITContext& jit, st::InstructionNod Value* const resultObject = jit.builder->CreateExtractValue(result, 0, "resultObject"); Value* const resultHolder = protectProducerNode(jit, jit.currentNode, resultObject); setNodeValue(jit, jit.currentNode, resultHolder); - -// Value* const resultHolder = protectProducerNode(jit, jit.currentNode, result); -// setNodeValue(jit, jit.currentNode, resultHolder); - - return true; } void MethodCompiler::doSendMessage(TJITContext& jit) { + // FIXME Temporary hack until block inference + // is not handled in method compiler + if (! &jit.inferContext) { + doSendGenericMessage(jit); + return; + } - st::InstructionNode* const markArgumentsNode = jit.currentNode->getArgument()->cast(); - assert(markArgumentsNode); - - st::InstructionNode* const receiverNode = markArgumentsNode->getArgument()->cast(); - assert(receiverNode); + const type::Type& arguments = jit.inferContext[*jit.currentNode->getArgument()]; - const st::TSmalltalkInstruction instruction = receiverNode->getInstruction(); + if (arguments.isArray() && !arguments.getSubTypes().empty()) { + TSymbolArray& literals = *jit.originMethod->literals; + const uint32_t literalIndex = jit.currentNode->getInstruction().getArgument(); + TSymbol* const selector = literals[literalIndex]; - // In case of a literal receiver we may encode direct method call - if (instruction.getOpcode() == opcode::pushLiteral || - instruction.getOpcode() == opcode::pushConstant) - { - if (doSendMessageToLiteral(jit, receiverNode)) + type::InferContext* const messageContext = m_typeSystem.inferMessage(selector, arguments, 0, false); + if (messageContext) { + doSendInferredMessage(jit, *messageContext); return; - } - - // We may optimize send to self if clas does not have children - // TODO More optimization possible: send to super, if chiildren do not override selector - if (instruction.getOpcode() == opcode::pushArgument && instruction.getArgument() == 0) - { - TClass* const receiverClass = jit.originMethod->klass; - if (receiverClass->package == globals.nilObject) { - if (doSendMessageToLiteral(jit, receiverNode, receiverClass)) - return; } } + doSendGenericMessage(jit); +} + +void MethodCompiler::doSendGenericMessage(TJITContext& jit) +{ Value* const arguments = getArgument(jit); // jit.popValue(); // First of all we need to get the actual message selector diff --git a/src/TypeAnalyzer.cpp b/src/TypeAnalyzer.cpp index e40e739..2ac2dbb 100644 --- a/src/TypeAnalyzer.cpp +++ b/src/TypeAnalyzer.cpp @@ -2,6 +2,7 @@ #include using namespace type; +using namespace st; static void printBlock(const Type& blockType, std::stringstream& stream) { if (blockType.getSubTypes().size() < Type::bstCaptureIndex) {