1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00

PyType codegen (WIP)

This commit is contained in:
A. R. Shajii 2023-02-11 17:55:05 -05:00
parent 92f9a274e7
commit 573f3f68c5
4 changed files with 237 additions and 15 deletions

View File

@ -660,29 +660,68 @@ void LLVMVisitor::writeToPythonExtension(const PyModule &pymod,
// Setup LLVM types & constants
auto *i64 = B->getInt64Ty();
auto *i32 = B->getInt32Ty();
auto *i8 = B->getInt8Ty();
auto *ptr = B->getInt8PtrTy();
auto *pyMethodDefType = llvm::StructType::create("PyMethodDef", ptr, ptr, i32, ptr);
auto *pyObjectType = llvm::StructType::create("PyObject", i64, ptr);
auto *pyVarObjectType = llvm::StructType::create("PyVarObject", pyObjectType, i64);
auto *pyModuleDefBaseType =
llvm::StructType::create("PyMethodDefBase", pyObjectType, ptr, i64, ptr);
auto *pyModuleDefType =
llvm::StructType::create("PyModuleDef", pyModuleDefBaseType, ptr, ptr, i64,
pyMethodDefType->getPointerTo(), ptr, ptr, ptr, ptr);
std::vector<llvm::Type *> pyNumberMethodsFields(36, ptr);
auto *pyNumberMethodsType =
llvm::StructType::create(*context, pyNumberMethodsFields, "PyNumberMethods");
std::vector<llvm::Type *> pySequenceMethodsFields(10, ptr);
auto *pySequenceMethodsType =
llvm::StructType::create(*context, pySequenceMethodsFields, "PySequenceMethods");
std::vector<llvm::Type *> pyMappingMethodsFields(3, ptr);
auto *pyMappingMethodsType =
llvm::StructType::create(*context, pyMappingMethodsFields, "PyMappingMethods");
std::vector<llvm::Type *> pyAsyncMethodsFields(4, ptr);
auto *pyAsyncMethodsType =
llvm::StructType::create(*context, pyAsyncMethodsFields, "PyAsyncMethods");
auto *pyBufferProcsType = llvm::StructType::create("PyBufferProcs", ptr, ptr);
auto *pyTypeObjectType = llvm::StructType::create(
"PyTypeObject", pyVarObjectType, ptr, i64, i64, ptr, i64, ptr, ptr, ptr, ptr, ptr,
ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, i64, ptr, ptr, ptr, ptr, i64, ptr, ptr,
ptr, ptr, ptr, ptr, ptr, ptr, ptr, i64, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr,
ptr, ptr, ptr, i32, ptr, ptr, i8);
auto *zero64 = B->getInt64(0);
auto *zero32 = B->getInt32(0);
auto *zero8 = B->getInt8(0);
auto *null = llvm::Constant::getNullValue(ptr);
auto *pyTypeType = new llvm::GlobalVariable(*M, ptr, /*isConstant=*/false,
llvm::GlobalValue::ExternalLinkage,
/*Initializer=*/nullptr, "PyType_Type");
auto allocUncollectable = llvm::cast<llvm::Function>(
M->getOrInsertFunction("seq_alloc_uncollectable", ptr, i64).getCallee());
allocUncollectable->setDoesNotThrow();
allocUncollectable->setReturnDoesNotAlias();
allocUncollectable->setOnlyAccessesInaccessibleMemory();
auto free = llvm::cast<llvm::Function>(
M->getOrInsertFunction("seq_free", B->getVoidTy(), ptr).getCallee());
free->setDoesNotThrow();
// Helpers
auto pyFunc = [&](Func *func) -> llvm::Constant * {
if (!func)
return null;
auto llvmName = getNameForFunction(func);
auto *llvmFunc = M->getFunction(llvmName);
seqassertn(llvmFunc, "function {} not found in LLVM module", llvmName);
llvmFunc = createPyTryCatchWrapper(llvmFunc);
return llvmFunc;
};
// Handle functions
std::vector<llvm::Constant *> pyMethods;
for (auto &pyfunc : pymod.functions) {
auto llvmName = getNameForFunction(pyfunc.func);
auto *llvmFunc = M->getFunction(llvmName);
seqassertn(llvmFunc, "function {} not found in LLVM module", llvmName);
llvmFunc = createPyTryCatchWrapper(llvmFunc);
auto *nameVar = new llvm::GlobalVariable(
*M, llvm::ArrayType::get(B->getInt8Ty(), pyfunc.name.length() + 1),
*M, llvm::ArrayType::get(i8, pyfunc.name.length() + 1),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantDataArray::getString(*context, pyfunc.name), ".pyext_func_name");
nameVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
@ -701,14 +740,14 @@ void LLVMVisitor::writeToPythonExtension(const PyModule &pymod,
auto *docsConst = null;
if (pyfunc.doc.empty()) {
auto *docsVar = new llvm::GlobalVariable(
*M, llvm::ArrayType::get(B->getInt8Ty(), pyfunc.doc.length() + 1),
*M, llvm::ArrayType::get(i8, pyfunc.doc.length() + 1),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantDataArray::getString(*context, pyfunc.doc), ".pyext_docstring");
docsVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
docsConst = llvm::ConstantExpr::getBitCast(docsVar, ptr);
}
pyMethods.push_back(llvm::ConstantStruct::get(pyMethodDefType, nameVar, llvmFunc,
flagConst, docsConst));
pyMethods.push_back(llvm::ConstantStruct::get(
pyMethodDefType, nameVar, pyFunc(pyfunc.func), flagConst, docsConst));
}
pyMethods.push_back(
llvm::ConstantStruct::get(pyMethodDefType, null, null, zero32, null));
@ -721,11 +760,11 @@ void LLVMVisitor::writeToPythonExtension(const PyModule &pymod,
// Construct PyModuleDef array
auto *pyObjectConst = llvm::ConstantStruct::get(pyObjectType, B->getInt64(1), null);
auto *pyModuleDefBaseConst = llvm::ConstantStruct::get(
pyModuleDefBaseType, pyObjectConst, null, B->getInt64(0), null);
auto *pyModuleDefBaseConst =
llvm::ConstantStruct::get(pyModuleDefBaseType, pyObjectConst, null, zero64, null);
auto *nameVar = new llvm::GlobalVariable(
*M, llvm::ArrayType::get(B->getInt8Ty(), pymod.name.length() + 1),
*M, llvm::ArrayType::get(i8, pymod.name.length() + 1),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantDataArray::getString(*context, pymod.name), ".pyext_module_name");
nameVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
@ -734,7 +773,7 @@ void LLVMVisitor::writeToPythonExtension(const PyModule &pymod,
auto *docsConst = null;
if (!pymod.doc.empty()) {
auto *docsVar = new llvm::GlobalVariable(
*M, llvm::ArrayType::get(B->getInt8Ty(), pymod.doc.length() + 1),
*M, llvm::ArrayType::get(i8, pymod.doc.length() + 1),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantDataArray::getString(*context, pymod.doc), ".pyext_docstring");
docsVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
@ -767,7 +806,171 @@ void LLVMVisitor::writeToPythonExtension(const PyModule &pymod,
B->CreateRet(B->CreateCall(pyModuleCreate,
{pyModuleConst, B->getInt32(PYEXT_PYTHON_ABI_VERSION)}));
// TODO: Codegen types, methods, etc.
for (auto &pytype : pymod.types) {
std::vector<llvm::Constant *> numberSlots = {
pyFunc(pytype.add), // nb_add
pyFunc(pytype.sub), // nb_subtract
pyFunc(pytype.mul), // nb_multiply
pyFunc(pytype.mod), // nb_remainder
pyFunc(pytype.divmod), // nb_divmod
pyFunc(pytype.pow), // nb_power
pyFunc(pytype.neg), // nb_negative
pyFunc(pytype.pos), // nb_positive
pyFunc(pytype.abs), // nb_absolute
pyFunc(pytype.bool_), // nb_bool
pyFunc(pytype.invert), // nb_invert
pyFunc(pytype.lshift), // nb_lshift
pyFunc(pytype.rshift), // nb_rshift
pyFunc(pytype.and_), // nb_and
pyFunc(pytype.xor_), // nb_xor
pyFunc(pytype.or_), // nb_or
pyFunc(pytype.int_), // nb_int
null, // nb_reserved
pyFunc(pytype.float_), // nb_float
pyFunc(pytype.iadd), // nb_inplace_add
pyFunc(pytype.isub), // nb_inplace_subtract
pyFunc(pytype.imul), // nb_inplace_multiply
pyFunc(pytype.imod), // nb_inplace_remainder
pyFunc(pytype.ipow), // nb_inplace_power
pyFunc(pytype.ilshift), // nb_inplace_lshift
pyFunc(pytype.irshift), // nb_inplace_rshift
pyFunc(pytype.iand), // nb_inplace_and
pyFunc(pytype.ixor), // nb_inplace_xor
pyFunc(pytype.ior), // nb_inplace_or
pyFunc(pytype.floordiv), // nb_floor_divide
pyFunc(pytype.truediv), // nb_true_divide
pyFunc(pytype.ifloordiv), // nb_inplace_floor_divide
pyFunc(pytype.itruediv), // nb_inplace_true_divide
pyFunc(pytype.index), // nb_index
pyFunc(pytype.matmul), // nb_matrix_multiply
pyFunc(pytype.imatmul), // nb_inplace_matrix_multiply
};
std::vector<llvm::Constant *> sequenceSlots = {
pyFunc(pytype.len), // sq_length
null, // sq_concat
null, // sq_repeat
pyFunc(pytype.getitem), // sq_item
null, // was_sq_slice
pyFunc(pytype.setitem), // sq_ass_item
null, // was_sq_ass_slice
pyFunc(pytype.contains), // sq_contains
null, // sq_inplace_concat
null, // sq_inplace_repeat
};
bool needNumberSlots =
std::find_if(numberSlots.begin(), numberSlots.end(),
[&](auto *v) { return v != null; }) != numberSlots.end();
bool needSequenceSlots =
std::find_if(sequenceSlots.begin(), sequenceSlots.end(),
[&](auto *v) { return v != null; }) != sequenceSlots.end();
auto *numberSlotsConst =
needNumberSlots ? llvm::ConstantStruct::get(pyNumberMethodsType, numberSlots)
: null;
auto *sequenceSlotsConst =
needSequenceSlots
? llvm::ConstantStruct::get(pySequenceMethodsType, sequenceSlots)
: null;
auto *nameVar = new llvm::GlobalVariable(
*M, llvm::ArrayType::get(B->getInt8Ty(), pytype.name.length() + 1),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantDataArray::getString(*context, pytype.name), ".pyext_type_name");
nameVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
auto *docsConst = null;
if (pytype.doc.empty()) {
auto *docsVar = new llvm::GlobalVariable(
*M, llvm::ArrayType::get(B->getInt8Ty(), pytype.doc.length() + 1),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantDataArray::getString(*context, pytype.doc),
".pyext_type_docstring");
docsVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
docsConst = llvm::ConstantExpr::getBitCast(docsVar, ptr);
}
auto *refType = cast<types::RefType>(pytype.type);
auto *llvmType = getLLVMType(pytype.type);
auto *objectType = llvm::StructType::get(pyObjectType, llvmType);
auto codonSize =
refType
? M->getDataLayout().getTypeAllocSize(getLLVMType(refType->getContents()))
: 0;
auto pySize = M->getDataLayout().getTypeAllocSize(objectType);
auto *alloc = llvm::cast<llvm::Function>(
M->getOrInsertFunction(pytype.name + ".py_alloc", ptr, ptr, i64).getCallee());
/*
{
auto *entry = llvm::BasicBlock::Create(*context, "entry", alloc);
B->SetInsertPoint(entry);
auto *memory = B->CreateCall(allocUncollectable, pySize);
// TODO
}
*/
std::vector<llvm::Constant *> typeSlots = {
llvm::ConstantStruct::get(
pyVarObjectType,
llvm::ConstantStruct::get(pyObjectType, B->getInt64(1), pyTypeType),
zero64), // PyObject_VAR_HEAD
nameConst, // const char *tp_name;
B->getInt64(pySize), // Py_ssize_t tp_basicsize;
zero64, // Py_ssize_t tp_itemsize;
// destructor tp_dealloc;
zero64, // Py_ssize_t tp_vectorcall_offset;
null, // getattrfunc tp_getattr;
null, // setattrfunc tp_setattr;
null, // PyAsyncMethods *tp_as_async;
pyFunc(pytype.repr), // reprfunc tp_repr;
numberSlotsConst, // PyNumberMethods *tp_as_number;
sequenceSlotsConst, // PySequenceMethods *tp_as_sequence;
null, // PyMappingMethods *tp_as_mapping;
pyFunc(pytype.hash), // hashfunc tp_hash;
pyFunc(pytype.call), // ternaryfunc tp_call;
pyFunc(pytype.str), // reprfunc tp_str;
null, // getattrofunc tp_getattro;
null, // setattrofunc tp_setattro;
null, // PyBufferProcs *tp_as_buffer;
zero64, // unsigned long tp_flags;
docsConst, // const char *tp_doc;
null, // traverseproc tp_traverse;
null, // inquiry tp_clear;
pyFunc(pytype.cmp), // richcmpfunc tp_richcompare;
zero64, // Py_ssize_t tp_weaklistoffset;
pyFunc(pytype.iter), // getiterfunc tp_iter;
pyFunc(pytype.iternext), // iternextfunc tp_iternext;
// PyMethodDef *tp_methods;
null, // PyMemberDef *tp_members;
// PyGetSetDef *tp_getset;
null, // PyTypeObject *tp_base;
null, // PyObject *tp_dict;
null, // descrgetfunc tp_descr_get;
null, // descrsetfunc tp_descr_set;
zero64, // Py_ssize_t tp_dictoffset;
pyFunc(pytype.init), // initproc tp_init;
alloc, // allocfunc tp_alloc;
null, // newfunc tp_new;
free, // freefunc tp_free;
null, // inquiry tp_is_gc;
null, // PyObject *tp_bases;
null, // PyObject *tp_mro;
null, // PyObject *tp_cache;
null, // void *tp_subclasses;
null, // PyObject *tp_weaklist;
null, // destructor tp_del;
zero32, // unsigned int tp_version_tag;
pyFunc(pytype.del), // destructor tp_finalize;
null, // vectorcallfunc tp_vectorcall;
B->getInt8(0), // char tp_watched;
};
// TODO
}
// set tp_base in module init func
writeToObjectFile(filename);
}

View File

@ -105,6 +105,7 @@ struct PyType {
Func *str = nullptr;
Func *cmp = nullptr;
Func *iter = nullptr;
Func *iternext = nullptr;
Func *del = nullptr;
Func *new_ = nullptr;
Func *init = nullptr;

View File

@ -165,6 +165,14 @@ SEQ_FUNC void *seq_alloc_atomic(size_t n) {
#endif
}
SEQ_FUNC void *seq_alloc_uncollectable(size_t n) {
#if USE_STANDARD_MALLOC
return malloc(n);
#else
return GC_MALLOC_UNCOLLECTABLE(n);
#endif
}
SEQ_FUNC void *seq_calloc(size_t m, size_t n) {
#if USE_STANDARD_MALLOC
return calloc(m, n);

View File

@ -11,6 +11,11 @@ def seq_alloc(a: int) -> cobj:
def seq_alloc_atomic(a: int) -> cobj:
pass
@pure
@C
def seq_alloc_uncollectable(a: int) -> cobj:
pass
@nocapture
@derives
@C
@ -61,6 +66,11 @@ def alloc(sz: int):
def alloc_atomic(sz: int):
return seq_alloc_atomic(sz)
# Allocates a block of memory via GC that is scanned,
# but not collected itself. Should be free'd explicitly.
def alloc_uncollectable(sz: int):
return seq_alloc_uncollectable(sz)
def realloc(p: cobj, newsz: int, oldsz: int):
return seq_realloc(p, newsz, oldsz)