Skip to content
Open
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
44 changes: 22 additions & 22 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ include(GNUInstallDirs)
# 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_TAG "9802d61921ad5688ae42e4e628d754fc1192244d" 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,10 @@ if(_python_platlib)
else()
set(CPPINTEROP_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
endif()
set(CPPINTEROP_INSTALL_DIR "${CPPINTEROP_INSTALL_PREFIX}/cppjit_backend")

# CppInterOp installs here; cppjit's own rules ship a subset, so the wheel
# owns every installed file.
set(CPPINTEROP_STAGE_DIR "${CMAKE_BINARY_DIR}/cppinterop-stage")

# Include cmake for CppInterOp config and build using ExternalProject.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddCppInterOp.cmake)
Expand All @@ -121,11 +124,13 @@ 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}"
# cling-only code paths need the flavor at compile time, not just in cmake
$<$<BOOL:${CPPJIT_USE_CLING}>:CPPJIT_USE_CLING>
)

target_include_directories(cppjit PRIVATE
Expand All @@ -134,7 +139,7 @@ target_include_directories(cppjit PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/cpyrt
${CMAKE_CURRENT_SOURCE_DIR}/src/interop
${CPPINTEROP_INSTALL_DIR}/include
${CPPINTEROP_STAGE_DIR}/include
${Python_INCLUDE_DIRS}
)

Expand All @@ -159,22 +164,17 @@ 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)
endforeach()
")

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

# ship the builtin headers of the build clang, laid out as a headers-only
# resource dir: only include/ ships
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
)
10 changes: 8 additions & 2 deletions cmake/AddCppInterOp.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ function(cppjit_add_cppinterop)
-DLLVM_DIR=${LLVM_DIR}
-DCPPINTEROP_ENABLE_TESTING=${CPPJIT_ENABLE_CPPINTEROP_TESTS}
-DBUILD_SHARED_LIBS=ON
-DCMAKE_INSTALL_PREFIX=${CPPINTEROP_INSTALL_DIR}
# The wheel ships a single unversioned library file.
-DCPPINTEROP_SHARED_LIBRARY_VERSIONING=OFF
-DCMAKE_INSTALL_PREFIX=${CPPINTEROP_STAGE_DIR}
-DCMAKE_INSTALL_LIBDIR=lib
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_STANDARD=17
Expand Down Expand Up @@ -85,12 +87,16 @@ function(cppjit_add_cppinterop)
set(_log_args "")
endif()

# Install only the library and headers, not CppInterOp's full install tree.
ExternalProject_Add(CppInterOp
${_source_args}
PREFIX "${CMAKE_BINARY_DIR}/CppInterOp"
CMAKE_ARGS ${_args}
# -stripped keeps .dynsym, so the dlsym-based dispatch still resolves.
INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR>
--target install-clangCppInterOp-stripped install-cppinterop-headers
BUILD_BYPRODUCTS
"${CPPINTEROP_INSTALL_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
"${CPPINTEROP_STAGE_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
${_log_args}
)

Expand Down
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
4 changes: 2 additions & 2 deletions python/cppjit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,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
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.

18 changes: 13 additions & 5 deletions src/cpyrt/CPPInstance.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -869,23 +869,30 @@ static PyObject* op_str(CPPInstance* self) {
}

// 2. Cling's pretty printing (not done through backend for performance
// reasons)
// reasons). Cling only: clang-repl has no cling namespace to look up, so the
// whole path compiles out and str() falls through to the generic repr.
#ifdef CPPJIT_USE_CLING

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be required to CppInterOp’s toString interface which will still crash for clang-repl but this time we can fix it there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to make that change here: route str() through Cpp::ObjToString and drop the cling-only block. clang-repl would then keep failing in toString until compiler-research/CppInterOp#1100 lands the fix there. Do you want that in this PR, or should we keep the short-circuit and switch once #1100 is fixed?

if (!ScopeFlagCheck(self, CPPScope::kNoPrettyPrint)) {
static PyObject* printValue = nullptr;
if (!printValue) {
PyObject* gbl =
PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppjit.gbl");
PyObject* cl = PyObject_GetAttrString(gbl, (char*)"cling");
printValue = PyObject_GetAttrString(cl, (char*)"printValue");
Py_DECREF(cl);
// no cling namespace exists unless user code declares one

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can just short-circuit this path completely if we know the interpreter is clang-repl, since that lookup would always return null. We could use the compile-time definition CPPJIT_USE_CLING, or a runtime check like in test/support.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — done. The whole pretty-printing block now sits behind #ifdef CPPJIT_USE_CLING, so clang-repl builds compile it out and str() falls straight through to the generic repr; strings on the clang-repl libcppjit.so shows the printValue lookup gone, and the object shrinks slightly. One thing the gate needed: CPPJIT_USE_CLING was only ever a CMake option and never reached the sources, so I added the matching target_compile_definitions entry — without it the #ifdef would have been false on the cling build too. The null-guards stay inside the cling branch, in case an odd cling state leaves the lookup empty. Worth naming the trade-off: a user-declared namespace cling { printValue } is no longer honored on clang-repl, which I think is right given the native value-printing direction in compiler-research/CppInterOp#1100.

PyObject* cl =
gbl ? PyObject_GetAttrString(gbl, (char*)"cling") : nullptr;
printValue =
cl ? PyObject_GetAttrString(cl, (char*)"printValue") : nullptr;
Py_XDECREF(cl);
// gbl is borrowed
if (printValue) {
Py_DECREF(printValue); // make borrowed
if (!PyCallable_Check(printValue))
printValue = nullptr; // unusable ...
}
if (!printValue) // unlikely
if (!printValue) {
PyErr_Clear();
ScopeFlagSet(self, CPPScope::kNoPrettyPrint);
}
}

if (printValue) {
Expand Down Expand Up @@ -929,6 +936,7 @@ static PyObject* op_str(CPPInstance* self) {
// if not available/specialized, don't try again
ScopeFlagSet(self, CPPScope::kNoPrettyPrint);
}
#endif // CPPJIT_USE_CLING

// 3. Generic printing as done in op_repr
return op_repr(self);
Expand Down
16 changes: 10 additions & 6 deletions src/cpyrt/CPPMethod.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,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 +564,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 @@ -1058,7 +1061,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: 2 additions & 1 deletion src/cpyrt/CPPOverload.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,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
Loading
Loading