Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions SOUL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
We're building a clojure JIT compiler in LLVM/C++ API. We use reference counting with consuming semantics and NaN boxing.

There's two components here - one is the frontend - a clojure program that creates AST, computes memory guidance (dropMemory for happy path and unwindMemory for landing pads)
and builds the binary proto.

The second - the backend which reads proto and compiles/executes.

Our JIT currently compiles the proto forms using LLVMs ORC2 and links them, on IR level with runtime written in C - which allows for huge inlining optimisations.

When working with tests - always stop and ask for permission if you want to change non-test code.
2 changes: 1 addition & 1 deletion backend-v2/bridge/InstanceCallStub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ InstanceCallSlowPath(void *slot, const char *methodName, int32_t argCount,
* from stack.
*/

std::cout << future.get().optimizedIR << std::endl;
// std::cout << future.get().optimizedIR << std::endl;

// Block until the JIT compilation is finished and get the executable
// address
Expand Down
3 changes: 3 additions & 0 deletions backend-v2/codegen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ llvm::Function *CodeGen::generateBaselineMethod(
valueEncoder.unboxDouble(TypedValue(type.boxed(), valRaw)).value;
} else if (type.isUnboxedType(booleanType)) {
val = valueEncoder.unboxBool(TypedValue(type.boxed(), valRaw)).value;
} else if (type.isUnboxedPointer()) {
val =
valueEncoder.unboxPointer(TypedValue(type.boxed(), valRaw)).value;
}
}

