diff --git a/codon/cir/llvm/llvisitor.cpp b/codon/cir/llvm/llvisitor.cpp index 3e7f6070..af3603e6 100644 --- a/codon/cir/llvm/llvisitor.cpp +++ b/codon/cir/llvm/llvisitor.cpp @@ -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 pyNumberMethodsFields(36, ptr); + auto *pyNumberMethodsType = + llvm::StructType::create(*context, pyNumberMethodsFields, "PyNumberMethods"); + std::vector pySequenceMethodsFields(10, ptr); + auto *pySequenceMethodsType = + llvm::StructType::create(*context, pySequenceMethodsFields, "PySequenceMethods"); + std::vector pyMappingMethodsFields(3, ptr); + auto *pyMappingMethodsType = + llvm::StructType::create(*context, pyMappingMethodsFields, "PyMappingMethods"); + std::vector 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( + M->getOrInsertFunction("seq_alloc_uncollectable", ptr, i64).getCallee()); + allocUncollectable->setDoesNotThrow(); + allocUncollectable->setReturnDoesNotAlias(); + allocUncollectable->setOnlyAccessesInaccessibleMemory(); + + auto free = llvm::cast( + 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 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 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 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(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( + 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 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); } diff --git a/codon/cir/pyextension.h b/codon/cir/pyextension.h index a98d6090..29a19e53 100644 --- a/codon/cir/pyextension.h +++ b/codon/cir/pyextension.h @@ -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; diff --git a/codon/runtime/lib.cpp b/codon/runtime/lib.cpp index f3d148ae..2f905b25 100644 --- a/codon/runtime/lib.cpp +++ b/codon/runtime/lib.cpp @@ -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); diff --git a/stdlib/internal/gc.codon b/stdlib/internal/gc.codon index 21ea5ed8..abdcd54e 100644 --- a/stdlib/internal/gc.codon +++ b/stdlib/internal/gc.codon @@ -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)