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
3 changes: 2 additions & 1 deletion backend-v2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ set(BACKEND_SOURCES
codegen/ops/HostInteropNode.cpp
codegen/ops/NewNode.cpp
codegen/ops/IsInstanceNode.cpp
codegen/ops/ThrowNode.cpp)
codegen/ops/ThrowNode.cpp
codegen/ops/FnNode.cpp)

add_subdirectory(runtime)

Expand Down
159 changes: 156 additions & 3 deletions backend-v2/codegen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ std::string CodeGen::generateInstanceCallBridge(
std::stringstream ss;
ss << std::hex << dist(gen);

std::string funcName =
moduleName + "_" + std::to_string(instanceType.determinedType()) + "_" +
ss.str();
std::string funcName = moduleName + "_" +
std::to_string(instanceType.determinedType()) + "_" +
ss.str();
// 1. Define Signature: (Instance, Arg1, ...) -> RTValue
// Specialized arguments use natural LLVM types for unboxed primitives
std::vector<llvm::Type *> llvmArgTypes;
Expand Down Expand Up @@ -167,6 +167,155 @@ std::string CodeGen::generateInstanceCallBridge(
return funcName;
}

llvm::Function *CodeGen::generateBaselineMethod(
const FnMethodNode &method,
const std::vector<std::pair<std::string, ObjectTypeSet>> &captureInfo) {
CLJ_ASSERT(TSContext != nullptr, "Codegen was moved");
static thread_local std::mt19937 gen(std::random_device{}());
std::uniform_int_distribution<uint64_t> dist;
std::stringstream ss;
ss << std::hex << dist(gen);

std::string funcName = "fn_" + ss.str();

// Create the function with baseline signature: (Frame*, Arg0, Arg1, Arg2,
// Arg3, Arg4) -> RTValue
Function *F =
Function::Create(types.baselineFunctionTy, Function::ExternalLinkage,
funcName, *TheModule);

// Use the guard to save current IR state and memory management context
FunctionScopeGuard scopeGuard(*this, F);

// Enforce frame pointers for reliable stack traces
F->addFnAttr("frame-pointer", "all");

// Create entry block
BasicBlock *BB = BasicBlock::Create(TheContext, "entry", F);
Builder.SetInsertPoint(BB);

// Unpack arguments from LLVM function
auto arg_it = F->arg_begin();
Value *framePtr = &*arg_it++; // Arg 0: Frame*
Value *regArgs[5]; // Arg 1-5: RTValue registers
for (int i = 0; i < 5; ++i) {
regArgs[i] = &*arg_it++;
}

// Set personality function for exception handling
FunctionType *personalityFnTy = FunctionType::get(types.i32Ty, true);
personalityFn =
TheModule->getOrInsertFunction("__gxx_personality_v0", personalityFnTy);
F->setPersonalityFn(cast<Function>(personalityFn.getCallee()));

// Prepare environment for body codegen
variableBindingStack.push();
variableTypesBindingsStack.push();

int isVariadic = method.isvariadic();
int numParams = method.params_size();
Value *nilValue = valueEncoder.boxNil().value;

// 1. Unpack parameters
for (int i = 0; i < numParams; ++i) {
const auto &paramNode = method.params(i);
std::string paramName = paramNode.subnode().binding().name();

Value *val = nullptr;

if (isVariadic && i == numParams - 1) {
// It's the variadic parameter. Unpack from frame->variadicSeq (index 2)
Value *variadicSeqPtr = Builder.CreateStructGEP(types.frameTy, framePtr,
2, "variadicSeq_ptr");
val = Builder.CreateLoad(types.RT_valueTy, variadicSeqPtr, "variadicSeq");
} else {
// Regular parameter
if (i < 5) {
// From registers
val = regArgs[i];
} else {
// From frame->locals[i-5] (index 5)
Value *localsBase =
Builder.CreateStructGEP(types.frameTy, framePtr, 5, "locals_base");
// Accessing element i-5 in the flexible array
Value *argPtr = Builder.CreateInBoundsGEP(
types.RT_valueTy, localsBase,
{Builder.getInt32(0), Builder.getInt32(i - 5)}, "arg_ptr");
val = Builder.CreateLoad(types.RT_valueTy, argPtr, "arg_val");
}
}

if (!val)
val = nilValue;

TypedValue paramTV(ObjectTypeSet::dynamicType(), val);
variableBindingStack.set(paramName, paramTV);
variableTypesBindingsStack.set(paramName, ObjectTypeSet::dynamicType());
}

// 2. Unpack closed overs (captures) with type propagation
if (!captureInfo.empty()) {
// a. Get method pointer from frame
Value *methodPtrPtr =
Builder.CreateStructGEP(types.frameTy, framePtr, 1, "method_ptr_ptr");
Value *methodPtr =
Builder.CreateLoad(types.ptrTy, methodPtrPtr, "method_ptr");

// b. Load closedOvers array pointer from method
Value *closedOversPtrPtr = Builder.CreateStructGEP(
types.methodTy, methodPtr, 6, "closedOvers_ptr_ptr");
Value *closedOversPtr =
Builder.CreateLoad(types.ptrTy, closedOversPtrPtr, "closedOvers_ptr");

// c. Unpack each capture
for (int i = 0; i < (int)captureInfo.size(); ++i) {
const std::string &name = captureInfo[i].first;
const ObjectTypeSet &type = captureInfo[i].second;

Value *valRawPtr = Builder.CreateInBoundsGEP(
types.RT_valueTy, closedOversPtr, Builder.getInt64(i), "capture_ptr");
Value *valRaw =
Builder.CreateLoad(types.RT_valueTy, valRawPtr, "capture_raw");

Value *val = valRaw;
if (!type.isBoxedType()) {
if (type.isUnboxedType(integerType)) {
val = valueEncoder.unboxInt32(TypedValue(type.boxed(), valRaw)).value;
} else if (type.isUnboxedType(doubleType)) {
val =
valueEncoder.unboxDouble(TypedValue(type.boxed(), valRaw)).value;
} else if (type.isUnboxedType(booleanType)) {
val = valueEncoder.unboxBool(TypedValue(type.boxed(), valRaw)).value;
}
}

variableBindingStack.set(name, TypedValue(type, val));
variableTypesBindingsStack.set(name, type);
}
}

// Generate body
TypedValue result = codegen(method.body(), ObjectTypeSet::all());

// TODO - it is not yet clear if this should always happen.
// Previous function calls cound introduce the flush as well, we need to
// carefully analyse the memory model for closed overs.
//
// llvm::FunctionType *flushTy =
// llvm::FunctionType::get(types.voidTy, {}, false);
// invokeManager.invokeRaw("Ebr_flush_critical", flushTy, {});

// Box result and return
Builder.CreateRet(valueEncoder.box(result).value);

// Clean up bindings
variableBindingStack.pop();
variableTypesBindingsStack.pop();

verifyFunction(*F);
return F;
}

TypedValue CodeGen::codegen(const Node &node,
const ObjectTypeSet &typeRestrictions) {
CLJ_ASSERT(TSContext != nullptr, "Codegen was moved");
Expand Down Expand Up @@ -283,6 +432,8 @@ TypedValue CodeGen::codegen(const Node &node,
// return codegen(node, node.subnode().try_(), typeRestrictions);
case opVar:
return codegen(node, node.subnode().var(), typeRestrictions);
case opFn:
return codegen(node, node.subnode().fn(), typeRestrictions);
case opWithMeta:
return codegen(node, node.subnode().withmeta(), typeRestrictions);
default: {
Expand All @@ -308,6 +459,8 @@ ObjectTypeSet CodeGen::getType(const Node &node,
return getType(node, node.subnode().vector(), typeRestrictions);
case opMap:
return getType(node, node.subnode().map(), typeRestrictions);
case opFn:
return getType(node, node.subnode().fn(), typeRestrictions);
case opStaticCall:
return getType(node, node.subnode().staticcall(), typeRestrictions);

Expand Down
27 changes: 27 additions & 0 deletions backend-v2/codegen/CodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ class CodeGen {
const ObjectTypeSet &instanceType,
const std::vector<ObjectTypeSet> &argTypes);

llvm::Function *generateBaselineMethod(
const FnMethodNode &method,
const std::vector<std::pair<std::string, ObjectTypeSet>> &captureInfo);

TypedValue codegen(const Node &node, const ObjectTypeSet &typeRestrictions);

TypedValue codegen(const Node &node, const ConstNode &subnode,
Expand All @@ -129,6 +133,8 @@ class CodeGen {
const ObjectTypeSet &typeRestrictions);
TypedValue codegen(const Node &node, const WithMetaNode &subnode,
const ObjectTypeSet &typeRestrictions);
TypedValue codegen(const Node &node, const FnNode &subnode,
const ObjectTypeSet &typeRestrictions);
TypedValue codegen(const Node &node, const StaticFieldNode &subnode,
const ObjectTypeSet &typeRestrictions);
TypedValue codegen(const Node &node, const LetNode &subnode,
Expand Down Expand Up @@ -171,6 +177,8 @@ class CodeGen {
const ObjectTypeSet &typeRestrictions);
ObjectTypeSet getType(const Node &node, const WithMetaNode &subnode,
const ObjectTypeSet &typeRestrictions);
ObjectTypeSet getType(const Node &node, const FnNode &subnode,
const ObjectTypeSet &typeRestrictions);
ObjectTypeSet getType(const Node &node, const StaticFieldNode &subnode,
const ObjectTypeSet &typeRestrictions);
ObjectTypeSet getType(const Node &node, const LetNode &subnode,
Expand Down Expand Up @@ -204,6 +212,25 @@ class CodeGen {
MemoryManagement &getMemoryManagement() { return memoryManagement; }
DynamicConstructor &getDynamicConstructor() { return dynamicConstructor; }

class FunctionScopeGuard {
CodeGen &cg;
llvm::IRBuilder<>::InsertPointGuard ipGuard;
size_t prevLexicalBlocksSize;

public:
FunctionScopeGuard(CodeGen &cg, llvm::Function *newFunction)
: cg(cg), ipGuard(cg.Builder),
prevLexicalBlocksSize(cg.LexicalBlocks.size()) {
cg.memoryManagement.pushState(newFunction);
}
~FunctionScopeGuard() {
cg.memoryManagement.popState();
while (cg.LexicalBlocks.size() > prevLexicalBlocksSize) {
cg.LexicalBlocks.pop_back();
}
}
};

private:
std::map<SourceLocation, std::string> formMap;
};
Expand Down
112 changes: 68 additions & 44 deletions backend-v2/codegen/LLVMTypes.cpp
Original file line number Diff line number Diff line change
@@ -1,54 +1,78 @@
#include "LLVMTypes.h"
#include "../RuntimeHeaders.h"
#include "../bridge/Exceptions.h"
#include <cstdint>

using namespace llvm;

namespace rt {

LLVMTypes::LLVMTypes(LLVMContext& context) {
i64Ty = Type::getInt64Ty(context);
i32Ty = Type::getInt32Ty(context);
i1Ty = Type::getInt1Ty(context);
doubleTy = Type::getDoubleTy(context);
ptrTy = PointerType::getUnqual(context); // Opaque pointer in LLVM 15+
wordTy = K_WORD_SIZE == 32 ? i32Ty : i64Ty;
voidTy = Type::getVoidTy(context);
RT_valueTy = i64Ty;

RT_objectTy = StructType::create(context,
{
wordTy, // atomicRefCount
i32Ty, // type (enum)

LLVMTypes::LLVMTypes(LLVMContext &context) {
i64Ty = Type::getInt64Ty(context);
i32Ty = Type::getInt32Ty(context);
i8Ty = Type::getInt8Ty(context);
i1Ty = Type::getInt1Ty(context);
doubleTy = Type::getDoubleTy(context);
ptrTy = PointerType::getUnqual(context); // Opaque pointer in LLVM 15+
wordTy = K_WORD_SIZE == 32 ? i32Ty : i64Ty;
voidTy = Type::getVoidTy(context);
RT_valueTy = i64Ty;

RT_objectTy = StructType::create(context,
{
wordTy, // atomicRefCount
i32Ty, // type (enum)
#ifdef USE_MEMORY_BANKS
Type::getInt8Ty(Ctx) // bankId
Type::getInt8Ty(Ctx) // bankId
#endif
},
"Clojure_Object");
}

llvm::Type *LLVMTypes::typeForType(const ObjectTypeSet &type) {
if (type.isBoxedType())
return i64Ty;
if (type.isUnboxedPointer())
return ptrTy;

if (type.isUnboxedType(integerType))
return i32Ty;
if (type.isUnboxedType(doubleType))
return doubleTy;
if (type.isUnboxedType(keywordType))
return i32Ty;
if (type.isUnboxedType(symbolType))
return i32Ty;
if (type.isUnboxedType(booleanType))
return i1Ty;

throwInternalInconsistencyException("Type not supported: " +
type.toString());

return voidTy;
}

} // namespace rt
},
"Clojure_Object");

frameTy = StructType::create(context,
{ptrTy, // leafFrame
ptrTy, // method
RT_valueTy, // variadicSeq
i32Ty, // bailoutEntryIndex
i32Ty, // localsCount
ArrayType::get(i64Ty, 0)}, // locals
"Clojure_Frame");

methodTy = StructType::create(context,
{i8Ty, // index
i8Ty, // isVariadic
i8Ty, // fixedArity
i8Ty, // closedOversCount
ptrTy, // baselineImplementation
ptrTy, // loopId
ptrTy}, // closedOvers
"Clojure_FunctionMethod");
/* Usually 6 args fit in registers */
baselineFunctionTy = FunctionType::get(
RT_valueTy,
{ptrTy, RT_valueTy, RT_valueTy, RT_valueTy, RT_valueTy, RT_valueTy},
false);
}

llvm::Type *LLVMTypes::typeForType(const ObjectTypeSet &type) {
if (type.isBoxedType())
return i64Ty;
if (type.isUnboxedPointer())
return ptrTy;

if (type.isUnboxedType(integerType))
return i32Ty;
if (type.isUnboxedType(doubleType))
return doubleTy;
if (type.isUnboxedType(keywordType))
return i32Ty;
if (type.isUnboxedType(symbolType))
return i32Ty;
if (type.isUnboxedType(booleanType))
return i1Ty;

throwInternalInconsistencyException("Type not supported: " + type.toString());

return voidTy;
}

} // namespace rt
Loading
Loading