Expand Down
16 changes: 9 additions & 7 deletions backend-v2/codegen/LLVMTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ LLVMTypes::LLVMTypes(LLVMContext &context) {
voidTy = Type::getVoidTy(context);
RT_valueTy = i64Ty;

RT_objectTy = StructType::create(context,
{
wordTy, // atomicRefCount
i32Ty, // type (enum)
std::vector<Type *> objectFields = {wordTy, i32Ty};
if (K_WORD_SIZE == 8) {
#ifdef USE_MEMORY_BANKS
Type::getInt8Ty(Ctx) // bankId
objectFields.push_back(i8Ty); // bankId
objectFields.push_back(ArrayType::get(i8Ty, 3)); // padding to 16
#else
objectFields.push_back(i32Ty); // padding to 16
#endif
},
"Clojure_Object");
}

RT_objectTy = StructType::create(context, objectFields, "Clojure_Object");

frameTy = StructType::create(context,
{ptrTy, // leafFrame
Expand Down
11 changes: 6 additions & 5 deletions backend-v2/codegen/invoke/InvokeManager_InstanceCall.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include "../../bridge/Exceptions.h"
#include "../../tools/EdnParser.h"
#include "../../tools/RTValueWrapper.h"
#include "../../tools/RandomID.h"
#include "../../types/ConstantClass.h"
#include "../CodeGen.h"
#include "InvokeManager.h"
#include "../../tools/RandomID.h"
#include "llvm/IR/MDBuilder.h"
#include <cstdint>
#include <sstream>
Expand Down Expand Up @@ -38,8 +38,8 @@ TypedValue InvokeManager::generateDynamicInstanceCall(
"mode");
}

cout << "Generating dynamic instance call for method: " << methodName << endl;
// Allocate an Inline Cache slot as a global variable in the module
// cout << "Generating dynamic instance call for method: " << methodName <<
// endl; Allocate an Inline Cache slot as a global variable in the module
auto *slotTy = StructType::get(TheContext, {types.wordTy, types.ptrTy});

std::stringstream ss;
Expand Down Expand Up @@ -379,8 +379,9 @@ TypedValue InvokeManager::generateDeterminedInstanceCall(
uint32_t target = fid->argTypes[i].determinedType();
Value *targetVal =
ConstantInt::get(this->types.i32Ty, (uint32_t)target);
Value *isType =
this->builder.CreateICmpEQ(this->builder.CreateTrunc(argRuntimeTypes[i], this->types.i32Ty), targetVal);
Value *isType = this->builder.CreateICmpEQ(
this->builder.CreateTrunc(argRuntimeTypes[i], this->types.i32Ty),
targetVal);

if (match) {
match = this->builder.CreateAnd(match, isType);
Expand Down
6 changes: 3 additions & 3 deletions backend-v2/codegen/ops/ConstNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,15 @@ ObjectTypeSet CodeGen::getType(const Node &node, const ConstNode &subnode,
node.otag() == "clojure.lang.Ratio" ||
node.tag() == "class clojure.lang.Ratio" ||
node.otag() == "class clojure.lang.Ratio") {
return ObjectTypeSet(ratioType, true, new ConstantRatio(subnode.val()))
return ObjectTypeSet(ratioType, false, new ConstantRatio(subnode.val()))
.restriction(typeRestrictions);
}

if (node.tag() == "clojure.lang.BigInt" ||
node.otag() == "clojure.lang.BigInt" ||
node.tag() == "class clojure.lang.BigInt" ||
node.otag() == "class clojure.lang.BigInt") {
return ObjectTypeSet(bigIntegerType, true,
return ObjectTypeSet(bigIntegerType, false,
new ConstantBigInteger(subnode.val()))
.restriction(typeRestrictions);
}
Expand All @@ -173,7 +173,7 @@ ObjectTypeSet CodeGen::getType(const Node &node, const ConstNode &subnode,
new ConstantInteger((int32_t)val))
.restriction(typeRestrictions);
} else {
return ObjectTypeSet(bigIntegerType, true,
return ObjectTypeSet(bigIntegerType, false,
new ConstantBigInteger(subnode.val()))
.restriction(typeRestrictions);
}
Expand Down
2 changes: 1 addition & 1 deletion backend-v2/codegen/ops/VarNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ TypedValue CodeGen::codegen(const Node &node, const VarNode &subnode,
const ObjectTypeSet &typeRestrictions) {
auto varName = subnode.var().substr(2);
ScopedRef<Var> var(compilerState.varRegistry.getCurrent(varName.c_str()));
cout << "Derefing var " << varName << endl;

if (!var)
throwCodeGenerationException(
"Unable to resolve var: " + varName + " in this context", node);
Expand Down
27 changes: 23 additions & 4 deletions backend-v2/runtime/Ebr.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,23 +175,42 @@ void Ebr_unregister_thread() {

void Ebr_force_reclaim() {
// There should be only two threads left: the current thread and the reclaimer
while (atomic_load(&active_threads) > 2)
int wait_ms = 0;
while (atomic_load(&active_threads) > 2) {
usleep(1000);

wait_ms++;
if (wait_ms >= 1000 && (wait_ms % 1000 == 0)) {
fprintf(stderr,
"[EBR WARNING] Ebr_force_reclaim waiting: active_threads = %zu "
"(> 2). Waited %d seconds.\n",
(size_t)atomic_load(&active_threads), wait_ms / 1000);
}
}

// Keep reclaiming until there are no more objects to reclaim.
// We use the synchronization_mutex to properly wait for the background
// We use the synchronization_mutex to properly wait for the background
// reclaimer thread if it is currently holding the "stolen" pending list.
int sync_wait_ms = 0;
while (true) {
Ebr_flush_critical();
Ebr_synchronize_and_reclaim();

pthread_mutex_lock(&synchronization_mutex);
bool is_empty = (atomic_load_explicit(&pending_reclamation, memory_order_acquire) == NULL);
bool is_empty = (atomic_load_explicit(&pending_reclamation,
memory_order_acquire) == NULL);
pthread_mutex_unlock(&synchronization_mutex);

if (is_empty) {
break;
}

usleep(1000);
sync_wait_ms++;
if (sync_wait_ms >= 1000 && (sync_wait_ms % 1000 == 0)) {
fprintf(stderr,
"[EBR WARNING] Ebr_force_reclaim waiting to empty pending_reclamation. Waited %d seconds.\n",
sync_wait_ms / 1000);
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions backend-v2/runtime/Function.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,14 @@ struct FunctionMethod *RT_updateICSlot(void *slot, RTValue currentVal,
}
return method;
}

void Function_promoteToShared(ClojureFunction *self, uword_t count) {
for (uword_t i = 0; i < self->methodCount; i++) {
FunctionMethod *method = &(self->methods[i]);
for (uword_t j = 0; j < method->closedOversCount; j++) {
promoteToShared(method->closedOvers[j]);
}
}
Object_promoteToSharedShallow((Object *)self, count);
}

2 changes: 2 additions & 0 deletions backend-v2/runtime/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ uword_t Function_hash(ClojureFunction *self);
String *Function_toString(ClojureFunction *self);
void Function_destroy(ClojureFunction *self);
void Function_cleanupOnce(ClojureFunction *self);
void Function_promoteToShared(ClojureFunction *self, uword_t count);

RTValue RT_invokeDynamic(RTValue funObj, RTValue *args, int32_t argCount);
FunctionMethod *Function_extractMethod(RTValue funObj, uword_t argCount);
struct FunctionMethod *RT_updateICSlot(void *slot, RTValue currentVal,
Expand Down
4 changes: 4 additions & 0 deletions backend-v2/runtime/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,10 @@ inline void Object_promoteToShared(Object *restrict self) {
PersistentVectorNode_promoteToShared((PersistentVectorNode *)self, count);
break;

case functionType:
Function_promoteToShared((ClojureFunction *)self, count);
break;

default:
Object_promoteToSharedShallow(self, count);
break;
Expand Down
13 changes: 10 additions & 3 deletions backend-v2/state/ThreadsafeRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../RuntimeHeaders.h"
#include "../bridge/Exceptions.h"
#include "runtime/Object.h"
#include "runtime/RTValue.h"
#include <mutex>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -45,6 +46,7 @@ template <typename T> class ThreadsafeRegistry {
auto it = registry.find(key);

if (manageRuntimeMemory && it != registry.end()) {
promoteToShared(RT_boxPtr(it->second));
Ptr_release((void *)it->second);
}

Expand All @@ -58,15 +60,20 @@ template <typename T> class ThreadsafeRegistry {
auto it = registry.find(key);

if (it != registry.end()) {
if (manageRuntimeMemory)
if (manageRuntimeMemory) {
Ptr_retain((void *)it->second);
}
return it->second;
}

T *newDef = factory();
registry[key] = newDef;
if (manageRuntimeMemory)
Ptr_retain(newDef); // Still need to return a fresh reference if it's being returned
if (manageRuntimeMemory) {
promoteToShared(RT_boxPtr(newDef));
Ptr_retain(newDef); // Still need to return a fresh reference if it's
// being returned
}

return newDef;
}

Expand Down
4 changes: 4 additions & 0 deletions backend-v2/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ set(TEST_MODULES
codegen/ThrowNode_test
codegen/FnNode_test
codegen/InvokeNode_test
codegen/InvokeMemory_test
codegen/VarCallIC_test
codegen/VarCallLeak_repro
codegen/DynamicInvoke_test
codegen/IFnInvoke_test
codegen/NewNodeDispatch_test
codegen/VariadicInvoke_test
codegen/Concurrency_test
codegen/RecurNode_test
codegen/EBRFlush_test
state/ClassRegistration_test
VarUAF_repro_test
Expand Down
Loading
Loading