Skip to content
Draft
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
26 changes: 13 additions & 13 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ include(GNUInstallDirs)
# This option won't make a lot of sense since we only ship the shared library in site-packages
# Perhaps this should permanently be OFF and users can build their own CppInterOp if they want to run the tests?
option(CPPJIT_ENABLE_CPPINTEROP_TESTS "enable CppInterOp tests" OFF)
set(CPPINTEROP_GIT_REPOSITORY "https://github.com/compiler-research/CppInterOp.git" CACHE STRING "")
set(CPPINTEROP_GIT_TAG "8d624c621a4b95e36ff73ac708c85a768287478f" CACHE STRING "")
set(CPPINTEROP_GIT_REPOSITORY "https://github.com/keremsahn/CppInterOp.git" CACHE STRING "")
set(CPPINTEROP_GIT_TAG "attr-design" CACHE STRING "")
set(CPPINTEROP_SOURCE_DIR "" CACHE PATH
"Override default CppInterOp built by ExternalProject_Add, with a path to local CppInterOp source")

Expand Down Expand Up @@ -101,7 +101,7 @@ if(_python_platlib)
else()
set(CPPINTEROP_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
endif()
set(CPPINTEROP_INSTALL_DIR "${CPPINTEROP_INSTALL_PREFIX}/cppjit_backend")
set(CPPINTEROP_INSTALL_DIR "${CPPINTEROP_INSTALL_PREFIX}/cppjit/interop")

# Include cmake for CppInterOp config and build using ExternalProject.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddCppInterOp.cmake)
Expand All @@ -121,11 +121,11 @@ add_dependencies(cppjit CppInterOp)
# falling back to the install prefix (see cppinterop_paths()); the clang
# major names the versioned compiler probed for the runtime resource dir.
target_compile_definitions(cppjit PRIVATE
CPPINTEROP_INSTALL_PREFIX="${CPPINTEROP_INSTALL_PREFIX}"
CPPINTEROP_LIBRARY="cppjit_backend/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
CPPINTEROP_INCLUDE_DIR="cppjit_backend/include"
CPPINTEROP_INSTALL_PREFIX="${CPPINTEROP_INSTALL_PREFIX}/cppjit"
CPPINTEROP_LIBRARY="interop/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
CPPINTEROP_INCLUDE_DIR="interop/include"
CPPJIT_CLANG_MAJOR="${LLVM_VERSION_MAJOR}"
CPPJIT_CLANG_INCLUDE_DIR="cppjit_backend/lib/clang/${LLVM_VERSION_MAJOR}"
CPPJIT_CLANG_INCLUDE_DIR="interop/lib/clang/${LLVM_VERSION_MAJOR}"
)

target_include_directories(cppjit PRIVATE
Expand Down Expand Up @@ -159,21 +159,21 @@ set_target_properties(cppjit PROPERTIES
PREFIX "lib"
)

# libcppjit.so is installed at the site-packages root (import libcppjit)
# the extension lives inside the package (import cppjit.libcppjit)
install(TARGETS cppjit
LIBRARY DESTINATION .
LIBRARY DESTINATION cppjit
)

# install CppInterOp libraries and headers
install(CODE "
file(GLOB _interop_libs \"${CPPINTEROP_INSTALL_DIR}/lib/libclangCppInterOp*\")
foreach(_lib \${_interop_libs})
file(INSTALL \${_lib} DESTINATION \${CMAKE_INSTALL_PREFIX}/cppjit_backend/lib)
file(INSTALL \${_lib} DESTINATION \${CMAKE_INSTALL_PREFIX}/cppjit/interop/lib)
endforeach()
")

install(CODE "
file(INSTALL \"${CPPINTEROP_INSTALL_DIR}/include/\" DESTINATION \${CMAKE_INSTALL_PREFIX}/cppjit_backend/include)
file(INSTALL \"${CPPINTEROP_INSTALL_DIR}/include/\" DESTINATION \${CMAKE_INSTALL_PREFIX}/cppjit/interop/include)
")

# ship the builtin headers of the build clang, laid out as a headers-only
Expand All @@ -185,7 +185,7 @@ if(NOT EXISTS "${_clang_resource_dir}/include")
"${LLVM_DIR} carries no clang resource directory")
endif()
install(DIRECTORY "${_clang_resource_dir}/include/"
DESTINATION "cppjit_backend/lib/clang/${LLVM_VERSION_MAJOR}/include"
DESTINATION "cppjit/interop/lib/clang/${LLVM_VERSION_MAJOR}/include"
)

# the public cpyrt API headers keep their installed cpyrt/ prefix
Expand All @@ -195,5 +195,5 @@ install(FILES
src/cpyrt/DispatchPtr.h
src/cpyrt/PyException.h
src/cpyrt/Reflex.h
DESTINATION cppjit_backend/include/cpyrt
DESTINATION cppjit/interop/include/cpyrt
)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ maintainers = [

[tool.scikit-build]
wheel.install-dir = "."
wheel.packages = ["python/cppjit", "python/cppjit_backend"]
wheel.packages = ["python/cppjit"]
cmake.build-type = "Release"

[[tool.dynamic-metadata]]
Expand Down
10 changes: 8 additions & 2 deletions python/cppjit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"add_library_path", # add a path to search for libraries
"add_autoload_map", # explicitly include an autoload map
"set_debug", # enable/disable debug output
"use_alloc_analyzer", # enable/disable memory ownership analyzer
]

import ctypes
Expand Down Expand Up @@ -348,10 +349,10 @@ def _setup_include_paths():
if os.path.basename(apipath_extra) == "cpyrt":
apipath_extra = os.path.dirname(apipath_extra)
else:
spec = importlib.util.find_spec("libcppjit")
spec = importlib.util.find_spec("cppjit.libcppjit")
if spec is not None and spec.origin:
apipath_extra = os.path.join(
os.path.dirname(spec.origin), "cppjit_backend", "include"
os.path.dirname(spec.origin), "interop", "include"
)

if apipath_extra and apipath_extra.lower() != "none":
Expand Down Expand Up @@ -397,6 +398,11 @@ def set_debug(enable=True):
gbl.Cpp.EnableDebugOutput(enable)


def use_alloc_analyzer(enable=True):
"""Enable/disable memory ownership analyzer"""
_backend.UseAllocAnalyzer(enable)


def _get_name(tt):
if isinstance(tt, str):
return tt
Expand Down
6 changes: 3 additions & 3 deletions python/cppjit/_cpython_cppjit.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
def _preload_backend_library():
# preload the merged extension with ctypes and run LoadCppInterOp() first,
# so the interpreter is ready before the extension module initializes
spec = importlib.util.find_spec("libcppjit")
spec = importlib.util.find_spec("cppjit.libcppjit")
if spec is None or not spec.origin:
raise ImportError("cannot locate the libcppjit extension module")
raise ImportError("cannot locate the cppjit.libcppjit extension module")
lib = ctypes.CDLL(spec.origin, ctypes.RTLD_GLOBAL)
if not lib.LoadCppInterOp():
raise RuntimeError("failed to load CppInterOp (LoadCppInterOp returned 0)")
Expand All @@ -32,7 +32,7 @@ def _preload_backend_library():

_w = _preload_backend_library()

import libcppjit as _backend # noqa: E402
from . import libcppjit as _backend # noqa: E402


### template support ---------------------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion python/cppjit_backend/__init__.py

This file was deleted.

1 change: 0 additions & 1 deletion python/cppjit_backend/_version.py

This file was deleted.

30 changes: 24 additions & 6 deletions src/cpyrt/CPPMethod.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ extern PyObject* gBusException;
extern PyObject* gSegvException;
extern PyObject* gIllException;
extern PyObject* gAbrtException;
extern bool gUseAllocAnalyzer;
} // namespace cppjit::cpyrt

//- public helper ------------------------------------------------------------
Expand Down Expand Up @@ -529,7 +530,14 @@ int cpyrt::CPPMethod::GetPriority() {
// type:
// interop::TCppType_t type = interop::GetMethodArgType(fMethod, iarg);

if (interop::IsBuiltin(aname)) {
// Not builtin and spelled "const void *", so match the compacted name.
std::string compact = aname;
compact.erase(std::remove(compact.begin(), compact.end(), ' '),
compact.end());

if (compact.find("void*") != std::string::npos) {
priority -= 1000; // void*/void** shouldn't be too greedy
} else if (interop::IsBuiltin(aname)) {
// complex type (note: double penalty: for complex and the template type)
if (strstr(aname.c_str(), "std::complex"))
priority -= 10; // prefer double, float, etc. over conversion
Expand Down Expand Up @@ -557,10 +565,6 @@ int cpyrt::CPPMethod::GetPriority() {
else if (strstr(aname.c_str(), "char") && aname[aname.size() - 1] != '*')
priority += -60; // prefer (const) char* over char

// oddball
else if (strstr(aname.c_str(), "void*"))
priority -= 1000; // void*/void** shouldn't be too greedy

} else {
// This is a user-defined type (class, struct, enum, etc.).

Expand Down Expand Up @@ -749,6 +753,19 @@ PyObject* cpyrt::CPPMethod::GetArgDefault(int iarg, bool silent) {

bool cpyrt::CPPMethod::IsConst() { return interop::IsConstMethod(GetMethod()); }

//----------------------------------------------------------------------------
interop::AllocType cpyrt::CPPMethod::GetAllocBehaviour() {
if (fAllocType.has_value())
return *fAllocType;
interop::AllocType attrResult = interop::IsAllocator(GetMethod());
if (attrResult == interop::AllocType::Unknown && gUseAllocAnalyzer) {
interop::AllocType analyzeResult = interop::GetAllocType(GetMethod());
fAllocType = analyzeResult;
return analyzeResult;
}
fAllocType = attrResult;
return attrResult;
}
//----------------------------------------------------------------------------
PyObject* cpyrt::CPPMethod::GetScopeProxy() {
// Get or build the scope of this method.
Expand Down Expand Up @@ -1058,7 +1075,8 @@ PyObject* cpyrt::CPPMethod::Call(CPPInstance*& self, cpyrt_PyArgs_t args,

// validity check that should not fail
if (!object) {
PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
PyErr_SetString(PyExc_ReferenceError, "no C++ object available");
ctxt->fFlags |= CallContext::kCppException;
return nullptr;
}

Expand Down
3 changes: 3 additions & 0 deletions src/cpyrt/CPPMethod.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "PyCallable.h"

// Standard
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -62,6 +63,7 @@ class CPPMethod : public PyCallable {
PyObject* GetCoVarNames() override;
PyObject* GetArgDefault(int iarg, bool silent = true) override;
bool IsConst() override;
cppjit::interop::AllocType GetAllocBehaviour() override;

PyObject* GetScopeProxy() override;
interop::TCppFuncAddr_t GetFunctionAddress() override;
Expand Down Expand Up @@ -116,6 +118,7 @@ class CPPMethod : public PyCallable {
protected:
// cached value that doubles as initialized flag (uninitialized if -1)
int fArgsRequired;
std::optional<cppjit::interop::AllocType> fAllocType;
};

} // namespace cppjit::cpyrt
Expand Down
9 changes: 8 additions & 1 deletion src/cpyrt/CPPOverload.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ static inline PyObject* HandleReturn(CPPOverload* pymeth, CPPInstance* im_self,
CPPInstance* cppres =
(CPPInstance*)(CPPInstance_Check(result) ? result : nullptr);

interop::AllocType AT =
pymeth->fMethodInfo->fMethods[0]->GetAllocBehaviour();
if (AT != interop::AllocType::None && AT != interop::AllocType::Null &&
AT != interop::AllocType::Unknown)
pymeth->fMethodInfo->fFlags |= CallContext::kIsCreator;

// if this method creates new objects, always take ownership
if (IsCreator(pymeth->fMethodInfo->fFlags)) {

Expand Down Expand Up @@ -624,7 +630,8 @@ static PyObject* mp_vectorcall(CPPOverload* pymeth, PyObject* const* args,
return HandleReturn(pymeth, im_self, result);

// fall through: python is dynamic, and so, the hashing isn't infallible
ctxt.fFlags &= ~CallContext::kAllowImplicit;
ctxt.fFlags &= ~(CallContext::kAllowImplicit | CallContext::kPyException |
CallContext::kCppException);
PyErr_Clear();
ResetCallState(pymeth->fSelf, im_self);
}
Expand Down
36 changes: 2 additions & 34 deletions src/cpyrt/CallContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,8 @@

namespace cppjit::cpyrt {

// small number that allows use of stack for argument passing
const int SMALL_ARGS_N = 8;

// convention to pass flag for direct calls (similar to Python's vector calls)
#define DIRECT_CALL ((size_t)1 << (8 * sizeof(size_t) - 1))

#ifndef CPYRT_PARAMETER
#define CPYRT_PARAMETER
// general place holder for function parameters
struct Parameter {
union Value {
bool fBool;
int8_t fInt8;
uint8_t fUInt8;
short fShort;
unsigned short fUShort;
int fInt;
unsigned int fUInt;
long fLong;
intptr_t fIntPtr;
unsigned long fULong;
long long fLLong;
unsigned long long fULLong;
int64_t fInt64;
uint64_t fUInt64;
float fFloat;
double fDouble;
long double fLDouble;
void* fVoidp;
} fValue;
void* fRef;
char fTypeCode;
};
#endif // CPYRT_PARAMETER
// Parameter and the call-ABI constants (SMALL_ARGS_N, DIRECT_CALL) come
// from the interop callcontext.h via cppjit_interop.h

// extra call information
struct CallContext {
Expand Down
3 changes: 3 additions & 0 deletions src/cpyrt/PyCallable.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class PyCallable {
virtual PyObject* GetCoVarNames() = 0;
virtual PyObject* GetArgDefault(int /* iarg */, bool silent = true) = 0;
virtual bool IsConst() { return false; }
virtual cppjit::interop::AllocType GetAllocBehaviour() {
return cppjit::interop::AllocType::None;
}

virtual PyObject* GetScopeProxy() = 0;
virtual interop::TCppFuncAddr_t GetFunctionAddress() = 0;
Expand Down
Loading
Loading