Skip to content

Commit

Permalink
Adds initial inference logic to the MethodCompiler and JitRuntime
Browse files Browse the repository at this point in the history
Issue: #17
Issue: #92
  • Loading branch information
0x7CFE committed Jul 9, 2016
1 parent eb8eeb9 commit 40eb706
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 147 deletions.
12 changes: 12 additions & 0 deletions include/inference.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
54 changes: 37 additions & 17 deletions include/jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <types.h>
#include "vm.h"
#include "analysis.h"
#include "inference.h"

#include <typeinfo>

Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -349,6 +372,7 @@ class MethodCompiler {

llvm::Function* compileMethod(
TMethod* method,
const type::Type& arguments,
llvm::Function* methodFunction = 0,
llvm::Value** contextHolder = 0
);
Expand All @@ -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);
}
);
};


Expand Down Expand Up @@ -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;
};

Expand All @@ -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();

Expand Down
65 changes: 50 additions & 15 deletions src/JITRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint32_t>(method) ^ reinterpret_cast<uint32_t>(method->name); // ^ 0xDEADBEEF;
if (!arguments || arguments->getSize() > ARG_CACHE_SIZE) {
m_cacheMisses++;
return 0;
}

const uint32_t hash = (reinterpret_cast<uint32_t>(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)
Expand All @@ -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<uint32_t>(method) ^ reinterpret_cast<uint32_t>(method->name); // ^ 0xDEADBEEF;
if (!arguments || arguments->getSize() > ARG_CACHE_SIZE)
return;

const std::size_t argSize = arguments->getSize();
const uint32_t hash = (reinterpret_cast<uint32_t>(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)
Expand Down Expand Up @@ -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";

Expand All @@ -391,7 +425,7 @@ void JITRuntime::sendMessage(TContext* callingContext, TSymbol* message, TObject

// Calling the method and returning the result
compiledMethodFunction = reinterpret_cast<TMethodFunction>(m_executionEngine->getPointerToFunction(methodFunction));
updateFunctionCache(method, compiledMethodFunction);
updateFunctionCache(method, compiledMethodFunction, messageArguments);

// outs() << *methodFunction << "\n";

Expand All @@ -401,14 +435,15 @@ 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
// because it is not used by JIT runtime. We also may skip the proper
// initialization of various objects such as stackTop and bytePointer.

// Creating context object and temporaries
// TODO Think about stack allocation
hptr<TObjectArray> newTemps = m_softVM->newObject<TObjectArray>(method->temporarySize);
newContext = m_softVM->newObject<TContext>();

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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() << " ...";

Expand Down
Loading

0 comments on commit 40eb706

Please sign in to comment.