diff --git a/README.md b/README.md index 32e3f3c95045d426c3b8e6741c12391eb38804e9..1d56c9ce869940eaa2757e31fdf1dda0e81ed30b 100644 --- a/README.md +++ b/README.md @@ -157,22 +157,6 @@ Currently, GPDPB is built with PXF by default (--enable-pxf is on). In order to build GPDB without pxf, simply invoke `./configure` with additional option `--disable-pxf`. PXF requires curl, so `--enable-pxf` is not compatible with the `--without-libcurl` option. -### Building GPDB with code generation enabled - -To build GPDB with code generation (codegen) enabled, you will need cmake 2.8 or higher -and a recent version of llvm and clang (include headers and developer libraries). Codegen utils -is currently developed against the LLVM 3.7.X release series. You can find more details about the codegen feature, -including details about obtaining the prerequisites, building and testing GPDB with codegen in the [Codegen README](src/backend/codegen). - -In short, you can change the `configure` with additional option -`--enable-codegen`, optionally giving the path to llvm and clang libraries on -your system. -``` -# Configure build environment to install at /usr/local/gpdb -# Enable CODEGEN -./configure --with-perl --with-python --with-libxml --enable-codegen --prefix=/usr/local/gpdb --with-codegen-prefix="/path/to/llvm;/path/to/clang" -``` - ### Building GPDB with gpperfmon enabled gpperfmon tracks a variety of queries, statistics, system properties, and metrics. diff --git a/concourse/README.md b/concourse/README.md index 6907f53ead4e9f6259792844e2f5a3e64c89ef97..b97b6606d2cc34cefc6bd09dbd922f05ba73e884 100644 --- a/concourse/README.md +++ b/concourse/README.md @@ -65,7 +65,6 @@ If a script file is not referenced in any of the directories it is considered ab There are some exceptions to this rule. Please do not create any more exceptions, and remove these as the occasion arises: -* `cpplint.py` is being used by codegen * `package_tarball.bash` is being used by [a gporca pipeline](https://github.com/greenplum-db/gporca/blob/master/concourse/pipeline.yml) diff --git a/concourse/scripts/build_gpdb.py b/concourse/scripts/build_gpdb.py index 6eb4bd046ba007ce39d0ece7da9bf3dd6d490a3d..8421858f25ab624abcc2588dbfb463cbe9c64265 100755 --- a/concourse/scripts/build_gpdb.py +++ b/concourse/scripts/build_gpdb.py @@ -9,10 +9,6 @@ import sys from builds.GpBuild import GpBuild -CODEGEN_MODE = 'codegen' -ORCA_CODEGEN_DEFAULT_MODE = "orca_codegen" -ORCA_MODE = 'orca' -PLANNER_MODE = 'planner' INSTALL_DIR = "/usr/local/gpdb" DEPENDENCY_INSTALL_DIR = "/usr/local" diff --git a/concourse/scripts/builds/GporcacodegenBuild.py b/concourse/scripts/builds/GporcacodegenBuild.py deleted file mode 100755 index e394cb7ad4f18de61e6a8ff483714428b8109a5f..0000000000000000000000000000000000000000 --- a/concourse/scripts/builds/GporcacodegenBuild.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import subprocess -import sys -from GpdbBuildBase import GpdbBuildBase - -class GporcacodegenBuild(GpdbBuildBase): - def __init__(self): - pass - - def configure(self): - return subprocess.call(' '.join(["./configure", - "--enable-codegen", - "--enable-mapreduce", - "--with-perl", - "--with-libxml", - "--with-python", - "--disable-gpfdist", - "--prefix=/usr/local/gpdb"]), - cwd="gpdb_src", shell=True) - def icg(self): - status = subprocess.call( - "printf '\nLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib\nexport \ - LD_LIBRARY_PATH' >> /usr/local/gpdb/greenplum_path.sh", shell=True) - if status: - return status - status = subprocess.call([ - "runuser gpadmin -c \"source /usr/local/gpdb/greenplum_path.sh \ - && make create-demo-cluster\""], cwd="gpdb_src/gpAux/gpdemo", shell=True) - if status: - return status - return subprocess.call([ - "runuser gpadmin -c \"source /usr/local/gpdb/greenplum_path.sh \ - && source gpAux/gpdemo/gpdemo-env.sh && PGOPTIONS='-c optimizer=on -c codegen=on' \ - make installcheck-good\""], cwd="gpdb_src", shell=True) - diff --git a/config/programs.m4 b/config/programs.m4 index 8db2fc4fdcdbb6fcafafbfefc9c83a10f5b92828..e8267347a1f417ddef9a80be4e5cc270d1a1139b 100644 --- a/config/programs.m4 +++ b/config/programs.m4 @@ -243,28 +243,6 @@ AC_DEFUN([PGAC_CHECK_STRIP], ])# PGAC_CHECK_STRIP -# GPAC_PATH_CMAKE -# --------------- -# Check for the 'cmake' program which is required for compiling -# Greenplum with Code Generation -AC_DEFUN([GPAC_PATH_CMAKE], -[ -if test -z "$CMAKE"; then - AC_PATH_PROGS(CMAKE, cmake) -fi - -if test -n "$CMAKE"; then - gpac_cmake_version=`$CMAKE --version 2>/dev/null | sed q` - if test -z "$gpac_cmake_version"; then - AC_MSG_ERROR([cmake is required for codegen, unable to identify version]) - fi - AC_MSG_NOTICE([using $gpac_cmake_version]) -else - AC_MSG_ERROR([cmake is required for codegen, unable to find binary]) -fi -]) # GPAC_PATH_CMAKE - - # GPAC_PATH_APR_1_CONFIG # ---------------------- # Check for apr-1-config, used by gpfdist diff --git a/configure b/configure index 15b486e53cb7cbbc268ed7f86cca427d58400a41..a7856ec48358db5082260601cdb1b026a7ff9787 100755 --- a/configure +++ b/configure @@ -743,8 +743,6 @@ enable_mapreduce enable_netbackup enable_ddboost enable_snmp -CMAKE -enable_codegen enable_orca autodepend TAS @@ -864,8 +862,6 @@ enable_cassert enable_debugntuplestore enable_testutils enable_orca -enable_codegen -with_codegen_prefix enable_snmp enable_ddboost enable_netbackup @@ -1552,7 +1548,6 @@ Optional Features: enable debug_ntuplestore (for debugging) --enable-testutils enable testing utilities --disable-orca disable ORCA optimizer - --enable-codegen enable code generation --enable-snmp enable snmp for MIB and alerts via TRAP/INFORM --enable-ddboost enable DD Boost support --enable-netbackup enable NetBackup support @@ -1581,9 +1576,6 @@ Optional Packages: --with-wal-segsize=SEGSIZE set WAL segment size in MB [16] --with-CC=CMD set compiler (deprecated) - --with-codegen-prefix="PATH;PATH" - semicolon separated prefix search path for LLVM and - Clang --with-tcl build Tcl modules (PL/Tcl) --with-tclconfig=DIR tclConfig.sh is in DIR --with-perl build Perl modules (PL/Perl) @@ -6287,149 +6279,6 @@ fi $as_echo "checking whether to build with ORCA... $enable_orca" >&6; } -# -# Enable code generation -# - -pgac_args="$pgac_args enable_codegen" - -# Check whether --enable-codegen was given. -if test "${enable_codegen+set}" = set; then : - enableval=$enable_codegen; - case $enableval in - yes) - -$as_echo "#define USE_CODEGEN 1" >>confdefs.h - - ;; - no) - : - ;; - *) - as_fn_error $? "no argument expected for --enable-codegen option" "$LINENO" 5 - ;; - esac - -else - enable_codegen=no - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: checking whether to build with codegen... $enable_codegen" >&5 -$as_echo "checking whether to build with codegen... $enable_codegen" >&6; } - - - -pgac_args="$pgac_args with_codegen_prefix" - - -# Check whether --with-codegen-prefix was given. -if test "${with_codegen_prefix+set}" = set; then : - withval=$with_codegen_prefix; - case $withval in - yes) - as_fn_error $? "argument required for --with-codegen-prefix option" "$LINENO" 5 - ;; - no) - as_fn_error $? "argument required for --with-codegen-prefix option" "$LINENO" 5 - ;; - *) - if test "${enable_codegen}" = no; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-codegen-prefix used without --enable-codegen" >&5 -$as_echo "$as_me: WARNING: --with-codegen-prefix used without --enable-codegen" >&2;} -fi - ;; - esac - -fi - - - -if test "$enable_codegen" = yes; then : - # then - -if test -z "$CMAKE"; then - for ac_prog in cmake -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CMAKE+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $CMAKE in - [\\/]* | ?:[\\/]*) - ac_cv_path_CMAKE="$CMAKE" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_CMAKE="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -CMAKE=$ac_cv_path_CMAKE -if test -n "$CMAKE"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CMAKE" >&5 -$as_echo "$CMAKE" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CMAKE" && break -done - -fi - -if test -n "$CMAKE"; then - gpac_cmake_version=`$CMAKE --version 2>/dev/null | sed q` - if test -z "$gpac_cmake_version"; then - as_fn_error $? "cmake is required for codegen, unable to identify version" "$LINENO" 5 - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: using $gpac_cmake_version" >&5 -$as_echo "$as_me: using $gpac_cmake_version" >&6;} -else - as_fn_error $? "cmake is required for codegen, unable to find binary" "$LINENO" 5 -fi - - - cd src/backend/codegen - if test "${prefix}" = NONE; then : - prefix=${ac_default_prefix} -fi - if test "${enable_debug}" = yes; then : - build_type="Debug" -else - build_type="Release" -fi - - CMAKE_CMD="${CMAKE} -DCMAKE_PREFIX_PATH="${with_codegen_prefix}" -DCMAKE_INSTALL_PREFIX=${prefix} -DCMAKE_BUILD_TYPE=${build_type} ." - echo "Executing cmake command : ${CMAKE_CMD}" - if ${CMAKE_CMD}; then : - -else - as_fn_error $? "'error while configuring codegen.'" "$LINENO" 5 -fi - cd - - -fi # fi - # # --enable-snmp enables snmp mib and trap/inform # diff --git a/configure.in b/configure.in index 09ee500333f09280a91952f0da9fb101920d293a..344b8388b6be9e73974611f123f95e75b95b3fa9 100644 --- a/configure.in +++ b/configure.in @@ -778,34 +778,6 @@ PGAC_ARG_BOOL(enable, orca, yes, [disable ORCA optimizer], AC_MSG_RESULT([checking whether to build with ORCA... $enable_orca]) AC_SUBST(enable_orca) -# -# Enable code generation -# -PGAC_ARG_BOOL(enable, codegen, no, [enable code generation], - [AC_DEFINE([USE_CODEGEN], 1, - [Define to 1 to enable code generation. (--enable-codegen)])]) -AC_MSG_RESULT([checking whether to build with codegen... $enable_codegen]) -AC_SUBST(enable_codegen) - -PGAC_ARG_REQ(with, codegen-prefix, - [["PATH;PATH"]], [semicolon separated prefix search path for LLVM and Clang], - [AS_IF([test "${enable_codegen}" = no], - [AC_MSG_WARN([--with-codegen-prefix used without --enable-codegen])])]) - -AS_IF([test "$enable_codegen" = yes], -[ # then - GPAC_PATH_CMAKE - - cd src/backend/codegen - AS_IF([test "${prefix}" = NONE], [prefix=${ac_default_prefix}]) - AS_IF([test "${enable_debug}" = yes], [build_type="Debug"], [build_type="Release"]) - - CMAKE_CMD="${CMAKE} -DCMAKE_PREFIX_PATH="${with_codegen_prefix}" -DCMAKE_INSTALL_PREFIX=${prefix} -DCMAKE_BUILD_TYPE=${build_type} ." - echo "Executing cmake command : ${CMAKE_CMD}" - AS_IF([${CMAKE_CMD}], [], [AC_MSG_ERROR(['error while configuring codegen.'])]) - cd - -]) # fi - # # --enable-snmp enables snmp mib and trap/inform # diff --git a/gpAux/Makefile.global b/gpAux/Makefile.global index e2ebf2b9637264131a13aea5709ee6858a9d094b..c35fdd762d3cd211f95efddd7d488f06a8b80563 100644 --- a/gpAux/Makefile.global +++ b/gpAux/Makefile.global @@ -35,7 +35,7 @@ export MPP_ARCH=$($(BLD_ARCH)_MPP_ARCH) endif # take over the gcc version we use -BLD_CC=$(shell which gcc) +BLD_CC=gcc hpux_ia64_CC=gcc osx106_x86_CC=gcc diff --git a/gpAux/extensions/gphdfs/regression/run_gphdfs_regression.sh b/gpAux/extensions/gphdfs/regression/run_gphdfs_regression.sh index 5a37f67aafff3b808326dd5f28a815b518e18b34..d29b56e5e16a5f68396294ba1cb2e292ffd9d531 100755 --- a/gpAux/extensions/gphdfs/regression/run_gphdfs_regression.sh +++ b/gpAux/extensions/gphdfs/regression/run_gphdfs_regression.sh @@ -89,7 +89,7 @@ _main() { $HADOOPCMD fs -rm -f -r /mapred/* # gphdfs_regress_schedule - PGOPTIONS="-c optimizer=off -c codegen=off -c gp_hadoop_home=${HADOOP_HOME} -c gp_hadoop_target_version=${GP_HADOOP_TARGET_VERSION}" ${PGREGRESS} --psqldir=$GPHOME/bin/ --init-file=$CURDIR/gphdfs_init_file --schedule=$CURDIR/gphdfs_regress_schedule --inputdir=$CURDIR/source_replaced --outputdir=. + PGOPTIONS="-c optimizer=off -c gp_hadoop_home=${HADOOP_HOME} -c gp_hadoop_target_version=${GP_HADOOP_TARGET_VERSION}" ${PGREGRESS} --psqldir=$GPHOME/bin/ --init-file=$CURDIR/gphdfs_init_file --schedule=$CURDIR/gphdfs_regress_schedule --inputdir=$CURDIR/source_replaced --outputdir=. } _main "$@" diff --git a/src/Makefile.global.in b/src/Makefile.global.in index e897216bb9a7d1729a9919bfe7592c2fb47acaea..3355e17972f4527b957214e4d3034e0ecc640a50 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -185,7 +185,6 @@ enable_coverage = @enable_coverage@ enable_thread_safety = @enable_thread_safety@ enable_largefile = @enable_largefile@ enable_orca = @enable_orca@ -enable_codegen = @enable_codegen@ enable_gpfdist = @enable_gpfdist@ enable_pxf = @enable_pxf@ enable_gphdfs = @enable_gphdfs@ diff --git a/src/backend/Makefile b/src/backend/Makefile index d79bd20dee61c9911ca1e0cdd0d98f4b74f6d6c5..2d0f3f1d2b605f2d8fd6e2a381f1b47d0eb88866 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -29,10 +29,6 @@ ifeq ($(enable_orca),yes) SUBDIRS += gpopt endif -ifeq ($(enable_codegen),yes) -SUBDIRS += codegen -endif - include $(srcdir)/common.mk # As of 1/2010: @@ -59,11 +55,6 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) -# adding codegen libraries -ifeq ($(enable_codegen),yes) -LIBS := $(LIBS) -L$(top_builddir)/src/backend/codegen -lgpcodegen -endif - # Greenplum uses threads in the backend LIBS := $(LIBS) -lpthread @@ -212,9 +203,6 @@ endif ifeq ($(enable_orca), yes) $(MAKE) -C gpopt $@ INSTLOC=$(DESTDIR)$(libdir) endif -ifeq ($(enable_codegen), yes) - $(MAKE) -C codegen $@ DESTDIR=$(DESTDIR) -endif install-bin: postgres $(POSTGRES_IMP) installdirs $(INSTALL_PROGRAM) postgres$(X) '$(DESTDIR)$(bindir)/postgres$(X)' diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 36296be920a9e410d95b043a4dd44eb5933a80f3..c6be409f44c4f48c1cf8327313f953494387d3ec 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -1129,10 +1129,7 @@ heap_deformtuple(HeapTuple tuple, * re-computing information about previously extracted attributes. * slot->tts_nvalid is the number of attributes already extracted. */ -#ifndef USE_CODEGEN -static -#endif -void +static void slot_deform_tuple(TupleTableSlot *slot, int natts) { HeapTuple tuple = TupGetHeapTuple(slot); @@ -1264,6 +1261,7 @@ _slot_getsomeattrs(TupleTableSlot *slot, int attnum) slot_deform_tuple(slot, attno); + /* * If tuple doesn't have all the atts indicated by tupleDesc, read the * rest as null diff --git a/src/backend/codegen/.gitignore b/src/backend/codegen/.gitignore deleted file mode 100644 index 59bfc01864800f24098dbc6335be3b5b668deca1..0000000000000000000000000000000000000000 --- a/src/backend/codegen/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app -*.t - -# Mac Finder cache -.DS_Store - -# Editor backup files -*~ -cscope.* -cctree.* - -# CMake files -CMakeFiles/ -CMakeCache.txt -CTestTestfile.cmake -cmake_install.cmake -install_manifest.txt -Makefile -Testing diff --git a/src/backend/codegen/CMakeLists.txt b/src/backend/codegen/CMakeLists.txt deleted file mode 100644 index a2db3de4fe6bb3de03127d131534ca7c582f3e8e..0000000000000000000000000000000000000000 --- a/src/backend/codegen/CMakeLists.txt +++ /dev/null @@ -1,399 +0,0 @@ -# Copyright 2015-2016 Pivotal Software, Inc. -# -# CMakeLists.txt -# Cmake configuration for building GPDB codegen module. -# - -cmake_minimum_required(VERSION 2.8.12) - -project(gpcodegen C CXX) -set(CMAKE_BUILD_FILES_DIRECTORY build) -set(CMAKE_BUILD_DIRECTORY build) -get_filename_component(TOP_SRC_DIR "../../.." ABSOLUTE) -set(MOCK_DIR ${TOP_SRC_DIR}/src/test/unit/mock) - - -# Options. Turn on with 'cmake -Dvar_name=ON' -option(build_examples "Build examples also" OFF) - -# Look for flags to enable C++11 support. -include(CheckCXXCompilerFlag) -CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_HAS_STD_CXX11) -if (COMPILER_HAS_STD_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -else() - CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_HAS_STD_CXX0X) - if (COMPILER_HAS_STD_CXX0X) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") - endif() -endif() - -# Turn on all warnings. -CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_HAS_WALL) -if (COMPILER_HAS_WALL) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -endif() - -#CHECK_CXX_COMPILER_FLAG("-pedantic" COMPILER_HAS_PEDANTIC) -#if (COMPILER_HAS_PEDANTIC) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic") -#endif() -#CHECK_CXX_COMPILER_FLAG("-fno-enforce-eh-specs" COMPILER_HAS_NO_ENFORCE_EH_SPECS) -#if (COMPILER_HAS_NO_ENFORCE_EH_SPECS) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-enforce-eh-specs") -#endif() - -# Suppress warnings about C99 extensions that should be supported in C++11 mode. -CHECK_CXX_COMPILER_FLAG("-Wno-c99-extensions" COMPILER_HAS_WNO_C99_EXTENSIONS) -if (COMPILER_HAS_WNO_C99_EXTENSIONS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c99-extensions") -endif() - -# Suppress warnings about C99 extensions that should be supported in C++11 mode. -CHECK_CXX_COMPILER_FLAG("-Wno-conversion-null" COMPILER_HAS_WNO_C99_EXTENSIONS) -if (COMPILER_HAS_WNO_C99_EXTENSIONS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null") -endif() - -# Suppress warnings about deprecated keyword register -CHECK_CXX_COMPILER_FLAG("-Wno-deprecated-register" COMPILER_HAS_WNO_C99_EXTENSIONS) -if (COMPILER_HAS_WNO_C99_EXTENSIONS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-register") -endif() - -macro (ADD_DEBUG_COMPILE_DEFINITION SYMBOL) -if (CMAKE_MAJOR_VERSION GREATER 2) - cmake_policy(SET CMP0043 NEW) - set_property( - DIRECTORY - APPEND PROPERTY COMPILE_DEFINITIONS $<$:${SYMBOL}> - ) -else() - set_property( - DIRECTORY - APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG ${SYMBOL} - ) -endif() -endmacro() - -# Turn on the CODEGEN_DEBUG flag if this is a debug build. -ADD_DEBUG_COMPILE_DEFINITION(CODEGEN_DEBUG) - - -# Check for POSIX I/O syscalls needed by TemporaryFile. -include(CheckCXXSymbolExists) -CHECK_CXX_SYMBOL_EXISTS(mkstemp "stdlib.h" HAVE_POSIX_MKSTEMP) -CHECK_CXX_SYMBOL_EXISTS(write "unistd.h" HAVE_POSIX_WRITE) -CHECK_CXX_SYMBOL_EXISTS(fsync "unistd.h" HAVE_POSIX_FSYNC) -if (HAVE_POSIX_MKSTEMP AND HAVE_POSIX_WRITE AND HAVE_POSIX_FSYNC) - set(codegen_tmpfile_sources utils/temporary_file.cc) - set_property(DIRECTORY - APPEND PROPERTY COMPILE_DEFINITIONS CODEGEN_HAVE_TEMPORARY_FILE) -else() - message(WARNING "Missing required POSIX I/O syscalls for temporary files. " - "Line-by-line DEBUG information for generated code will not " - "be available.") -endif() - -# Include our include paths. -include_directories(${TOP_SRC_DIR}/src/include) -include_directories(include) - -# Pull in LLVM libraries. -find_package(LLVM REQUIRED CONFIG) -message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") -message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") -include_directories(${LLVM_INCLUDE_DIRS}) -add_definitions(${LLVM_DEFINITIONS}) - -# Disable RTTI (C++ run-time type information) if LLVM was built without it. -if (NOT LLVM_ENABLE_RTTI) - CHECK_CXX_COMPILER_FLAG("-fno-rtti" COMPILER_HAS_FNO_RTTI) - if (COMPILER_HAS_FNO_RTTI) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") - else() - message(WARNING "LLVM was built without RTTI (run-time type information) " - "support, but compiler does not support -fno-rtti flag to " - "also build gpcodegen without RTTI support. You may see " - "linking errors about undefined references to typeinfo for " - "various LLVM classes.") - endif() -endif() - -set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") - -# Some distros (Fedora, maybe others?) package LLVM as a single monolithic -# library instead of a shared library. -option(MONOLITHIC_LLVM_LIBRARY - "Look for a single monolithic LLVM library instead of modular libraries" - OFF) -if (MONOLITHIC_LLVM_LIBRARY) - find_package(LLVMMonolithic REQUIRED) -endif() - -# Pull in Clang libraries using our custom CMake module. -find_package(Clang REQUIRED) -include_directories(${CLANG_INCLUDE_DIRS}) - -# Core codegen library. -# This builds all the codegen-related C++ sources into one library -add_library(gpcodegen SHARED - utils/clang_compiler.cc - utils/codegen_utils.cc - utils/gp_codegen_utils.cc - utils/gp_assert.cc - - codegen_interface.cc - codegen_manager.cc - const_expr_tree_generator.cc - exec_variable_list_codegen.cc - slot_getattr_codegen.cc - exec_eval_expr_codegen.cc - expr_tree_generator.cc - op_expr_tree_generator.cc - pg_date_func_generator.cc - pg_numeric_func_generator.cc - var_expr_tree_generator.cc - advance_aggregates_codegen.cc - - ${codegen_tmpfile_sources}) - -if(APPLE) - set(WL_START_GROUP "") - set(WL_END_GROUP "") -else() - set(WL_START_GROUP "-Wl,--start-group") - set(WL_END_GROUP "-Wl,--end-group") -endif() - -# By default, the Darwin linker throws an error if there are any undefined -# references in a dynamic library. Instead, it should wait till it's loaded -# by the postgres binary. -if(APPLE) - set(WL_UNDEFINED_DYNLOOKUP "-Wl,-undefined -Wl,dynamic_lookup") -else() - set(WL_UNDEFINED_DYNLOOKUP "") -endif() - -target_link_libraries(gpcodegen ${WL_START_GROUP} ${CLANG_LIBRARIES} ${WL_END_GROUP} ${WL_UNDEFINED_DYNLOOKUP}) -if (MONOLITHIC_LLVM_LIBRARY) - target_link_libraries(gpcodegen ${LLVM_MONOLITHIC_LIBRARIES}) -else() - # Here we link against the LLVM libraries that we use directly, as well as - # those that are needed by the Clang libraries that we use (e.g. objcarcopts, - # which the Clang frontend requires even though we do not compile any - # objective-C). The llvm_map_components_to_libnames() function also takes care - # of pulling in any transitive linking dependencies for the libraries we - # specify. - llvm_map_components_to_libnames(codegen_llvm_libs - analysis bitwriter core executionengine ipo - irreader linker mc mcjit native objcarcopts - option passes support target) - target_link_libraries(gpcodegen ${WL_START_GROUP} ${codegen_llvm_libs} ${WL_END_GROUP}) -endif() - -# This macro checks to see if the given C symbol is defined in the given LIBRARY. A library with -# an appropriate name is searched for in the LIBPATH. VARIABLE is set to true if the symbol is -# found defined as a type in (T in the output of nm) in the library, or set false otherwise. -macro(CHECK_SYMBOL_DEFINED LIBRARY CSYMBOL LIBPATH VARIABLE) - find_library(LIBLOCATION ${LIBRARY} ${LIBPATH}) - if(LIBLOCATION STREQUAL "LIBLOCATION-NOTFOUND") - message(FATAL_ERROR "${LIBRARY} not found in ${LIBPATH}.") - endif() - find_program(NM_BIN "nm") - execute_process(COMMAND ${NM_BIN} ${LIBLOCATION} - COMMAND grep "T" # Symbol is defined - COMMAND grep ${CSYMBOL} - RESULT_VARIABLE RETURN_CODE - OUTPUT_QUIET) - if(${RETURN_CODE} EQUAL 0) # One or more lines were selected - set(${VARIABLE} true) - elseif(${RETURN_CODE} EQUAL 1) # No lines were selected. - set(${VARIABLE} false) - else() - message(FATAL_ERROR "Attempted to determine it ${CSYMBOL} is defined in ${LIBLOCATION} " - "but the execution failed. Return code = ${RETURN_CODE}") - endif() -endmacro() - -if(APPLE) - set(ASSERT_FUNCTION_TO_OVERRIDE "__assert_rtn") -elseif(UNIX) - set(ASSERT_FUNCTION_TO_OVERRIDE "__assert_fail") -endif() - -if(CMAKE_BUILD_TYPE STREQUAL Debug) - # Since we want to override assert handling in GPDB, we need to make sure - # that LLVM and Clang libraries we depend on haven't done that already - # If they do, we simply report a WARNING, and skip assert overriding in GPDB. - CHECK_SYMBOL_DEFINED(LLVMSupport ${ASSERT_FUNCTION_TO_OVERRIDE} ${LLVM_LIBRARY_DIRS} LLVM_ASSERT_REDEFINED) - if (LLVM_ASSERT_REDEFINED) - message(WARNING - "Found ${ASSERT_FUNCTION_TO_OVERRIDE} redefined in LLVM libraries. " - "Disabling GPDB codegen assert handling! " - "To enable this, rebuild LLVM libraries with -DLLVM_ENABLE_CRASH_OVERRIDES=off.") - else() - ADD_DEBUG_COMPILE_DEFINITION(CODEGEN_GPDB_ASSERT_HANDLING) - endif() -endif() - -get_filename_component(full_install_name_dir "${CMAKE_INSTALL_PREFIX}/lib" ABSOLUTE) -set_target_properties( - gpcodegen PROPERTIES - INSTALL_NAME_DIR ${full_install_name_dir} - MACOSX_RPATH ON) - -# Integrate with GPDB build system. -# Here we compile the GPDB wrappers and link it with the gpcodegen shared -# library to create a binary SUBSYS.o as expected by GPDB make system. We -# invoke the linker with -nostdlib since we don't really want to create a full -# executable. -add_executable(SUBSYS.o codegen_wrapper.cc) -set_target_properties(SUBSYS.o - PROPERTIES - LINK_FLAGS "-Wl,-r -nostdlib") -target_link_libraries( - gpcodegen -) - -# Integrate with GPDB build system -# GPDB unit tests use unittest-check instead of test, so we add an alias target -# that calls the ctests we registered above. -add_custom_target(check - COMMAND ${CMAKE_CTEST_COMMAND}) -add_custom_target(unittest-check - COMMAND ${CMAKE_COMMAND} . - COMMAND ${CMAKE_MAKE_PROGRAM} check -) - -# Googletest framework for tests. -SET(GTEST_DIR ../../../gpAux/extensions/googletest/googletest) -add_subdirectory(${GTEST_DIR} ${CMAKE_BINARY_DIR}/gtest EXCLUDE_FROM_ALL) -enable_testing() - -set(TEST_LIB_INC_DIRECTORIES - ${TOP_SRC_DIR}/src/test/unit/cmockery - ${GTEST_DIR}/include) - -function(prepend_path var prefix) - SET(listVar "") - FOREACH(f ${ARGN}) - LIST(APPEND listVar "${prefix}/${f}") - ENDFOREACH(f) - SET(${var} "${listVar}" PARENT_SCOPE) -endfunction(prepend_path) - - -# Usage add_cmock_gtest ${TEST_NAME} ${TEST_SOURCES} ${MOCK_DIR}/hello_mock.o ${MOCK_DIR}/world_mock.o) -function(add_cmockery_gtest TEST_NAME TEST_SOURCES) - set(FILES_TO_LINK ${OBJFILES}) - - foreach(MOCK_OBJ_NAME ${ARGN}) - string(REPLACE "_mock" "" REAL_OBJ_NAME ${MOCK_OBJ_NAME}) - string(REPLACE "${MOCK_DIR}" "${TOP_SRC_DIR}/src" REAL_OBJ_NAME ${REAL_OBJ_NAME}) - list(REMOVE_ITEM FILES_TO_LINK ${REAL_OBJ_NAME}) - - if(EXISTS ${MOCK_OBJ_NAME}) - list(APPEND FILES_TO_LINK ${MOCK_OBJ_NAME}) - else() - string(REPLACE ".o" ".c" REAL_SRC_NAME ${REAL_OBJ_NAME}) - string(REPLACE ".o" ".c" MOCK_SRC_NAME ${MOCK_OBJ_NAME}) - get_filename_component(REAL_SRC_ABS_PATH ${REAL_SRC_NAME} ABSOLUTE) - execute_process(COMMAND python mocker.py ${REAL_SRC_ABS_PATH} - WORKING_DIRECTORY ${MOCK_DIR}) - list(APPEND FILES_TO_LINK ${MOCK_SRC_NAME}) - endif() - endforeach() - - add_executable(${TEST_NAME} EXCLUDE_FROM_ALL - ${TEST_SOURCES} - codegen_wrapper.cc - ${FILES_TO_LINK} - ${MOCK_OBJS} - ${CMOCKERY_OBJS} - ) - target_include_directories(${TEST_NAME} PUBLIC ${TEST_LIB_INC_DIRECTORIES}) - # Bring these from $ENV{LIBS} - target_link_libraries(${TEST_NAME} "-ldl -lnetsnmp -lpam -lxml2 -lpgport -lbz2 -lrt -lssl -lcrypto -lkrb5 -lcom_err -lgssapi_krb5 -lz -lldap -lreadline -lcrypt -lm -lcurl -L${CMAKE_INSTALL_PREFIX}/lib -L../../port -lpgport_srv" gpcodegen gtest) - add_test(${TEST_NAME} ${TEST_NAME}) - add_dependencies(check ${TEST_NAME}) -endfunction(add_cmockery_gtest) - -# Get the list for all real objects from objfiles.txt for CMockery integration -set(TXT_OBJFILE ${TOP_SRC_DIR}/src/backend/objfiles.txt) -if(EXISTS ${TXT_OBJFILE}) - file(GLOB_RECURSE GPOPT_OBJS RELATIVE ${TOP_SRC_DIR} ${TOP_SRC_DIR}/src/backend/gpopt/*.o) - set(EXCL_OBJS - src/backend/main/main.o - ${GPOPT_OBJS}) - - file(READ ${TXT_OBJFILE} OBJFILES) - string(REPLACE "\n" ";" OBJFILES "${OBJFILES}") - string(REPLACE " " ";" OBJFILES "${OBJFILES}") - set(OBJFILES ${OBJFILES}) - - foreach(F ${OBJFILES}) - get_filename_component(_F ${F} NAME) - if ("${_F}" STREQUAL "objfiles.txt") - list(REMOVE_ITEM OBJFILES ${F}) - endif() - endforeach() - - foreach(OBJ ${EXCL_OBJS}) - list(REMOVE_ITEM OBJFILES ${OBJ}) - endforeach() - prepend_path(OBJFILES ${TOP_SRC_DIR} ${OBJFILES}) - - - set(MOCK_OBJS "") - foreach(OBJ - ${TOP_SRC_DIR}/src/test/unit/mock/main_mock.o - ${TOP_SRC_DIR}/src/test/unit/mock/gpopt_mock.o - ) - if(EXISTS ${OBJ}) - set(MOCK_OBJS ${OBJ} ${MOCK_OBJS}) - endif() - endforeach() - - set(CMOCKERY_DIR ${TOP_SRC_DIR}/src/test/unit/cmockery) - set(CMOCKERY_OBJS ${CMOCKERY_DIR}/cmockery.o) -endif() - -# Add CMockery tests -# All tests must be linked with postgres, even if they don't need the cmockery -# framework since libgpcodegen may now have references to postgres symbols. -# -# Usage add_cmock_gtest ${TEST_NAME} ${TEST_SOURCES} ${MOCK_DIR}/hello_mock.o ${MOCK_DIR}/world_mock.o) -if(EXISTS ${TXT_OBJFILE}) - add_cmockery_gtest(codegen_framework_unittest.t - tests/codegen_framework_unittest.cc - ) - add_cmockery_gtest(codegen_pg_func_generator_unittest.t - tests/codegen_pg_func_generator_unittest.cc - ) - add_cmockery_gtest(clang_compiler_unittest.t - tests/clang_compiler_unittest.cc - ) - add_cmockery_gtest(instance_method_wrappers_unittest.t - tests/instance_method_wrappers_unittest.cc - ) - add_cmockery_gtest(codegen_utils_unittest.t - tests/codegen_utils_unittest.cc - ) - add_cmockery_gtest(gp_codegen_utils_unittest.t - tests/gp_codegen_utils_unittest.cc - ) -endif() - - -# Examples -if (build_examples) - add_subdirectory(example) -endif() - -# Installation -install(TARGETS gpcodegen DESTINATION lib) - -# Clean up -set(CMAKE_FILES Testing CMakeCache.txt CTestTestfile.cmake cmake_install.cmake install_manifest.txt Makefile) -set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_FILES}") diff --git a/src/backend/codegen/README.md b/src/backend/codegen/README.md deleted file mode 100644 index 9304607f468512727712fb971aca48ea1089c679..0000000000000000000000000000000000000000 --- a/src/backend/codegen/README.md +++ /dev/null @@ -1,282 +0,0 @@ -# GPDB codegen utils (src/backend/codegen/utils) -Support library for integrating LLVM code-generation in GPDB. -This augments the core functionality provided by the LLVM C++ libraries for -program construction, optimization, and execution with additional features to -help runtime-generated code integrate smoothly with statically compiled code. -Key features include: - -* A `CodegenUtils` class that encapsulates LLVM modules, optimization passes, - and execution engines, managing the whole lifetime of generated code. -* Fully automatic mapping between C++ types and LLVM IR types (including - function types of any arity) using templates. -* A type-safe and fully automatic foreign function interface that allows any - statically compiled function or external function pointer to be called by - generated code, and vice versa, to allow statically compiled code to get - pointers to JIT-compiled functions that can be called. -* A family of wrapper function templates that allow instance methods of C++ - classes to be easily used with the foreign-function interface without the need - to manually write bespoke wrapper code. -* A frontend based on Clang for compiling in-memory C/C++ source code snippets - for code generation, including foreign-function support that has feature - parity with the IR version. -* Optional generation of debugging information so that runtime-generated and JIT - compiled C++ code can be interactively debugged by GDB just like statically - compiled code. - -# Contents - -1. [Building Codegen Utils](#building-codegen-utils) -2. [Coding Guidelines](#coding-guidelines) -3. [Debugging Generated Code](#debugging-generated-code) -4. [Learning Resources](#learning-resources) -5. [Codegen GPDB Function](#codegen-gpdb-function) - -## Building Codegen Utils - -### Prerequisites -To build codegen utils, you will need cmake 2.8 or higher and a recent version of -llvm and clang (including headers and developer libraries) (codegen utils is -currently developed against the LLVM 3.7.X release series). Here's how to -acquire these dependencies for various OSes. - -#### Gentoo Linux -Gentoo has the latest stable LLVM in portage. It's easy to install like any -other package: -``` -sudo emerge --sync -sudo emerge -n cmake llvm clang -``` - -#### Debian or Ubuntu Linux - -##### Latest OS Versions -Debian 9 "Stretch" and Ubuntu 15.10 "Wily" and above provide packages for the -necessary versions of cmake, LLVM, and dependencies that can be installed as -follows: -``` -sudo apt-get update -sudo apt-get install -y build-essential zlib1g-dev libedit-dev cmake llvm-3.7 llvm-3.7-dev clang-3.7 libclang-3.7-dev -``` - -##### Older Releases -Older versions of Debian and Ubuntu do not package the latest LLVM version -required, but you may be able to install it from the -[APT packages provided by the LLVM project](http://llvm.org/apt/). This should -work with Debian 8 "Jessie" or Ubuntu 14.04 "Trusty" and later. - -First, install some necessary prerequisite development pacakges. -``` -sudo apt-get update -sudo apt-get install -y build-essential zlib1g-dev libedit-dev cmake wget -``` - -Next, edit `/etc/apt/sources.list` and add these two lines (example is for Debian -Jessie, [use the appropriate directories for your OS listed here](http://llvm.org/apt/)). -``` -deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.7 main -deb-src http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.7 main -``` - -Now, add the GPG key for the LLVM repo to your trusted keys, rerun -`apt-get update`, and install the LLVM packages like so: -``` -wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add - -sudo apt-get update -sudo apt-get install -y llvm-3.7 llvm-3.7-dev clang-3.7 libclang-3.7-dev -``` - -#### FreeBSD -FreeBSD 10.2-STABLE comes with LLVM and Clang 3.4.1 installed by default, but -it also provides the more recent versions that we need in the package manager. -They can be installed like this: -``` -sudo pkg update -sudo pkg install -y cmake llvm37 clang37 -``` - -When building codegen, you will need to point cmake at the more recent LLVM -like so: - -``` -./configure --enable-codegen --with-codegen-prefix=/usr/local/llvm37 -``` - -#### Mac OS X -Installing LLVM and clang libraries is easiest if you use an add-on package -manager like [Homebrew](http://brew.sh/). Do not worry about conflicts with -Apple's development tools and distribution of LLVM, Homebrew will install the -side-by-side and NOT override the defaults. - -If you do not already have homebrew, you can install it with this shell snippet: -``` -ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" -``` - -As of this writing, the "stable" version of LLVM in Homebrew is 3.6. To install -the newer LLVM 3.7 libraries, do the following: -``` -brew tap homebrew/versions -brew install --with-clang llvm37 -``` - -Unfortunately, the cmake configuration module for LLVM that is installed by -brew is broken. Fortunately, we can fix it. Edit -`/usr/local/opt/llvm37/lib/llvm-3.7/share/llvm/cmake/LLVMConfig.cmake` and fix -the path detection logic at the front of the file (before -`set(LLVM_VERSION_MAJOR 3)`) to look like this: -``` -# Compute the CMake directory from the LLVMConfig.cmake file location. -get_filename_component(_LLVM_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -# Compute the installation prefix from the LLVMConfig.cmake file location. -get_filename_component(LLVM_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) -get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) -get_filename_component(LLVM_INSTALL_PREFIX "${LLVM_INSTALL_PREFIX}" PATH) -set(_LLVM_LIBRARY_DIR "${LLVM_INSTALL_PREFIX}/lib") -``` - -Because the version of LLVM installed by brew is not installed in one of the -default locations, you will need to point cmake at it when building codegen -like so: -``` -./configure --enable-codegen --with-codegen-prefix=/usr/local/opt/llvm37/lib/llvm-3.7 -``` - -### Testing -You can run unit tests as follows -``` -make -C src/backend/codegen unittest-check -``` - -## Coding Guidelines - -This module is written using the -[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). -We have a few additional style rules that aren't covered by the guide: -* Publicly visible classes (and their public methods) should have doxygen - comments explaining them. Private methods/members and internal helper classes - don't need full doxygen comments, but some ordinary comments to clarify and - improve readability are often a good idea. -* Sigils indicating a pointer (`*`) or reference (`&` or `&&` for - rvalue-reference) type should be written next to the name of the type, not the - name of a variable or function (e.g. write `int* my_pointer;` not - `int *my_pointer;`. - - -## Debugging Generated Code - -### Viewing Generated Code - -LLVM Modules have a `dump()` method that prints out the complete IR source for -a module to stderr. Similarly, when generating C++ source, an LLVM Twine's -complete text can be printed to stderr by the `dump()` method. Finally, if -errors are encountered during processing of C++ source by -`ClangCompiler::CompileCppSource()` clang error messages will be logged to -stderr. - -### Validating Generated Code - -LLVM provides some functions in the header `llvm/IR/Verifier.h` that can be used -to verify that modules (`verifyModule()`) and individual functions -(`verifyFunction()`) are well-formed before they are compiled. When compiling -C++ source code, the return value of `ClangCompiler::CompileCppSource()` -indicates whether compilation was successful. - -### Interactive Debugging with GDB - -GDB can be used to debug generated C++ source code just like statically -compiled C++ source. Calling `ClangCompiler::CompileCppSource()` with the option -`debug = true` causes DWARF debugging information to be included with the -compiled code and dumps the source code to a temporary file, enabling the usual -GDB features. Some caveats apply: - -1. Debugging for JITed code only works on platforms that use the - [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) binary - format. This includes Linux and all the major BSD flavors, but does NOT - include Mac OS X (which uses Mach-O). -2. LLVM's MCJIT execution engine uses special hooks to expose symbols to the - debugger. These hooks are only supported by GDB 7.0+ (and appear to be only - partially supported by LLDB, so debugging with GDB is recommended). - -When running an executable under GDB, you can set a breakpoint on a generated -function that hasn't been compiled yet. GDB will alert you that the function is -not defined and ask if you want to make the breakpoint pending on a future -shared library load (say yes). - -Currently full debugging symbols are only generated when compiling from C++ -source. Work is in progress to expose debugging information for generated IR. - -## Learning Resources -We have collected some documentation and resources to learn more about using -LLVM generally and about some specific programming techniques that are used in -codegen. When we refer to "LLVM IR" below, that means LLVM intermediate -representation, which is the internal language used to build up programs in -LLVM. IR is assembly-like, but it is strongly-typed, uses static single -assignment, and has the illusion of unlimited registers. - -### LLVM Resources -* [LLVM Tutorial](http://llvm.org/docs/tutorial/) A guided excercise in - creating an interpreter for a "toy" language using LLVM. Covers the basics - of creating values, expressions, control flow, and functions in LLVM IR using - the C++ IRBuilder interface. -* [LLVM Language Reference Manual](http://llvm.org/docs/LangRef.html) A - detailed reference about the LLVM IR language. Especially useful are the - sections labelled "Type System", "Instruction Reference", and "Intrinsic - Functions". -* [LLVM Programmer's Manual](http://llvm.org/docs/ProgrammersManual.html) A - guide to working with the LLVM C++ code base. This describes some of the - general-purpose APIs and data structures that are used internally by LLVM, - and also provides a useful guide to the most important parts of the C++ API - for LLVM IR objects in the section labelled "The Core LLVM Class Hierarchy - Reference". -* [LLVM Doxygen](http://llvm.org/doxygen/) A more or less complete code - reference for LLVM. Contains automatically-generated documentation for almost - every publicly visible class and method in the LLVM source. - -### C++ Resources -* [Template Specialization](http://www.cprogramming.com/tutorial/template_specialization.html) - A brief introduction to template specialization and partial specialization, - which can be used to make a special version of a class template for a - specific type or category of types. -* [SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) - A wikipedia article on the SFINAE (substitution failure is not an error) - idiom in C++. SFINAE helps resolve multiple different overloads or partial - specializations that might apply when instantiating a template down to just - one by eliminating versions of a template that can't possibly "fit". -* [C++ type_traits library](http://en.cppreference.com/w/cpp/header/type_traits) - A reference guide to the `type_traits` header introduced to the standard - library in C++11. `type_traits` provides many useful templates to help with - template metaprogramming. An especially important one is - [std::enable_if](http://en.cppreference.com/w/cpp/types/enable_if), which - is used to selectively enable a partial template specialization depending on - a compile-time boolean constant. -* [C++ Variadic Templates Guide](http://eli.thegreenplace.net/2014/variadic-templates-in-c/) - A nice guided tour of variadic templates in C++11 and how to use them to make - variadic code that is both type-safe and fast. - -## Codegen GPDB Function -In order to facilitate codegen GPDB function, we introduced the following classes: -* `CodegenInterface` - Interface for all code generators. -* `BaseCodegen` - Inherits CodegenInterface and provides common implementation for all - generators that derive from it. -* `CodegenManager` - Manages all CodegenInterface. - -More details for above classes are available in doxygen document or in respective source code. - -Below are the necessary steps to codegen a target function *F* (e.g. `slot_deform_tuple`) which is -access through an operator. - -1. Create a CodegenManager instance in the corresponding operator. -2. Create a new generator class *GC* (E.g. `SlotDeformTupleCodegen`) that derives from BaseCodegen and - implements a function that generartes IR instructions / C++ code for *F*. -3. Store *GC* and a function pointer to *F* in an appropriate struct. For instance the proper struct - for `slot_deform_tuple` is `TupleTableSlot`. -4. Enroll *GC* with the manager for current operator during `ExecInit`. Enrollment - process makes sure that the function pointer initally points to the regular version of *F*. -5. Replace the actual function call in GPDB with a call to the above function pointer. - -After `ExecInit`, manager uses `CodegenInterface` to generate the runtime code. On successful generation, -manager swaps the function pointer (see Step 3) to point to the generated version of *F*. Note that, *GC* -stores the target function *F* along with a reference to a function pointer to *F* (see Step 3). This -mechanism allows manager to fallback on the regular version of *F* when code generation fails. \ No newline at end of file diff --git a/src/backend/codegen/advance_aggregates_codegen.cc b/src/backend/codegen/advance_aggregates_codegen.cc deleted file mode 100644 index aed2760e167723830e7c8d46e151c51caba10dd9..0000000000000000000000000000000000000000 --- a/src/backend/codegen/advance_aggregates_codegen.cc +++ /dev/null @@ -1,512 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// advance_aggregates_codegen.cc -// -// @doc: -// Generates code for AdvanceAggregates function. -// -//--------------------------------------------------------------------------- -#include "codegen/advance_aggregates_codegen.h" -#include "codegen/op_expr_tree_generator.h" - -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/utils/utility.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "executor/nodeAgg.h" -#include "utils/palloc.h" -#include "executor/executor.h" -#include "nodes/nodes.h" -} - -namespace llvm { -class BasicBlock; -class Function; -class Value; -} // namespace llvm - -using gpcodegen::AdvanceAggregatesCodegen; - -constexpr char AdvanceAggregatesCodegen::kAdvanceAggregatesPrefix[]; - -AdvanceAggregatesCodegen::AdvanceAggregatesCodegen( - CodegenManager* manager, - AdvanceAggregatesFn regular_func_ptr, - AdvanceAggregatesFn* ptr_to_regular_func_ptr, - AggState *aggstate) -: BaseCodegen(manager, - kAdvanceAggregatesPrefix, - regular_func_ptr, - ptr_to_regular_func_ptr), - aggstate_(aggstate) { -} - -bool AdvanceAggregatesCodegen::GenerateAdvanceTransitionFunction( - gpcodegen::GpCodegenUtils* codegen_utils, - llvm::Value* llvm_pergroup_arg, - int aggno, - gpcodegen::PGFuncGeneratorInfo* pg_func_info, - llvm::Value* llvm_mem_manager_arg) { - assert(nullptr != pg_func_info); - auto irb = codegen_utils->ir_builder(); - AggStatePerAgg peraggstate = &aggstate_->peragg[aggno]; - assert(nullptr != peraggstate); - llvm::Value *llvm_newval = nullptr; - - // External functions - llvm::Function* llvm_MemoryContextSwitchTo = - codegen_utils->GetOrRegisterExternalFunction(MemoryContextSwitchTo, - "MemoryContextSwitchTo"); - llvm::Function* llvm_datumCopyWithMemManager = - codegen_utils->GetOrRegisterExternalFunction(datumCopyWithMemManager, - "datumCopyWithMemManager"); - - // Generation-time constants - llvm::Value *llvm_tuplecontext = codegen_utils->GetConstant( - aggstate_->tmpcontext->ecxt_per_tuple_memory); - - // Retrieve pergroup's useful members - llvm::Value* llvm_pergroupstate = irb->CreateGEP( - llvm_pergroup_arg, {codegen_utils->GetConstant( - sizeof(AggStatePerGroupData) * aggno)}); - llvm::Value* llvm_pergroupstate_transValue_ptr = - codegen_utils->GetPointerToMember( - llvm_pergroupstate, &AggStatePerGroupData::transValue); - llvm::Value* llvm_pergroupstate_transValueIsNull_ptr = - codegen_utils->GetPointerToMember( - llvm_pergroupstate, &AggStatePerGroupData::transValueIsNull); - llvm::Value* llvm_pergroupstate_noTransValue_ptr = - codegen_utils->GetPointerToMember( - llvm_pergroupstate, &AggStatePerGroupData::noTransValue); - - assert(nullptr != peraggstate->aggref); - assert(pg_func_info->llvm_args.size() == 1 + - list_length(peraggstate->aggref->args)); - // Initialize llvm_args[0] to transValue. - pg_func_info->llvm_args[0] = irb->CreateLoad( - llvm_pergroupstate_transValue_ptr); - // fcinfo->argnull[0] = *transValueIsNull; - pg_func_info->llvm_args_isNull[0] = irb->CreateLoad( - llvm_pergroupstate_transValueIsNull_ptr); - - // If transfn is strict then we have to implement the checks appeared in - // invoke_agg_trans_func. This block contains the code of advance_aggregates - // after invoke_agg_trans_func's invocation. - llvm::BasicBlock* continue_advance_aggregate_block = codegen_utils-> - CreateBasicBlock("continue_advance_aggregate_block", - pg_func_info->llvm_main_func); - - // If transition function is strict then check i) if there are null arguments - // ii) if transition value is null, and iii) if transvalue has been set - if (peraggstate->transfn.fn_strict) { - // Block that contains the checks for null arguments - llvm::BasicBlock* strict_check_for_null_args_block = codegen_utils-> - CreateBasicBlock("strict_check_for_null_args_block", - pg_func_info->llvm_main_func); - // Block that checks if transvalue has been set - llvm::BasicBlock* strict_check_notransValue_block = codegen_utils-> - CreateBasicBlock("strict_check_notransValue_block", - pg_func_info->llvm_main_func); - // Block that contains instructions that will be executed when function - // is strict and transvalue has not be set before. - llvm::BasicBlock* strict_set_transvalue_block = codegen_utils-> - CreateBasicBlock("strict_set_transvalue_block", - pg_func_info->llvm_main_func); - // Block that checks if transvalue is null - llvm::BasicBlock* strict_check_transvalueisNull_block = codegen_utils-> - CreateBasicBlock("strict_check_transvalueisNull_block", - pg_func_info->llvm_main_func); - // Block that implements transfn - llvm::BasicBlock* generate_transfn_block = codegen_utils-> - CreateBasicBlock("generate_transfn_block", - pg_func_info->llvm_main_func); - - irb->CreateBr(strict_check_for_null_args_block); - - // strict_check_for_null_args_block - // -------------------------------- - // Checks if there is a NULL argument. If yes then go to - // null_argument_block; generate_function_block otherwise. - // For a strict transfn, nothing happens when there's a NULL input; - // we just keep the prior transValue. - GenerateStrictLogic(codegen_utils, *pg_func_info, - 1 /* do not examine transvalue*/, - strict_check_for_null_args_block, - continue_advance_aggregate_block, - strict_check_notransValue_block); - - // strict_check_noTransValue_block - // ------------------------------ - // Check if transvalue has been set - irb->SetInsertPoint(strict_check_notransValue_block); - irb->CreateCondBr(irb->CreateLoad(llvm_pergroupstate_noTransValue_ptr), - strict_set_transvalue_block /*true*/, - strict_check_transvalueisNull_block /*false*/); - - // strict_set_transvalue_block - // --------------------------- - // transValue has not been initialized. This is the first non-NULL input - // value. We use it as the initial value for transValue. - // We must copy the datum into aggcontext if it is pass-by-ref. - // We do not need to pfree the old transValue, since it's NULL. - irb->SetInsertPoint(strict_set_transvalue_block); - // newVal = datumCopyWithMemManager(transValue, fcinfo->arg[1], - // transtypeByVal, transtypeLen, mem_manager); {{{ - // Make sure that fcinfo->arg[1] (= llvm_args[1]) has been initialized {{ - llvm::Value* llvm_arg1_ptr = irb->CreateAlloca( - codegen_utils->GetType(), nullptr, "llvm_arg1_ptr"); - if (pg_func_info->llvm_args.size() > 1) { - irb->CreateStore(pg_func_info->llvm_args[1], llvm_arg1_ptr); - } else { - // transfn uses the transvalue only (e.g., int8inc) - irb->CreateStore(codegen_utils->GetConstant(0), llvm_arg1_ptr); - } - // }} - llvm_newval = irb->CreateCall( - llvm_datumCopyWithMemManager, - {irb->CreateLoad(llvm_pergroupstate_transValue_ptr), - irb->CreateLoad(llvm_arg1_ptr), - codegen_utils->GetConstant(peraggstate->transtypeByVal), - codegen_utils->GetConstant( - static_cast(peraggstate->transtypeLen)), - llvm_mem_manager_arg}); - irb->CreateStore(llvm_newval, llvm_pergroupstate_transValue_ptr); - // }}} newVal = datumCopyWithMemManager(...) - // *transValueIsNull = false; - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_pergroupstate_transValueIsNull_ptr); - // *noTransvalue = false; - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_pergroupstate_noTransValue_ptr); - irb->CreateBr(continue_advance_aggregate_block); - - // strict_check_transvalueisNull_block - // ----------------------------------- - // Check if transvalue is null. - // Don't call a strict function with NULL inputs. - irb->SetInsertPoint(strict_check_transvalueisNull_block); - irb->CreateCondBr(irb->CreateLoad(llvm_pergroupstate_transValueIsNull_ptr), - continue_advance_aggregate_block /*true*/, - generate_transfn_block /*false*/); - - // generate_transfn_block - // ---------------------- - // Generate code that implements transfn - irb->SetInsertPoint(generate_transfn_block); - } - // If transfn is strict, then this code is included in generate_transfn_block; - // in advance_transition_function_block otherwise. - // oldContext = MemoryContextSwitchTo(tuplecontext); - llvm::Value *llvm_oldContext = irb->CreateCall(llvm_MemoryContextSwitchTo, - {llvm_tuplecontext}); - gpcodegen::PGFuncGeneratorInterface* pg_func_gen = - gpcodegen::OpExprTreeGenerator::GetPGFuncGenerator( - peraggstate->transfn.fn_oid); - if (nullptr == pg_func_gen) { - elog(DEBUG1, "We do not support built-in function with oid = %d", - peraggstate->transfn.fn_oid); - return false; - } - bool isGenerated = - pg_func_gen->GenerateCode(codegen_utils, *pg_func_info, &llvm_newval, - llvm_pergroupstate_transValueIsNull_ptr); - if (!isGenerated) { - elog(DEBUG1, "Function with oid = %d was not generated successfully!", - peraggstate->transfn.fn_oid); - return false; - } - // We do not need to set *transValueIsNull = fcinfo->isnull, since - // transValueIsNull is passed as argument to pg_func_gen->GenerateCode - - // !fcinfo->isnull - llvm::Value* llvm_fcinfo_is_not_null = irb->CreateNot( - irb->CreateLoad(llvm_pergroupstate_transValueIsNull_ptr)); - - if (!peraggstate->transtypeByVal) { - llvm::Value* llvm_newval_by_ref = - codegen_utils->CreateCppTypeToDatumCast(llvm_newval); - - // if (DatumGetPointer(newVal) != DatumGetPointer(transValue) && - // !fcinfo->isnull) {{ - llvm::Value* llvm_val_changed = irb->CreateICmpNE( - codegen_utils->CreateDatumToCppTypeCast(llvm_newval), - codegen_utils->CreateDatumToCppTypeCast( - irb->CreateLoad(llvm_pergroupstate_transValue_ptr))); - - llvm::BasicBlock* transtypebyref_block = codegen_utils->CreateBasicBlock( - "transtypebyref_block", pg_func_info->llvm_main_func); - llvm::BasicBlock* end_transtypebyref_block = - codegen_utils->CreateBasicBlock("end_transtypebyref_block", - pg_func_info->llvm_main_func); - - irb->CreateCondBr(irb->CreateAnd(llvm_val_changed, llvm_fcinfo_is_not_null), - transtypebyref_block /* true */, - end_transtypebyref_block /* false */); - // }} if (DatumGetPointer ... - irb->SetInsertPoint(transtypebyref_block); - // newVal = datumCopyWithMemManager(transValue, newVal, transtypeByVal, - // transtypeLen, mem_manager); {{ - llvm_newval_by_ref = irb->CreateCall( - llvm_datumCopyWithMemManager, { - irb->CreateLoad(llvm_pergroupstate_transValue_ptr), - llvm_newval_by_ref, - codegen_utils->GetConstant(peraggstate->transtypeByVal), - codegen_utils->GetConstant( - static_cast(peraggstate->transtypeLen)), - llvm_mem_manager_arg}); - // pergroupstate->transValue = newval - irb->CreateStore(llvm_newval_by_ref, - llvm_pergroupstate_transValue_ptr); - // }} newVal = datumCopyWithMemManager ... - irb->CreateBr(end_transtypebyref_block); - - irb->SetInsertPoint(end_transtypebyref_block); - } else { - // pergroupstate->transValue = newval - irb->CreateStore(codegen_utils->CreateCppTypeToDatumCast(llvm_newval), - llvm_pergroupstate_transValue_ptr); - } - - // if (!fcinfo->isnull) - // *noTransvalue = false; - // MemoryContextSwitchTo(oldContext); {{{ - llvm::BasicBlock* set_noTransvalue_block = codegen_utils-> - CreateBasicBlock("set_noTransvalue_block", - pg_func_info->llvm_main_func); - llvm::BasicBlock* switch_memory_context_block = codegen_utils-> - CreateBasicBlock("switch_memory_context_block", - pg_func_info->llvm_main_func); - irb->CreateCondBr(llvm_fcinfo_is_not_null, - set_noTransvalue_block /*true*/, - switch_memory_context_block /*false*/); - - // set_noTransvalue_block - // ---------------------- - // Set noTransValue to false when transValue is not null - irb->SetInsertPoint(set_noTransvalue_block); - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_pergroupstate_noTransValue_ptr); - irb->CreateBr(switch_memory_context_block); - - // switch_memory_context_block - // --------------------------- - // Switch to old memory context before you generate code for the rest of the - // transition functions - irb->SetInsertPoint(switch_memory_context_block); - // MemoryContextSwitchTo(oldContext); - irb->CreateCall(llvm_MemoryContextSwitchTo, {llvm_oldContext}); - irb->CreateBr(continue_advance_aggregate_block); - // }}} if (!fcinfo->isnull) ... - - // continue_advance_aggregate_block - // -------------------------------- - // Continue with the rest code in advance_aggregates - irb->SetInsertPoint(continue_advance_aggregate_block); - - return true; -} - -bool AdvanceAggregatesCodegen::GenerateAdvanceAggregates( - gpcodegen::GpCodegenUtils* codegen_utils) { - - assert(NULL != codegen_utils); - if (nullptr == aggstate_) { - return false; - } - - auto irb = codegen_utils->ir_builder(); - - llvm::Function* advance_aggregates_func = CreateFunction( - codegen_utils, GetUniqueFuncName()); - - // BasicBlock of function entry. - llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock( - "entry_block", advance_aggregates_func); - llvm::BasicBlock* implementation_block = codegen_utils->CreateBasicBlock( - "implementation_block", advance_aggregates_func); - llvm::BasicBlock* error_aggstate_block = codegen_utils->CreateBasicBlock( - "error_aggstate_block", advance_aggregates_func); - llvm::BasicBlock* overflow_block = codegen_utils->CreateBasicBlock( - "overflow_block", advance_aggregates_func); - - // External functions - llvm::Function* llvm_ExecTargetList = - codegen_utils->GetOrRegisterExternalFunction(ExecTargetList, - "ExecTargetList"); - llvm::Function* llvm_ExecVariableList = - codegen_utils->GetOrRegisterExternalFunction(ExecVariableList, - "ExecVariableList"); - - // Function argument to advance_aggregates - llvm::Value* llvm_aggstate_arg = ArgumentByPosition( - advance_aggregates_func, 0); - llvm::Value* llvm_pergroup_arg = ArgumentByPosition( - advance_aggregates_func, 1); - llvm::Value* llvm_mem_manager_arg = ArgumentByPosition( - advance_aggregates_func, 2); - - // Generation-time constants - llvm::Value* llvm_aggstate = codegen_utils->GetConstant(aggstate_); - - // entry block - // ---------- - irb->SetInsertPoint(entry_block); - -#ifdef CODEGEN_DEBUG - EXPAND_CREATE_ELOG(codegen_utils, DEBUG1, - "Codegen'ed advance_aggregates called!"); -#endif - - // Compare aggstate given during code generation and the one passed - // in as an argument to advance_aggregates - irb->CreateCondBr( - irb->CreateICmpEQ(llvm_aggstate, llvm_aggstate_arg), - implementation_block /* true */, - error_aggstate_block /* false */); - - // implementation block - // ---------- - irb->SetInsertPoint(implementation_block); - - for (int aggno = 0; aggno < aggstate_->numaggs; aggno++) { - // Generate the code of each aggregate function in a different block. - llvm::BasicBlock* advance_aggregate_block = codegen_utils-> - CreateBasicBlock("advance_aggregate_block_aggno_" - + std::to_string(aggno), advance_aggregates_func); - - irb->CreateBr(advance_aggregate_block); - - // advance_aggregate block - // ---------- - // We generate code for advance_transition_function. - irb->SetInsertPoint(advance_aggregate_block); - - AggStatePerAgg peraggstate = &aggstate_->peragg[aggno]; - - if (peraggstate->numSortCols > 0) { - elog(DEBUG1, "We don't codegen DISTINCT and/or ORDER BY case"); - return false; - } - - Aggref *aggref = peraggstate->aggref; - if (!aggref) { - elog(DEBUG1, "We don't codegen non-aggref functions"); - return false; - } - - assert(peraggstate->evalproj); - // Number of attributes to be retrieved. This is one less than - // number of arguments of the transition function, since the transition - // value is passed as the first argument to the transition function. - int nargs = peraggstate->transfn.fn_nargs - 1; - assert(nargs >= 0); - - // Since we do not support ordered functions, we do not need to store - // the value of the variables, which are used as input to the aggregate - // function, in a slot. - llvm::Value* llvm_in_args_ptr = irb->CreateAlloca( - codegen_utils->GetType(), - codegen_utils->GetConstant(nargs)); - llvm::Value* llvm_in_isnulls_ptr = irb->CreateAlloca( - codegen_utils->GetType(), - codegen_utils->GetConstant(nargs)); - - llvm::BasicBlock* advance_transition_function_block = codegen_utils-> - CreateBasicBlock("advance_transition_function_block_aggno_" - + std::to_string(aggno), advance_aggregates_func); - - // Although the (nargs > 0) check does not exist in the regular - // advance_aggregates, the calls to ExecVariableList and - // ExecTargetList becomes a no-op when it is true. - // So we can avoid the call all together. - if (nargs > 0) { - if (peraggstate->evalproj->pi_isVarList) { - irb->CreateCall(llvm_ExecVariableList, { - codegen_utils->GetConstant(peraggstate->evalproj), - llvm_in_args_ptr, - llvm_in_isnulls_ptr}); - } else { - irb->CreateCall(llvm_ExecTargetList, { - codegen_utils->GetConstant(peraggstate->evalproj->pi_targetlist), - codegen_utils->GetConstant(peraggstate->evalproj->pi_exprContext), - llvm_in_args_ptr, - llvm_in_isnulls_ptr, - codegen_utils->GetConstant(peraggstate->evalproj->pi_itemIsDone), - codegen_utils->GetConstant(nullptr)}); - } - } - - irb->CreateBr(advance_transition_function_block); - - // advance_transition_function block - // ---------- - // We generate code for advance_transition_function. - irb->SetInsertPoint(advance_transition_function_block); - - // Collect input arguments (also if they are NULL or not) and the transition - // value in a vector. The transition value is stored at the first position. - std::vector llvm_in_args(nargs+1); - std::vector llvm_in_args_isNull(nargs+1); - for (int i=0; i < nargs; ++i) { - llvm_in_args[i+1] = irb->CreateLoad( - irb->CreateInBoundsGEP( - codegen_utils->GetType(), - llvm_in_args_ptr, - codegen_utils->GetConstant(i))); - llvm_in_args_isNull[i+1] = irb->CreateLoad( - irb->CreateInBoundsGEP( - codegen_utils->GetType(), - llvm_in_isnulls_ptr, - codegen_utils->GetConstant(i))); - } - - gpcodegen::PGFuncGeneratorInfo pg_func_info( - advance_aggregates_func, - overflow_block, - llvm_in_args, - llvm_in_args_isNull); - - bool isGenerated = GenerateAdvanceTransitionFunction( - codegen_utils, llvm_pergroup_arg, aggno, - &pg_func_info, llvm_mem_manager_arg); - if (!isGenerated) - return false; - } // End of for loop - - irb->CreateRetVoid(); - - // Error aggstate block - // --------------- - irb->SetInsertPoint(error_aggstate_block); - - EXPAND_CREATE_ELOG(codegen_utils, ERROR, "Codegened advance_aggregates: " - "use of different aggstate."); - - irb->CreateRetVoid(); - - // Overflow block - // --------------- - irb->SetInsertPoint(overflow_block); - // We error out during the execution of built-in function. - irb->CreateRetVoid(); - - return true; -} - - -bool AdvanceAggregatesCodegen::GenerateCodeInternal( - GpCodegenUtils* codegen_utils) { - bool isGenerated = GenerateAdvanceAggregates(codegen_utils); - - if (isGenerated) { - elog(DEBUG1, "AdvanceAggregates was generated successfully!"); - return true; - } else { - elog(DEBUG1, "AdvanceAggregates generation failed!"); - return false; - } -} diff --git a/src/backend/codegen/cmake/FindClang.cmake b/src/backend/codegen/cmake/FindClang.cmake deleted file mode 100644 index de6b82cc6d7ce5b7c7b72cd75edc026f18a630f1..0000000000000000000000000000000000000000 --- a/src/backend/codegen/cmake/FindClang.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2015-2016 Pivotal Software, Inc. -# -# FindClang.cmake -# Module to find necessary Clang libraries. - -# Some distros (Fedora, maybe others?) package clang as a single monolithic -# library instead of a shared library. -option(MONOLITHIC_CLANG_LIBRARY - "Look for a single monolithic clang library instead of modular libraries" - OFF) - -find_path(CLANG_INCLUDE_DIR clang/Tooling/Tooling.h - HINTS ${LLVM_INCLUDE_DIRS}) -set(CLANG_INCLUDE_DIRS ${CLANG_INCLUDE_DIR}) - -if (MONOLITHIC_CLANG_LIBRARY) - find_library(CLANG_LIBRARY - NAMES clang libclang - HINTS ${LLVM_LIBRARY_DIRS}) - set(CLANG_LIBVARS CLANG_LIBRARY) - set(CLANG_LIBRARIES ${CLANG_LIBRARY}) -else() - set(CLANG_COMPONENTS - CodeGen Frontend Tooling AST Basic Lex Driver Edit Parse Sema - Serialization ASTMatchers Rewrite ToolingCore Analysis) - - foreach(CLANG_COMPONENT ${CLANG_COMPONENTS}) - find_library(CLANG_${CLANG_COMPONENT}_LIBRARY - NAMES clang${CLANG_COMPONENT} libclang${CLANG_COMPONENT} - HINTS ${LLVM_LIBRARY_DIRS}) - set(CLANG_LIBVARS ${CLANG_LIBVARS} CLANG_${CLANG_COMPONENT}_LIBRARY) - set(CLANG_LIBRARIES ${CLANG_LIBRARIES} ${CLANG_${CLANG_COMPONENT}_LIBRARY}) - endforeach() -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Clang DEFAULT_MSG - ${CLANG_LIBVARS} - CLANG_INCLUDE_DIR) - -mark_as_advanced(CLANG_INCLUDE_DIR - ${CLANG_LIBVARS}) diff --git a/src/backend/codegen/cmake/FindLLVMMonolithic.cmake b/src/backend/codegen/cmake/FindLLVMMonolithic.cmake deleted file mode 100644 index 01efa78b2967e656abcc7dbfb30a18e754e4ea09..0000000000000000000000000000000000000000 --- a/src/backend/codegen/cmake/FindLLVMMonolithic.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2015-2016 Pivotal Software, Inc. -# -# FindLLVMMonolithic.cmake -# Module to find a single monolithic LLVM library (LLVM is packaged this way on -# Fedora, for example). - -find_library(LLVM_MONOLITHIC_LIBRARY - NAMES llvm libllvm LLVM libLLVM llvm-3.7 libllvm-3.7 LLVM-3.7 libLLVM-3.7 - HINTS ${LLVM_LIBRARY_DIRS} /usr/lib/llvm /usr/lib64/llvm /usr/lib32/llvm) -set(LLVM_MONOLITHIC_LIBRARIES ${LLVM_MONOLITHIC_LIBRARY}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LLVM-monolithic DEFAULT_MSG - LLVM_MONOLITHIC_LIBRARY) - -mark_as_advanced(LLVM_MONOLITHIC_LIBRARY) diff --git a/src/backend/codegen/codegen_interface.cc b/src/backend/codegen/codegen_interface.cc deleted file mode 100644 index a9b5cee3c2b8f1dc99ce6d90951bd4c30ca66975..0000000000000000000000000000000000000000 --- a/src/backend/codegen/codegen_interface.cc +++ /dev/null @@ -1,24 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_interface.cc -// -// @doc: -// Implementation of codegen interface's static function -// -//--------------------------------------------------------------------------- -#include "codegen/codegen_interface.h" - -#include - -using gpcodegen::CodegenInterface; - -// Initalization of unique counter -unsigned CodegenInterface::unique_counter_ = 0; - -std::string CodegenInterface::GenerateUniqueName( - const std::string& orig_func_name) { - return orig_func_name + std::to_string(unique_counter_++); -} diff --git a/src/backend/codegen/codegen_manager.cc b/src/backend/codegen/codegen_manager.cc deleted file mode 100644 index 846d4faf774181db8b4f09de9de218409078050d..0000000000000000000000000000000000000000 --- a/src/backend/codegen/codegen_manager.cc +++ /dev/null @@ -1,124 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_manager.cpp -// -// @doc: -// Implementation of a code generator manager -// -//--------------------------------------------------------------------------- -#include -#include -#include -#include -#include - -#include "llvm/Support/raw_ostream.h" - -#include "codegen/codegen_interface.h" -#include "codegen/codegen_manager.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/gp_codegen_utils.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "utils/guc.h" -} - -using gpcodegen::CodegenManager; - -CodegenManager::CodegenManager(const std::string& module_name) { - module_name_ = module_name; - codegen_utils_.reset(new gpcodegen::GpCodegenUtils(module_name)); -} - -bool CodegenManager::EnrollCodeGenerator( - CodegenFuncLifespan funcLifespan, CodegenInterface* generator) { - // Only CodegenFuncLifespan_Parameter_Invariant is supported as of now - assert(funcLifespan == CodegenFuncLifespan_Parameter_Invariant); - assert(nullptr != generator); - enrolled_code_generators_.emplace_back(generator); - return true; -} - -unsigned int CodegenManager::GenerateCode() { - // First, allow all code generators to initialize their dependencies - for (size_t i = 0; i < enrolled_code_generators_.size(); ++i) { - // NB: This list is still volatile at this time, as more generators may be - // enrolled as we iterate to initialize dependencies. - enrolled_code_generators_[i]->InitDependencies(); - } - // Then ask them to generate code - unsigned int success_count = 0; - for (std::unique_ptr& generator : - enrolled_code_generators_) { - success_count += generator->GenerateCode(codegen_utils_.get()); - } - return success_count; -} - -unsigned int CodegenManager::PrepareGeneratedFunctions() { - unsigned int success_count = 0; - - // If no generator registered, just return with success count as 0 - if (enrolled_code_generators_.empty()) { - return success_count; - } - - STATIC_ASSERT_OPTIMIZATION_LEVEL(kNone, - CODEGEN_OPTIMIZATION_LEVEL_NONE); - STATIC_ASSERT_OPTIMIZATION_LEVEL(kLess, - CODEGEN_OPTIMIZATION_LEVEL_LESS); - STATIC_ASSERT_OPTIMIZATION_LEVEL(kDefault, - CODEGEN_OPTIMIZATION_LEVEL_DEFAULT); - STATIC_ASSERT_OPTIMIZATION_LEVEL(kAggressive, - CODEGEN_OPTIMIZATION_LEVEL_AGGRESSIVE); - - // Call GpCodegenUtils to compile entire module - bool compilation_status = codegen_utils_->PrepareForExecution( - gpcodegen::GpCodegenUtils::OptimizationLevel(codegen_optimization_level), - true); - - if (!compilation_status) { - return success_count; - } - - // On successful compilation, go through all generator and swap - // the pointer so compiled function get called - gpcodegen::GpCodegenUtils* codegen_utils = codegen_utils_.get(); - for (std::unique_ptr& generator : - enrolled_code_generators_) { - success_count += generator->SetToGenerated(codegen_utils); - } - return success_count; -} - -void CodegenManager::NotifyParameterChange() { - // no support for parameter change yet - assert(false); -} - -bool CodegenManager::InvalidateGeneratedFunctions() { - // no support for invalidation of generated function - assert(false); - return false; -} - -const std::string& CodegenManager::GetExplainString() { - return explain_string_; -} - -void CodegenManager::AccumulateExplainString() { - explain_string_.clear(); - // This is called only when EXPLAIN CODEGEN. Because we don't want to compile - // at this time, we need to call CodegenUtils::Optimize to "optimize" LLVM IR. - codegen_utils_->Optimize(gpcodegen::CodegenUtils::OptimizationLevel( - codegen_optimization_level), - gpcodegen::CodegenUtils::SizeLevel::kNormal, - false); - llvm::raw_string_ostream out(explain_string_); - codegen_utils_->PrintUnderlyingModules(out); -} diff --git a/src/backend/codegen/codegen_wrapper.cc b/src/backend/codegen/codegen_wrapper.cc deleted file mode 100644 index 7036fff83beea6989cedfdba41682190605337a4..0000000000000000000000000000000000000000 --- a/src/backend/codegen/codegen_wrapper.cc +++ /dev/null @@ -1,175 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_wrapper.cc -// -// @doc: -// C wrappers for initialization of code generator. -// -//--------------------------------------------------------------------------- - -#include "codegen/codegen_wrapper.h" - -#include -#include -#include - -#include "codegen/codegen_config.h" -#include "codegen/base_codegen.h" -#include "codegen/codegen_manager.h" -#include "codegen/exec_eval_expr_codegen.h" -#include "codegen/exec_variable_list_codegen.h" -#include "codegen/expr_tree_generator.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/advance_aggregates_codegen.h" - -extern "C" { -#include "lib/stringinfo.h" -#include "postgres.h" // NOLINT(build/include) -} - -using gpcodegen::CodegenManager; -using gpcodegen::BaseCodegen; -using gpcodegen::ExecVariableListCodegen; -using gpcodegen::ExecEvalExprCodegen; -using gpcodegen::AdvanceAggregatesCodegen; - -// Current code generator manager that oversees all code generators -static void* ActiveCodeGeneratorManager = nullptr; - -// Perform global set-up tasks for code generation. Returns 0 on -// success, nonzero on error. -unsigned int InitCodegen() { - return gpcodegen::GpCodegenUtils::InitializeGlobal(); -} - -void* CodeGeneratorManagerCreate(const char* module_name) { - if (!codegen) { - return nullptr; - } - return new CodegenManager(module_name); -} - -unsigned int CodeGeneratorManagerGenerateCode(void* manager) { - if (!codegen) { - return 0; - } - return static_cast(manager)->GenerateCode(); -} - -unsigned int CodeGeneratorManagerPrepareGeneratedFunctions(void* manager) { - if (!codegen) { - return 0; - } - return static_cast(manager)->PrepareGeneratedFunctions(); -} - -unsigned int CodeGeneratorManagerNotifyParameterChange(void* manager) { - // parameter change notification is not supported yet - assert(false); - return 0; -} - -void CodeGeneratorManagerAccumulateExplainString(void* manager) { - if (!codegen) { - return; - } - assert(nullptr != manager); - static_cast(manager)->AccumulateExplainString(); -} - -char* CodeGeneratorManagerGetExplainString(void* manager) { - if (!codegen) { - return nullptr; - } - StringInfo return_string = makeStringInfo(); - appendStringInfoString( - return_string, - static_cast(manager)->GetExplainString().c_str()); - return return_string->data; -} - -void CodeGeneratorManagerDestroy(void* manager) { - delete (static_cast(manager)); -} - -void* GetActiveCodeGeneratorManager() { - return ActiveCodeGeneratorManager; -} - -void SetActiveCodeGeneratorManager(void* manager) { - ActiveCodeGeneratorManager = manager; -} - -Datum -slot_getattr_regular(TupleTableSlot *slot, int attnum, bool *isnull) { - return slot_getattr(slot, attnum, isnull); -} - -int -att_align_nominal_regular(int cur_offset, char attalign) { - return att_align_nominal(cur_offset, attalign); -} - -void SET_VARSIZE_regular(void* ptr, size_t len) { - SET_VARSIZE(ptr, len); -} - -uint32 -VARSIZE_regular(void* ptr) { - return VARSIZE(ptr); -} - -void* ExecVariableListCodegenEnroll( - ExecVariableListFn regular_func_ptr, - ExecVariableListFn* ptr_to_chosen_func_ptr, - ProjectionInfo* proj_info, - TupleTableSlot* slot) { - CodegenManager* manager = static_cast( - GetActiveCodeGeneratorManager()); - ExecVariableListCodegen* generator = - CodegenManager::CreateAndEnrollGenerator( - manager, - regular_func_ptr, - ptr_to_chosen_func_ptr, - proj_info, - slot); - return generator; -} - -void* ExecEvalExprCodegenEnroll( - ExecEvalExprFn regular_func_ptr, - ExecEvalExprFn* ptr_to_chosen_func_ptr, - ExprState *exprstate, - ExprContext *econtext, - PlanState* plan_state) { - CodegenManager* manager = static_cast( - GetActiveCodeGeneratorManager()); - ExecEvalExprCodegen* generator = - CodegenManager::CreateAndEnrollGenerator( - manager, - regular_func_ptr, - ptr_to_chosen_func_ptr, - exprstate, - econtext, - plan_state); - return generator; -} - -void* AdvanceAggregatesCodegenEnroll( - AdvanceAggregatesFn regular_func_ptr, - AdvanceAggregatesFn* ptr_to_chosen_func_ptr, - AggState *aggstate) { - CodegenManager* manager = static_cast( - GetActiveCodeGeneratorManager()); - AdvanceAggregatesCodegen* generator = - CodegenManager::CreateAndEnrollGenerator( - manager, - regular_func_ptr, - ptr_to_chosen_func_ptr, - aggstate); - return generator; -} - diff --git a/src/backend/codegen/const_expr_tree_generator.cc b/src/backend/codegen/const_expr_tree_generator.cc deleted file mode 100644 index 845228ee76b92a11601a082901c3a0849b5d6a6b..0000000000000000000000000000000000000000 --- a/src/backend/codegen/const_expr_tree_generator.cc +++ /dev/null @@ -1,69 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// const_expr_tree_generator.cc -// -// @doc: -// Object that generate code for const expression. -// -//--------------------------------------------------------------------------- - -#include -#include - -#include "codegen/const_expr_tree_generator.h" -#include "codegen/expr_tree_generator.h" -#include "codegen/utils/gp_codegen_utils.h" - -#include "llvm/IR/Constant.h" - - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "nodes/execnodes.h" -#include "nodes/nodes.h" -#include "nodes/primnodes.h" -} -namespace llvm { -class Value; -} // namespace llvm - - -using gpcodegen::ConstExprTreeGenerator; -using gpcodegen::ExprTreeGenerator; - -bool ConstExprTreeGenerator::VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree) { - assert(nullptr != expr_state && - nullptr != expr_state->expr && - T_Const == nodeTag(expr_state->expr) && - nullptr != expr_tree); - expr_tree->reset(new ConstExprTreeGenerator(expr_state)); - return true; -} - -ConstExprTreeGenerator::ConstExprTreeGenerator(const ExprState* expr_state) : - ExprTreeGenerator(expr_state, ExprTreeNodeType::kConst) { -} - -bool ConstExprTreeGenerator::GenerateCode(GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) { - assert(nullptr != llvm_out_value); - assert(nullptr != llvm_isnull_ptr); - auto irb = codegen_utils->ir_builder(); - Const* const_expr = reinterpret_cast(expr_state()->expr); - // const_expr->constvalue is a datum - *llvm_out_value = codegen_utils->GetConstant(const_expr->constvalue); - // *isNull = con->constisnull; - irb->CreateStore( - codegen_utils->GetConstant(const_expr->constisnull), - llvm_isnull_ptr); - - return true; -} diff --git a/src/backend/codegen/exec_eval_expr_codegen.cc b/src/backend/codegen/exec_eval_expr_codegen.cc deleted file mode 100644 index eaa7f32e2345b8a109e20766bcdcc7e60d717ba0..0000000000000000000000000000000000000000 --- a/src/backend/codegen/exec_eval_expr_codegen.cc +++ /dev/null @@ -1,189 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// exec_eval_expr_codegen.cc -// -// @doc: -// Generates code for ExecEvalExpr function. -// -//--------------------------------------------------------------------------- -#include -#include -#include -#include -#include - -#include "codegen/base_codegen.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/exec_eval_expr_codegen.h" -#include "codegen/expr_tree_generator.h" -#include "codegen/op_expr_tree_generator.h" -#include "codegen/slot_getattr_codegen.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/utils/utility.h" - -#include "llvm/IR/Argument.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/IRBuilder.h" - - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "nodes/execnodes.h" -#include "utils/elog.h" -#include "executor/tuptable.h" -#include "nodes/nodes.h" -} - -namespace llvm { -class BasicBlock; -class Function; -class Value; -} // namespace llvm - -using gpcodegen::ExecEvalExprCodegen; -using gpcodegen::SlotGetAttrCodegen; - -constexpr char ExecEvalExprCodegen::kExecEvalExprPrefix[]; - -ExecEvalExprCodegen::ExecEvalExprCodegen( - CodegenManager* manager, - ExecEvalExprFn regular_func_ptr, - ExecEvalExprFn* ptr_to_regular_func_ptr, - ExprState *exprstate, - ExprContext *econtext, - PlanState* plan_state) - : BaseCodegen(manager, - kExecEvalExprPrefix, - regular_func_ptr, ptr_to_regular_func_ptr), - exprstate_(exprstate), - plan_state_(plan_state), - gen_info_(econtext, nullptr, nullptr, nullptr, 0), - slot_getattr_codegen_(nullptr), - expr_tree_generator_(nullptr) { -} - -bool ExecEvalExprCodegen::InitDependencies() { - OpExprTreeGenerator::InitializeSupportedFunction(); - ExprTreeGenerator::VerifyAndCreateExprTree( - exprstate_, &gen_info_, &expr_tree_generator_); - // Prepare dependent slot_getattr() generation - PrepareSlotGetAttr(); - return true; -} - -void ExecEvalExprCodegen::PrepareSlotGetAttr() { - TupleTableSlot* slot = nullptr; - assert(nullptr != plan_state_); - switch (nodeTag(plan_state_)) { - case T_SeqScanState: - case T_TableScanState: - // Generate dependent slot_getattr() implementation for the given slot - if (gen_info_.max_attr > 0) { - slot = reinterpret_cast(plan_state_) - ->ss_ScanTupleSlot; - assert(nullptr != slot); - } - break; - case T_AggState: - // For now, we assume that tuples for the Aggs are already going to be - // deformed in which case, we can avoid generating and calling the - // generated slot_getattr(). This may not be true always, but calling the - // regular slot_getattr() will still preserve correctness. - break; - default: - elog(DEBUG1, - "Attempting to generate ExecEvalExpr for an unsupported operator!"); - } - - if (nullptr != slot) { - slot_getattr_codegen_ = SlotGetAttrCodegen::GetCodegenInstance( - manager(), slot, gen_info_.max_attr); - } -} - -bool ExecEvalExprCodegen::GenerateExecEvalExpr( - gpcodegen::GpCodegenUtils* codegen_utils) { - - assert(NULL != codegen_utils); - if (nullptr == exprstate_ || - nullptr == exprstate_->expr || - nullptr == gen_info_.econtext) { - return false; - } - - if (nullptr == expr_tree_generator_.get()) { - return false; - } - - // If slot_getattr_codegen_ is not set or generation fails - // we revert to use the external slot_getattr() - if (nullptr == slot_getattr_codegen_ || - false == slot_getattr_codegen_->GenerateCode(codegen_utils)) { - gen_info_.llvm_slot_getattr_func = - codegen_utils->GetOrRegisterExternalFunction(slot_getattr_regular, - "slot_getattr_regular"); - } else { - gen_info_.llvm_slot_getattr_func = - slot_getattr_codegen_->GetGeneratedFunction(); - assert(nullptr != gen_info_.llvm_slot_getattr_func); - } - - llvm::Function* exec_eval_expr_func = CreateFunction( - codegen_utils, GetUniqueFuncName()); - - // Function arguments to ExecVariableList - llvm::Value* llvm_isnull_arg = ArgumentByPosition(exec_eval_expr_func, 2); - - // BasicBlock of function entry. - llvm::BasicBlock* llvm_entry_block = codegen_utils->CreateBasicBlock( - "entry", exec_eval_expr_func); - llvm::BasicBlock* llvm_error_block = codegen_utils->CreateBasicBlock( - "error_block", exec_eval_expr_func); - - gen_info_.llvm_main_func = exec_eval_expr_func; - gen_info_.llvm_error_block = llvm_error_block; - - auto irb = codegen_utils->ir_builder(); - - irb->SetInsertPoint(llvm_entry_block); - -#ifdef CODEGEN_DEBUG - EXPAND_CREATE_ELOG(codegen_utils, - DEBUG1, - "Codegen'ed expression evaluation called!"); -#endif - - // Generate code from expression tree generator - llvm::Value* value = nullptr; - bool is_generated = expr_tree_generator_->GenerateCode(codegen_utils, - gen_info_, - &value, - llvm_isnull_arg); - if (!is_generated || - nullptr == value) { - return false; - } - - llvm::Value* llvm_ret_value = codegen_utils->CreateCppTypeToDatumCast(value); - irb->CreateRet(llvm_ret_value); - - irb->SetInsertPoint(llvm_error_block); - irb->CreateRet(codegen_utils->GetConstant(0)); - return true; -} - - -bool ExecEvalExprCodegen::GenerateCodeInternal(GpCodegenUtils* codegen_utils) { - bool isGenerated = GenerateExecEvalExpr(codegen_utils); - - if (isGenerated) { - elog(DEBUG1, "ExecEvalExpr was generated successfully!"); - return true; - } else { - elog(DEBUG1, "ExecEvalExpr generation failed!"); - return false; - } -} diff --git a/src/backend/codegen/exec_variable_list_codegen.cc b/src/backend/codegen/exec_variable_list_codegen.cc deleted file mode 100644 index e3b24e021610a1cd4d43e912ec1524b5525880ca..0000000000000000000000000000000000000000 --- a/src/backend/codegen/exec_variable_list_codegen.cc +++ /dev/null @@ -1,285 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_utils.cpp -// -// @doc: -// Contains different code generators -// -//--------------------------------------------------------------------------- - - -#include -#include -#include -#include -#include - - -#include "codegen/base_codegen.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/exec_variable_list_codegen.h" -#include "codegen/slot_getattr_codegen.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/utils/utility.h" - -#include "llvm/IR/Argument.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" - - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "executor/tuptable.h" -#include "nodes/execnodes.h" -#include "utils/elog.h" -#include "access/tupdesc.h" -#include "nodes/pg_list.h" -} - -namespace llvm { -class BasicBlock; -class Function; -class Value; -} // namespace llvm - -using gpcodegen::ExecVariableListCodegen; -using gpcodegen::SlotGetAttrCodegen; - -constexpr char ExecVariableListCodegen::kExecVariableListPrefix[]; - -ExecVariableListCodegen::ExecVariableListCodegen( - CodegenManager* manager, - ExecVariableListFn regular_func_ptr, - ExecVariableListFn* ptr_to_regular_func_ptr, - ProjectionInfo* proj_info, - TupleTableSlot* slot) - : BaseCodegen(manager, - kExecVariableListPrefix, - regular_func_ptr, - ptr_to_regular_func_ptr), - proj_info_(proj_info), - slot_(slot), - max_attr_(0), - slot_getattr_codegen_(nullptr) { -} - -bool ExecVariableListCodegen::InitDependencies() { - assert(nullptr != proj_info_); - assert(nullptr != proj_info_->pi_targetlist); - - // Find the largest attribute index in projInfo->pi_targetlist - max_attr_ = *std::max_element( - proj_info_->pi_varNumbers, - proj_info_->pi_varNumbers + list_length(proj_info_->pi_targetlist)); - slot_getattr_codegen_ = SlotGetAttrCodegen::GetCodegenInstance( - manager(), slot_, max_attr_); - return true; -} - -bool ExecVariableListCodegen::GenerateExecVariableList( - gpcodegen::GpCodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - static_assert(sizeof(Datum) == sizeof(int64_t), - "sizeof(Datum) doesn't match sizeof(int64)"); - - if ( nullptr == proj_info_->pi_varSlotOffsets ) { - elog(DEBUG1, - "Cannot codegen ExecVariableList because varSlotOffsets are null"); - return false; - } - - // Only do codegen if all the elements in the target list are on the same - // tuple slot This is an assumption for scan nodes, but we fall back when - // joins are involved. - for (int i = list_length(proj_info_->pi_targetlist) - 1; i > 0; i--) { - if (proj_info_->pi_varSlotOffsets[i] != - proj_info_->pi_varSlotOffsets[i-1]) { - elog(DEBUG1, - "Cannot codegen ExecVariableList because multiple slots to deform."); - return false; - } - } - - // System attribute - if (max_attr_ <= 0) { - elog(DEBUG1, "Cannot generate code for ExecVariableList" - "because max_attr is negative (i.e., system attribute)."); - return false; - } else if (max_attr_ > slot_->tts_tupleDescriptor->natts) { - elog(DEBUG1, "Cannot generate code for ExecVariableList" - "because max_attr is greater than natts."); - return false; - } - - llvm::Function* exec_variable_list_func = CreateFunction( - codegen_utils, GetUniqueFuncName()); - - auto irb = codegen_utils->ir_builder(); - - // BasicBlocks - llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock( - "entry", exec_variable_list_func); - // BasicBlock for checking correct slot - llvm::BasicBlock* slot_check_block = codegen_utils->CreateBasicBlock( - "slot_check", exec_variable_list_func); - // BasicBlock for main. - llvm::BasicBlock* main_block = codegen_utils->CreateBasicBlock( - "main", exec_variable_list_func); - // BasicBlock for final computations. - llvm::BasicBlock* final_block = codegen_utils->CreateBasicBlock( - "final", exec_variable_list_func); - llvm::BasicBlock* fallback_block = codegen_utils->CreateBasicBlock( - "fallback", exec_variable_list_func); - - // Generation-time constants - llvm::Value* llvm_max_attr = codegen_utils->GetConstant(max_attr_); - llvm::Value* llvm_slot = codegen_utils->GetConstant(slot_); - - // Function arguments to ExecVariableList - llvm::Value* llvm_projInfo_arg = ArgumentByPosition(exec_variable_list_func, - 0); - llvm::Value* llvm_values_arg = ArgumentByPosition(exec_variable_list_func, 1); - llvm::Value* llvm_isnull_arg = ArgumentByPosition(exec_variable_list_func, 2); - - - // Generate slot_getattr for attributes all the way to max_attr - llvm::Function* slot_getattr_func = nullptr; - // If slot_getattr_codegen_ is not set or generation fails - // we revert to use the external slot_getattr() - if (nullptr == slot_getattr_codegen_ || - false == slot_getattr_codegen_->GenerateCode(codegen_utils)) { - slot_getattr_func = codegen_utils->GetOrRegisterExternalFunction( - slot_getattr_regular, "slot_getattr_regular"); - } else { - slot_getattr_func = slot_getattr_codegen_->GetGeneratedFunction(); - assert(nullptr != slot_getattr_func); - } - - // In case the above generation failed, no point in continuing since that was - // the most crucial part of ExecVariableList code generation. - if (nullptr == slot_getattr_func) { - elog(DEBUG1, "Cannot generate code for ExecVariableList " - "because slot_getattr generation failed!"); - return false; - } - - // Entry block - // ----------- - irb->SetInsertPoint(entry_block); -#ifdef CODEGEN_DEBUG - EXPAND_CREATE_ELOG(codegen_utils, - DEBUG1, - "Codegen'ed ExecVariableList called!"); -#endif - irb->CreateBr(slot_check_block); - - // Slot check block - // ---------------- - - irb->SetInsertPoint(slot_check_block); - llvm::Value* llvm_econtext = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_projInfo_arg, &ProjectionInfo::pi_exprContext)); - - // We want to fall back when ExecVariableList is called with a slot that's - // different from the one we generated the function (eg HashJoin). We also - // assume only 1 slot and that the slot is in a scan node ie from - // exprContext->ecxt_scantuple or varOffset = 0 - llvm::Value* llvm_slot_arg = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_econtext, &ExprContext::ecxt_scantuple)); - - irb->CreateCondBr( - irb->CreateICmpEQ(llvm_slot, llvm_slot_arg), - main_block /* true */, - fallback_block /* false */); - - - // Main block - // ---------- - irb->SetInsertPoint(main_block); - // Allocate a dummy int so that slot_getattr can write isnull out - llvm::Value* llvm_dummy_isnull = - irb->CreateAlloca(codegen_utils->GetType()); - irb->CreateCall(slot_getattr_func, { - llvm_slot, - llvm_max_attr, - llvm_dummy_isnull - }); - - irb->CreateBr(final_block); - - - // Final Block - // ----------- - irb->SetInsertPoint(final_block); - llvm::Value* llvm_slot_PRIVATE_tts_isnull /* bool* */ = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_isnull)); - llvm::Value* llvm_slot_PRIVATE_tts_values /* Datum* */ = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_values)); - - // This code from ExecVariableList copies the contents of the isnull & values - // arrays in the slot to output variable from the function parameters to - // ExecVariableList - int *varNumbers = proj_info_->pi_varNumbers; - for (int i = list_length(proj_info_->pi_targetlist) - 1; i >= 0; i--) { - // *isnull = slot->PRIVATE_tts_isnull[attnum-1]; - llvm::Value* llvm_isnull_from_slot_val = - irb->CreateLoad(irb->CreateInBoundsGEP( - llvm_slot_PRIVATE_tts_isnull, - {codegen_utils->GetConstant(varNumbers[i] - 1)})); - llvm::Value* llvm_isnull_ptr = - irb->CreateInBoundsGEP(llvm_isnull_arg, - {codegen_utils->GetConstant(i)}); - irb->CreateStore(llvm_isnull_from_slot_val, llvm_isnull_ptr); - - // values[i] = slot_getattr(varSlot, varNumber+1, &(isnull[i])); - llvm::Value* llvm_value_from_slot_val = - irb->CreateLoad(irb->CreateInBoundsGEP( - llvm_slot_PRIVATE_tts_values, - {codegen_utils->GetConstant(varNumbers[i] - 1)})); - llvm::Value* llvm_values_ptr = - irb->CreateInBoundsGEP(llvm_values_arg, - {codegen_utils->GetConstant(i)}); - irb->CreateStore(llvm_value_from_slot_val, llvm_values_ptr); - } - - irb->CreateRetVoid(); - - - // Fall back Block - // --------------- - irb->SetInsertPoint(fallback_block); - EXPAND_CREATE_ELOG(codegen_utils, - DEBUG1, - "Falling back to regular ExecVariableList"); - - codegen_utils->CreateFallback( - codegen_utils->GetOrRegisterExternalFunction(ExecVariableList, - "ExecVariableList"), - exec_variable_list_func); - - return true; -} - - - - -bool ExecVariableListCodegen::GenerateCodeInternal( - GpCodegenUtils* codegen_utils) { - bool isGenerated = GenerateExecVariableList(codegen_utils); - - if (isGenerated) { - elog(DEBUG1, "ExecVariableList was generated successfully!"); - return true; - } else { - elog(DEBUG1, "ExecVariableList generation failed!"); - return false; - } -} diff --git a/src/backend/codegen/expr_tree_generator.cc b/src/backend/codegen/expr_tree_generator.cc deleted file mode 100644 index 10904867b822afcc3ac9fd4f46808cac8b4724dd..0000000000000000000000000000000000000000 --- a/src/backend/codegen/expr_tree_generator.cc +++ /dev/null @@ -1,70 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// expr_tree_generator.cc -// -// @doc: -// Base class for expression tree to generate code -// -//--------------------------------------------------------------------------- -#include -#include - -#include "codegen/const_expr_tree_generator.h" -#include "codegen/expr_tree_generator.h" -#include "codegen/op_expr_tree_generator.h" -#include "codegen/var_expr_tree_generator.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "nodes/execnodes.h" -#include "utils/elog.h" -#include "nodes/nodes.h" -} - -using gpcodegen::ExprTreeGenerator; - -bool ExprTreeGenerator::VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree) { - assert(nullptr != expr_state && - nullptr != expr_state->expr && - nullptr != expr_tree); - - if (!(IsA(expr_state, FuncExprState) || - IsA(expr_state, ExprState))) { - elog(DEBUG1, "Input expression state type (%d) is not supported", - expr_state->type); - return false; - } - expr_tree->reset(nullptr); - bool supported_expr_tree = false; - switch (nodeTag(expr_state->expr)) { - case T_OpExpr: { - supported_expr_tree = OpExprTreeGenerator::VerifyAndCreateExprTree( - expr_state, gen_info, expr_tree); - break; - } - case T_Var: { - supported_expr_tree = VarExprTreeGenerator::VerifyAndCreateExprTree( - expr_state, gen_info, expr_tree); - break; - } - case T_Const: { - supported_expr_tree = ConstExprTreeGenerator::VerifyAndCreateExprTree( - expr_state, gen_info, expr_tree); - break; - } - default : { - supported_expr_tree = false; - elog(DEBUG1, "Unsupported expression tree %d found", - nodeTag(expr_state->expr)); - } - } - assert((!supported_expr_tree && nullptr == expr_tree->get()) || - (supported_expr_tree && nullptr != expr_tree->get())); - return supported_expr_tree; -} diff --git a/src/backend/codegen/include/codegen/advance_aggregates_codegen.h b/src/backend/codegen/include/codegen/advance_aggregates_codegen.h deleted file mode 100644 index f1cf1d5231a1ccc2209ae6b81cbcef5834030446..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/advance_aggregates_codegen.h +++ /dev/null @@ -1,104 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// advance_aggregates_codegen.h -// -// @doc: -// Headers for AdvanceAggregates codegen. -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_ADVANCEAGGREGATES_CODEGEN_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_ADVANCEAGGREGATES_CODEGEN_H_ - -#include "codegen/base_codegen.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/pg_func_generator_interface.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -class AdvanceAggregatesCodegen: public BaseCodegen { - public: - /** - * @brief Constructor - * - * @param regular_func_ptr Regular version of the target function. - * @param ptr_to_chosen_func_ptr Reference to the function pointer that the - * caller will call. - * @param aggstate The AggState to use for generating code. - * - * @note The ptr_to_chosen_func_ptr can refer to either the generated - * function or the corresponding regular version. - * - **/ - explicit AdvanceAggregatesCodegen( - CodegenManager* manager, - AdvanceAggregatesFn regular_func_ptr, - AdvanceAggregatesFn* ptr_to_regular_func_ptr, - AggState *aggstate); - - virtual ~AdvanceAggregatesCodegen() = default; - - protected: - /** - * @brief Generate code for advance_aggregates. - * - * @param codegen_utils - * - * @return true on successful generation; false otherwise. - * - * This implementation does not support percentile and ordered aggregates. - * - * If at execution time, we see any of the above types of attributes, - * we fall backs to the regular function. - * - */ - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final; - - private: - AggState *aggstate_; - - static constexpr char kAdvanceAggregatesPrefix[] = "AdvanceAggregates"; - - /** - * @brief Generates runtime code that implements advance_aggregates. - * - * @param codegen_utils Utility to ease the code generation process. - * @return true on successful generation. - **/ - bool GenerateAdvanceAggregates(gpcodegen::GpCodegenUtils* codegen_utils); - - /** - * @brief Generates runtime code that implements advance_transition_function. - * It is called by GenerateAdvanceAggregates to generate code for a given - * aggregate function. - * - * @param codegen_utils Utility to ease the code generation process. - * @param llvm_pergroup_arg LLVM pointer to pergroup argument of regular - * advance_aggregates - * @param aggno ith aggregate function - * @param advance_aggregates_func LLVM function pointer to the code generated - * advance_aggregate function - * @param error_block LLVM block for treating errors - * @param llvm_arg argument of aggregate function - * - * @return true on successful generation; false otherwise. - */ - bool GenerateAdvanceTransitionFunction( - gpcodegen::GpCodegenUtils* codegen_utils, - llvm::Value* llvm_pergroup_arg, - int aggno, - gpcodegen::PGFuncGeneratorInfo* pg_func_info, - llvm::Value* llvm_mem_manager_arg); -}; - -/** @} */ - -} // namespace gpcodegen -#endif // GPCODEGEN_ADVANCEAGGREGATES_CODEGEN_H_ diff --git a/src/backend/codegen/include/codegen/base_codegen.h b/src/backend/codegen/include/codegen/base_codegen.h deleted file mode 100644 index b87415b64bbaaddc659611226e44cf8b00abf3f7..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/base_codegen.h +++ /dev/null @@ -1,244 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// base_codegen.h -// -// @doc: -// Base class for all the code generators with common implementation -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_BASE_CODEGEN_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_BASE_CODEGEN_H_ - -extern "C" { -#include -} - -#include -#include -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/codegen_config.h" -#include "codegen/codegen_interface.h" - -#include "llvm/IR/Function.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/raw_ostream.h" - - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -// Forward declaration -class CodegenManager; - -/** - * @brief Base code generator with common implementation that other - * code generators can use. - * - * @tparam FuncPtrType Function type for regular version of target functions - * or generated functions. - **/ -template -class BaseCodegen: public CodegenInterface { - public: - /** - * @brief Destroys the code generator and reverts back to using regular - * version of the target function. - **/ - virtual ~BaseCodegen() { - SetToRegular(regular_func_ptr_, ptr_to_chosen_func_ptr_); - } - - bool InitDependencies() override { - return true; - } - - bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils) final { - bool valid_generated_functions = true; - valid_generated_functions &= GenerateCodeInternal(codegen_utils); - - // Do this check only if it enabled by guc - if (codegen_validate_functions && valid_generated_functions) { - for (llvm::Function* function : uncompiled_generated_functions_) { - assert(nullptr != function); - std::string func_name = function->getName(); - std::string error_message = ""; - llvm::raw_string_ostream out(error_message); - // Verify function returns true if there are errors. - valid_generated_functions &= !llvm::verifyFunction(*function, &out); - out.flush(); - if (!valid_generated_functions) { - elog(WARNING, "Broken function found %s: %s", - func_name.c_str(), error_message.c_str()); - break; - } - } - } - if (!valid_generated_functions) { - // If failed to generate, or have invalid functions, make sure we - // do clean up by erasing all the llvm functions. - for (llvm::Function* function : uncompiled_generated_functions_) { - assert(nullptr != function); - function->eraseFromParent(); - } - } - is_generated_ = valid_generated_functions; - // We don't need to keep these pointers any more - std::vector().swap(uncompiled_generated_functions_); - return is_generated_; - } - - bool SetToRegular() final { - assert(nullptr != regular_func_ptr_); - SetToRegular(regular_func_ptr_, ptr_to_chosen_func_ptr_); - return true; - } - - bool SetToGenerated(gpcodegen::GpCodegenUtils* codegen_utils) final { - if (false == IsGenerated()) { - assert(*ptr_to_chosen_func_ptr_ == regular_func_ptr_); - return false; - } - - FuncPtrType compiled_func_ptr = codegen_utils->GetFunctionPointer< - FuncPtrType>(GetUniqueFuncName()); - - if (nullptr != compiled_func_ptr) { - *ptr_to_chosen_func_ptr_ = compiled_func_ptr; - return true; - } - return false; - } - - void Reset() final { - SetToRegular(); - } - - const std::string& GetOrigFuncName() const final { - return orig_func_name_; - } - - const std::string& GetUniqueFuncName() const final { - return unique_func_name_; - } - - bool IsGenerated() const final { - return is_generated_; - } - - /** - * @return Regular version of the target function. - * - **/ - FuncPtrType GetRegularFuncPointer() { - return regular_func_ptr_; - } - - /** - * @brief Sets up the caller to use the corresponding regular version of the - * target function. - * - * @param regular_func_ptr Regular version of the target function. - * @param ptr_to_chosen_func_ptr Reference to caller. - * - * @return true on setting to regular version. - **/ - static bool SetToRegular(FuncPtrType regular_func_ptr, - FuncPtrType* ptr_to_chosen_func_ptr) { - assert(nullptr != ptr_to_chosen_func_ptr); - assert(nullptr != regular_func_ptr); - *ptr_to_chosen_func_ptr = regular_func_ptr; - return true; - } - - protected: - /** - * @brief Constructor - * - * @param manager The manager in which this is enrolled. - * @param orig_func_name Original function name. - * @param regular_func_ptr Regular version of the target function. - * @param ptr_to_chosen_func_ptr Reference to the function pointer that the caller will call. - * - * @note The ptr_to_chosen_func_ptr can refer to either the generated function or the - * corresponding regular version. - * - **/ - explicit BaseCodegen(gpcodegen::CodegenManager* manager, - const std::string& orig_func_name, - FuncPtrType regular_func_ptr, - FuncPtrType* ptr_to_chosen_func_ptr) - : manager_(manager), - orig_func_name_(orig_func_name), - unique_func_name_(CodegenInterface::GenerateUniqueName(orig_func_name)), - regular_func_ptr_(regular_func_ptr), - ptr_to_chosen_func_ptr_(ptr_to_chosen_func_ptr), - is_generated_(false) { - // Initialize the caller to use regular version of target function. - SetToRegular(regular_func_ptr, ptr_to_chosen_func_ptr); - } - - gpcodegen::CodegenManager* manager() const { - return manager_; - } - - /** - * @brief Generates specialized code at run time. - * - * @note A template method design pattern to be overridden by the sub-class to implement - * the actual code generation. - * - * @note This is being called from GenerateCode and derived class will implement actual - * code generation - * - * @param codegen_utils Utility to ease the code generation process. - * @return true on successful generation. - **/ - virtual bool GenerateCodeInternal( - gpcodegen::GpCodegenUtils* codegen_utils) = 0; - - /** - * @brief Create llvm Function for given type and store the function pointer - * in vector - * - * @note If generation fails, this class takes responsibility to clean up all - * the functions it created - * - * @tparam FunctionType Type of the function to create - * - * @param codegen_utils Utility to ease the code generation process. - * @param function_name Name of the function to create - * @return llvm::Function pointer - **/ - public: - template - llvm::Function* CreateFunction(gpcodegen::GpCodegenUtils* codegen_utils, - const std::string& function_name) { - assert(nullptr != codegen_utils); - llvm::Function* function = codegen_utils->CreateFunction( - function_name); - assert(nullptr != function); - uncompiled_generated_functions_.push_back(function); - return function; - } - - private: - gpcodegen::CodegenManager* manager_; - std::string orig_func_name_; - std::string unique_func_name_; - FuncPtrType regular_func_ptr_; - FuncPtrType* ptr_to_chosen_func_ptr_; - bool is_generated_; - // To track uncompiled llvm functions it creates and erase from - // llvm module on failed generations. - std::vector uncompiled_generated_functions_; -}; -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_BASE_CODEGEN_H_ diff --git a/src/backend/codegen/include/codegen/codegen_config.h b/src/backend/codegen/include/codegen/codegen_config.h deleted file mode 100644 index d528e201d203298ef31d734276612f07c5301055..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/codegen_config.h +++ /dev/null @@ -1,81 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_config.h -// -// @doc: -// Configure Class to handle gucs in gpdb -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_CODEGEN_CONFIG_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_CODEGEN_CONFIG_H_ - -extern "C" { -// Variables that are defined in guc -extern bool init_codegen; -extern bool codegen; -extern bool codegen_validate_functions; -extern bool codegen_exec_variable_list; -extern bool codegen_slot_getattr; -extern bool codegen_exec_eval_expr; -extern bool codegen_advance_aggregate; -// TODO(shardikar): Retire this GUC after performing experiments to find the -// tradeoff of codegen-ing slot_getattr() (potentially by measuring the -// difference in the number of instructions) when one of the first few -// attributes is varlen. -extern int codegen_varlen_tolerance; -} - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -// Forward declaration -class ExecVariableListCodegen; -class SlotGetAttrCodegen; -class ExecEvalExprCodegen; -class AdvanceAggregatesCodegen; - -class CodegenConfig { - public: - /** - * @brief Template function to check if generator enabled. - * - * @tparam ClassType Type of Code Generator class - * - * @return true if respective generator is enabled with gpdb guc. - **/ - template - inline static bool IsGeneratorEnabled(); -}; - -template<> -inline bool CodegenConfig::IsGeneratorEnabled() { - return codegen_exec_variable_list; -} - -template<> -inline bool CodegenConfig::IsGeneratorEnabled() { - return codegen_slot_getattr; -} - -template<> -inline bool CodegenConfig::IsGeneratorEnabled() { - return codegen_exec_eval_expr; -} - -template<> -inline bool CodegenConfig::IsGeneratorEnabled() { - return codegen_advance_aggregate; -} - - -/** @} */ - -} // namespace gpcodegen - -#endif // GPCODEGEN_CODEGEN_CONFIG_H_ diff --git a/src/backend/codegen/include/codegen/codegen_interface.h b/src/backend/codegen/include/codegen/codegen_interface.h deleted file mode 100644 index db1f477ebcbe500bee36f0321e1daf9c628bb747..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/codegen_interface.h +++ /dev/null @@ -1,115 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_interface.h -// -// @doc: -// Interface for all code generator -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_CODEGEN_INTERFACE_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_CODEGEN_INTERFACE_H_ - -#include -#include - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -// Forward declaration -class GpCodegenUtils; - -/** - * @brief Interface for all code generators. - **/ -class CodegenInterface { - public: - virtual ~CodegenInterface() = default; - - /** - * @brief Hook to request for and initialize any dependencies - * - * @return true on success - */ - virtual bool InitDependencies() = 0; - - /** - * @brief Generates specialized code at run time. - * - * - * @param codegen_utils Utility to ease the code generation process. - * @return true on successful generation. - **/ - virtual bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils) = 0; - - /** - * @brief Sets up the caller to use the corresponding regular version of the - * target function. - * - * - * @return true on setting to regular version. - **/ - virtual bool SetToRegular() = 0; - - /** - * @brief Sets up the caller to use the generated function instead of the - * regular version. - * - * @param codegen_utils Facilitates in obtaining the function pointer from - * the compiled module. - * @return true on successfully setting to generated functions - **/ - virtual bool SetToGenerated(gpcodegen::GpCodegenUtils* codegen_utils) = 0; - - /** - * @brief Resets the state of the generator, including reverting back to - * the regular version of the function. - * - **/ - virtual void Reset() = 0; - - /** - * - * @return Original function name. - * - **/ - virtual const std::string& GetOrigFuncName() const = 0; - - /** - * @return Unique function name of the generated function to avoid name collision. - * - **/ - virtual const std::string& GetUniqueFuncName() const = 0; - - /** - * @return true if the code generation is successful. - * - **/ - virtual bool IsGenerated() const = 0; - - protected: - /** - * @brief Utility function to construct a unique function name from the - * original function name by appending a numeric suffix. - * - * @param orig_func_name Function name that needs to be made unique. - * @return Unique string for given input string. - * - **/ - static std::string GenerateUniqueName(const std::string& orig_func_name); - - private: - // Unique counter for all instances of Codegen Interface. - static unsigned unique_counter_; -}; - -/** @} */ - -} // namespace gpcodegen - -#endif // GPCODEGEN_CODEGEN_INTERFACE_H_ diff --git a/src/backend/codegen/include/codegen/codegen_manager.h b/src/backend/codegen/include/codegen/codegen_manager.h deleted file mode 100644 index 38dfff17c5274885c9c237e9698a7e54e274fe35..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/codegen_manager.h +++ /dev/null @@ -1,193 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_manager.h -// -// @doc: -// Object that manage all CodegenInterface and GpCodegenUtils -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_CODEGEN_MANAGER_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_CODEGEN_MANAGER_H_ - -#include -#include -#include - -#include "codegen/utils/macros.h" -#include "codegen/codegen_config.h" -#include "codegen/codegen_interface.h" -#include "codegen/base_codegen.h" - -namespace gpcodegen { -/** \addtogroup gpcodegen - * @{ - */ - -// Forward declaration of GpCodegenUtils to manage llvm module -class GpCodegenUtils; - -// Forward declaration of a CodegenInterface that will be managed by manager -class CodegenInterface; - -/** - * @brief Object that manages all code gen. - **/ -class CodegenManager { - public: - /** - * @brief Constructor. - * - * @param module_name A human-readable name for the module that this - * CodegenManager will manage. - **/ - explicit CodegenManager(const std::string& module_name); - - ~CodegenManager() = default; - - /** - * @brief Template function to facilitate enroll for any type of - * CodegenInterface that CodegenManager wants to keep track of. - * - * @tparam ClassType Type of Code Generator class that derives from - * CodegenInterface. - * @tparam FuncType Type of the function pointer that CodegenManager swaps. - * @tparam Args Variable argument that ClassType will take in its constructor - * - * @param manager Current Codegen Manager - * @param regular_func_ptr Regular version of the target function. - * @param ptr_to_chosen_func_ptr Pointer to the function pointer that the - * caller will call. - * @param args Variable length argument for ClassType - * - * This function creates a new code generator object of type ClassType using - * the passed-in args, and enrolls it in the given codegen manager. - * - * It does not create a generator when codegen or manager is unset, or the - * code generator ClassType is disabled (with the appropriate GUC). - * It always initializes the given double function pointer - * (ptr_to_chosen_func_ptr) to the regular_func_ptr. - * - * This transfers the ownership of the code generator to the manager. - * - * @return Pointer to ClassType - **/ - template - static ClassType* CreateAndEnrollGenerator( - CodegenManager* manager, - FuncType regular_func_ptr, - FuncType* ptr_to_chosen_func_ptr, - Args&&... args) { // NOLINT(build/c++11) - assert(nullptr != regular_func_ptr); - assert(nullptr != ptr_to_chosen_func_ptr); - - bool can_enroll = - // manager may be NULL if ExecInitNode/ExecProcNode weren't previously - // called. This happens e.g during gpinitsystem. - (nullptr != manager) && - codegen && // if codegen guc is false - // if generator is disabled - CodegenConfig::IsGeneratorEnabled(); - if (!can_enroll) { - gpcodegen::BaseCodegen::SetToRegular( - regular_func_ptr, ptr_to_chosen_func_ptr); - return nullptr; - } - - ClassType* generator = new ClassType( - manager, - regular_func_ptr, - ptr_to_chosen_func_ptr, - std::forward(args)...); - bool is_enrolled = manager->EnrollCodeGenerator( - CodegenFuncLifespan_Parameter_Invariant, generator); - assert(is_enrolled); - return generator; - } - - /** - * @brief Enroll a code generator with manager - * - * @note Manager manages the memory of enrolled generator. - * - * @param funcLifespan Life span of the enrolling generator. Based on life span, - * corresponding GpCodegenUtils will be used for code generation - * @param generator Generator that needs to be enrolled with manager. - * @return true on successful enrollment. - **/ - bool EnrollCodeGenerator(CodegenFuncLifespan funcLifespan, - CodegenInterface* generator); - - /** - * @brief Request all enrolled generators to generate code. - * - * @return The number of enrolled codegen that successfully generated code. - **/ - unsigned int GenerateCode(); - - /** - * @brief Compile all the generated functions. On success, - * a pointer to the generated method becomes available to the caller. - * - * @return The number of enrolled codegen that successully generated code - * and 0 on failure - **/ - unsigned int PrepareGeneratedFunctions(); - - /** - * @brief Notifies the manager of a parameter change. - * - * @note This is called during a ReScan or other parameter change process. - * Upon receiving this notification the manager may invalidate all the - * generated code that depend on parameters. - * - **/ - void NotifyParameterChange(); - - /** - * @brief Invalidate all generated functions. - * - * @return true if successfully invalidated. - **/ - bool InvalidateGeneratedFunctions(); - - /** - * @return Number of enrolled generators. - **/ - size_t GetEnrollmentCount() { - return enrolled_code_generators_.size(); - } - - /* - * @brief Accumulate the explain string with a dump of all the underlying LLVM - * modules - */ - void AccumulateExplainString(); - - /* - * @brief Return the previous accumulated explain string - */ - const std::string& GetExplainString(); - - private: - // GpCodegenUtils provides a facade to LLVM subsystem. - std::unique_ptr codegen_utils_; - - std::string module_name_; - - // List of all enrolled code generators. - std::vector> enrolled_code_generators_; - - // Holds the dumped IR of all underlying modules for EXPLAIN CODEGEN queries - std::string explain_string_; - - DISALLOW_COPY_AND_ASSIGN(CodegenManager); -}; - -/** @} */ - -} // namespace gpcodegen -#endif // GPCODEGEN_CODEGEN_MANAGER_H_ diff --git a/src/backend/codegen/include/codegen/const_expr_tree_generator.h b/src/backend/codegen/include/codegen/const_expr_tree_generator.h deleted file mode 100644 index daabbd916b533c96c37b5694316f83772f287dc0..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/const_expr_tree_generator.h +++ /dev/null @@ -1,52 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// const_expr_tree_generator.h -// -// @doc: -// Object that generate code for const expression. -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_CONST_EXPR_TREE_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_CONST_EXPR_TREE_GENERATOR_H_ - -#include "codegen/expr_tree_generator.h" - -#include "llvm/IR/Value.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Object that generate code for const expression. - **/ -class ConstExprTreeGenerator : public ExprTreeGenerator { - public: - static bool VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree); - - bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) final; - - protected: - /** - * @brief Constructor. - * - * @param expr_state Expression state from epxression tree - **/ - explicit ConstExprTreeGenerator(const ExprState* expr_state); -}; - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_CONST_EXPR_TREE_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/exec_eval_expr_codegen.h b/src/backend/codegen/include/codegen/exec_eval_expr_codegen.h deleted file mode 100644 index 64f0c388029bc2734af8f93c8538b06d04c918a1..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/exec_eval_expr_codegen.h +++ /dev/null @@ -1,103 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// exec_eval_expr_codegen.h -// -// @doc: -// Headers for ExecEvalExpr codegen. -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_EXECEVALEXPR_CODEGEN_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_EXECEVALEXPR_CODEGEN_H_ - -#include "codegen/base_codegen.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/expr_tree_generator.h" -#include "codegen/slot_getattr_codegen.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -class ExecEvalExprCodegen: public BaseCodegen { - public: - /** - * @brief Constructor - * - * @param regular_func_ptr Regular version of the target function. - * @param ptr_to_chosen_func_ptr Reference to the function pointer that the - * caller will call. - * @param exprstate The ExprState to use for generating code. - * @param econtext The ExprContext to use for generating code. - * @param slot The slot to use for generating code. - * - * @note The ptr_to_chosen_func_ptr can refer to either the generated - * function or the corresponding regular version. - * - **/ - explicit ExecEvalExprCodegen(CodegenManager* manager, - ExecEvalExprFn regular_func_ptr, - ExecEvalExprFn* ptr_to_regular_func_ptr, - ExprState *exprstate, - ExprContext *econtext, - PlanState* plan_state); - - virtual ~ExecEvalExprCodegen() = default; - - bool InitDependencies() override; - - protected: - /** - * @brief Generate code for expression evaluation. - * - * @param codegen_utils - * - * @return true on successful generation; false otherwise. - * - * @note Walks down expression tree and create respective ExprTreeGenerator - * to generate code. - * - * This implementation does not support: - * (1) Null attributes - * (2) Variable length attributes - * - * If at execution time, we see any of the above types of attributes, - * we fall backs to the regular function. - * - */ - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final; - - private: - ExprState *exprstate_; - PlanState* plan_state_; - - ExprTreeGeneratorInfo gen_info_; - SlotGetAttrCodegen* slot_getattr_codegen_; - std::unique_ptr expr_tree_generator_; - - static constexpr char kExecEvalExprPrefix[] = "ExecEvalExpr"; - - /** - * @brief Generates runtime code that implements expression evaluation. - * - * @param codegen_utils Utility to ease the code generation process. - * @return true on successful generation. - **/ - bool GenerateExecEvalExpr(gpcodegen::GpCodegenUtils* codegen_utils); - - /** - * @brief Prepare generation of dependent slot_getattr() if necessary - * @return true on successful generation. - **/ - void PrepareSlotGetAttr(); -}; - -/** @} */ - -} // namespace gpcodegen -#endif // GPCODEGEN_EXECEVALEXPR_CODEGEN_H_ diff --git a/src/backend/codegen/include/codegen/exec_variable_list_codegen.h b/src/backend/codegen/include/codegen/exec_variable_list_codegen.h deleted file mode 100644 index 2c1ddfc05b8e749b5e83ea8aba3335fea5473952..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/exec_variable_list_codegen.h +++ /dev/null @@ -1,107 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// exec_variable_list.h -// -// @doc: -// Headers for slot_deform_tuple codegen -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_EXECVARIABLELIST_CODEGEN_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_EXECVARIABLELIST_CODEGEN_H_ - -#include "codegen/codegen_wrapper.h" -#include "codegen/slot_getattr_codegen.h" -#include "codegen/base_codegen.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -class ExecVariableListCodegen: public BaseCodegen { - public: - /** - * @brief Constructor - * - * @param regular_func_ptr Regular version of the target function. - * @param ptr_to_chosen_func_ptr Reference to the function pointer that the caller will call. - * @param slot The slot to use for generating code. - * - * @note The ptr_to_chosen_func_ptr can refer to either the generated function or the - * corresponding regular version. - * - **/ - explicit ExecVariableListCodegen(CodegenManager* manager, - ExecVariableListFn regular_func_ptr, - ExecVariableListFn* ptr_to_regular_func_ptr, - ProjectionInfo* proj_info, - TupleTableSlot* slot); - - virtual ~ExecVariableListCodegen() = default; - - bool InitDependencies() override; - - protected: - /** - * @brief Generate code for the code path ExecVariableList > slot_getattr > - * _slot_getsomeattrs > slot_deform_tuple. - * - * @param codegen_utils - * - * @return true on successful generation; false otherwise. - * - * @note Generate code for code path ExecVariableList > slot_getattr > - * _slot_getsomeattrs > slot_deform_tuple. The regular implementation of - * ExecvariableList, for each attribute A in the target list, retrieves - * the slot that A comes from and calls slot_getattr. slot_get_attr() will - * eventually call slot_deform_tuple (through _slot_getsomeattrs), which fetches - * all yet unread attributes of the slot until the given attribute. - * - * This function generates the code for the case that all the - * attributes in target list use the same slot (created during a - * scan i.e, ecxt_scantuple). Moreover, instead of looping over the target - * list one at a time, this approach uses slot_getattr only once, with the - * largest attribute index from the target list. - * - * If code generation is not possible for any reason (e.g., attributes - * use different slots), then the function returns false and the codegen manager - * will manage the clean up. - * - * This implementation does not support: - * (1) Null attributes - * (2) Variable length attributes - * (3) Attributes passed by reference - * - * If at execution time, we see any of the above types of attributes, - * we fall backs to the regular function. - */ - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final; - - private: - ProjectionInfo* proj_info_; - TupleTableSlot* slot_; - - int max_attr_; - SlotGetAttrCodegen* slot_getattr_codegen_; - - - static constexpr char kExecVariableListPrefix[] = "ExecVariableList"; - - /** - * @brief Generates runtime code that implements ExecVariableList. - * - * @param codegen_utils Utility to ease the code generation process. - * @return true on successful generation. - **/ - bool GenerateExecVariableList(gpcodegen::GpCodegenUtils* codegen_utils); -}; - -/** @} */ - -} // namespace gpcodegen -#endif // GPCODEGEN_EXECVARIABLELIST_CODEGEN_H_ diff --git a/src/backend/codegen/include/codegen/expr_tree_generator.h b/src/backend/codegen/include/codegen/expr_tree_generator.h deleted file mode 100644 index 35d99dfcfda63548c35d5a608970217ff0ba1336..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/expr_tree_generator.h +++ /dev/null @@ -1,151 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// expr_tree_generator.h -// -// @doc: -// Base class for expression tree to generate code -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_EXPR_TREE_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_EXPR_TREE_GENERATOR_H_ - -#include -#include -#include -#include -#include - -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/codegen_wrapper.h" - -#include "llvm/IR/Value.h" - -typedef struct ExprContext ExprContext; -typedef struct ExprState ExprState; -typedef struct Expr Expr; -typedef struct OpExpr OpExpr; -typedef struct Var Var; -typedef struct Const Const; - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -enum class ExprTreeNodeType { - kConst = 0, - kVar = 1, - kOperator = 2 -}; - -/** - * @brief Object that holds the information needed for creating the ExprTree - * for the code generation of expressions in ExecQual, ExecEvalExpr etc. - * - * @note Members of this structures may be initialized at different stages (see - * ExecEvalExprCodegen::GenerateExecEvalExpr). Some are available immediately at - * the time of code generation, others need to be calculated by traversing the - * expression tree by the ExprTreeGenerator::VerifyAndCreateExprTree pass. - * However, all members must be initialized before the - * ExecEvalExprCodegen::GenerateCode pass when they are all required. - * - **/ -struct ExprTreeGeneratorInfo { - // Members that are immediately available at operator init. - ExprContext* econtext; - - // Convenience members for code generation - llvm::Function* llvm_main_func; - llvm::BasicBlock* llvm_error_block; - - // Members that will be updated by the - // ExprTreeGenerator::VerifyAndCreateExprTree pass - - // Keep track of the max_attr needed for the expression. Needed by - // the code generation logic for slot_getattr() - int16_t max_attr; - - // Records the generated slot_getattr() if generation was successful; a - // pointer to the external function otherwise. - llvm::Function* llvm_slot_getattr_func; - - ExprTreeGeneratorInfo( - ExprContext* econtext, - llvm::Function* llvm_main_func, - llvm::BasicBlock* llvm_error_block, - llvm::Function* llvm_slot_getattr_func, - int16_t max_attr) : - econtext(econtext), - llvm_main_func(llvm_main_func), - llvm_error_block(llvm_error_block), - max_attr(max_attr), - llvm_slot_getattr_func(llvm_slot_getattr_func) { - } -}; - -class ExprTreeGenerator { - public: - virtual ~ExprTreeGenerator() = default; - - /** - * @brief Verify if we support the given expression tree, and create an - * instance of ExprTreeGenerator if supported. - * - * @param expr_state Expression state from expression tree. - * @param gen_info Information needed for generating the expression tree. - * @param expr_tree Hold the new instance of expression tree class. - * - * @return true when it can codegen otherwise it return false. - **/ - static bool VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree); - - /** - * @brief Generate the code for given expression. - * - * @param codegen_utils Utility to easy code generation. - * @param gen_info Information needed for generating the expression - * tree. - * @param llvm_out_value Store the expression results - * @param llvm_isnull_ptr Set to true if current expr is null - * - * @return true when it generated successfully otherwise it return false. - **/ - virtual bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** value, - llvm::Value* const llvm_isnull_ptr) = 0; - - - /** - * @return Expression state - **/ - const ExprState* expr_state() { return expr_state_; } - - protected: - /** - * @brief Constructor. - * - * @param expr_state Expression state - * @param node_type Type of the ExprTreeGenerator - **/ - ExprTreeGenerator(const ExprState* expr_state, - ExprTreeNodeType node_type) : - expr_state_(expr_state) {} - - private: - const ExprState* expr_state_; - DISALLOW_COPY_AND_ASSIGN(ExprTreeGenerator); -}; - - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_EXPR_TREE_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/op_expr_tree_generator.h b/src/backend/codegen/include/codegen/op_expr_tree_generator.h deleted file mode 100644 index 531a73487a88e300986ee4df3e31cf5decaacb6a..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/op_expr_tree_generator.h +++ /dev/null @@ -1,86 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// op_expr_tree_generator.h -// -// @doc: -// Object that generate code for operator expression. -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_OP_EXPR_TREE_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_OP_EXPR_TREE_GENERATOR_H_ - -#include - -#include "codegen/expr_tree_generator.h" -#include "codegen/pg_func_generator_interface.h" -#include "codegen/pg_func_generator.h" - -#include "llvm/IR/Value.h" - - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -using CodeGenFuncMap = std::unordered_map>; - -/** - * @brief Object that generate code for operator expression. - **/ -class OpExprTreeGenerator : public ExprTreeGenerator { - public: - /** - * @brief Initialize PG operator function that we support for code generation. - **/ - static void InitializeSupportedFunction(); - - static bool VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree); - - /** - * @brief Checks if a built-in function can be code generated. - * - * @param oid The oid of the function. - * @return If function is supported, then returns a pointer to the - * PGFuncGeneratorInterface of the function; nullptr otherwise. - **/ - static gpcodegen::PGFuncGeneratorInterface* GetPGFuncGenerator( - unsigned int oid); - - bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) final; - - protected: - /** - * @brief Constructor. - * - * @param expr_state Expression state - * @param arguments Arguments to operator as list of ExprTreeGenerator - **/ - OpExprTreeGenerator( - const ExprState* expr_state, - std::vector< - std::unique_ptr< - ExprTreeGenerator>>&& arguments); // NOLINT(build/c++11) - - private: - std::vector> arguments_; - // Map of supported function with respective generator to generate code - static CodeGenFuncMap supported_function_; -}; - - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_OP_EXPR_TREE_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/pg_arith_func_generator.h b/src/backend/codegen/include/codegen/pg_arith_func_generator.h deleted file mode 100644 index 36085d26e07282df02c6af4afcc60a3f6302be4a..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/pg_arith_func_generator.h +++ /dev/null @@ -1,467 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_arith_func_generator.h -// -// @doc: -// Class with Static member function to generate code for +, - and * operator -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_PG_ARITH_FUNC_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_PG_ARITH_FUNC_GENERATOR_H_ - -#include -#include -#include - -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/pg_func_generator_interface.h" - -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Value.h" - -extern "C" { -#include "utils/elog.h" -} - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - - -namespace gpcodegen_ArithOp_detail { -// ArithOpOverFlowErrorMsg has various template specializations to -// handle error message for different C++ types. The specialized versions -// have a static method OverFlowErrMsg() that returns an overflow error message -// based on CppType as const char* -template -class ArithOpOverFlowErrorMsg {}; - -template -class ArithOpOverFlowErrorMsg< -IntType, -typename std::enable_if::value>::type> { - public: - static const char* OverFlowErrMsg() { return "integer out of range"; } -}; - -template <> -class ArithOpOverFlowErrorMsg< -float> { - public: - static const char* OverFlowErrMsg() { return "float4 out of range"; } -}; - -template <> -class ArithOpOverFlowErrorMsg< -double> { - public: - static const char* OverFlowErrMsg() { return "value out of range: overflow"; } -}; -} // namespace gpcodegen_ArithOp_detail - -using gpcodegen_ArithOp_detail::ArithOpOverFlowErrorMsg; - - -/** - * @brief Class with Static member function to generate code for +, - and * - * operator - * - * @tparam rtype Return type of function - * @tparam Arg0 First argument's type - * @tparam Arg1 Second argument's type - **/ -template -class PGArithFuncGenerator { - template - using CGArithOpTemplateFunc = llvm::Value* (GpCodegenUtils::*)( - llvm::Value* arg0, llvm::Value* arg1); - using CGArithOpFunc = CGArithOpTemplateFunc; - - public: - /** - * @brief Create LLVM Mul instruction with check for overflow - * - * @param codegen_utils Utility to easy code generation. - * @param llvm_main_func Current function for which we are generating code - * @param llvm_error_block Basic Block to jump when error happens - * @param llvm_args Vector of llvm arguments for the function - * @param llvm_out_value Store the results of function - * - * @return true if generation was successful otherwise return false - * - * @note If there is overflow, it will do elog::ERROR and then jump to given - * error block. - **/ - static bool MulWithOverflow(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - return ArithOpWithOverflow( - codegen_utils, - &gpcodegen::GpCodegenUtils::CreateMulOverflow, - ArithOpOverFlowErrorMsg::OverFlowErrMsg(), - pg_func_info, - llvm_out_value); - } - - /** - * @brief Create LLVM Add instruction with check for overflow - * - * @param codegen_utils Utility to easy code generation. - * @param llvm_main_func Current function for which we are generating code - * @param llvm_error_block Basic Block to jump when error happens - * @param llvm_args Vector of llvm arguments for the function - * @param llvm_out_value Store the results of function - * - * @return true if generation was successful otherwise return false - * - * @note If there is overflow, it will do elog::ERROR and then jump to given - * error block. - **/ - static bool AddWithOverflow(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - return ArithOpWithOverflow( - codegen_utils, - &gpcodegen::GpCodegenUtils::CreateAddOverflow, - ArithOpOverFlowErrorMsg::OverFlowErrMsg(), - pg_func_info, - llvm_out_value); - } - - /** - * @brief Create LLVM instructions to check if arguments are NULL. - * - * @param codegen_utils Utility for easy code generation. - * @param pg_func_info Details for pgfunc generation - * @param llvm_isnull_ptr Records if result is NULL - * @param llvm_out_value_ptr Store location for the result - * @param llvm_is_set_ptr Pointer to flag that shows if a value has been - * assigned to the contents of llvm_out_value_ptr - * - * @return true if generation was successful otherwise return false - * - * @note It is used only if built-in function is not strict. - * This function implements the first part of int4_sum built-in - * function that checks for NULL arguments. - **/ - static bool CreateArgumentNullChecks(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value* llvm_out_value_ptr, - llvm::Value* llvm_is_set_ptr, - llvm::Value* const llvm_isnull_ptr) { - assert(nullptr != codegen_utils); - assert(nullptr != llvm_out_value_ptr); - assert(nullptr != llvm_is_set_ptr); - assert(pg_func_info.llvm_args.size() == - pg_func_info.llvm_args_isNull.size()); - auto irb = codegen_utils->ir_builder(); - - // Entry block that shows that clearly shows the beginning of CheckNull - llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock( - "CheckNull_entry_block", pg_func_info.llvm_main_func); - // Block that includes instructions in case that arg0 is null - llvm::BasicBlock* arg0_is_null_block = codegen_utils-> - CreateBasicBlock("CheckNull_arg0_is_null_block", - pg_func_info.llvm_main_func); - // Block that includes instructions in case that arg0 is not null - llvm::BasicBlock* arg0_is_not_null_block = codegen_utils-> - CreateBasicBlock("CheckNull_arg0_is_not_null_block", - pg_func_info.llvm_main_func); - // Block that includes instructions in case that arg1 is null - llvm::BasicBlock* arg1_is_null_block = codegen_utils-> - CreateBasicBlock("CheckNull_arg1_is_null_block", - pg_func_info.llvm_main_func); - // Block that includes instructions in case that arg1 is not null - llvm::BasicBlock* arg1_is_not_null_block = codegen_utils-> - CreateBasicBlock("CheckNull_arg1_is_not_null_block", - pg_func_info.llvm_main_func); - // Block that includes instructions in case that there is a null argument - llvm::BasicBlock* return_null_block = codegen_utils->CreateBasicBlock( - "CheckNull_return_null_block", pg_func_info.llvm_main_func); - // All CheckNull blocks create branch to continue_block (final block) - llvm::BasicBlock* continue_block = codegen_utils->CreateBasicBlock( - "CheckNull_continue_block", pg_func_info.llvm_main_func); - irb->CreateBr(entry_block); - - // entry_block - // ----------- - // Generate code that examines if arg0 is null - irb->SetInsertPoint(entry_block); - // if (PG_ARGISNULL(0)) - irb->CreateCondBr(pg_func_info.llvm_args_isNull[0], - arg0_is_null_block /* true */, - arg0_is_not_null_block /* false */); - - // arg0_is_null_block - // ------------------ - // arg0 is NULL, so examine if arg1 is NULL too. - irb->SetInsertPoint(arg0_is_null_block); - // if (PG_ARGISNULL(1)) - irb->CreateCondBr(pg_func_info.llvm_args_isNull[1], - return_null_block /* true */, - arg1_is_not_null_block /* false */); - - // return_null_block - // ----------------- - // Generate code for PG_RETURN_NULL() - irb->SetInsertPoint(return_null_block); - // fcinfo->isnull = true; - irb->CreateStore(codegen_utils->GetConstant(true), - llvm_isnull_ptr); - // similar to: return (Datum) 0 - irb->CreateStore(codegen_utils->GetConstant(0), - llvm_out_value_ptr); - // set the content of llvm_is_set_ptr to true, so that we do not need to - // execute the code of non-strict built-in function - irb->CreateStore(codegen_utils->GetConstant(true), - llvm_is_set_ptr); - irb->CreateBr(continue_block); - - // arg1_is_not_null_block - // ---------------------- - // In this case, arg0 is NULL and arg1 is not NULL. - irb->SetInsertPoint(arg1_is_not_null_block); - // val = (int64) PG_GETARG_INT32(1) - // PG_RETURN_INT64(val) - irb->CreateStore(codegen_utils->CreateCast( - pg_func_info.llvm_args[1]), llvm_out_value_ptr); - // set the content of llvm_is_set_ptr to true, so that we do not need to - // execute the code of non-strict built-in function - irb->CreateStore(codegen_utils->GetConstant(true), - llvm_is_set_ptr); - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_isnull_ptr); - irb->CreateBr(continue_block); - - // arg0_is_not_null_block - // ---------------------- - // arg0 is not null, examine if arg1 is null - // If both arguments are not NULL, then just continue and execute the - // generated code of the built-in function - irb->SetInsertPoint(arg0_is_not_null_block); - // if (PG_ARGISNULL(1)) - irb->CreateCondBr(pg_func_info.llvm_args_isNull[1], - arg1_is_null_block, - continue_block); - - // arg1_is_null_block - // ------------------ - // arg0 is not NULL, but arg1 is NULL - // Generate code for PG_RETURN_INT64(val), where val = arg0 - irb->SetInsertPoint(arg1_is_null_block); - - irb->CreateStore(codegen_utils->CreateCast( - pg_func_info.llvm_args[0]), - llvm_out_value_ptr); - // set the content of llvm_is_set_ptr to true, so that we do not need to - // execute the code of non-strict built-in function - irb->CreateStore(codegen_utils->GetConstant(true), - llvm_is_set_ptr); - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_isnull_ptr); - irb->CreateBr(continue_block); - - // continue_block - // -------------- - // Continue with the rest of the generated code - irb->SetInsertPoint(continue_block); - - return true; - } - - - - /** - * @brief Create LLVM Sub instruction with check for overflow - * - * @param codegen_utils Utility to easy code generation. - * @param llvm_main_func Current function for which we are generating code - * @param llvm_error_block Basic Block to jump when error happens - * @param llvm_args Vector of llvm arguments for the function - * @param llvm_out_value Store the results of function - * - * @return true if generation was successful otherwise return false - * - * @note If there is overflow, it will do elog::ERROR and then jump to given - * error block. - **/ - static bool SubWithOverflow(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - return ArithOpWithOverflow( - codegen_utils, - &gpcodegen::GpCodegenUtils::CreateSubOverflow, - ArithOpOverFlowErrorMsg::OverFlowErrMsg(), - pg_func_info, - llvm_out_value); - } - - static bool ArithOpWithOverflow(gpcodegen::GpCodegenUtils* codegen_utils, - CGArithOpFunc codegen_mem_funcptr, - const char* error_msg, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value); -}; - - -/** - * @brief Class with Static member function to generate code for unary - * operator such as ++, -- etc. - * - * @tparam rtype Return type of function - * @tparam Arg Argument's type - **/ -template -class PGArithUnaryFuncGenerator { - template - using CGArithOpTemplateFunc = llvm::Value* (GpCodegenUtils::*)( - llvm::Value* arg); - using CGArithOpFunc = CGArithOpTemplateFunc; - - public: - /** - * @brief Increase the value of a variable with check for overflow - * - * @param codegen_utils Utility for easy code generation. - * @param pg_func_info Details for pgfunc generation - * @param llvm_out_value Store location for the result - * - * @return true if generation was successful otherwise return false - * - * @note If there is overflow, it will do elog::ERROR and then jump to given - * error block. - **/ - static bool IncWithOverflow(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - return PGArithUnaryFuncGenerator::ArithOpWithOverflow( - codegen_utils, - &gpcodegen::GpCodegenUtils::CreateIncOverflow, - ArithOpOverFlowErrorMsg::OverFlowErrMsg(), - pg_func_info, - llvm_out_value); - } - - static bool ArithOpWithOverflow(gpcodegen::GpCodegenUtils* codegen_utils, - CGArithOpFunc codegen_mem_funcptr, - const char* error_msg, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value); -}; - -/** - * @brief Utility function for generating code for overflow checking. This - * includes an overflow block and non-overflow block, check and conditional - * branch instructions. - * - * @param codegen_utils Utility for easy code generation. - * @param pg_func_info Details for pgfunc generation - * @param llvm_arith_output Result of the arithmetic operation - * @param error_msg Error message to use when overflow occurs - * @param llvm_out_value Store location for the result - * - * @return true if generation was successful otherwise return false - * - * @note Only integral types are currently supported. - **/ -template -static bool CreateOverflowCheckLogic( - gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value* llvm_arith_output, - const char* error_msg, - llvm::Value** llvm_out_value) { - assert(nullptr != codegen_utils); - assert(nullptr != llvm_arith_output); - assert(nullptr != error_msg); - - llvm::IRBuilder<>* irb = codegen_utils->ir_builder(); - - llvm::BasicBlock* llvm_non_overflow_block = codegen_utils->CreateBasicBlock( - "arith_non_overflow_block", pg_func_info.llvm_main_func); - llvm::BasicBlock* llvm_overflow_block = codegen_utils->CreateBasicBlock( - "arith_overflow_block", pg_func_info.llvm_main_func); - - *llvm_out_value = irb->CreateExtractValue(llvm_arith_output, 0); - llvm::Value* llvm_overflow_flag = - irb->CreateExtractValue(llvm_arith_output, 1); - - irb->CreateCondBr(llvm_overflow_flag, llvm_overflow_block, - llvm_non_overflow_block); - - irb->SetInsertPoint(llvm_overflow_block); - EXPAND_CREATE_EREPORT(codegen_utils, - ERROR, - ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE, - error_msg); - irb->CreateBr(pg_func_info.llvm_error_block); - - irb->SetInsertPoint(llvm_non_overflow_block); - - return true; -} - -template -bool PGArithUnaryFuncGenerator::ArithOpWithOverflow( - gpcodegen::GpCodegenUtils* codegen_utils, - CGArithOpFunc codegen_mem_funcptr, - const char* error_msg, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - assert(nullptr != codegen_utils); - assert(nullptr != llvm_out_value); - assert(nullptr != codegen_mem_funcptr); - assert(nullptr != error_msg); - - // Assumed caller checked vector size and nullptr for codegen_utils - llvm::Value* casted_arg = - codegen_utils->CreateCast(pg_func_info.llvm_args[0]); - - llvm::Value* llvm_arith_output = (codegen_utils->*codegen_mem_funcptr)( - casted_arg); - - return CreateOverflowCheckLogic(codegen_utils, - pg_func_info, - llvm_arith_output, - error_msg, - llvm_out_value); -} - - -template -bool PGArithFuncGenerator::ArithOpWithOverflow( - gpcodegen::GpCodegenUtils* codegen_utils, - CGArithOpFunc codegen_mem_funcptr, - const char* error_msg, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - assert(nullptr != codegen_utils); - assert(nullptr != llvm_out_value); - assert(nullptr != codegen_mem_funcptr); - assert(nullptr != error_msg); - - // Assumed caller checked vector size and nullptr for codegen_utils - llvm::Value* casted_arg0 = - codegen_utils->CreateCast(pg_func_info.llvm_args[0]); - llvm::Value* casted_arg1 = - codegen_utils->CreateCast(pg_func_info.llvm_args[1]); - - llvm::Value* llvm_arith_output = - (codegen_utils->*codegen_mem_funcptr)(casted_arg0, casted_arg1); - - return CreateOverflowCheckLogic(codegen_utils, - pg_func_info, - llvm_arith_output, - error_msg, - llvm_out_value); -} - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_PG_ARITH_FUNC_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/pg_date_func_generator.h b/src/backend/codegen/include/codegen/pg_date_func_generator.h deleted file mode 100644 index b36f702e0b5c2605043a7fcb90194d3436c4a2a2..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/pg_date_func_generator.h +++ /dev/null @@ -1,84 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_date_func_generator.h -// -// @doc: -// Base class for date functions to generate code -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_PG_DATE_FUNC_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_PG_DATE_FUNC_GENERATOR_H_ - -#include -#include -#include - -#include "codegen/base_codegen.h" -#include "codegen/pg_func_generator_interface.h" -#include "codegen/utils/codegen_utils.h" - -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Value.h" - -namespace llvm { -class Value; -} // namespace llvm - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -class GpCodegenUtils; -struct PGFuncGeneratorInfo; - -/** - * @brief Class with Static member function to generate code for date - * operators. - **/ - -class PGDateFuncGenerator { - public: - /** - * @brief Create instructions for date_le_timestamp function - * - * @param codegen_utils Utility to easy code generation. - * @param llvm_main_func Current function for which we are generating code - * @param llvm_error_block Basic Block to jump when error happens - * @param llvm_args Vector of llvm arguments for the function - * @param llvm_out_value Store the results of function - * - * @return true if generation was successful otherwise return false - * - **/ - static bool DateLETimestamp( - gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value); - - private: - /** - * @brief Internal routines for promoting date to timestamp and timestamp - * with time zone (see date2timestamp). - * - * @param codegen_utils Utility to easy code generation. - * @param llvm_main_func Current function for which we are generating code - * @param llvm_arg llvm value for the date - * @param llvm_error_block Basic Block to jump when error happens - * - * @note If there is overflow, it will do elog::ERROR and then jump to given - * error block. - */ - static llvm::Value* GenerateDate2Timestamp( - GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info); -}; - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_PG_ARITH_FUNC_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/pg_func_generator.h b/src/backend/codegen/include/codegen/pg_func_generator.h deleted file mode 100644 index b0e6d7375036eba863f0ac54ee67e1132ca33513..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/pg_func_generator.h +++ /dev/null @@ -1,552 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_func_generator.h -// -// @doc: -// Object to manage code generation for Greenplum's function -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_PG_FUNC_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_PG_FUNC_GENERATOR_H_ - -#include -#include -#include - -#include "codegen/pg_func_generator_interface.h" -#include "codegen/utils/gp_codegen_utils.h" - -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Value.h" - -namespace gpcodegen { - -// Helper template classes for gp_func_generators nested in here -namespace pg_func_generator_detail { - -// PGFuncArgPreProcessor is a variadic template to assist in pre-processing a -// variable number of arguments to PGFuncBaseGenerator. Each specialization has -// a method called 'PreProcessArgs' that takes a pointer to 'CodegenUtils', an -// input vector of 'llvm:Values*' and another vector of 'llvm::Values*' that it -// populates by pre-processing the input vector. During pre-processsing, we -// generate LLVM instructions to convert the input 'llvm::Value*' values (that -// are expected to be of the type 'Datum') to the appropriate CppType from the -// variadic template list, and populate the output vector with coverted -// 'llvm::Values*' in the same order as input. -// -// e.g. PGFuncArgPreProcessor::PreProcessArgs(cu, vin, vout) will -// create instructions to convert vin[0] to int and vin[1] to bool and populate -// 'vout'. -template -class PGFuncArgPreProcessor; - -// Base version for zero argument types does nothing. -template <> -class PGFuncArgPreProcessor<> { - private: - static bool PreProcessArgsInternal( - gpcodegen::GpCodegenUtils* codegen_utils, - std::vector::const_iterator llvm_in_args_iter, - std::vector* llvm_out_args) { - return true; - } - - // Make all template specializations of PGFuncArgPreProcessor friends - template - friend class PGFuncArgPreProcessor; -}; - -// Version for 1+ arguments that recurses -template -class PGFuncArgPreProcessor { - public: - static bool PreProcessArgs( - gpcodegen::GpCodegenUtils* codegen_utils, - const std::vector& llvm_in_args, - std::vector* llvm_out_args) { - assert(nullptr != codegen_utils && - nullptr != llvm_out_args); - - - if (llvm_in_args.size() != 1 /* HeadType */ + sizeof...(TailTypes)) { - return false; - } - - return PreProcessArgsInternal( - codegen_utils, llvm_in_args.begin(), llvm_out_args); - } - - private: - static bool PreProcessArgsInternal( - gpcodegen::GpCodegenUtils* codegen_utils, - std::vector::const_iterator llvm_in_args_iter, - std::vector* llvm_out_args) { - assert(nullptr != codegen_utils); - assert(nullptr != llvm_out_args); - assert(nullptr != *llvm_in_args_iter); - - assert(codegen_utils->GetType() == (*llvm_in_args_iter)->getType()); - - // Convert Datum to given cpp type. - llvm_out_args->push_back( - codegen_utils->CreateDatumToCppTypeCast(*llvm_in_args_iter)); - return PGFuncArgPreProcessor::PreProcessArgsInternal( - codegen_utils, ++llvm_in_args_iter, llvm_out_args); - } - - // Make all template specializations of PGFuncArgPreProcessor friends - template - friend class PGFuncArgPreProcessor; -}; - -} // namespace pg_func_generator_detail - - -/** \addtogroup gpcodegen - * @{ - */ - -typedef bool (*PGFuncGeneratorFn)(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value); - -typedef bool (*PGCheckNullFuncGeneratorFn)( - gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value* llvm_out_value_ptr, - llvm::Value* llvm_is_set_ptr, - llvm::Value* const llvm_isnull_ptr); - -/** - * @brief Create LLVM instructions to check if there are NULL arguments. - * If there is a NULL argument, then it creates a branch to a block - * that returns 0; otherwise it creates a branch to a block that - * implements the built-in function. - * - * @param codegen_utils Utility for easy code generation - * @param pg_func_info Details for pgfunc generation - * @param first_arg_index Avoid nullity checks for the arguments located - * before this index - * @param entry_block Block that checks arguments' nullity - * @param null_block Block that returns 0 - * @param generation_block Block that implements the built-in function - * - * @return true if generation was successful otherwise return false - * - * @note It is used only if built-in function is strict. - **/ -static inline bool GenerateStrictLogic(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - int first_arg_index, - llvm::BasicBlock* entry_block, - llvm::BasicBlock* null_block, - llvm::BasicBlock* generation_block) { - assert(nullptr != codegen_utils); - auto irb = codegen_utils->ir_builder(); - - irb->SetInsertPoint(entry_block); - // Stores if there is a NULL argument - llvm::Value* llvm_there_is_null_arg_ptr = irb->CreateAlloca( - codegen_utils->GetType(), - nullptr, "llvm_there_is_null_arg_ptr"); - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_there_is_null_arg_ptr); - for (int i = first_arg_index; i < pg_func_info.llvm_args_isNull.size(); ++i) { - irb->CreateStore( - irb->CreateOr(pg_func_info.llvm_args_isNull[i], - irb->CreateLoad(llvm_there_is_null_arg_ptr)), - llvm_there_is_null_arg_ptr); - } - irb->CreateCondBr(irb->CreateLoad(llvm_there_is_null_arg_ptr), - null_block /*true*/, - generation_block /*false*/); - return true; -} - - -/** - * @brief Object that use an IRBuilder member function that to generate code for - * given binary GPDB function - * - * @tparam ReturnType C++ type of return type of the GPDB function - * @tparam Arg0 C++ type of 1st argument of the GPDB function - * @tparam Arg1 C++ type of 2nd argument of the GPDB function - * - **/ -template -class PGIRBuilderFuncGenerator - : public PGFuncGeneratorInterface { - public: - // Type of the IRBuilder function we need to call - using IRBuilderFuncPtrType = llvm::Value* (llvm::IRBuilder<>::*) ( - llvm::Value*, llvm::Value*, const llvm::Twine&); - - /** - * @brief Constructor. - * - * @param pg_func_oid Greenplum function Oid - * @param pg_func_name Greenplum function name - * @param mem_func_ptr IRBuilder member function pointer - * @param is_strict Records if built-in function is strict - **/ - PGIRBuilderFuncGenerator(int pg_func_oid, - const std::string& pg_func_name, - IRBuilderFuncPtrType mem_func_ptr, - bool is_strict) - : pg_func_oid_(pg_func_oid), - pg_func_name_(pg_func_name), - func_ptr_(mem_func_ptr), - is_strict_(is_strict) { - } - - std::string GetName() final { - return pg_func_name_; - } - - size_t GetTotalArgCount() final { - // Support Binary IRBuilder functions only - return 2; - } - - bool IsStrict() final { - return is_strict_; - } - - bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) final { - assert(nullptr != llvm_out_value); - assert(nullptr != codegen_utils); - std::vector llvm_preproc_args; - - if (!pg_func_generator_detail::PGFuncArgPreProcessor:: - PreProcessArgs( - codegen_utils, pg_func_info.llvm_args, &llvm_preproc_args)) { - return false; - } - - llvm::IRBuilder<>* irb = codegen_utils->ir_builder(); - - if (!IsStrict()) { - // We do not support non-strict PGIRBuilderFuncGenerator - elog(DEBUG1, "Non-strict PGIRBuilderFuncGenerators are not supported"); - return false; - } - - // Block that will be the entry point of the implementation of argument - // checks for strict built-in functions. - llvm::BasicBlock* strict_logic_entry_block = codegen_utils-> - CreateBasicBlock("strict_logic_entry_block", - pg_func_info.llvm_main_func); - // Block for generating the code of the function. - llvm::BasicBlock* generate_function_block = codegen_utils-> - CreateBasicBlock("generate_function_block", - pg_func_info.llvm_main_func); - // Block that returns (Datum) 0 if there is NULL argument. - llvm::BasicBlock* null_argument_block = codegen_utils->CreateBasicBlock( - "null_argument_block", pg_func_info.llvm_main_func); - // Block that receives values for llvm_out_value using a phi node that - // has incoming edges from the previous two blocks - llvm::BasicBlock* set_llvm_out_value_block = codegen_utils-> - CreateBasicBlock("PGGenericFuncGenerator_set_llvm_out_value_block", - pg_func_info.llvm_main_func); - irb->CreateBr(strict_logic_entry_block); - - // strict_logic_block - // -------------------- - // Checks if there is a NULL argument. If yes then go to - // null_argument_block; generate_function_block otherwise. - bool isGenerated = GenerateStrictLogic(codegen_utils, pg_func_info, - 0 /* examine all arguments*/, - strict_logic_entry_block, - null_argument_block, - generate_function_block); - if (!isGenerated) { - elog(DEBUG1, "strict_logic_block generation failed"); - return false; - } - - // generate_function_block - // ----------------------- - // Generate code for the built-in function - irb->SetInsertPoint(generate_function_block); - // Pointer to the temporary value of llvm_out_value after built-in - // function's execution - llvm::Value* llvm_func_tmp_value = nullptr; - // Generate code for the built-in function - llvm_func_tmp_value = (irb->*this->func_ptr_)(llvm_preproc_args[0], - llvm_preproc_args[1], ""); - // Keep track of the last created block during execution of the built-in - // function. This will be used as an incoming edge to the phi node. - llvm::BasicBlock* func_generation_last_block = irb->GetInsertBlock(); - assert(llvm_func_tmp_value->getType() == - codegen_utils->GetType()); - // Explicitly set isnull to false so that we can cover the case that isnull - // has not been initialized to false. This is useful because in codegen - // we do not use temporary struct fcinfo. - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_isnull_ptr); - irb->CreateBr(set_llvm_out_value_block); - - // null_argument_block - // ------------------- - // Actions to be takes when there is a null argument - irb->SetInsertPoint(null_argument_block); - // *isNull = true; - irb->CreateStore( - codegen_utils->GetConstant(true), - llvm_isnull_ptr); - llvm::Value* llvm_zero = codegen_utils->GetConstant(0); - irb->CreateBr(set_llvm_out_value_block); - - // set_llvm_out_value_block - // ------------------------ - // Create the phi node and set the value of llvm_out_value accordingly - irb->SetInsertPoint(set_llvm_out_value_block); - llvm::PHINode* llvm_out_value_phinode = irb->CreatePHI( - codegen_utils->GetType(), 2); - llvm_out_value_phinode->addIncoming(llvm_zero, - null_argument_block); - llvm_out_value_phinode->addIncoming(llvm_func_tmp_value, - func_generation_last_block); - *llvm_out_value = llvm_out_value_phinode; - - return true; - } - - private: - int pg_func_oid_; - std::string pg_func_name_; - IRBuilderFuncPtrType func_ptr_; - bool is_strict_; -}; - -/** - * @brief Object that uses a static member function to generate code for - * given Greenplum's function - * - * @tparam FuncPtrType Function type that generate code - * @tparam Args Argument CppTypes - * - **/ -template -class PGGenericFuncGenerator : public PGFuncGeneratorInterface { - public: - /** - * @brief Constructor. - * - * @param pg_func_oid Greenplum function Oid - * @param pg_func_name Greenplum function name - * @param func_ptr function pointer to static function - * @param check_null_func_ptr function pointer to static function that - * generates code for nullity checks - * @param is_strict Records if built-in function is strict - **/ - PGGenericFuncGenerator(int pg_func_oid, - const std::string& pg_func_name, - PGFuncGeneratorFn func_ptr, - PGCheckNullFuncGeneratorFn check_null_func_ptr, - bool is_strict) - : pg_func_oid_(pg_func_oid), - pg_func_name_(pg_func_name), - func_ptr_(func_ptr), - check_null_func_ptr_(check_null_func_ptr), - is_strict_(is_strict) { - } - - std::string GetName() final { - return pg_func_name_; - } - - size_t GetTotalArgCount() final { - return sizeof...(Args); - } - - bool IsStrict() final { - return is_strict_; - } - - bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) final { - assert(nullptr != codegen_utils); - assert(nullptr != llvm_out_value); - auto irb = codegen_utils->ir_builder(); - std::vector llvm_preproc_args; - - if (!pg_func_generator_detail::PGFuncArgPreProcessor:: - PreProcessArgs( - codegen_utils, pg_func_info.llvm_args, &llvm_preproc_args)) { - return false; - } - - PGFuncGeneratorInfo pg_processed_func_info(pg_func_info.llvm_main_func, - pg_func_info.llvm_error_block, - llvm_preproc_args, - pg_func_info.llvm_args_isNull); - - // Block that is the entry point to built-in function's generated code - llvm::BasicBlock* generate_function_block = codegen_utils-> - CreateBasicBlock("PGGenericFuncGenerator_generate_function_block", - pg_func_info.llvm_main_func); - // Block that receives values for llvm_out_value using a phi node that - // has incoming edges from the previous two blocks - llvm::BasicBlock* set_llvm_out_value_block = codegen_utils-> - CreateBasicBlock("PGGenericFuncGenerator_set_llvm_out_value_block", - pg_func_info.llvm_main_func); - // Block that implements the required actions if there is a null argument. - // The implementation depends if function is strict. - llvm::BasicBlock* null_argument_block = nullptr; - // It contains the value of llvm_out_value when there is a null attribute. - llvm::Value* llvm_check_null_tmp_value = nullptr; - - // In both cases we create blocks that implement nullity checks. - // The generation of functions's code along with the set of llvm_out - // value happens after the if...else statements for both scenarios. - if (!IsStrict()) { - if (nullptr != check_null_func_ptr_) { - // Block that is the entry point to generated code that examine if there - // are NULL arguments - llvm::BasicBlock* non_strict_logic_entry_block = codegen_utils-> - CreateBasicBlock("non_strict_logic_entry_block", - pg_func_info.llvm_main_func); - irb->CreateBr(non_strict_logic_entry_block); - - // non_strict_logic_entry_block - // ------------------------- - // Generate check for NULL arguments logic - irb->SetInsertPoint(non_strict_logic_entry_block); - // This variable will tell us if we set the llvm_out_value during - // the check for NULL attributes. - llvm::Value* llvm_is_set_ptr = irb->CreateAlloca( - codegen_utils->GetType(), nullptr, "llvm_is_set_ptr"); - // Initially, set it to false - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_is_set_ptr); - // Pointer to temporary value of llvm_out_value after check for NULLs - llvm::Value* llvm_check_null_value_ptr = irb->CreateAlloca( - codegen_utils->GetType(), nullptr, - "llvm_check_null_value_ptr"); - // Invoke CheckNulls function - this->check_null_func_ptr_(codegen_utils, - pg_processed_func_info, - llvm_check_null_value_ptr, - llvm_is_set_ptr, - llvm_isnull_ptr); - // Keep track of the last created block during the check for NULL - // attributes. This will be used as an incoming edge to the phi node. - null_argument_block = irb->GetInsertBlock(); - // Temporary value of llvm_out_value after check for NULLs - llvm_check_null_tmp_value = - irb->CreateLoad(llvm_check_null_value_ptr); - assert(llvm_check_null_tmp_value->getType() == - codegen_utils->GetType()); - irb->CreateCondBr(irb->CreateLoad(llvm_is_set_ptr), - set_llvm_out_value_block /* true */, - generate_function_block /* false */); - } else { - // check_null_func_ptr_ is null, so we do not need to generate logic - // for examining if there are null arguments. - irb->CreateBr(generate_function_block); - } - } else { - // Block that will be the entry point of the implementation of argument - // checks for strict built-in functions. - llvm::BasicBlock* strict_logic_entry_block = codegen_utils-> - CreateBasicBlock("strict_logic_entry_block", - pg_func_info.llvm_main_func); - // Block that returns (Datum) 0 if there is NULL argument. - null_argument_block = codegen_utils->CreateBasicBlock( - "null_argument_block", pg_func_info.llvm_main_func); - irb->CreateBr(strict_logic_entry_block); - - // strict_logic_block - // -------------------- - // Checks if there is a NULL argument. If yes then go to - // null_argument_block; generate_function_block otherwise. - bool isGenerated = GenerateStrictLogic(codegen_utils, pg_func_info, - 0 /* examine all arguments*/, - strict_logic_entry_block, - null_argument_block, - generate_function_block); - if (!isGenerated) { - elog(DEBUG1, "strict_logic_block generation failed"); - return false; - } - - // null_argument_block - // ------------------- - // Actions to be taken when there is a null argument - irb->SetInsertPoint(null_argument_block); - // *isNull = true; - irb->CreateStore( - codegen_utils->GetConstant(true), - llvm_isnull_ptr); - llvm_check_null_tmp_value = codegen_utils->GetConstant(0); - irb->CreateBr(set_llvm_out_value_block); - } - - // generate_function_block - // ----------------------- - // Generate code for the built-in function - irb->SetInsertPoint(generate_function_block); - // Pointer to the temporary value of llvm_out_value after built-in - // function's execution - llvm::Value* llvm_func_generation_tmp_value = nullptr; - // Generate code for the built-in function - this->func_ptr_(codegen_utils, - pg_processed_func_info, - &llvm_func_generation_tmp_value); - // Keep track of the last created block during execution of the built-in - // function. This will be used as an incoming edge to the phi node. - llvm::BasicBlock* func_generation_last_block = irb->GetInsertBlock(); - assert(llvm_func_generation_tmp_value->getType() == - codegen_utils->GetType()); - // Explicitly set isnull to false so that we can cover the case that isnull - // has not been initialized to false. This is useful because in codegen - // we do not use temporary struct fcinfo. - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_isnull_ptr); - irb->CreateBr(set_llvm_out_value_block); - - // set_llvm_out_value_block - // ------------------------ - // Create the phi node and set the value of llvm_out_value accordingly - irb->SetInsertPoint(set_llvm_out_value_block); - llvm::PHINode* llvm_out_value_phinode; - if (IsStrict() || nullptr != check_null_func_ptr_) { - // we do not generate null_argument_block for non-strict functions - // where check_null_func_ptr_ == nullptr - llvm_out_value_phinode = irb->CreatePHI( - codegen_utils->GetType(), 2); - llvm_out_value_phinode->addIncoming(llvm_check_null_tmp_value, - null_argument_block); - } else { - llvm_out_value_phinode = irb->CreatePHI( - codegen_utils->GetType(), 1); - } - llvm_out_value_phinode->addIncoming(llvm_func_generation_tmp_value, - func_generation_last_block); - *llvm_out_value = llvm_out_value_phinode; - return true; - } - - private: - int pg_func_oid_; - const std::string& pg_func_name_; - PGFuncGeneratorFn func_ptr_; - PGCheckNullFuncGeneratorFn check_null_func_ptr_; - bool is_strict_; -}; - - - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_PG_FUNC_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/pg_func_generator_interface.h b/src/backend/codegen/include/codegen/pg_func_generator_interface.h deleted file mode 100644 index d1825a98a0d753bf9922b54e549d6be44be3ae54..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/pg_func_generator_interface.h +++ /dev/null @@ -1,100 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_func_generator_interface.h -// -// @doc: -// Interface for all Greenplum Function generator. -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_PG_FUNC_GENERATOR_INTERFACE_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_PG_FUNC_GENERATOR_INTERFACE_H_ - -#include -#include -#include - -#include "llvm/IR/Value.h" - -#include "codegen/utils/gp_codegen_utils.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Object that holds the information needed for generating the builtin - * postgres functions. - * - **/ -struct PGFuncGeneratorInfo { - // Convenience members for code generation - llvm::Function* llvm_main_func; - llvm::BasicBlock* llvm_error_block; - - // llvm arguments for the function. - // This can be updated while generating the code. - std::vector llvm_args; - - // Store whether arguments are NULL or not. - // This can be updated while generating the code. - std::vector llvm_args_isNull; - - PGFuncGeneratorInfo( - llvm::Function* llvm_main_func, - llvm::BasicBlock* llvm_error_block, - const std::vector& llvm_args, - const std::vector& llvm_args_isNull) : - llvm_main_func(llvm_main_func), - llvm_error_block(llvm_error_block), - llvm_args(llvm_args), - llvm_args_isNull(llvm_args_isNull) { - } -}; - -/** - * @brief Interface for all code generators. - **/ -class PGFuncGeneratorInterface { - public: - virtual ~PGFuncGeneratorInterface() = default; - - /** - * @return Greenplum function name for which it generate code - **/ - virtual std::string GetName() = 0; - - /** - * @return Total number of arguments for the function. - **/ - virtual size_t GetTotalArgCount() = 0; - - /** - * @return True if function is strict; false otherwise. - */ - virtual bool IsStrict() = 0; - - /** - * @brief Generate the code for Greenplum function. - * - * @param codegen_utils Utility to easy code generation. - * @param pg_gen_info Information needed for generating the function. - * @param llvm_out_value Store the results of function - * @param llvm_isnull_ptr Set to true if result is null - * - * @return true when it generated successfully otherwise it return false. - **/ - virtual bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) = 0; -}; - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_PG_FUNC_GENERATOR_INTERFACE_H_ diff --git a/src/backend/codegen/include/codegen/pg_numeric_func_generator.h b/src/backend/codegen/include/codegen/pg_numeric_func_generator.h deleted file mode 100644 index 6bf8fa4ab44b28a90071e535ac320de7786c4e78..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/pg_numeric_func_generator.h +++ /dev/null @@ -1,162 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_numeric_func_generator.h -// -// @doc: -// Base class for numeric functions to generate code -// -//--------------------------------------------------------------------------- - -#ifndef GPDB_PG_NUMERIC_FUNC_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPDB_PG_NUMERIC_FUNC_GENERATOR_H_ - -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/pg_func_generator.h" -#include "codegen/pg_func_generator_interface.h" - -#include "llvm/IR/Constant.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Value.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "c.h" // NOLINT(build/include) -#include "utils/numeric.h" -} - -namespace llvm { -class Value; -} - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -class GpCodegenUtils; -struct PGFuncGeneratorInfo; - -/** - * @brief Class with Static member function to generate code for numeric - * operators. - **/ - -class PGNumericFuncGenerator { - public: - /** - * @brief Create LLVM instructions for intfloat_avg_accum_decum function, - * which is called by int8_avg_accum and float8_avg_accum - * built-in functions. Only accum case has been implemented. - * - * @tparam CType Data type of input argument. - * @param codegen_utils Utility for easy code generation. - * @param pg_func_info Details for pgfunc generation - * @param llvm_out_value Variable to keep the result - * - * @return true if generation was successful otherwise return false. - **/ - template - static bool GenerateIntFloatAvgAccum( - gpcodegen::GpCodegenUtils* codegen_utils, - const gpcodegen::PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value); - - /** - * @brief Create LLVM instructions for intfloat_avg_amalg_demalg function, - * which is called by int8_avg_amalg and float8_avg_amalg - * built-in functions. Only amalg case has been implemented. - * - * @param codegen_utils Utility for easy code generation. - * @param pg_func_info Details for pgfunc generation - * @param llvm_out_value Variable to keep the result - * - * @return true if generation was successful otherwise return false. - **/ - static bool GenerateIntFloatAvgAmalg( - gpcodegen::GpCodegenUtils* codegen_utils, - const gpcodegen::PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value); - - private: - /** - * @brief A helper function that creates LLVM instructions that check if a - * pointer points to a memory chunk that has a given size. - * - * @param codegen_utils Utility for easy code generation. - * @param llvm_ptr Pointer to the memory chunk - * @param llvm_size Expected size - * @param llvm_out_cond Will contain the result of the condition. - * - * @return true if generation was successful otherwise return false. - **/ - static bool GenerateVarlenSizeCheck(gpcodegen::GpCodegenUtils* codegen_utils, - llvm::Value* llvm_ptr, - llvm::Value* llvm_size, - llvm::Value** llvm_out_cond); - - /** - * @brief A helper function that creates LLVM instructions which implement - * the logic of the first `if` statement in intfloat_avg_accum_decum - * and intfloat_avg_amalg_demalg. - * - * @param codegen_utils Utility for easy code generation. - * @param llvm_in_transdata_ptr Pointer to transdata llvm value - * @param llvm_out_trandata_ptr If llvm_in_transdata_ptr is not valid, - * then llvm_out_trandata_ptr will point to - * a valid transdata llvm value. - * - * @return true if generation was successful otherwise return false. - **/ - static bool GeneratePallocTransdata(gpcodegen::GpCodegenUtils* codegen_utils, - llvm::Value* llvm_in_transdata_ptr, - llvm::Value** llvm_out_trandata_ptr); -}; - -template -bool PGNumericFuncGenerator::GenerateIntFloatAvgAccum( - gpcodegen::GpCodegenUtils* codegen_utils, - const gpcodegen::PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - // TODO(nikos): Can we figure if we need to detoast during generation? - llvm::Function* llvm_pg_detoast_datum = codegen_utils-> - GetOrRegisterExternalFunction(pg_detoast_datum, "pg_detoast_datum"); - - auto irb = codegen_utils->ir_builder(); - - llvm::Value* llvm_in_transdata_ptr = - irb->CreateCall(llvm_pg_detoast_datum, {pg_func_info.llvm_args[0]}); - llvm::Value* llvm_newval = codegen_utils->CreateCast( - pg_func_info.llvm_args[1]); - - // if(transdata == NULL || - // VARSIZE(transdata) != sizeof(IntFloatAvgTransdata)) { ... } {{ - llvm::Value* llvm_transdata_ptr = nullptr; - GeneratePallocTransdata(codegen_utils, - llvm_in_transdata_ptr, - &llvm_transdata_ptr); - // }} - // ++transdata->count; - // transdata->sum += newval; {{ - llvm::Value* llvm_transdata_sum_ptr = codegen_utils->GetPointerToMember( - llvm_transdata_ptr, &IntFloatAvgTransdata::sum); - llvm::Value* llvm_transdata_count_ptr = codegen_utils->GetPointerToMember( - llvm_transdata_ptr, &IntFloatAvgTransdata::count); - irb->CreateStore( - irb->CreateFAdd(irb->CreateLoad(llvm_transdata_sum_ptr), llvm_newval), - llvm_transdata_sum_ptr); - irb->CreateStore(irb->CreateAdd(irb->CreateLoad(llvm_transdata_count_ptr), - codegen_utils->GetConstant(1)), - llvm_transdata_count_ptr); - // }} - *llvm_out_value = llvm_transdata_ptr; - return true; -} - -/** @} */ -} // namespace gpcodegen - -#endif // GPDB_PG_NUMERIC_FUNC_GENERATOR_H_ diff --git a/src/backend/codegen/include/codegen/slot_getattr_codegen.h b/src/backend/codegen/include/codegen/slot_getattr_codegen.h deleted file mode 100644 index aa16bfd7bad8061577bbcf524c7ddb7e70a94d0c..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/slot_getattr_codegen.h +++ /dev/null @@ -1,179 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// slot_getattr_codegen.h -// -// @doc: -// Contains slot_getattr generator -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_SLOT_GETATTR_CODEGEN_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_SLOT_GETATTR_CODEGEN_H_ - -#include -#include - -#include "codegen/codegen_wrapper.h" -#include "codegen/base_codegen.h" -#include "codegen/utils/gp_codegen_utils.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "utils/elog.h" -#include "access/htup.h" -#include "nodes/execnodes.h" -#include "executor/tuptable.h" - -} - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -class SlotGetAttrCodegen : public BaseCodegen { - public: - /** - * @brief Request code generation for the codepath slot_getattr > - * _slot_getsomeattr > slot_deform_tuple for the given slot and max_attr - * - * @param codegen_utils Utilities for easy code generation - * @param slot Use the TupleDesc from this slot to generate - * @param max_attr Generate slot deformation up to this many attributes - * @param out_func Return the llvm::Function that will be generated - * - * @note This method does not actually do any code generation, but simply - * caches the information necessary for code generation when - * GenerateSlotGetAttr() is called. This way we can de-duplicate multiple - * requests for multiple slot_getattr() implementation producing only one per - * slot. - * - **/ - static SlotGetAttrCodegen* GetCodegenInstance( - gpcodegen::CodegenManager* manager, - TupleTableSlot* slot, - int max_attr); - - virtual ~SlotGetAttrCodegen(); - - /** - * @brief Generate code for the codepath slot_getattr > _slot_getsomeattr > - * slot_deform_tuple for the given slot and max_attr - * - * @param codegen_utils Utilities for easy code generation - * - * Based on parameters given to SlotGetAttr::GetCodegenInstance(), the maximum - * max_attr is tracked for each slot. Then for each slot, we generate code for - * slot_getattr() that deforms tuples in that slot up to the maximum max_attr. - * - * @note This is a wrapper around GenerateSlotGetAttrInternal that handles the - * case when generation fails, and cleans up a possible broken function by - * removing it from the module. - * - * TODO(shardikar, krajaraman) Remove this wrapper after a shared code - * generation framework implementation is complete. - */ - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) override; - - /* - * @return A pointer to the yet un-compiled llvm::Function that will be - * generated and populate by this module - * - * @note This is initialized as NULL when on creation. Only if after code - * generation is performed by calling SlotGetAttrCodegen::GenerateCode and is - * successful, can this be assumed return a valid llvm::Function* - */ - llvm::Function* GetGeneratedFunction() { - return llvm_function_; - } - - private: - /** - * @brief Constructor for SlotGetAttrCodegen - * - * @note The call to the constructor of BaseCodegen passes in a pointer to a - * dummy SlotGetAttrFn stored in this class. Thus SlotGetAttrCodegen can - * inherit all the functionality of BaseCodegen, including the pointer - * swapping logic, with low risk. However the pointer swapping will be of no - * consequence. - */ - SlotGetAttrCodegen(gpcodegen::CodegenManager* manager, - TupleTableSlot* slot, - int max_attr) - : BaseCodegen( - manager, kSlotGetAttrPrefix, slot_getattr_regular, &dummy_func_), - slot_(slot), - max_attr_(max_attr), - llvm_function_(nullptr) { - } - - /** - * @brief Generate code for the codepath slot_getattr > _slot_getsomeattr > - * slot_deform_tuple - * - * @param codegen_utils Utilities for easy code generation - * @param slot Use the TupleDesc from this slot to generate - * @param max_attr Generate slot deformation up to this many attributes - * @param out_func Return the llvm::Function that will be generated - * - * @return true on success generation; false otherwise - * - * @note Generate code for code path slot_getattr > * _slot_getsomeattrs > - * slot_deform_tuple. slot_getattr() will eventually call slot_deform_tuple - * (through _slot_getsomeattrs), which fetches all yet unread attributes of - * the slot until the given attribute. - * - * This implementation does not support: - * (1) Attributes passed by reference - * - * If at execution time, we see any of the above types of attributes, - * we fall backs to the regular function. - **/ - bool GenerateSlotGetAttr( - gpcodegen::GpCodegenUtils* codegen_utils, - TupleTableSlot* slot, - int max_attr, - llvm::Function* out_func); - - /** - * @brief Removes the entry of this SlotGetAttrCodegen from the static cache. - */ - void RemoveSelfFromCache(); - - TupleTableSlot* slot_; - // Max attribute to deform to - int max_attr_; - // Primary function to be generated and populated - llvm::Function* llvm_function_; - // A dummy function pointer that can be swapped by the BaseCodegen - // implementation - SlotGetAttrFn dummy_func_; - - static constexpr char kSlotGetAttrPrefix[] = "slot_getattr"; - /** - * Utility map indexed by CodegenManager pointer to track all instances of - * SlotGetAttrCodegen created by SlotGetAttrCodegen::GetCodegenInstance. - * Each entry of the utility map contains another map keyed on the de-duplication key - * used by SlotGetAttrCodegen::GetCodegenInstance i.e slot. - * - * Entries are inserted into this map on creation of SlotGetAttrCodegen objects, and - * removed on destruction. When an inner-map for some manager becomes empty - * (when the manager itself is destroyed), the entry for that manager is - * erased from the outer-map as well - * - * Map of the form: { manager -> { slot -> slot_getattr_codegen } } - */ - typedef std::unordered_map - SlotGetAttrCodegenCache; - static std::unordered_map - codegen_cache_by_manager; -}; - -/** @} */ - -} // namespace gpcodegen -#endif // GPCODEGEN_SLOT_GETATTR_CODEGEN_H_ diff --git a/src/backend/codegen/include/codegen/utils/annotated_type.h b/src/backend/codegen/include/codegen/utils/annotated_type.h deleted file mode 100644 index 5876c5cc2e273e1600d7f1d9f6a7a3c5283b8f81..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/annotated_type.h +++ /dev/null @@ -1,165 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// annotated_type.h -// -// @doc: -// Augments an llvm::Type with additional information about C++ type -// properties that are not captured by the LLVM type system. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_ANNOTATED_TYPE_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_ANNOTATED_TYPE_H_ - -#include -#include -#include - -#include "codegen/utils/annotated_type_detail.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Type.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Augments an llvm::Type with additional information about C++ type - * properties that are not captured by the LLVM type system. - **/ -struct AnnotatedType { - /** - * @brief Create an AnnotatedType to represent the base scalar type T (not - * a pointer or reference type), automatically inferring its - * properties. - * - * @tparam T A C++ scalar type. - * @param llvm_type The type's representation in LLVM, obtained by calling - * CodegenUtils::GetType() or similar. - * @return An AnnotatedType that includes additional information about T not - * captured by the llvm::Type. - **/ - template - static AnnotatedType CreateScalar(llvm::Type* llvm_type) { - return AnnotatedType{ - llvm_type, - false, // is_voidptr - false, // is_reference - annotated_type_detail::IsUnsignedAdapter::value, - annotated_type_detail::IsLong::value, - annotated_type_detail::IsLongLong::value, - {std::is_const::value}, - {std::is_volatile::value}}; - } - - /** - * @brief Create an AnnotatedType to represent a void pointer type T - * (optionally with const and/or volatile qualifiers). - * - * @tparam T A C++ void pointer type with any combination of const and/or - * volatile qualifiers. - * @param llvm_type The type's representation in LLVM, obtained by calling - * CodegenUtils::GetType() or similar. - * @return An AnnotatedType that includes additional information about the - * void pointer type T not captured by the llvm::Type. - **/ - template - static AnnotatedType CreateVoidPtr(llvm::Type* llvm_type) { - return AnnotatedType{ - llvm_type, - true, // is_voidptr - false, // is_reference - false, // is_unsigned - false, // is_long - false, // is_long_long - {std::is_const::type>::value, - std::is_const::value}, - {std::is_volatile::type>::value, - std::is_volatile::value}}; - } - - // Value semantics. - AnnotatedType(const AnnotatedType&) = default; - AnnotatedType(AnnotatedType&&) = default; - - AnnotatedType& operator=(const AnnotatedType&) = default; - AnnotatedType& operator=(AnnotatedType&&) = default; - - bool operator==(const AnnotatedType& other) const { - return (llvm_type == other.llvm_type) - && (is_voidptr == other.is_voidptr) - && (is_reference == other.is_reference) - && (explicitly_unsigned == other.explicitly_unsigned) - && (is_long == other.is_long) - && (is_long_long == other.is_long_long) - && (is_const.size() == other.is_const.size()) - && (is_volatile.size() == other.is_volatile.size()) - && (std::equal(is_const.begin(), - is_const.end(), - other.is_const.begin())) - && (std::equal(is_volatile.begin(), - is_volatile.end(), - other.is_volatile.begin())); - } - - /** - * @brief Modify an AnnotatedType in place, adding a pointer to it. CV - * qualifiers of the pointer are autodetected from the template - * parameter T. - **/ - template - AnnotatedType& AddPointer() { - llvm_type = llvm_type->getPointerTo(); - is_const.push_back(std::is_const::value); - is_volatile.push_back(std::is_volatile::value); - return *this; - } - - /** - * @brief Convert the outermost pointer in this AnnotatedType to a reference. - **/ - AnnotatedType& ConvertToReference() { - is_reference = true; - return *this; - } - - // Type's representation in LLVM; - llvm::Type* llvm_type; - - // Is this an untyped void* instead of char*? - bool is_voidptr : 1; - - // Is the outermost pointer actually a C++ reference? - bool is_reference : 1; - - // Does the innermost scalar type have an explicit unsigned qualifier? - bool explicitly_unsigned : 1; - - // Is the innermost scalar type the builtin type "long" (which may be 32 - // or 64 bits depending on architecture)? - bool is_long: 1; - - // Is the innermost scalar type the builtin type "long long"? - bool is_long_long: 1; - - // Capture CV-qualifiers for chains of pointers/references. Element 0 is the - // base scalar type, element 1 is the pointer to that type, element 2 is - // pointer-to-pointer, etc. - std::vector is_const; - std::vector is_volatile; -}; - -/** @} */ - -} // namespace gpcodegen - -#endif // GPCODEGEN_ANNOTATED_TYPE_H_ -// EOF diff --git a/src/backend/codegen/include/codegen/utils/annotated_type_detail.h b/src/backend/codegen/include/codegen/utils/annotated_type_detail.h deleted file mode 100644 index 4f35b88556c22fa2fc16f11e1423fe5488020fcc..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/annotated_type_detail.h +++ /dev/null @@ -1,116 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// annotated_type_detail.h -// -// @doc: -// Additional type_traits-style templates used to implement AnnotatedType are -// contained in this nested detail namespace. They are not considered part of -// the public API. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_ANNOTATED_TYPE_DETAIL_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_ANNOTATED_TYPE_DETAIL_H_ - -#include - -namespace gpcodegen { - -// Additional type_traits-style templates used to implement AnnotatedType are -// contained in this nested detail namespace. They are not considered part of -// the public API. -namespace annotated_type_detail { - -// Adapter for std::is_unsigned with some additional specializations. Detects -// the signedness of the underlying type for enums, and does NOT consider bool -// to be unsigned (although bool is an unsigned type, it is illegal to use it -// with the 'unsigned' qualifier keyword). -template -struct IsUnsignedAdapter - : std::integral_constant::value> {}; - -template -struct IsUnsignedAdapter< - EnumT, - typename std::enable_if::value>::type> - : IsUnsignedAdapter::type> {}; - -template -struct IsUnsignedAdapter< - BoolT, - typename std::enable_if< - std::is_same::type>::value>::type> - : std::integral_constant {}; - - -// Traits template that strips away "const", "volatile", and "unsigned" -// qualifiers from a scalar type. Also has a partial specialization for enums -// that converts the enum to its underlying integral type and does the same -// transformations. -template -struct BaseScalar { - typedef typename std::remove_cv::type type; -}; - -// Explicit specializations for bool with different CV qualifiers. We must -// explicitly specialize for bool because std::make_signed (used in the -// general integer case) is an incomplete type. -template <> -struct BaseScalar { - typedef bool type; -}; - -template <> -struct BaseScalar { - typedef bool type; -}; - -template <> -struct BaseScalar { - typedef bool type; -}; - -template <> -struct BaseScalar { - typedef bool type; -}; - -// Partial specialization for integer types. -template -struct BaseScalar< - IntegralT, - typename std::enable_if::value>::type> { - typedef typename std::make_signed< - typename std::remove_cv::type>::type type; -}; - -// Partial specialization for enums, which maps to the underlying integer type. -template -struct BaseScalar::value>::type> - : BaseScalar::type> {}; - -// Detect if T (or the underlying type of T, if T is an enum), stripped of CV -// and unsigned qualifiers, is the same as the built-in type long. -template -using IsLong = std::is_same::type>; - -// Detect if T (or the underlying type of T, if T is an enum), stripped of CV -// and unsigned qualifiers, is the same as the built-in type long long. -template -using IsLongLong = std::is_same::type>; - -} // namespace annotated_type_detail -} // namespace gpcodegen - -#endif // GPCODEGEN_ANNOTATED_TYPE_DETAIL_H_ -// EOF diff --git a/src/backend/codegen/include/codegen/utils/clang_compiler.h b/src/backend/codegen/include/codegen/utils/clang_compiler.h deleted file mode 100644 index b96a5b575817cfe91594349cef7137c0f90a50fd..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/clang_compiler.h +++ /dev/null @@ -1,318 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// clang_compiler.h -// -// @doc: -// Tool for compiling in-memory C++ source code snippets into LLVM -// modules which can then be codegened. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_CLANG_COMPILER_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_CLANG_COMPILER_H_ - -#include -#include -#include -#include - -#include "codegen/utils/macros.h" -#include "codegen/utils/codegen_utils.h" - -namespace llvm { class Twine; } - -namespace gpcodegen { - -struct AnnotatedType; - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Tool for compiling in-memory C++ source code snippets into LLVM - * modules which can then be codegened. - * - * ClangCompiler allows code-generation from snippets written in high-level C++ - * instead of programatically building up LLVM IR. In many cases, this will be - * more user-friendly for programmers. The basic workflow for code generation - * from C++ is as follows: - * - * 1. Create a CodegenUtils object to manage the actual compilation and - * execution. - * 2. Create a ClangCompiler object to act as front-end for compiling C++ - * source code for use with the CodegenUtils. - * 3. Build up C++ source code from in-memory strings as an llvm::Twine - * (llvm's Twine class is a string-like data type that allows for - * efficient concatenation). Note that any generated functions that we - * wish to call should be marked `extern "C"` so that they have C linkage - * and can be refered to by an ordinary non-mangled name when they are - * compiled. - * 4. Call ClangCompiler::CompileCppSource() to compile C++ source code into - * an llvm Module managed by the CodegenUtils. - * 5. Call CodegenUtils::Optimize() and CodegenUtils::PrepareForExecution() - * to compile the code and get it ready to execute. Note that we would do - * the same thing to compile generated IR code, except that it makes sense - * to use higher levels of optimization when starting from C++ sources, as - * the initial output of the clang frontend is NOT well-optimized IR. - * 6. Call CodegenUtils::GetFunctionPointer() to get a callable function - * pointer to any desired generated function. - **/ -class ClangCompiler { - public: - /** - * @brief Constructor. - * - * @param codegen_utils The CodegenUtils instance to compile with. The - * specified CodegenUtils's LLVMContext will be used, and all - * successfully-compiled modules will be inserted into the - * CodegenUtils. - **/ - explicit ClangCompiler(CodegenUtils* codegen_utils) - : code_generator_(codegen_utils) { - } - - /** - * @brief Compile a C++ source-code snippet (i.e. an in-memory translation - * unit) to an LLVM IR Module and place it in the CodegenUtils. - * - * @note It is recommended to mark any function you wish to call from outside - * of generated code as extern "C" so that its name will not be mangled - * and you can easily retrieve it by name from - * CodegenUtils::GetFunctionPointer(). - * - * @param source_code The C++ source code to compile. - * @param debug If true, generate additional debugging information and dump - * source_code to a temporary file so that generated code can be - * analyzed and inspected by a debugger like GDB or LLDB. Note that - * this requires file I/O to dump out source code, so it is a somewhat - * heavyweight operation. - * @return true if compilation was successful, false if some error occured. - **/ - bool CompileCppSource(const llvm::Twine& source_code, - const bool debug = false); - - /** - * @brief Convert an AnnotatedType to the equivalent C++ type as a snippet of - * C++ source code. - * - * @note The following caveats about type mapping apply: - * 1. AnnotatedType does not carry any information about typedefs, so the - * returned C++ type will be always be a built-in type rather than a - * typedef-ed alias. For example, if the original type was - * std::uint16_t, this method will return "unsigned short". - * 2. Enums are converted to their underlying integer representation. - * 3. Pointers and references to classes or structs are converted to - * untyped void* pointers (with the same const/volatile qualifiers). - * In particular, this means that a reference to a class/struct will - * become a void* pointer (this is the only case where a reference - * becomes a pointer, otherwise the distinction is preserved). - * - * @param annotated_type An AnnotatedType created by - * CodegenUtils::GetAnnotatedType(). - * @return The equivalent of annotated_type as a C++ type (in string form). - **/ - static std::string CppTypeFromAnnotatedType( - const AnnotatedType& annotated_type); - - /** - * @brief Generate forward-declarations for the named external functions - * registered in the CodegenUtils that this ClangCompiler is attached - * to. - * - * This allows (named) external functions registered with - * CodegenUtils::RegisterExternalFunction() to be called from generated C++ - * code. The string returned by this method can be concatenated with other - * C++ source code that calls the external function(s) and passed to - * CompileCppSource() for compilation. - * - * @return A C++ source snippet with forward declarations (marked extern "C") - * for each named external function registered in the CodegenUtils. - **/ - std::string GenerateExternalFunctionDeclarations() const; - - /** - * @brief Generate a C++ source snippet that represents a literal constant - * value. - * - * @note The same caveats about type conversions noted for - * CppTypeFromAnnotatedType() also apply to this method. - * - * @tparam CppType The type of the literal value to convert to a C++ source - * snippet (in most cases this can be automatically inferred and - * need not be literally specified). - * @param constant_value The literal value to convert to a C++ source snippet. - * @return A C++ source snippet that parses to the literal constant_value. - **/ - template - std::string GetLiteralConstant(const CppType constant_value); - - private: - CodegenUtils* code_generator_; - - DISALLOW_COPY_AND_ASSIGN(ClangCompiler); -}; - -/** @} */ - -// ---------------------------------------------------------------------------- -// Implementation of ClangCompiler::GetLiteralConstant(). - -// Helper functions and classes for GetLiteralConstant() are encapsulated in -// this nested namespace and are not considered part of the public API. -namespace clang_compiler_detail { - -// Get a representation of a double as a literal string in the C99 format for -// hexadecimal floating-point literals. This is preferable to the usual decimal -// representation because it is always precise and free of rounding errors. -std::string HexDouble(const double value); - -// ConstantPrinter has template specializations to handle different categories -// of C++ types. Specializations provide a static Print() method that takes a -// const CppType 'value' and a CodegenUtils* pointer (referring to the -// 'code_generator_' member of the calling ClangCompiler) and returns a string -// containing a C++ source snippet that parses to the original literal 'value'. -template -class ConstantPrinter { -}; - -// Explicit specialization for bool. -template <> -class ConstantPrinter { - public: - static std::string Print(const bool value, CodegenUtils* codegen_utils) { - return value ? "true" : "false"; - } -}; - -// Partial specialization for integer types other than bool. -template -class ConstantPrinter< - IntType, - typename std::enable_if::value>::type> { - public: - static std::string Print(const IntType value, CodegenUtils* codegen_utils) { - // Enclose the literal in a static_cast that transforms it to the exact type - // expected. - std::string literal("static_cast<"); - literal.append(ClangCompiler::CppTypeFromAnnotatedType( - codegen_utils->template GetAnnotatedType())); - literal.append(">("); - - literal.append(std::to_string(value)); - - // Add "u" suffix if literal is unsigned. - if (std::is_unsigned::value) { - literal.push_back('u'); - } - - // Add "l" or "ll" suffix as necessary for the inner literal to be parsed - // with the right precision. - if (sizeof(IntType) > sizeof(int)) { - literal.push_back('l'); - if (sizeof(IntType) > sizeof(long) || // NOLINT(runtime/int) - std::is_same::value) { // NOLINT(runtime/int) - literal.push_back('l'); - } - } - literal.push_back(')'); - - return literal; - } -}; - -// Explicit specialization for float. -template <> -class ConstantPrinter { - public: - static std::string Print(const float value, CodegenUtils* codegen_utils) { - // Get an unambiguously-rounded representation of the literal, then append - // an "f" to denote that the literal is a 32-bit float instead of a 64-bit - // double. - std::string literal(HexDouble(value)); - literal.push_back('f'); - return literal; - } -}; - -// Explicit specialization for double. -template <> -class ConstantPrinter { - public: - static std::string Print(const double value, CodegenUtils* codegen_utils) { - return HexDouble(value); - } -}; - -// Partial specialization for enums. Maps enums to their underlying integer -// type. -template -class ConstantPrinter< - EnumType, - typename std::enable_if::value>::type> { - public: - static std::string Print(const EnumType value, - CodegenUtils* codegen_utils) { - return ConstantPrinter::type> - ::Print(static_cast::type>( - value), - codegen_utils); - } -}; - -// Partial specialization for pointers. -template -class ConstantPrinter { - public: - static std::string Print(PointedType* const value, - CodegenUtils* codegen_utils) { - if (value == nullptr) { - std::string literal("static_cast<"); - literal.append(ClangCompiler::CppTypeFromAnnotatedType( - codegen_utils->template GetAnnotatedType())); - literal.append(">(nullptr)"); - return literal; - } else { - // Cast the literal address to the appropriate pointer type. - std::string literal("reinterpret_cast<"); - literal.append(ClangCompiler::CppTypeFromAnnotatedType( - codegen_utils->template GetAnnotatedType())); - literal.append(">("); - - // Write the literal address in integer form. - literal.append(std::to_string(reinterpret_cast(value))); - literal.append("ull)"); - return literal; - } - } -}; - -// Explicit specialization for nullptr_t. -template <> -class ConstantPrinter { - public: - static std::string Print(std::nullptr_t value, - CodegenUtils* codegen_utils) { - return "nullptr"; - } -}; - -} // namespace clang_compiler_detail - -template -std::string ClangCompiler::GetLiteralConstant(const CppType constant_value) { - return clang_compiler_detail::ConstantPrinter::Print( - constant_value, - code_generator_); -} - -} // namespace gpcodegen - -#endif // GPCODEGEN_CLANG_COMPILER_H_ -// EOF diff --git a/src/backend/codegen/include/codegen/utils/codegen_utils.h b/src/backend/codegen/include/codegen/utils/codegen_utils.h deleted file mode 100644 index 30dbcb87b1f243f19c283f4adb19a8544e1ebbf0..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/codegen_utils.h +++ /dev/null @@ -1,1731 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_utils.h -// -// @doc: -// Object that manages runtime code generation for a single LLVM module. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_CODEGEN_UTILS_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_CODEGEN_UTILS_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "codegen/utils/annotated_type.h" -#include "codegen/utils/macros.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Analysis/CallGraph.h" -#include "llvm/ExecutionEngine/ExecutionEngine.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalValue.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Value.h" -#include "llvm/Transforms/Utils/Cloning.h" - -namespace gpcodegen { - -// Forward declaration of helper class for friending purposes. -namespace codegen_utils_detail { -template class ConstantMaker; -template class FunctionTypeUnpacker; -template class ArithOpMaker; -} // namespace codegen_utils_detail - - -// Easy static check to match integer macros with enum equivalents -#define STATIC_ASSERT_OPTIMIZATION_LEVEL(ename, mname) \ - static_assert(gpcodegen::CodegenUtils::OptimizationLevel::ename == \ - gpcodegen::CodegenUtils::OptimizationLevel(mname), \ - "gpcodegen::CodegenUtils::OptimizationLevel::" #ename \ - " == " #mname) - - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Object that manages runtime code generation for a single LLVM module. - **/ -class CodegenUtils { - public: - enum class OptimizationLevel : unsigned { - kNone = 0, // -O0 - kLess, // -O1 - kDefault, // -O2 - kAggressive // -O3 - }; - - enum class SizeLevel : unsigned { - kNormal = 0, // Default - kSmall, // -Os - kTiny // -Oz - }; - - /** - * @brief Constructor. - * - * @param module_name A human-readable name for the module that this - * CodegenUtils will manage. - **/ - explicit CodegenUtils(llvm::StringRef module_name); - - virtual ~CodegenUtils() { - } - - /** - * @brief Initialize global LLVM state (in particular, information about the - * host machine which will be the target for code generation). Must be - * called at least once before creating CodegenUtils objects. - * - * @return true if initilization was successful, false if some part of - * initialization failed. - **/ - static bool InitializeGlobal(); - - /** - * @return An IRBuilder object that can be used to generate IR instructions. - **/ - llvm::IRBuilder<>* ir_builder() { - return &ir_builder_; - } - - /** - * @return The LLVM Module that is managed by this CodegenUtils, or NULL if - * PrepareForExecution() has already been called. - * - * @note When a CodegenUtils is initially created, a Module is created with - * it and it is accessible via this method. The Module is mutable and - * new code can be inserted into it UNTIL PrepareForExecution() is - * called, at which point the Module's code is "frozen" and can no - * longer be modified, and this method returns NULL. - **/ - llvm::Module* module() { - return module_.get(); - } - - /** - * @brief Get a C++ type's equivalent in the LLVM type system. - * - * @note Because of some differences between the C++ and LLVM type systems, - * some caveats about using this method apply: - * 1. LLVM's type system has no notion of const-ness, so const and/or - * volatile-qualified versions of a C++ type are treated the same as - * the base type. - * 2. In LLVM, the convention for generic "untyped" pointers is to - * represent them as i8* (a pointer to an 8-bit integer, equivalent - * to char* in C++). The method respects that convention, and - * returns the LLVM i8* type when called with CppType = void* or - * some cv-qualified version thereof. - * 3. C++ enums are converted to their underlying integer type. - * 4. C++ references are converted to pointers (this is how references - * are implemented under-the-covers in C++ anyway). - * 5. Pointers or references to classes or structs are converted to - * generic untyped pointers (i8* in LLVM, equivalent to void* in - * C++). - * - * @tparam CppType a C++ type to map to an LLVM type. - * @return A pointer to CppType's equivalent LLVM type in this - * CodegenUtils's context. - **/ - template - llvm::Type* GetType(); - - /** - * @brief Get a C++ type's equivalent in the LLVM type system with additional - * annotations to capture C++ type properties that are not reflected by - * the LLVM type. - * - * @tparam CppType A C++ type to map to an LLVM type with annotations. - * @return An AnnotatedType object that contains a pointer to CppType's - * equivalent LLVM type along with annotations capturing additional - * properties of CppType. - **/ - template - AnnotatedType GetAnnotatedType(); - - /** - * @brief Get a function's type-signature as an LLVM FunctionType. - * - * @note The ReturnType and all ArgumentTypes are converted according to the - * same rules as GetType(). - * - * @tparam ReturnType The function's return type. - * @tparam ArgumentTypes The types of any number of arguments to the function. - * @param is_var_arg Whether the function has trailing variable arguments list - * @return A pointer to the complete function type-signature's equivalent as - * an LLVM FunctionType in this CodegenUtils's context. - **/ - template - llvm::FunctionType* GetFunctionType(const bool is_var_arg = false); - - /** - * @brief Get an LLVM constant based on a C++ value. - * - * @note Unique LLVM constants are created on-demand and have the same - * lifetime as the LLVMContext that they were created in. - * @note If constant_value is an lvalue-reference, then the llvm::Constant - * produced will be based on the referent value, NOT the reference - * itself. If reference semantics are desired, then convert to a - * pointer first by taking the address of the reference with '&'. - * - * @param constant_value A C++ value to map to an LLVM constant. - * @return A pointer to an llvm::Constant object with constant_value's value - * in this CodegenUtils's context. - **/ - template - llvm::Constant* GetConstant(const CppType constant_value); - - /** - * @brief Get a pointer to a member variable in a struct (or a public member - * variable in a class), generating instructions at the IRBuilder's - * current insertion point. - * - * @note In most cases, the caller does not need to explicitly specify - * template parameters, as they can be inferred automatically based on - * pointers_to_members. - * @note This method only does address computation and returns an LLVM pointer - * to the desired member variable in a struct. It does not actually - * dereference any pointers or generate any load instructions. To obtain - * the actual value held in the member variable, simply create a load - * instruction with the returned pointer as the argument. - * - * @tparam PointerToMemberTypes Any number of pointer-to-member types of the - * form MemberType StructType::* - * @param base_ptr A pointer to an instance of a top-level struct or class to - * access into. - * @param pointers_to_members Any number of pointer-to-members of the form - * MemberType StructType::*. This allows accessing members of nested - * structs arbitrarily deep in a single call. - * @return A pointer to the member denoted by pointers_to_members in the - * struct indicated by base_ptr. - **/ - template - llvm::Value* GetPointerToMember( - llvm::Value* base_ptr, - PointerToMemberTypes&&... pointers_to_members) { - return GetPointerToMemberImpl( - base_ptr, - nullptr, // cast_type - 0, // cumulative_offset - std::forward(pointers_to_members)...); - } - - /** - * @brief Create an LLVM function in the module managed by this - * CodegenUtils. - * - * @note This method creates an empty Function object with no body. Caller - * can then create BasicBlocks inside the Function to implement its - * actual body. - * - * @tparam FuncType FunctionType. e.g ReturnType (*)(ArgumenTypes) - * - * @param name The function's name. - * @param is_var_arg Whether the function has trailing variable arguments list - * @param linkage The linkage visibility of the function. Defaults to - * ExternalLinkage, which makes the function visible and callable from - * anywhere. - * @return A pointer to a newly-created empty function in this - * CodegenUtils's module (object is owned by this CodegenUtils). - **/ - template - llvm::Function* CreateFunction( - const llvm::Twine& name, - const bool is_var_arg = false, - const llvm::GlobalValue::LinkageTypes linkage - = llvm::GlobalValue::ExternalLinkage); - - /** - * @brief Create a new BasicBlock inside a Function in this CodegenUtils's - * module. - * - * @param name The name of the BasicBlock. This is not required to be unique, - * and may be an empty string for an anonymous BasicBlock. - * @param parent The Function that the BasicBlock should be created in. The - * first BasicBlock created in a Function is that Function's entry - * point, and any subsequently created BasicBlocks are entered via - * instructions like 'br' or 'switch'. - * @return A new BasicBlock inside parent. - **/ - llvm::BasicBlock* CreateBasicBlock(const llvm::Twine& name, - llvm::Function* parent) { -#ifdef CODEGEN_DEBUG - // DEBUG-only assertion to make sure that caller does not try to generate IR - // for an external function. - assert(!parent->getName().startswith(kExternalFunctionNamePrefix)); -#endif // CODEGEN_DEBUG - return llvm::BasicBlock::Create(context_, name, parent, nullptr); - } - - /** - * @brief Use LLVM intrinsic to create Multiplication with overflow - * instruction - * - * @tparam CppType CppType for multiplication - * @param arg0 First argument - * @param arg1 Second argument - * - * @return LLVM Value as a pair of results and overflow flag. - **/ - template - llvm::Value* CreateMulOverflow(llvm::Value* arg0, llvm::Value* arg1); - - /** - * @brief Use LLVM intrinsic to create Add with overflow - * instruction - * - * @tparam CppType CppType for add - * @param arg0 First argument - * @param arg1 Second argument - * - * @return LLVM Value as a pair of results and overflow flag. - **/ - template - llvm::Value* CreateAddOverflow(llvm::Value* arg0, llvm::Value* arg1); - - /** - * @brief Use LLVM intrinsic to increase the value of a variable by one - * with overflow instruction - * - * @tparam CppType CppType for add - * @param arg Input variable - * - * @return LLVM Value as a pair of results and overflow flag. - **/ - template - llvm::Value* CreateIncOverflow(llvm::Value* arg); - - /** - * @brief Use LLVM intrinsic to create Subtract with overflow - * instruction - * - * @tparam CppType CppType for subtract - * @param arg0 First argument - * @param arg1 Second argument - * - * @return LLVM Value as a pair of results and overflow flag. - **/ - template - llvm::Value* CreateSubOverflow(llvm::Value* arg0, llvm::Value* arg1); - - /** - * @brief Cast given llvm::value to DestType. - * - * @tparam DestType Destination type - * @tparam SrcType Source type - * @param value Value that needs to be casted. Expected to be of same type as - * SrcType - * - * @return LLVM Value of DestType - **/ - template - llvm::Value* CreateCast(llvm::Value* value); - - /** - * @brief Similar to tuple in C++, this helps to create fixed-size collection - * of hetrogeneous values. - * - * @param members Different types of values that needs to be in the - * collection - * @param name Optional arguments to specify the name of the returned - * variable in IR - * - * @return Struct object allocated in stack, that contains all members. - **/ - llvm::AllocaInst* CreateMakeTuple(const std::vector& members, - const std::string& name = ""); - - /** - * @brief Register an external function if previously unregistered. Otherwise - * return a pointer to the previously registered llvm::Function - * - * @warning This method returns a pointer to an llvm::Function object. The - * caller should NOT attempt to add BasicBlocks to the function, as - * that would cause conflicts when mapping the function to its - * external implementation during PrepareForExecution(). - * - * @tparam ReturnType The return type of the external_function. This does not - * need to be specified if external_function is not overloaded (it - * will be inferred automatically). - * @tparam argument_types The types of the arguments to external_function. - * These do not need to be specified if external_function is not - * overloaded (they will be inferred automatically). - * @param external_function A function pointer to install for use in this - * CodegenUtils. - * @param name An optional name to refer to the external function by. If - * non-empty, this CodegenUtils will record additional information - * so that the registered function will also be callable by its name - * in C++ source code compiled by ClangCompiler (see - * ClangCompiler::GenerateExternalFunctionDeclarations()). - * @param is_var_arg Whether the function has trailing variable arguments list - * @return A callable LLVM function. - */ - template - llvm::Function* GetOrRegisterExternalFunction( - ReturnType (*external_function)(ArgumentTypes...), - const std::string& name = "", - const bool is_var_arg = false) { - std::unordered_map::iterator it; - bool key_absent; - - std::tie(it, key_absent) = external_functions_.emplace( - reinterpret_cast(external_function), - name.empty() ? GenerateExternalFunctionName() - : name); - - if (!key_absent) { - return module()->getFunction(it->second); - } else { - if (!name.empty()) { - RecordNamedExternalFunction(name); - } - return CreateFunctionImpl( - it->second, is_var_arg); - } - } - - template - llvm::Function* GetOrRegisterExternalFunction( - ReturnType (*external_function)(ArgumentTypes..., ...), - const std::string& name = "") { - return GetOrRegisterExternalFunction( - reinterpret_cast(external_function), - name, - true); - } - - /** - * @brief Optimize the code in the module managed by this CodegenUtils before - * execution. - * - * This method applies "generic" IR-to-IR optimization passes and is intended - * to be run before the final code-generation in PrepareForExecution(). It - * runs both function-level and whole module-level optimization passes. - * - * @note It is recommended to run this method just once before calling - * PrepareForExecution(). - * - * @param generic_opt_level How aggressively to optimize code. Higher levels - * will enable more computationally expensive optimization passes that - * may produce better-optimized code at the cost of increased up-front - * optimization time. - * @param size_level How aggressively to attempt to reduce the total size of - * code. Higher levels (especially kTiny) may actually lead to - * worse-performing code because they prioritize size-reduction over - * performance. - * @param optimize_for_host_cpu If true, attempt to exploit information about - * the specific CPU model we are running on during optimization (for - * example, LLVM may vectorize code based on what SIMD instructions are - * available). - * @return true if optimization was successful, false if some error occured. - **/ - bool Optimize(const OptimizationLevel generic_opt_level, - const SizeLevel size_level, - const bool optimize_for_host_cpu); - - /** - * @brief Prepare code generated by this CodegenUtils for execution. - * - * Internally, this creates an LLVM MCJIT ExecutionEngine and gives ownership - * of the Module to it. Actual compilation of functions may be deferred until - * the first call to GetUntypedFunctionPointer(). - * - * @note The cpu_opt_level and optimize_for_host_cpu options for this method - * only affect the actual generation of machine code. See also the - * Optimize() method, which controls more general code transformations. - * - * @param cpu_opt_level The level of optimization to apply when translating - * LLVM IR to machine code. Higher levels may produce better optimized - * code at the expense of increased compilation time. - * @param optimize_for_host_cpu If true, LLVM will optimize generated machine - * code for the specific CPU model we are running on. - * @return true if an ExecutionEngine was set up successfully, false if some - * error occured. - **/ - bool PrepareForExecution(const OptimizationLevel cpu_opt_level, - const bool optimize_for_host_cpu); - - /** - * @brief Get a pointer to the compiled machine-code version of a function - * generated by this CodegenUtils. - * - * @note PrepareForExecution() should be called before calling this method. - * - * @tparam ReturnType The function's return type. - * @tparam ArgumentTypes The types of any number of arguments to the function. - * @param function_name The name of the function to retrieve. - * @return A pointer to the compiled version of the function specified by - * function_name, or NULL if the function is not found or couldn't - * be compiled. - **/ - template - FunctionType GetFunctionPointer(const std::string& function_name); - - - /** - * @brief Generate the commonly used "fallback case" that generates a call to - * the regular_function passing all parameters from generated_function. - * - * @tparam FuncType FunctionType. e.g ReturnType (*)(ArgumenTypes) - * @param regular_function LLVM Function pointer to the regular fallback function - * @param generated_function LLVM Function pointer to the function being generated - * - **/ - template - void CreateFallback(llvm::Function* regular_function, - llvm::Function* generated_function); - - /* - * @brief Force inline an LLVM function at the call site. Note that this only - * does one level of inlining. - * - * @param LLVM call Instruction where the called function should be inlined - * @return false if it is not possible to inline. true otherwise. - */ - bool InlineFunction(llvm::CallInst* call_inst) { - // Check that the call instruction belongs to a BasicBlock which is part of - // a valid function - if (!(call_inst && call_inst->getParent() - && call_inst->getParent()->getParent())) { - return false; - } - llvm::InlineFunctionInfo info; - return llvm::InlineFunction(llvm::CallSite(call_inst), info); - } - - /* - * @brief Dump the IR of all underlying LLVM modules. - * - * @param out Stream to send the output to - as required by llvm::Value::print - */ - void PrintUnderlyingModules(llvm::raw_ostream& out); // NOLINT - - protected: - /** - * @return LLVMContext - **/ - llvm::LLVMContext* context() { - return &context_; - } - - private: - // Give ClangCompiler access to 'context_' add allow it to add compiled C++ - // sources to 'auxiliary_modules_'. - friend class ClangCompiler; - - template - friend class codegen_utils_detail::ConstantMaker; - - template - friend class codegen_utils_detail::ArithOpMaker; - - template - friend class codegen_utils_detail::FunctionTypeUnpacker; - - // Allow ClangCompilerTest to inspect 'auxiliary_modules_' to check if they - // have debugging information attached. - friend class ClangCompilerTest; - - // Records an external function's name and annotated type-signature so that a - // declaration for the function can be recreated by ClangCompiler. - struct NamedExternalFunction { - std::string name; - AnnotatedType return_type; - std::vector argument_types; - }; - - static constexpr char kExternalVariableNamePrefix[] = "_gpcodegenv"; - static constexpr char kExternalFunctionNamePrefix[] = "_gpcodegenx"; - - // Used internally when CreateFunction is called. Given the ReturnType - // and ArgumentTypes this will create an LLVM functions in the module - // managed by this CodegenUtils. - template - llvm::Function* CreateFunctionImpl( - const llvm::Twine& name, - const bool is_var_arg = false, - const llvm::GlobalValue::LinkageTypes linkage - = llvm::GlobalValue::ExternalLinkage); - - // Used internall when GetFunctionPointer is called. Given the ReturnType - // and ArgumentTypes this will get a pointer to the compiled machine-code - // version of a function generated by this CodegenUtils. - template - auto GetFunctionPointerImpl(const std::string& function_name) - -> ReturnType (*)(ArgumentTypes...); - - // Used internally when GetConstant() is called with a pointer type. Creates a - // new GlobalVariable with external linkage in '*module_' and remembers its - // name and address in 'external_global_variables_'. GlobalVariables created - // by this method are eventually mapped to their actual addresses in memory - // by PrepareForExecution(). - llvm::GlobalVariable* AddExternalGlobalVariable(llvm::Type* type, - const void* address); - - // Check that a function with the specified 'function_name' has the - // 'expected_function_type', crashing with a failed assertion if it does not. - // Calling this method can be slow (particularly after calling - // PrepareForExecution()), so it is intended mainly to be used in DEBUG - // builds. - void CheckFunctionType(const std::string& function_name, - const llvm::FunctionType* expected_function_type); - - // Generate a unique name for an external global variable. - std::string GenerateExternalVariableName(); - - // Generate a unique name for an external function. - std::string GenerateExternalFunctionName(); - - // Helper method for GetPointerToMember(). This base version does the actual - // address computation and casting. - llvm::Value* GetPointerToMemberImpl(llvm::Value* base_ptr, - llvm::Type* cast_type, - const std::size_t cumulative_offset); - - // Helper method for GetPointerToMember(). This variadic template recursively - // consumes pointers-to-members, adding up 'cumulative_offset' and resolving - // 'MemberType' to '*cast_type' at the penultimate level of recursion before - // calling the base version above. - template - llvm::Value* GetPointerToMemberImpl( - llvm::Value* base_ptr, - llvm::Type* cast_type, - const std::size_t cumulative_offset, - MemberType StructType::* pointer_to_member, - TailPointerToMemberTypes&&... tail_pointers_to_members); - - // Helper method to call any llvm intrinsic feature. E.g. CreateMulOverflow. - // TODO(krajaramn) : Support any number of arguments - llvm::Value* CreateIntrinsicInstrCall(llvm::Intrinsic::ID Id, - llvm::ArrayRef Tys, - llvm::Value* arg0, - llvm::Value* arg1); - - // Helper method for RegisterExternalFunction() that appends an entry for an - // external function to 'named_external_functions_'. - template - void RecordNamedExternalFunction(const std::string& name); - - llvm::LLVMContext context_; - llvm::IRBuilder<> ir_builder_; - - // Primary module directly managed by this CodegenUtils. - std::unique_ptr module_; - - // Additional modules to codegen from, generated by tools like ClangCompiler. - std::vector> auxiliary_modules_; - - std::unique_ptr engine_; - - // Map of (address, function_name) for each external function registered by - // GetOrRegisterExternalFunction(). PrepareForExecution() adds a mapping for - // each such function to '*engine_' after creating it. - std::unordered_map external_functions_; - - // Keep track of additional information about named external functions so that - // ClangCompiler can reconstruct accurate declarations for them. - std::vector named_external_functions_; - - // Pairs of (variable_name, address) for external GlobalVariables created by - // AddExternalGlobalVariable(). PrepareForExecution() adds a mapping for each - // such GlobalVariable to '*engine_' after creating it. - std::vector> - external_global_variables_; - - // Counters for external variables/functions registered in this CodegenUtils. - // Used by GenerateExternalVariableName() and GenerateExternalFunctionName(), - // respectively, to generate unique names for functions/globals. - unsigned external_variable_counter_; - unsigned external_function_counter_; - - DISALLOW_COPY_AND_ASSIGN(CodegenUtils); -}; - - -/** @} */ - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::GetType() and -// CodegenUtils::GetAnnotatedType(). - -// Because function template partial specialization is not allowed, we use -// helper classes to implement GetType() and GetAnnotatedType(). They are -// encapsulated in this nested namespace, which is not considered part of the -// public API. -namespace codegen_utils_detail { - -// type_traits-style template that detects whether 'T' is bool, or a const -// and/or volatile qualified version of bool. -template -using is_bool = std::is_same::type>; - -// TypeMaker has various template specializations to handle different -// categories of C++ types. The specializations provide a static method Get() -// that takes an 'llvm::LLVMContext*' pointer as an argument and returns an -// 'llvm::Type*' pointer that points to the equivalent of 'CppType' in LLVM's -// type system. Specializations also provide a static method GetAnnotated() -// that returns an AnnotatedType with additional information about properties of -// the C++ type. The base version of this template is empty. -template -class TypeMaker { -}; - -// Explicit specialization for void. -template <> -class TypeMaker { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getVoidTy(*context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateScalar(Get(context)); - } -}; - -// NOTE: LLVM's type system does not have a notion of const-ness, so the const -// and mutable versions of a C++ type map to the same LLVM type. Instead of -// explicit specialization for bool, float, and double below, we use partial -// specialization with some type_traits magic to capture all the const and -// volatile-qualified versions of such a type in a single template. - -// Specialization for bool (treated as a 1-bit integer in LLVM IR). -template -class TypeMaker< - BoolType, - typename std::enable_if::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getInt1Ty(*context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateScalar(Get(context)); - } -}; - -// Specialization for 32-bit float. -template -class TypeMaker< - FloatType, - typename std::enable_if::type>::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getFloatTy(*context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateScalar(Get(context)); - } -}; - -// Specialization for 64-bit double. -template -class TypeMaker< - DoubleType, - typename std::enable_if::type>::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getDoubleTy(*context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateScalar(Get(context)); - } -}; - -// Partial specialization for integer types other than bool. -template -class TypeMaker< - IntType, - typename std::enable_if::value - && !is_bool::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getIntNTy(*context, sizeof(IntType) << 3); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateScalar(Get(context)); - } -}; - -// Partial specialization for enums, which are converted to their underlying -// integer representation. -template -class TypeMaker< - EnumType, - typename std::enable_if::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return TypeMaker::type>::Get( - context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateScalar(Get(context)); - } -}; - -// Partial specialization for pointers to non-class, non-void types. -template -class TypeMaker< - PtrType, - typename std::enable_if< - std::is_pointer::value - && !std::is_class::type>::value - && !std::is_void::type>::value> - ::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return TypeMaker::type>::Get(context) - ->getPointerTo(); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return TypeMaker::type> - ::GetAnnotated(context).template AddPointer(); - } -}; - -// Partial specialization for pointers to class/struct types. These are -// converted to untyped pointers (i8* in LLVM, equivalent to void* in C++). -template -class TypeMaker< - ClassPtrType, - typename std::enable_if< - std::is_pointer::value - && std::is_class< - typename std::remove_pointer::type>::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getInt8PtrTy(*context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateVoidPtr(Get(context)); - } -}; - -// Special case for void*. Although it is possible to make a pointer to LLVM's -// void type, convention in LLVM is that all general-purpose "untyped" pointers -// are i8*. This partial specialization captures all the different const and/or -// volatile qualified versions of void*. -template -class TypeMaker< - VoidPtrType, - typename std::enable_if< - std::is_pointer::value - && std::is_void< - typename std::remove_pointer::type>::value>::type> { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return llvm::Type::getInt8PtrTy(*context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return AnnotatedType::CreateVoidPtr(Get(context)); - } -}; - -// Partial specialization for (lvalue-)references. A reference is really just -// a pointer. -template -class TypeMaker { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - return TypeMaker::Get(context); - } - - static AnnotatedType GetAnnotated(llvm::LLVMContext* context) { - return TypeMaker::GetAnnotated(context).ConvertToReference(); - } -}; - -// Partial specialization for C/C++ array types, which are converted to their -// corresponding llvm array types -template -class TypeMaker { - public: - static llvm::Type* Get(llvm::LLVMContext* context) { - llvm::Type* unit_type = - codegen_utils_detail::TypeMaker::Get(context); - return llvm::ArrayType::get(unit_type, N); - } -}; - -} // namespace codegen_utils_detail - -template -llvm::Type* CodegenUtils::GetType() { - return codegen_utils_detail::TypeMaker::Get(&context_); -} - -template -AnnotatedType CodegenUtils::GetAnnotatedType() { - return codegen_utils_detail::TypeMaker::GetAnnotated(&context_); -} - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::GetFunctionType(). - -// Helper template classes are nested in this namespace and are not considered -// part of the public API. -namespace codegen_utils_detail { - -// TypeVectorBuilder is a variadic template. Specializations of -// TypeVectorBuilder have a two static methods AppendTypes() and -// AppendAnnotatedTypes(). AppendTypes() takes a 'CodegenUtils*' pointer and a -// pointer to a vector of 'llvm::Type*' pointers. -// Calling TypeVectorBuilder::AppendTypes() appends the -// equivalent llvm::Type for each of 'ArgumentTypes' to the vector. Similarly, -// AppendAnnotatedTypes() appends an AnnotatedType for each of 'ArgumentTypes' -// to a vector. -template -class TypeVectorBuilder; - -// Base version for zero argument types does nothing. -template <> -class TypeVectorBuilder<> { - public: - static void AppendTypes(CodegenUtils* generator, - std::vector* types) { - } - - static void AppendAnnotatedTypes( - CodegenUtils* generator, - std::vector* annotated_types) { - } -}; - -// Version for 1+ argument types appends converts and appends the first type, -// then recurses. -template -class TypeVectorBuilder { - public: - static_assert(!std::is_same::value, - "void is not allowed as an argument type for " - "gpcodegen::CodegenUtils::GetFunctionType()"); - - static void AppendTypes(CodegenUtils* generator, - std::vector* types) { - types->push_back(generator->GetType()); - TypeVectorBuilder::AppendTypes(generator, types); - } - - static void AppendAnnotatedTypes( - CodegenUtils* generator, - std::vector* annotated_types) { - annotated_types->emplace_back(generator->GetAnnotatedType()); - TypeVectorBuilder::AppendAnnotatedTypes(generator, - annotated_types); - } -}; - -} // namespace codegen_utils_detail - -template -llvm::FunctionType* CodegenUtils::GetFunctionType(const bool is_var_arg) { - std::vector argument_types; - codegen_utils_detail::TypeVectorBuilder::AppendTypes( - this, - &argument_types); - return llvm::FunctionType::get(GetType(), - argument_types, - is_var_arg); -} - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::GetConstant(). - -// Helper template classes are nested in this namespace and are not considered -// part of the public API. -namespace codegen_utils_detail { - -// ConstantMaker has various template specializations to handle constants of -// different C++ types. The specialized versions have a static method Get() -// that takes a 'constant_value' of type 'const CppType' and a pointer to -// a CodegenUtils object, and returns a pointer to an llvm::Constant -// equivalent to 'constant_value' in the CodegenUtils's context. -template -class ConstantMaker { -}; - -// Partial specialization for unsigned integer types (including bool). -template -class ConstantMaker< - UnsignedIntType, - typename std::enable_if< - std::is_integral::value - && std::is_unsigned::value>::type> { - public: - static_assert(sizeof(UnsignedIntType) <= sizeof(std::uint64_t), - "Unable to make an integer constant wider than 64 bits."); - - static llvm::Constant* Get(const UnsignedIntType constant_value, - CodegenUtils* generator) { - return llvm::ConstantInt::get(generator->GetType(), - constant_value); - } -}; - -// Partial specialization for signed integer types. -template -class ConstantMaker< - SignedIntType, - typename std::enable_if< - std::is_integral::value - && std::is_signed::value>::type> { - public: - static_assert(sizeof(SignedIntType) <= sizeof(std::int64_t), - "Unable to make an integer constant wider than 64 bits."); - - static llvm::Constant* Get(const SignedIntType constant_value, - CodegenUtils* generator) { - return llvm::ConstantInt::getSigned(generator->GetType(), - constant_value); - } -}; - -// Partial specialization for enums (mapped to the underlying integer type). -template -class ConstantMaker< - EnumType, - typename std::enable_if::value>::type> { - public: - static llvm::Constant* Get(const EnumType constant_value, - CodegenUtils* generator) { - typedef typename std::underlying_type::type EnumAsIntType; - return ConstantMaker::Get( - static_cast(constant_value), - generator); - } -}; - -// Explicit specialization for 32-bit float. -template <> -class ConstantMaker { - public: - static llvm::Constant* Get(const float constant_value, - CodegenUtils* generator) { - return llvm::ConstantFP::get(generator->GetType(), - constant_value); - } -}; - -// Explicit specialization for 64-bit double. -template <> -class ConstantMaker { - public: - static llvm::Constant* Get(const double constant_value, - CodegenUtils* generator) { - return llvm::ConstantFP::get(generator->GetType(), - constant_value); - } -}; - -// Partial specialization for pointers. -template -class ConstantMaker { - public: - static llvm::Constant* Get(const PointedType* constant_value, - CodegenUtils* generator) { - if (constant_value == nullptr) { - return llvm::ConstantPointerNull::get( - static_cast(generator->GetType())); - } else { - // If pointer is non-NULL, assume it points to valid memory and make the - // pointed-to data a global variable. - return generator->AddExternalGlobalVariable( - generator->GetType()->getPointerElementType(), - constant_value); - } - } -}; - -} // namespace codegen_utils_detail - -template -llvm::Constant* CodegenUtils::GetConstant(const CppType constant_value) { - return codegen_utils_detail::ConstantMaker::Get(constant_value, - this); -} - -// ---------------------------------------------------------------------------- -// Implementation of recursive variadic version of -// CodegenUtils::GetPointerToMemberImpl(). - -template -llvm::Value* CodegenUtils::GetPointerToMemberImpl( - llvm::Value* base_ptr, - llvm::Type* cast_type, - const std::size_t cumulative_offset, - MemberType StructType::* pointer_to_member, - TailPointerToMemberTypes&&... tail_pointers_to_members) { - // Sanity check. '*cast_type' should not be set until the final level of - // recursion. - assert(cast_type == nullptr); - - // Calculate the offset (in bytes) of the member denoted by - // '*pointer_to_member' inside StructType. - const std::size_t member_offset - = reinterpret_cast( - &(static_cast(0)->*pointer_to_member)); - - // If we have reached the end of pointer-to-member arguments, it is time to - // determine the member type to cast the returned pointer to. - if (sizeof...(tail_pointers_to_members) == 0) { - cast_type = GetType(); - } - - return GetPointerToMemberImpl( - base_ptr, - cast_type, - cumulative_offset + member_offset, - std::forward(tail_pointers_to_members)...); -} - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::RecordNamedExternalFunction() - -template -void CodegenUtils::RecordNamedExternalFunction(const std::string& name) { - NamedExternalFunction named_external_fn{name, - GetAnnotatedType(), - {}}; - codegen_utils_detail::TypeVectorBuilder - ::AppendAnnotatedTypes(this, - &named_external_fn.argument_types); - - named_external_functions_.emplace_back(std::move(named_external_fn)); -} - - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::GetTypeDefFunctionPointer & -// CodegenUtils::CreateFunctionPointer(). - -namespace codegen_utils_detail { -/** - * @brief Templated helper class that provides a static method which - * unpack the return type, argument types and call the respective - * method - * - * @tparam FunctionType Function type. - * @tparam MethodPtr A pointer-to-method. - **/ -template -class FunctionTypeUnpacker; - -// Partial specialization of FunctionTypeUnpacker. -// This class will unpack the function type to ReturnType and ArgumentTypes. -// GetFunctionPointerImpl - Call CodegenUtils's GetFunctionPointerImpl -// CreateFunctionImpl - Call CodegenUtils's CreateFunctionImpl. -template -class FunctionTypeUnpacker { - public: - using R = ReturnType; - - static llvm::Function* CreateFunctionImpl( - CodegenUtils* codegen_utils, - const llvm::Twine& name, - const bool is_var_arg, - const llvm::GlobalValue::LinkageTypes linkage) { - return codegen_utils->CreateFunctionImpl( - name, is_var_arg, linkage); - } - - static auto GetFunctionPointerImpl(gpcodegen::CodegenUtils* codegen_utils, - const std::string& func_name) - -> ReturnType (*)(ArgumentTypes...) { - return codegen_utils->GetFunctionPointerImpl( - func_name); - } -}; -} // namespace codegen_utils_detail - -template -llvm::Function* CodegenUtils::CreateFunction( - const llvm::Twine& name, - const bool is_var_arg, - const llvm::GlobalValue::LinkageTypes linkage) { - return codegen_utils_detail::FunctionTypeUnpacker:: - CreateFunctionImpl(this, name, is_var_arg, linkage); -} - -template -llvm::Function* CodegenUtils::CreateFunctionImpl( - const llvm::Twine& name, - const bool is_var_arg, - const llvm::GlobalValue::LinkageTypes linkage) { - return llvm::Function::Create( - GetFunctionType(is_var_arg), - linkage, - name, - module_.get()); -} - -template -FunctionType CodegenUtils::GetFunctionPointer( - const std::string& function_name) { - return codegen_utils_detail::FunctionTypeUnpacker:: - GetFunctionPointerImpl(this, function_name); -} - -template -auto CodegenUtils::GetFunctionPointerImpl(const std::string& function_name) - -> ReturnType (*)(ArgumentTypes...) { - if (engine_) { -#ifdef CODEGEN_DEBUG - CheckFunctionType(function_name, - GetFunctionType()); -#endif - return reinterpret_cast( - engine_->getFunctionAddress(function_name)); - } else { - return nullptr; - } -} - -template -void CodegenUtils::CreateFallback(llvm::Function* regular_function, - llvm::Function* generated_function) { - assert(regular_function != nullptr); - assert(generated_function != nullptr); - - std::vector forwarded_args; - for (llvm::Argument& arg : generated_function->args()) { - forwarded_args.push_back(&arg); - } - - llvm::CallInst* call_fallback_func = ir_builder()->CreateCall( - regular_function, forwarded_args); - /* Return the result of the call, or void if the function returns void. */ - if (std::is_same::R, - void>::value) { - ir_builder()->CreateRetVoid(); - } else { - ir_builder()->CreateRet(call_fallback_func); - } -} - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::CreateMulOverflow, CreateSubOverflow... - -// Helper template classes are nested in this namespace and are not considered -// part of the public API. -namespace codegen_utils_detail { - -// ArithOpMaker has various template specializations to handle different -// categories of C++ types. The specializations provide a static method -// CreateMulOverflow(), CreateAddOverflow(), CreateSubOverflow() etc.. -// that takes an CodegenUtils and arguments and call respective llvm instruction -// for given CppType. -// The base version of this template is empty. -template -class ArithOpMaker { -}; - -// Partial specialization for unsigned integer types (including bool). -template -class ArithOpMaker< -UnsignedIntType, -typename std::enable_if< -std::is_integral::value -&& std::is_unsigned::value>::type> { - public: - static llvm::Value* CreateAddOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(generator->GetType(), arg0, arg1); - return generator->CreateIntrinsicInstrCall( - llvm::Intrinsic::uadd_with_overflow, - generator->GetType(), - arg0, - arg1); - } - static llvm::Value* CreateSubOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(generator->GetType(), arg0, arg1); - return generator->CreateIntrinsicInstrCall( - llvm::Intrinsic::usub_with_overflow, - generator->GetType(), - arg0, - arg1); - } - static llvm::Value* CreateMulOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(generator->GetType(), arg0, arg1); - return generator->CreateIntrinsicInstrCall( - llvm::Intrinsic::umul_with_overflow, - generator->GetType(), - arg0, - arg1); - } - - private: - static void Checker(llvm::Type* llvm_unsigned_type, - llvm::Value* arg0, - llvm::Value* arg1) { - assert(nullptr != arg0 && nullptr != arg0->getType()); - assert(nullptr != arg1 && nullptr != arg1->getType()); - assert(arg0->getType()->isIntegerTy()); - assert(arg0->getType()->getScalarSizeInBits() == - llvm_unsigned_type->getScalarSizeInBits()); - assert(arg1->getType()->isIntegerTy()); - assert(arg1->getType()->getScalarSizeInBits() == - llvm_unsigned_type->getScalarSizeInBits()); - } -}; - -// Partial specialization for signed integer types. -template -class ArithOpMaker< -SignedIntType, -typename std::enable_if< -std::is_integral::value -&& std::is_signed::value>::type> { - public: - static llvm::Value* CreateAddOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(generator->GetType(), arg0, arg1); - return generator->CreateIntrinsicInstrCall( - llvm::Intrinsic::sadd_with_overflow, - generator->GetType(), - arg0, - arg1); - } - - static llvm::Value* CreateSubOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(generator->GetType(), arg0, arg1); - return generator->CreateIntrinsicInstrCall( - llvm::Intrinsic::ssub_with_overflow, - generator->GetType(), - arg0, - arg1); - } - - static llvm::Value* CreateMulOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(generator->GetType(), arg0, arg1); - return generator->CreateIntrinsicInstrCall( - llvm::Intrinsic::smul_with_overflow, - generator->GetType(), - arg0, - arg1); - } - private: - static void Checker(llvm::Type* llvm_signed_type, - llvm::Value* arg0, - llvm::Value* arg1) { - assert(nullptr != arg0 && nullptr != arg0->getType()); - assert(nullptr != arg1 && nullptr != arg1->getType()); - assert(arg0->getType()->isIntegerTy()); - assert(arg0->getType()->getScalarSizeInBits() == - llvm_signed_type->getScalarSizeInBits()); - assert(arg1->getType()->isIntegerTy()); - assert(arg1->getType()->getScalarSizeInBits() == - llvm_signed_type->getScalarSizeInBits()); - } -}; - -// Partial specialization for enums (mapped to the underlying integer type). -template -class ArithOpMaker< -EnumType, -typename std::enable_if::value>::type> { - public: -}; - -// Explicit specialization for 32-bit float. -template <> -class ArithOpMaker { - public: -}; - -// Explicit specialization for 64-bit double. -template <> -class ArithOpMaker { - public: - static llvm::Value* CreateAddOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(arg0, arg1); - - auto irb = generator->ir_builder(); - llvm::Value* llvm_result = irb->CreateFAdd(arg0, arg1); - - llvm::AllocaInst* llvm_tuple_ptr = - CreateResultWithOverflow(generator, - llvm_result, - arg0, - arg1, - generator->GetConstant(true)); - return irb->CreateLoad(llvm_tuple_ptr); - } - - static llvm::Value* CreateSubOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(arg0, arg1); - auto irb = generator->ir_builder(); - llvm::Value* llvm_result = irb->CreateFSub(arg0, arg1); - - llvm::AllocaInst* llvm_tuple_ptr = - CreateResultWithOverflow(generator, - llvm_result, - arg0, - arg1, - generator->GetConstant(true)); - return irb->CreateLoad(llvm_tuple_ptr); - } - - static llvm::Value* CreateMulOverflow(CodegenUtils* generator, - llvm::Value* arg0, - llvm::Value* arg1) { - Checker(arg0, arg1); - auto irb = generator->ir_builder(); - llvm::Value* llvm_result = irb->CreateFMul(arg0, arg1); - - llvm::Value* llvm_arg0_zero = irb-> - CreateFCmpOEQ(arg0, generator->GetConstant(0), "is_arg0_zero"); - llvm::Value* llvm_arg1_zero = irb-> - CreateFCmpOEQ(arg1, generator->GetConstant(0), "is_arg1_zero"); - llvm::Value* llvm_zero_valid = irb->CreateOr(llvm_arg0_zero, - llvm_arg1_zero, - "llvm_zero_valid"); - llvm::AllocaInst* llvm_tuple_ptr = - CreateResultWithOverflow(generator, - llvm_result, - arg0, - arg1, - llvm_zero_valid); - return irb->CreateLoad(llvm_tuple_ptr); - } - - - private: - static void Checker(llvm::Value* arg0, - llvm::Value* arg1) { - assert(nullptr != arg0 && nullptr != arg0->getType()); - assert(nullptr != arg1 && nullptr != arg1->getType()); - assert(arg0->getType()->isDoubleTy()); - assert(arg1->getType()->isDoubleTy()); - } - - - // Implements CHECKFLOATVAL(float_utils.h), but instead of error out, - // it returns true when an overflow occurs. - static bool CheckDoubleVal(double val, - double arg0, - double arg1, - bool zero_is_valid) { - bool inf_is_valid = isinf(arg0) || isinf(arg1); - if (isinf(val) && !(inf_is_valid)) { - return true; - } - if ((val) == 0.0 && !(zero_is_valid)) { - return true; - } - return false; - } - - // Returns a struct that contains the result value and the overflow flag of - // the operation. It mimics llvm integer intrinsics. - static llvm::AllocaInst* CreateResultWithOverflow( - CodegenUtils* generator, - llvm::Value* llvm_result, - llvm::Value* arg0, - llvm::Value* arg1, - llvm::Value* zero_is_valid) { - auto irb = generator->ir_builder(); - llvm::Function* llvm_float_overflow_func = - generator->GetOrRegisterExternalFunction( - CheckDoubleVal, "CheckDoubleVal"); - llvm::Value* llvm_overflow = irb->CreateCall( - llvm_float_overflow_func, - { llvm_result, arg0, arg1, zero_is_valid}); - llvm::AllocaInst* llvm_tuple_ptr = generator->CreateMakeTuple( - { llvm_result, llvm_overflow }, "float_result"); - return llvm_tuple_ptr; - } -}; - -} // namespace codegen_utils_detail - - -template -llvm::Value* CodegenUtils::CreateAddOverflow(llvm::Value* arg0, - llvm::Value* arg1) { - return codegen_utils_detail::ArithOpMaker::CreateAddOverflow(this, - arg0, - arg1); -} - -template -llvm::Value* CodegenUtils::CreateIncOverflow(llvm::Value* arg) { - return codegen_utils_detail::ArithOpMaker:: - CreateAddOverflow(this, arg, GetConstant(1)); -} - -template -llvm::Value* CodegenUtils::CreateSubOverflow(llvm::Value* arg0, - llvm::Value* arg1) { - return codegen_utils_detail::ArithOpMaker::CreateSubOverflow(this, - arg0, - arg1); -} - -template -llvm::Value* CodegenUtils::CreateMulOverflow(llvm::Value* arg0, - llvm::Value* arg1) { - return codegen_utils_detail::ArithOpMaker::CreateMulOverflow(this, - arg0, - arg1); -} - -// ---------------------------------------------------------------------------- -// Implementation of CodegenUtils::CreateCast(). - -// Helper template classes are nested in this namespace and are not considered -// part of the public API. -namespace codegen_utils_detail { - -// CastMaker has various template specializations to handle casting of -// different C++ types. The specialized versions have a static method -// CreateCast() that takes an 'llvm::Value' of SrcType and a pointer to -// CodegenUtils object, and returns a pointer to an -// 'llvm::Value' equivalent to DestType -template -class CastMaker { -}; - -// Partial specialization for unsigned int to signed / unsigned integer. -template -class CastMaker< - IntegerType, - UnsignedIntType, - typename std::enable_if< - std::is_integral::value - && std::is_integral::value - && std::is_unsigned::value>::type> { - public: - static llvm::Value* CreateCast(llvm::Value* value, - CodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - llvm::Type* llvm_dest_type = codegen_utils->GetType(); - Checker(value, llvm_dest_type); - return codegen_utils->ir_builder()->CreateZExtOrTrunc(value, - llvm_dest_type); - } - private: - static void Checker(llvm::Value* value, - llvm::Type* llvm_dest_type) { - assert(nullptr != value); - assert(nullptr != value->getType()); - assert(value->getType()->isIntegerTy()); - assert(nullptr != llvm_dest_type); - assert(llvm_dest_type->isIntegerTy()); - } -}; - -// Partial specialization for signed int to signed / unsigned integer. -template -class CastMaker< - IntegerType, - SignedIntType, - typename std::enable_if< - std::is_integral::value - && std::is_integral::value - && std::is_signed::value>::type> { - public: - static llvm::Value* CreateCast(llvm::Value* value, - CodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - llvm::Type* llvm_dest_type = codegen_utils->GetType(); - Checker(value, llvm_dest_type); - - return codegen_utils->ir_builder()->CreateSExtOrTrunc(value, - llvm_dest_type); - } - private: - static void Checker(llvm::Value* value, - llvm::Type* llvm_dest_type) { - assert(nullptr != value); - assert(nullptr != value->getType()); - assert(value->getType()->isIntegerTy()); - assert(nullptr != llvm_dest_type); - assert(llvm_dest_type->isIntegerTy()); - } -}; - -// Partial specialization for any floating point to 32-bit float -template -class CastMaker< - float, - FloatingPoint, - typename std::enable_if< - std::is_floating_point::value>::type> { - public: - static llvm::Value* CreateCast(llvm::Value* value, - CodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - llvm::Type* llvm_dest_type = codegen_utils->GetType(); - Checker(value, llvm_dest_type); - if (value->getType()->isFloatTy()) { return value; } - return codegen_utils->ir_builder()->CreateFPTrunc( - value, llvm_dest_type); - } - private: - static void Checker(llvm::Value* value, - llvm::Type* llvm_dest_type) { - assert(nullptr != value); - assert(nullptr != value->getType()); - assert(value->getType()->isFloatTy() || - value->getType()->isDoubleTy()); - assert(nullptr != llvm_dest_type); - assert(llvm_dest_type->isFloatTy()); - } -}; - -// Partial specialization for any floating point to 64-bit float -template -class CastMaker< - double, - FloatingPoint, - typename std::enable_if< - std::is_floating_point::value>::type> { - public: - static llvm::Value* CreateCast(llvm::Value* value, - CodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - llvm::Type* llvm_dest_type = codegen_utils->GetType(); - Checker(value, llvm_dest_type); - if (value->getType()->isDoubleTy()) { return value; } - return codegen_utils->ir_builder()->CreateFPExt( - value, llvm_dest_type); - } - private: - static void Checker(llvm::Value* value, - llvm::Type* llvm_dest_type) { - assert(nullptr != value); - assert(nullptr != value->getType()); - assert(value->getType()->isFloatTy() || - value->getType()->isDoubleTy()); - assert(nullptr != llvm_dest_type); - assert(llvm_dest_type->isDoubleTy()); - } -}; - -// Partial specialization for any signed integer to 64-bit float -template -class CastMaker< - double, - SignedIntType, - typename std::enable_if< - std::is_integral::value && - std::is_signed::value>::type> { - public: - static llvm::Value* CreateCast(llvm::Value* value, - CodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - - llvm::Type* llvm_dest_type = codegen_utils->GetType(); - Checker(value, llvm_dest_type); - return codegen_utils->ir_builder()->CreateSIToFP( - value, llvm_dest_type); - } - private: - static void Checker(llvm::Value* value, - llvm::Type* llvm_dest_type) { - assert(nullptr != value); - assert(nullptr != value->getType()); - assert(value->getType()->isIntegerTy()); - assert(nullptr != llvm_dest_type); - assert(llvm_dest_type->isDoubleTy()); - } -}; - -// Partial specialization for any unsigned integer to 64-bit float -template -class CastMaker< - double, - UnsignedIntType, - typename std::enable_if< - std::is_integral::value && - std::is_unsigned::value>::type> { - public: - static llvm::Value* CreateCast(llvm::Value* value, - CodegenUtils* codegen_utils) { - assert(nullptr != codegen_utils); - - llvm::Type* llvm_dest_type = codegen_utils->GetType(); - Checker(value, llvm_dest_type); - return codegen_utils->ir_builder()->CreateUIToFP( - value, llvm_dest_type); - } - private: - static void Checker(llvm::Value* value, - llvm::Type* llvm_dest_type) { - assert(nullptr != value); - assert(nullptr != value->getType()); - assert(value->getType()->isIntegerTy()); - assert(nullptr != llvm_dest_type); - assert(llvm_dest_type->isDoubleTy()); - } -}; - -} // namespace codegen_utils_detail - -template -llvm::Value* CodegenUtils::CreateCast(llvm::Value* value) { - return codegen_utils_detail::CastMaker::CreateCast( - value, this); -} - -} // namespace gpcodegen - -#endif // GPCODEGEN_CODEGEN_UTILS_H_ -// EOF diff --git a/src/backend/codegen/include/codegen/utils/gp_codegen_utils.h b/src/backend/codegen/include/codegen/utils/gp_codegen_utils.h deleted file mode 100644 index 0dffca31f59820cd1ecf31f2db836b8068e12c19..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/gp_codegen_utils.h +++ /dev/null @@ -1,345 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// gp_codegen_utils.h -// -// @doc: -// Object that extends the functionality of CodegenUtils by adding GPDB -// specific functionality and utilities to aid in the runtime code generation -// for a LLVM module -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_GP_CODEGEN_UTILS_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_GP_CODEGEN_UTILS_H_ - -#include "codegen/utils/codegen_utils.h" -#include "codegen/codegen_wrapper.h" - -extern "C" { -#include "utils/elog.h" -#include "utils/palloc.h" -#include "nodes/memnodes.h" -} - -#define EXPAND_CREATE_ELOG(codegen_utils, elevel, ...) \ - codegen_utils->CreateElog(__FILE__, __LINE__, PG_FUNCNAME_MACRO, \ - elevel, ##__VA_ARGS__) - -#define EXPAND_CREATE_EREPORT(codegen_utils, elevel, ecode, errmsg_fmt, ...) \ - codegen_utils->CreateEreport(__FILE__, __LINE__, PG_FUNCNAME_MACRO, \ - TEXTDOMAIN, elevel, ecode, errmsg_fmt, \ - ##__VA_ARGS__) - -#define EXPAND_CREATE_PALLOC(codegen_utils, sz) \ - codegen_utils->CreatePalloc(sz, __FILE__, PG_FUNCNAME_MACRO, __LINE__) - -namespace gpcodegen { - -class GpCodegenUtils : public CodegenUtils { - public: - /** - * @brief Constructor. - * - * @param module_name A human-readable name for the module that this - * CodegenUtils will manage. - **/ - explicit GpCodegenUtils(llvm::StringRef module_name) - : CodegenUtils(module_name) { - } - - ~GpCodegenUtils() { - } - - /* - * @brief Create LLVM instructions to call elog_start() and elog_finish(). - * - * @warning This method does not create instructions for any sort of exception - * handling. In the case that elog throws an error, the code with jump - * straight out of the compiled module back to the last location in GPDB - * that setjump was called. - * - * @param file_name name of the file from which CreateElog is called - * @param lineno number of the line in the file from which CreateElog is called - * @param func_name name of the function that calls CreateElog - * @param llvm_elevel llvm::Value pointer to an integer representing the error level - * @param llvm_fmt llvm::Value pointer to the format string - * @tparam args llvm::Value pointers to arguments to elog() - */ - template - void CreateElog( - const char* file_name, - int lineno, - const char* func_name, - llvm::Value* llvm_elevel, - llvm::Value* llvm_fmt, - const V ... args ) { - llvm::Function* llvm_elog_start = - GetOrRegisterExternalFunction(elog_start, "elog_start"); - llvm::Function* llvm_elog_finish = - GetOrRegisterExternalFunction(elog_finish, "elog_finish"); - - ir_builder()->CreateCall( - llvm_elog_start, { - GetConstant(file_name), // Filename - GetConstant(lineno), // line number - GetConstant(func_name) // function name - }); - ir_builder()->CreateCall( - llvm_elog_finish, { - llvm_elevel, - llvm_fmt, - args... }); - } - - /* - * @brief Create LLVM instructions to call elog_start() and elog_finish(). - * A convenient alternative that automatically converts an integer elevel and - * format string to LLVM constants. - * - * @warning This method does not create instructions for any sort of exception - * handling. In the case that elog throws an error, the code with jump - * straight out of the compiled module back to the last location in GPDB - * that setjump was called. - * - * @param file_name name of the file from which CreateElog is called - * @param lineno number of the line in the file from which CreateElog is called - * @param func_name name of the function that calls CreateElog - * @param elevel Integer representing the error level - * @param fmt Format string - * @tparam args llvm::Value pointers to arguments to elog() - */ - template - void CreateElog( - const char* file_name, - int lineno, - const char* func_name, - int elevel, - const char* fmt, - const V ... args ) { - CreateElog(file_name, lineno, func_name, GetConstant(elevel), - GetConstant(fmt), args...); - } - - - /* - * @brief Create LLVM instructions to implement ereport; but only for - * ereport calls of the form: ereport(elevel, (errcode(code), errmsg(fmt, args...)). - * - * The reason for support only this form is because the ereport() mechanism is - * very flexible because of overloaded C macros - so much so that it is tricky - * to implement using the LLVM API. For example, it may appear from the above - * code that errcode and errmsg are called before any errstart or errfinish. - * However because of the way the macro is implemented, errcode and errmsg are - * called *after* errstart and only if errstart returns true - * - * @note This function calls the following external functions: errstart, errcode, errmsg - * and errfinish. - * - * @param file_name name of the file from which CreateEreport is called - * @param lineno number of the line in the file from which CreateEreport is called - * @param func_name name of the function that calls CreateEreport - * @param domain llvm::Value pointer to message domain of the report - * @param elevel llvm::Value pointer to integer representing the error level - * @param ecode llvm::Value pointer to error code passed to a call to - * @param errmsg_fmt llvm::Value pointer to format string - * @tparam args llvm::Value pointers to arguments to elog() - */ - template - void CreateEreport( - const char* file_name, - int lineno, - const char* func_name, - const char* domain, - int elevel, - int ecode, - const char* errmsg_fmt, - const V ... args ) { - CreateEreport(file_name, - lineno, - func_name, - GetConstant(domain), - GetConstant(elevel), - GetConstant(ecode), - GetConstant(errmsg_fmt)); - } - - /* - * @brief Create LLVM instructions to implements ereport; but only for - * ereport calls of the form: ereport(elevel, (errcode(code), errmsg(fmt, args...)). - * - * @param file_name name of the file from which CreateEreport is called - * @param lineno number of the line in the file from which CreateEreport is called - * @param func_name name of the function that calls CreateEreport - * @param domain message domain of the report - * @param elevel Integer representing the error level - * @param ecode error code passed to a call to - * @param errmsg_fmt Format string - * @tparam args llvm::Value pointers to arguments to elog() - */ - template - void CreateEreport( - const char* file_name, - int lineno, - const char* func_name, - llvm::Value* domain, - llvm::Value* elevel, - llvm::Value* ecode, - llvm::Value* errmsg_fmt, - const V ... args ) { - // Make sure the external functions required are available - llvm::Function* llvm_errstart = - GetOrRegisterExternalFunction(errstart, "errstart"); - llvm::Function* llvm_errcode = - GetOrRegisterExternalFunction(errcode, "errcode"); - llvm::Function* llvm_errmsg = - GetOrRegisterExternalFunction(errmsg, "errmsg"); - llvm::Function* llvm_errfinish = - GetOrRegisterExternalFunction(errfinish, "errfinish"); - - auto irb = ir_builder(); - - // Retrive the current function to create new blocks - llvm::Function* current_function = irb->GetInsertBlock()->getParent(); - - // Case when errstart returns true - llvm::BasicBlock* errfinish_block = - CreateBasicBlock("errfinish", current_function); - // Case when errstart returns false - llvm::BasicBlock* rest_ereport_block = - CreateBasicBlock("rest_ereport", current_function); - // Case when elevel >= ERROR - llvm::BasicBlock* abort_block = - CreateBasicBlock("abort", current_function); - // Done with all cases - llvm::BasicBlock* end_ereport_block = - CreateBasicBlock("end_ereport", current_function); - - - // if (errstart(...)) errfinish(...) {{{ - llvm::Value* llvm_ret = irb->CreateCall( - llvm_errstart, { - elevel, - GetConstant(file_name), - GetConstant(lineno), - GetConstant(func_name), - domain - }); - - irb->CreateCondBr(llvm_ret, - errfinish_block /* true */, - rest_ereport_block /* false */); - - irb->SetInsertPoint(errfinish_block); - irb->CreateCall(llvm_errfinish, { - irb->CreateCall(llvm_errcode, {ecode}), - irb->CreateCall(llvm_errmsg, {errmsg_fmt, args...}) }); - irb->CreateBr(rest_ereport_block); - // }}} - - // if (elevel >= ERROR) abort() {{{ - irb->SetInsertPoint(rest_ereport_block); - irb->CreateCondBr(irb->CreateICmpSGE(elevel, GetConstant(ERROR)), - abort_block /* true */, - end_ereport_block /* false */); - - irb->SetInsertPoint(abort_block); - irb->CreateCall(GetOrRegisterExternalFunction(abort, "abort")); - - irb->CreateBr(end_ereport_block); - // }}} - - // Set up for adding instructions past this point - irb->SetInsertPoint(end_ereport_block); - } - - /** - * @brief Create a Cast instruction to convert given llvm::Value of Datum type - * to given Cpp type - * - * @note Depend on cpp type's size, it will do trunc or bit cast or - * int2ptr cast. This is same as converting gpdb's Datum to cpptype. - * - * @tparam CppType Destination cpp type - * @param value LLVM Value on which casting has to be done. - * - * @return LLVM Value that casted to given Cpp type. - **/ - template - llvm::Value* CreateDatumToCppTypeCast(llvm::Value* value) { - assert(nullptr != value); - llvm::Type* llvm_dest_type = GetType(); - // If it is a pointer, do direct pointer cast - if (llvm_dest_type->isPointerTy()) { - return ir_builder()->CreateIntToPtr(value, llvm_dest_type); - } - unsigned dest_size = llvm_dest_type->getScalarSizeInBits(); - assert(0 != dest_size); - - llvm::Type* llvm_src_type = value->getType(); - unsigned src_size = llvm_src_type->getScalarSizeInBits(); - - // Given src value, it should be of type Datum(int64_t) - assert(llvm_src_type->isIntegerTy()); - assert(src_size == sizeof(Datum) << 3); - - // Datum size should be the largest - assert(dest_size <= src_size); - - // Get dest type as integer type with same size - llvm::Type* llvm_dest_as_int_type = llvm::IntegerType::get(*context(), - dest_size); - llvm::Value* llvm_casted_value = value; - - if (dest_size < src_size) { - llvm_casted_value = ir_builder()->CreateTrunc(value, - llvm_dest_as_int_type); - } - - if (llvm_src_type->getTypeID() != llvm_dest_type->getTypeID()) { - return ir_builder()->CreateBitCast(llvm_casted_value, llvm_dest_type); - } - - return llvm_casted_value; - } - - /** - * @brief Create instructions to call MemoryContextAllocImpl in the - * CurrentMemoryContext. Use the macro EXPAND_CREATE_PALLOC to get the - * line number, function name and file name. - * - * @param size Size to allocate in the CurrentMemoryContext - * @param file File name - * @param func Function name - * @param line Line number - * @return LLVM::Value pointer to the allocated memory - */ - llvm::Value* CreatePalloc(Size size, - const char* file, - const char *func, - int line); - - /** - * @brief Create a Cast instruction to convert given llvm::Value of any type - * to Datum - * - * @note This is same as converting any cpptype to Datum in gpdb. - * - * @param value LLVM Value on which casting has to be done. - * @param is_src_unsigned true if given value is of unsigned type. - * - * - * @return LLVM Value that casted to Datum type. - **/ - llvm::Value* CreateCppTypeToDatumCast(llvm::Value* value, - bool is_src_unsigned = false); -}; -} // namespace gpcodegen - -#endif // GPCODEGEN_GP_CODEGEN_UTILS_H -// EOF diff --git a/src/backend/codegen/include/codegen/utils/instance_method_wrappers.h b/src/backend/codegen/include/codegen/utils/instance_method_wrappers.h deleted file mode 100644 index 2b842fba90f9b9c482eaac09058d59563fcc82cc..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/instance_method_wrappers.h +++ /dev/null @@ -1,289 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// instance_method_wrappers.h -// -// @doc: -// Templated helper class that provides a static Call() method which -// wraps an invocation of an instance method of a class. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_INSTANCE_METHOD_WRAPPERS_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_INSTANCE_METHOD_WRAPPERS_H_ - -#include - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Templated helper class that provides a static Call() method which - * wraps an invocation of an instance method of a class. - * - * @note Writing out the explicit MethodType parameter can be cumbersome and - * error-prone. See the macros GPCODEGEN_WRAP_METHOD(), - * GPCODEGEN_WRAP_OVERLOADED_METHOD(), and - * GPCODEGEN_WRAP_OVERLOADED_METHOD_ZERO_ARGS() which provide some - * additional syntactic sugar to make this easier. - * - * @tparam MethodType A pointer-to-method type. - * @tparam MethodPtr A pointer-to-method. - **/ -template -class InstanceMethodWrapper; - -/** - * @brief Partial specialization of InstanceMethodWrapper for methods without - * a const and/or volatile qualifier. - **/ -template -class InstanceMethodWrapper { - public: - static ReturnType Call(ClassType* object, - ArgumentTypes... args) { - return (object->*MethodPtr)(args...); - } -}; - -/** - * @brief Partial specialization of InstanceMethodWrapper for const-qualified - * methods. - **/ -template -class InstanceMethodWrapper { - public: - static ReturnType Call(const ClassType* object, - ArgumentTypes... args) { - return (object->*MethodPtr)(args...); - } -}; - -/** - * @brief Partial specialization of InstanceMethodWrapper for volatile-qualified - * methods. - **/ -template -class InstanceMethodWrapper< - ReturnType (ClassType::*)(ArgumentTypes...) volatile, - MethodPtr> { - public: - static ReturnType Call(volatile ClassType* object, - ArgumentTypes... args) { - return (object->*MethodPtr)(args...); - } -}; - -/** - * @brief Partial specialization of InstanceMethodWrapper for methods which are - * both const and volatile qualified. - **/ -template -class InstanceMethodWrapper< - ReturnType (ClassType::*)(ArgumentTypes...) const volatile, - MethodPtr> { - public: - static ReturnType Call(const volatile ClassType* object, - ArgumentTypes... args) { - return (object->*MethodPtr)(args...); - } -}; - -/** - * @brief Macro which provides some syntactic sugar for - * InstanceMethodWrapper::Call() by using - * automatic type deduction for the type of a pointer-to-method. - * - * @note This macro doesn't work with overloaded methods. See - * GPCODEGEN_WRAP_OVERLOADED_METHOD() instead. - * - * @param method_ptr A pointer-to-method. - **/ -#define GPCODEGEN_WRAP_METHOD(method_ptr) \ - ::gpcodegen::InstanceMethodWrapper::Call - -/** - * @brief Macro which provides syntactic sugar for InstanceMethodWrapper::Call() - * to be used on an overloaded class method. Provides a wrapper for this - * specific version of an overloaded method: - * `ReturnType ClassName::MethodName(...) Qualifiers` - * - * @note This macro is for overloads that take at least one argument. See - * GPCODEGEN_WRAP_OVERLOADED_METHOD_ZERO_ARGS() for overloads that take no - * argument (other than the implicit 'this' pointer). (Many compilers will - * actually allow zero variadic arguments in a variadic macro invocation, - * but this is an extension and not an official requirement of the C - * preprocessor standard). - * @note The return type of an overloaded function or method is not normally - * used in overload resolution by the C++ compiler, but ReturnType must - * nonetheless be specified correctly here because it is a part of the - * method's type signature, and there is no way to automatically deduce it - * when the method itself is a template parameter. - * - * @param ReturnType The type that the desired overloaded version of MethodName - * returns. - * @param ClassName The name of the class that the overloaded method is a member - * of. - * @param MethodName The name of the overloaded method. - * @param Qualifiers Method-level qualifiers (e.g. 'const', 'volatile', or - * 'const volatile'. This is OPTIONAL and should be left empty (by - * writing two successive commas in the macro argument list) if the - * desired version of the method has no cv-qualifiers. - * @param ... Variadic arguments for the types of parameters passed to the - * overloaded method. - **/ -#define GPCODEGEN_WRAP_OVERLOADED_METHOD(ReturnType, \ - ClassName, \ - MethodName, \ - Qualifiers, \ - ...) \ - ::gpcodegen::InstanceMethodWrapper< \ - ReturnType (ClassName::*)(__VA_ARGS__) Qualifiers, \ - &ClassName::MethodName> \ - ::Call - -/** - * @brief Special version of GPCODEGEN_WRAP_OVERLOADED_METHOD() for overloads - * that take no argument other than the implicit 'this' pointer. - * - * @param ReturnType The type that the desired overloaded version of MethodName - * returns. - * @param ClassName The name of the class that the overloaded method is a member - * of. - * @param MethodName The name of the overloaded method. - * @param Qualifiers Method-level qualifiers (e.g. 'const', 'volatile', or - * 'const volatile'. This is OPTIONAL and should be left empty (by - * writing a comma and then the closing right-paren immediately - * following) if the desired version of the method has no cv-qualifiers. - **/ -#define GPCODEGEN_WRAP_OVERLOADED_METHOD_ZERO_ARGS(ReturnType, \ - ClassName, \ - MethodName, \ - Qualifiers) \ - ::gpcodegen::InstanceMethodWrapper< \ - ReturnType (ClassName::*)() Qualifiers, \ - &ClassName::MethodName> \ - ::Call - -/** - * @brief Templated wrapper function that invokes the 'new' operator to create - * a new object on the heap. - * - * @tparam ClassType The type of the object to create. - * @tparam ArgumentTypes... The types of arguments (if any) to ClassType's - * constructor. - * @param args The arguments (if any) to forward to a constructor of ClassType. - * @return A new heap-allocated instance of ClassType (i.e. the result of - * calling `new ClassType(args...)`). - **/ -template -ClassType* WrapNew(ArgumentTypes... args) { - return new ClassType(args...); -} - -/** - * @brief Templated wrapper function that invokes the placement form of the - * 'new' operator to create a new object a a specific pre-allocated place - * in memory. - * - * @warning All of the usual caveats about placement new also apply here, e.g. - * memory must be allocated and of a large enough size and proper - * alignment for ClassType. - * - * @tparam ClassType The type of the object to create. - * @tparam ArgumentTypes... The types of arguments (if any) to ClassType's - * constructor. - * @param place The location in memory to place the new instance of ClassType. - * @param args The arguments (if any) to forward to a constructor of ClassType. - * @return A new instance of ClassType placed at place (i.e. the result of - * calling `new(place) ClassType(args...)`). - **/ -template -ClassType* WrapPlacementNew(void* place, ArgumentTypes... args) { - return new(place) ClassType(args...); -} - -/** - * @brief Templated wrapper function that invokes the 'new' operator with the - * move constructor for a class (if any) to move-from an existing object. - * - * @note This template works around the fact that CodegenUtils does not handle - * rvalue-references. It takes a pointer which it dereferences and - * converts to an rvalue-reference that is moved-from. - * - * @tparam ClassType The type of object to create. - * @param original A pointer to an instance of ClassType that is moved-from. - * @return A new heap-allocated instance of ClassType that is moved-from - * original. - **/ -template -ClassType* WrapNewMove(ClassType* original) { - return new ClassType(std::move(*original)); -} - -/** - * @brief Templated wrapper function that invokes the placement form of the - * 'new' operator with the move constructor for a class (if any) to - * move-from an existing object. - * - * @note This template works around the fact that CodegenUtils does not handle - * rvalue-references. It takes a pointer which it dereferences and - * converts to an rvalue-reference that is moved-from. - * @warning All of the usual caveats about placement new also apply here, e.g. - * memory must be allocated and of a large enough size and proper - * alignment for ClassType. - * - * @tparam ClassType The type of object to create. - * @param place The location in memory to place the new instance of ClassType. - * @param original A pointer to an instance of ClassType that is moved-from. - * @return A new instance of ClassType placed at place and moved-from original. - **/ -template -ClassType* WrapPlacementNewMove(void* place, ClassType* original) { - return new(place) ClassType(std::move(*original)); -} - -/** - * @brief Templated wrapper function that invokes the 'delete' operator to - * destroy an object. - * - * @tparam ClassType The type of object to destroy. - * @param object A pointer to an object to delete. - **/ -template -void WrapDelete(ClassType* object) { - delete object; -} - -/** - * @brief Templated wrapper function that invokes an object's destructor without - * deleting it (i.e. the destructor is run, but memory for the object - * itself is not freed). - * - * @tparam ClassType The type of object to invoke the destructor on. - * @param object A pointer to an object to invoke the destructor on. - **/ -template -void WrapDestructor(ClassType* object) { - object->~ClassType(); -} - -/** @} */ - -} // namespace gpcodegen - -#endif // GPCODEGEN_INSTANCE_METHOD_WRAPPERS_H_ diff --git a/src/backend/codegen/include/codegen/utils/macros.h b/src/backend/codegen/include/codegen/utils/macros.h deleted file mode 100644 index 5f572d09322f0cbc54aef027e061b6af15f98f9a..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/macros.h +++ /dev/null @@ -1,24 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// macros.h -// -// @doc: -// Utility macros -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_MACROS_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_MACROS_H_ - -#define DISALLOW_COPY_AND_ASSIGN(classname) \ - classname(const classname& orig) = delete; \ - classname& operator=(const classname &orig) = delete - -#endif // GPCODEGEN_MACROS_H_ -// EOF diff --git a/src/backend/codegen/include/codegen/utils/temporary_file.h b/src/backend/codegen/include/codegen/utils/temporary_file.h deleted file mode 100644 index d9b1fafa5a5416d630942a4d591f773213f884f8..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/temporary_file.h +++ /dev/null @@ -1,156 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// temporary_file.h -// -// @doc: -// A lightweight interface to a temporary file created by POSIX mkstemp(). -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_TEMPORARY_FILE_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_TEMPORARY_FILE_H_ - -#include -#include - -#include "codegen/utils/macros.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief A lightweight interface to a temporary file created by POSIX - * mkstemp(). - **/ -class TemporaryFile { - public: - /** - * @brief Constructor. Does not actually open the file for writing; see - * Open(). - * - * @param prefix The prefix path for the desired temporary file. Six random - * characters will be appended to this prefix when the file is actually - * created. - **/ - explicit TemporaryFile(const char* prefix); - - /** - * @brief Destructor. Closes the file if currently open. - **/ - ~TemporaryFile(); - - /** - * @brief Look up the path to the temporary directory that we should use. - * - * This function has the following order of precedence for lookups: - * 1. The standard UNIX "TMPDIR" environment variable, if set. - * 2. The value of the P_tmpdir macro provided by the C library, if - * defined. - * 3. "/tmp" - * - * @return The path to the temporary directory that should be used by this - * process. - **/ - static const char* TemporaryDirectoryPath(); - - /** - * @brief Attempt to actually open the temporary file. - * - * @return true on success, false on failure. - **/ - bool Open(); - - /** - * @return Whether the file is currently open. - **/ - bool IsOpen() const { - return fd_ != -1; - } - - /** - * @brief Get the actual filename of the temporary file (if open). - * - * @return The actual filename of the temporary file after a successful call - * to Open(), or the filename template (with six consecutive 'X' - * characters in place of random characters) if the file is not yet - * open. - **/ - const char* Filename() const { - return filename_buffer_; - } - - /** - * @brief Write an arbitrary byte array to an open TemporaryFile. - * - * @param buffer A pointer to the actual data to write. - * @param buffer_size The number of bytes to write. - * @return true if write was successful, false otherwise. - **/ - bool Write(const void* buffer, - const std::size_t buffer_size); - - /** - * @brief Convenience function to write a C++ string. - * - * @param str A string to write. - * @return true if write was successful, false otherwise. - **/ - bool WriteString(const std::string& str) { - return Write(str.c_str(), str.size()); - } - - /** - * @brief Convenience function to write an LLVM StringRef. - * - * @param string_ref A StringRef to write. - * @return true if write was successful, false otherwise. - **/ - bool WriteStringRef(const llvm::StringRef string_ref) { - return Write(string_ref.data(), string_ref.size()); - } - - /** - * @brief Convenience function to write an LLVM Twine. - * - * @param twine A Twine to write. - * @return true if write was successful, false otherwise. - **/ - bool WriteTwine(const llvm::Twine& twine) { - if (twine.isSingleStringRef()) { - return WriteStringRef(twine.getSingleStringRef()); - } else { - return WriteString(twine.str()); - } - } - - /** - * @brief Flush the TemporaryFile, ensuring that all previous writes are - * persistent on disk. - * - * @return true on success, false if failed to flush everything. - **/ - bool Flush(); - - private: - char* filename_buffer_; - int fd_; - - DISALLOW_COPY_AND_ASSIGN(TemporaryFile); -}; - -/** @} */ - -} // namespace gpcodegen - -#endif // GPCODEGEN_TEMPORARY_FILE_H_ diff --git a/src/backend/codegen/include/codegen/utils/utility.h b/src/backend/codegen/include/codegen/utils/utility.h deleted file mode 100644 index 2b305c9cd83591d3dd16f4ae3051d92ee6662e5d..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/utils/utility.h +++ /dev/null @@ -1,57 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// utility.h -// -// @doc: -// Convenience function to get a function argument by its position. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#ifndef GPCODEGEN_UTILITY_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_UTILITY_H_ - -#include -#include "llvm/IR/Function.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Convenience function to get a function argument by its position. - * - * @param function A function to get an argument from. - * @param position The ordered position of the desired argument. - * @return A pointer to the specified argument, or NULL if the specified - * position was beyond the end of function's arguments. - **/ -static inline llvm::Argument* ArgumentByPosition(llvm::Function* function, - const unsigned position) { - llvm::Function::arg_iterator it = function->arg_begin(); - if (it == function->arg_end()) { - return nullptr; - } - - for (unsigned idx = 0; idx < position; ++idx) { - if (++it == function->arg_end()) { - return nullptr; - } - } - - return &(*it); -} - -/** @} */ - -} // namespace gpcodegen - -#endif // GPCODEGEN_UTILITY_H_ -// EOF diff --git a/src/backend/codegen/include/codegen/var_expr_tree_generator.h b/src/backend/codegen/include/codegen/var_expr_tree_generator.h deleted file mode 100644 index d0ec7069951e6be2c73f55bef7f4d4c1fc1bb833..0000000000000000000000000000000000000000 --- a/src/backend/codegen/include/codegen/var_expr_tree_generator.h +++ /dev/null @@ -1,52 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// var_expr_tree_generator.h -// -// @doc: -// Object that generator code for variable expression. -// -//--------------------------------------------------------------------------- -#ifndef GPCODEGEN_VAR_EXPR_TREE_GENERATOR_H_ // NOLINT(build/header_guard) -#define GPCODEGEN_VAR_EXPR_TREE_GENERATOR_H_ - -#include "codegen/expr_tree_generator.h" -#include "codegen/codegen_wrapper.h" - -#include "llvm/IR/Value.h" - -namespace gpcodegen { - -/** \addtogroup gpcodegen - * @{ - */ - -/** - * @brief Object that generator code for variable expression. - **/ -class VarExprTreeGenerator : public ExprTreeGenerator { - public: - static bool VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree); - - bool GenerateCode(gpcodegen::GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) final; - protected: - /** - * @brief Constructor. - * - * @param expr_state Expression state - **/ - explicit VarExprTreeGenerator(const ExprState* expr_state); -}; - -/** @} */ -} // namespace gpcodegen - -#endif // GPCODEGEN_VAR_EXPR_TREE_GENERATOR_H_ diff --git a/src/backend/codegen/op_expr_tree_generator.cc b/src/backend/codegen/op_expr_tree_generator.cc deleted file mode 100644 index 7051a8acc0a5c0b49c66d629eb79c66510b38a64..0000000000000000000000000000000000000000 --- a/src/backend/codegen/op_expr_tree_generator.cc +++ /dev/null @@ -1,324 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// op_expr_tree_generator.h -// -// @doc: -// Object that generate code for operator expression. -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include -#include - -#include "codegen/expr_tree_generator.h" -#include "codegen/op_expr_tree_generator.h" -#include "codegen/pg_func_generator.h" -#include "codegen/pg_func_generator_interface.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/pg_arith_func_generator.h" -#include "codegen/pg_date_func_generator.h" -#include "codegen/pg_numeric_func_generator.h" - -#include "llvm/IR/IRBuilder.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "c.h" // NOLINT(build/include) -#include "nodes/execnodes.h" -#include "utils/elog.h" -#include "nodes/nodes.h" -#include "nodes/pg_list.h" -#include "nodes/primnodes.h" -} - -namespace llvm { -class Value; -} // namespace llvm - -using gpcodegen::OpExprTreeGenerator; -using gpcodegen::ExprTreeGenerator; -using gpcodegen::GpCodegenUtils; -using gpcodegen::PGFuncGeneratorInterface; -using gpcodegen::PGFuncGeneratorFn; -using gpcodegen::CodeGenFuncMap; -using llvm::IRBuilder; - - -CodeGenFuncMap -OpExprTreeGenerator::supported_function_; - -void OpExprTreeGenerator::InitializeSupportedFunction() { - if (!supported_function_.empty()) { return; } - - supported_function_[141] = std::unique_ptr( - new PGGenericFuncGenerator( - 141, - "int4mul", - &PGArithFuncGenerator::MulWithOverflow, - nullptr, - true)); - - supported_function_[149] = std::unique_ptr( - new PGIRBuilderFuncGenerator( - 149, - "int4le", - &IRBuilder<>::CreateICmpSLE, - true)); - - supported_function_[177] = std::unique_ptr( - new PGGenericFuncGenerator( - 177, - "int4pl", - &PGArithFuncGenerator::AddWithOverflow, - nullptr, - true)); - - // int4_sum is not a strict function. It checks if there are NULL arguments - // and performs actions accordingly. - supported_function_[1841] = std::unique_ptr( - new PGGenericFuncGenerator( - 1841, - "int4_sum", - &PGArithFuncGenerator::AddWithOverflow, - &PGArithFuncGenerator:: - CreateArgumentNullChecks, - false)); - - supported_function_[181] = std::unique_ptr( - new PGGenericFuncGenerator( - 181, - "int4mi", - &PGArithFuncGenerator::SubWithOverflow, - nullptr, - true)); - - supported_function_[463] = std::unique_ptr( - new PGGenericFuncGenerator( - 463, - "int8pl", - &PGArithFuncGenerator::AddWithOverflow, - nullptr, - true)); - - supported_function_[1219] = std::unique_ptr( - new PGGenericFuncGenerator( - 1219, - "int8inc", - &PGArithUnaryFuncGenerator::IncWithOverflow, - nullptr, - true)); - - // int8inc is not strict, but it does not check if there are NULL attributes. - supported_function_[2803] = std::unique_ptr( - new PGGenericFuncGenerator( - 2803, - "int8inc", - &PGArithUnaryFuncGenerator::IncWithOverflow, - nullptr, - false)); - - supported_function_[216] = std::unique_ptr( - new PGGenericFuncGenerator( - 216, - "float8mul", - &PGArithFuncGenerator::MulWithOverflow, - nullptr, - true)); - - supported_function_[218] = std::unique_ptr( - new PGGenericFuncGenerator( - 218, - "float8pl", - &PGArithFuncGenerator::AddWithOverflow, - nullptr, - true)); - - supported_function_[219] = std::unique_ptr( - new PGGenericFuncGenerator( - 219, - "float8mi", - &PGArithFuncGenerator::SubWithOverflow, - nullptr, - true)); - - supported_function_[1088] = std::unique_ptr( - new PGIRBuilderFuncGenerator( - 1088, "date_le", &IRBuilder<>::CreateICmpSLE, - true)); - - supported_function_[2339] = std::unique_ptr( - new PGGenericFuncGenerator( - 2339, - "date_le_timestamp", - &PGDateFuncGenerator::DateLETimestamp, - nullptr, - true)); - - supported_function_[1963] = std::unique_ptr( - new PGGenericFuncGenerator( - 1963, - "int4_avg_accum", - &PGNumericFuncGenerator::GenerateIntFloatAvgAccum, - nullptr, - true)); - - supported_function_[3108] = std::unique_ptr( - new PGGenericFuncGenerator( - 3108, - "float8_avg_accum", - &PGNumericFuncGenerator::GenerateIntFloatAvgAccum, - nullptr, - true)); - - supported_function_[6009] = std::unique_ptr( - new PGGenericFuncGenerator( - 6009, - "int8_avg_amalg", - &PGNumericFuncGenerator::GenerateIntFloatAvgAmalg, - nullptr, - true)); - - supported_function_[3111] = std::unique_ptr( - new PGGenericFuncGenerator( - 3111, - "float8_avg_amalg", - &PGNumericFuncGenerator::GenerateIntFloatAvgAmalg, - nullptr, - true)); -} - -PGFuncGeneratorInterface* OpExprTreeGenerator::GetPGFuncGenerator( - unsigned int oid) { - InitializeSupportedFunction(); - auto itr = supported_function_.find(oid); - if (itr == supported_function_.end()) { - return nullptr; - } - return itr->second.get(); -} - -OpExprTreeGenerator::OpExprTreeGenerator( - const ExprState* expr_state, - std::vector< - std::unique_ptr>&& arguments) // NOLINT(build/c++11) - : ExprTreeGenerator(expr_state, ExprTreeNodeType::kOperator), - arguments_(std::move(arguments)) { -} - -bool OpExprTreeGenerator::VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree) { - assert(nullptr != expr_state && - nullptr != expr_state->expr && - T_OpExpr == nodeTag(expr_state->expr) && - nullptr != expr_tree); - - OpExpr* op_expr = reinterpret_cast(expr_state->expr); - expr_tree->reset(nullptr); - PGFuncGeneratorInterface* pg_func_gen = GetPGFuncGenerator(op_expr->opfuncid); - if (nullptr == pg_func_gen) { - elog(DEBUG1, "Unsupported operator %d.", op_expr->opfuncid); - return false; - } - - List *arguments = reinterpret_cast(expr_state)->args; - assert(nullptr != arguments); - // In ExecEvalFuncArgs - assert(list_length(arguments) == - static_cast(pg_func_gen->GetTotalArgCount())); - - ListCell *arg = nullptr; - bool supported_tree = true; - std::vector> expr_tree_arguments; - foreach(arg, arguments) { - // retrieve argument's ExprState - ExprState *argstate = reinterpret_cast(lfirst(arg)); - assert(nullptr != argstate); - std::unique_ptr arg(nullptr); - supported_tree &= ExprTreeGenerator::VerifyAndCreateExprTree(argstate, - gen_info, - &arg); - if (!supported_tree) { - break; - } - assert(nullptr != arg); - expr_tree_arguments.push_back(std::move(arg)); - } - if (!supported_tree) { - return supported_tree; - } - expr_tree->reset(new OpExprTreeGenerator(expr_state, - std::move(expr_tree_arguments))); - return true; -} - -bool OpExprTreeGenerator::GenerateCode(GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) { - assert(nullptr != llvm_out_value); - *llvm_out_value = nullptr; - OpExpr* op_expr = reinterpret_cast(expr_state()->expr); - CodeGenFuncMap::iterator itr = supported_function_.find(op_expr->opfuncid); - auto irb = codegen_utils->ir_builder(); - - if (itr == supported_function_.end()) { - // Operators are stored in pg_proc table. - // See postgres.bki for more details. - elog(WARNING, "Unsupported operator %d.", op_expr->opfuncid); - return false; - } - - // Get the interface to generate code for operator function - PGFuncGeneratorInterface* pg_func_interface = itr->second.get(); - assert(nullptr != pg_func_interface); - - if (arguments_.size() != pg_func_interface->GetTotalArgCount()) { - elog(WARNING, "Expected argument size to be %lu\n", - pg_func_interface->GetTotalArgCount()); - return false; - } - bool arg_generated = true; - std::vector llvm_arguments; - std::vector llvm_arguments_isNull; - for (auto& arg : arguments_) { - llvm::Value* llvm_arg_isnull_ptr = irb->CreateAlloca( - codegen_utils->GetType(), nullptr, "isNull"); - irb->CreateStore(codegen_utils->GetConstant(false), - llvm_arg_isnull_ptr); - - llvm::Value* llvm_arg = nullptr; - arg_generated &= arg->GenerateCode(codegen_utils, - gen_info, - &llvm_arg, - llvm_arg_isnull_ptr); - if (!arg_generated) { - return false; - } - llvm_arguments.push_back(llvm_arg); - llvm_arguments_isNull.push_back(irb->CreateLoad(llvm_arg_isnull_ptr)); - } - llvm::Value* llvm_op_value = nullptr; - PGFuncGeneratorInfo pg_func_info(gen_info.llvm_main_func, - gen_info.llvm_error_block, - llvm_arguments, - llvm_arguments_isNull); - - bool retval = pg_func_interface->GenerateCode(codegen_utils, - pg_func_info, - &llvm_op_value, - llvm_isnull_ptr); - - // convert return type to Datum - *llvm_out_value = codegen_utils->CreateCppTypeToDatumCast(llvm_op_value); - return retval; -} diff --git a/src/backend/codegen/pg_date_func_generator.cc b/src/backend/codegen/pg_date_func_generator.cc deleted file mode 100644 index a7d43727c1e1d6cb41d07fc80ee4a69854eff2cc..0000000000000000000000000000000000000000 --- a/src/backend/codegen/pg_date_func_generator.cc +++ /dev/null @@ -1,97 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_date_func_generator.cc -// -// @doc: -// Base class for date functions to generate code -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include - -#include "codegen/pg_arith_func_generator.h" -#include "codegen/pg_date_func_generator.h" -#include "codegen/pg_func_generator_interface.h" -#include "codegen/utils/gp_codegen_utils.h" - -#include "llvm/IR/Constant.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Value.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "c.h" // NOLINT(build/include) -#include "utils/timestamp.h" -} - -using gpcodegen::GpCodegenUtils; -using gpcodegen::PGDateFuncGenerator; -using gpcodegen::PGFuncGeneratorInfo; - -bool PGDateFuncGenerator::DateLETimestamp( - gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - - llvm::IRBuilder<>* irb = codegen_utils->ir_builder(); - - // llvm_args[0] is of date type - llvm::Value* llvm_arg0_Timestamp = GenerateDate2Timestamp( - codegen_utils, pg_func_info); - - // timestamp_cmp_internal {{{ -#ifdef HAVE_INT64_TIMESTAMP - *llvm_out_value = - irb->CreateICmpSLE(llvm_arg0_Timestamp, pg_func_info.llvm_args[1]); -#else - // TODO(nikos): We do not support NaNs. - elog(DEBUG1, "Timestamp != int_64: NaNs are not supported."); - return false; -#endif - // }}} - - return true; -} - -llvm::Value* PGDateFuncGenerator::GenerateDate2Timestamp( - GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info) { - - assert(pg_func_info.llvm_args.size() > 1); - assert(nullptr != pg_func_info.llvm_args[0]); - assert(nullptr != pg_func_info.llvm_args[0]->getType()); - -#ifdef HAVE_INT64_TIMESTAMP - - llvm::Value *llvm_USECS_PER_DAY = codegen_utils-> - GetConstant(USECS_PER_DAY); - - llvm::Value* llvm_out_value = nullptr; - PGFuncGeneratorInfo pg_timestamp_func_info( - pg_func_info.llvm_main_func, - pg_func_info.llvm_error_block, - {pg_func_info.llvm_args[0], llvm_USECS_PER_DAY}, - pg_func_info.llvm_args_isNull); - - PGArithFuncGenerator::ArithOpWithOverflow( - codegen_utils, - &gpcodegen::GpCodegenUtils::CreateMulOverflow, - "date out of range for timestamp", - pg_timestamp_func_info, - &llvm_out_value); - - return llvm_out_value; -#else - llvm::Value* llvm_arg_64 = codegen_utils->CreateCast( - pg_func_info.llvm_args[0]); - llvm::Value *llvm_SECS_PER_DAY = codegen_utils-> - GetConstant(SECS_PER_DAY); - return codegen_utils->ir_builder()->CreateMul(llvm_arg_64, llvm_SECS_PER_DAY); -#endif -} diff --git a/src/backend/codegen/pg_numeric_func_generator.cc b/src/backend/codegen/pg_numeric_func_generator.cc deleted file mode 100644 index 91ec03e287efb37150228227e21dae51040a5601..0000000000000000000000000000000000000000 --- a/src/backend/codegen/pg_numeric_func_generator.cc +++ /dev/null @@ -1,160 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// pg_numeric_func_generator.cc -// -// @doc: -// Base class for numeric functions to generate code -// -//--------------------------------------------------------------------------- - -#include "codegen/pg_numeric_func_generator.h" - -using gpcodegen::GpCodegenUtils; -using gpcodegen::PGNumericFuncGenerator; -using gpcodegen::PGFuncGeneratorInfo; - -bool PGNumericFuncGenerator::GenerateIntFloatAvgAmalg( - gpcodegen::GpCodegenUtils* codegen_utils, - const gpcodegen::PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - // TODO(nikos): Can we figure if we need to detoast during generation? - llvm::Function* llvm_pg_detoast_datum = codegen_utils-> - GetOrRegisterExternalFunction(pg_detoast_datum, "pg_detoast_datum"); - - auto irb = codegen_utils->ir_builder(); - llvm::Function* current_function = irb->GetInsertBlock()->getParent(); - - llvm::Value* llvm_in_tr0 = - irb->CreateCall(llvm_pg_detoast_datum, {pg_func_info.llvm_args[0]}); - llvm::Value* llvm_in_tr1 = - irb->CreateCall(llvm_pg_detoast_datum, {pg_func_info.llvm_args[1]}); - - // if(transdata == NULL || - // VARSIZE(transdata) != sizeof(IntFloatAvgTransdata)) { ... } - llvm::Value* llvm_tr0 = nullptr; - GeneratePallocTransdata(codegen_utils, llvm_in_tr0, &llvm_tr0); - - // if(tr1 == NULL || VARSIZE(tr1) != sizeof(IntFloatAvgTransdata)) - llvm::Value* llvm_varlena_null_size_cond = nullptr; - GenerateVarlenSizeCheck(codegen_utils, - llvm_in_tr1, - codegen_utils-> - GetConstant(sizeof(IntFloatAvgTransdata)), - &llvm_varlena_null_size_cond); - - llvm::BasicBlock* update_block = codegen_utils->CreateBasicBlock( - "update_block", current_function); - llvm::BasicBlock* end_update_block = codegen_utils->CreateBasicBlock( - "end_update_block", current_function); - irb->CreateCondBr(llvm_varlena_null_size_cond, - end_update_block, - update_block); - - irb->SetInsertPoint(update_block); - // tr0->count += tr1->count; - // tr0->sum += tr1->sum; {{ - llvm::Value* llvm_tr0_sum_ptr = - codegen_utils->GetPointerToMember(llvm_tr0, &IntFloatAvgTransdata::sum); - llvm::Value* llvm_tr0_count_ptr = - codegen_utils->GetPointerToMember(llvm_tr0, &IntFloatAvgTransdata::count); - llvm::Value* llvm_tr1_sum_ptr = codegen_utils-> - GetPointerToMember(llvm_in_tr1, &IntFloatAvgTransdata::sum); - llvm::Value* llvm_tr1_count_ptr = codegen_utils-> - GetPointerToMember(llvm_in_tr1, &IntFloatAvgTransdata::count); - irb->CreateStore(irb->CreateFAdd( - irb->CreateLoad(llvm_tr0_sum_ptr), - irb->CreateLoad(llvm_tr1_sum_ptr)), - llvm_tr0_sum_ptr); - irb->CreateStore(irb->CreateAdd( - irb->CreateLoad(llvm_tr0_count_ptr), - irb->CreateLoad(llvm_tr1_count_ptr)), - llvm_tr0_count_ptr); - // }} - irb->CreateBr(end_update_block); - irb->SetInsertPoint(end_update_block); - - *llvm_out_value = llvm_tr0; - return true; -} - -bool PGNumericFuncGenerator::GenerateVarlenSizeCheck( - gpcodegen::GpCodegenUtils* codegen_utils, - llvm::Value* llvm_ptr, - llvm::Value* llvm_size, - llvm::Value** llvm_out_cond) { - - llvm::Function* llvm_varsize = codegen_utils-> - GetOrRegisterExternalFunction(VARSIZE_regular, "VARSIZE_regular"); - - auto irb = codegen_utils->ir_builder(); - - *llvm_out_cond = irb->CreateOr( - irb->CreateICmpEQ(llvm_ptr, codegen_utils->GetConstant(nullptr)), - irb->CreateICmpNE( - irb->CreateCall(llvm_varsize, {llvm_ptr}), - llvm_size)); - return true; -} - -bool PGNumericFuncGenerator::GeneratePallocTransdata( - gpcodegen::GpCodegenUtils* codegen_utils, - llvm::Value* llvm_in_transdata_ptr, - llvm::Value** llvm_out_trandata_ptr) { - - llvm::Function* llvm_set_varsize = codegen_utils-> - GetOrRegisterExternalFunction(SET_VARSIZE_regular, "SET_VARSIZE_regular"); - - auto irb = codegen_utils->ir_builder(); - - llvm::BasicBlock* entry_block = irb->GetInsertBlock(); - llvm::Function* current_function = entry_block->getParent(); - llvm::BasicBlock* transdata_palloc_block = codegen_utils-> - CreateBasicBlock("transdata_palloc_block", current_function); - llvm::BasicBlock* end_transdata_palloc_block = codegen_utils-> - CreateBasicBlock("end_transdata_palloc_block", current_function); - - // if(tr0 == NULL || VARSIZE(tr0) != sizeof(IntFloatAvgTransdata)) {{ - llvm::Value* palloc_cond = nullptr; - GenerateVarlenSizeCheck(codegen_utils, - llvm_in_transdata_ptr, - codegen_utils->GetConstant( - sizeof(IntFloatAvgTransdata)), - &palloc_cond); - irb->CreateCondBr(palloc_cond, - transdata_palloc_block, - end_transdata_palloc_block); - // }} - irb->SetInsertPoint(transdata_palloc_block); - // tr0 = (IntFloatAvgTransdata *) palloc(sizeof(IntFloatAvgTransdata)); - llvm::Value* llvm_palloc_transdata_ptr = - EXPAND_CREATE_PALLOC(codegen_utils, sizeof(IntFloatAvgTransdata)); - // SET_VARSIZE(tr0, sizeof(IntFloatAvgTransdata)); - irb->CreateCall(llvm_set_varsize, { - llvm_palloc_transdata_ptr, - codegen_utils->GetConstant(sizeof(IntFloatAvgTransdata))}); - // tr0->sum = 0; - irb->CreateStore(codegen_utils->GetConstant(0), - codegen_utils->GetPointerToMember( - llvm_palloc_transdata_ptr, &IntFloatAvgTransdata::sum)); - // tr0->count = 0; - irb->CreateStore(codegen_utils->GetConstant(0), - codegen_utils->GetPointerToMember( - llvm_palloc_transdata_ptr, - &IntFloatAvgTransdata::count)); - - irb->CreateBr(end_transdata_palloc_block); - irb->SetInsertPoint(end_transdata_palloc_block); - assert(llvm_in_transdata_ptr->getType() == - llvm_palloc_transdata_ptr->getType()); - llvm::PHINode* llvm_transdata_ptr = irb-> - CreatePHI(llvm_in_transdata_ptr->getType(), 2); - llvm_transdata_ptr->addIncoming(llvm_in_transdata_ptr, entry_block); - llvm_transdata_ptr->addIncoming(llvm_palloc_transdata_ptr, - transdata_palloc_block); - - *llvm_out_trandata_ptr = llvm_transdata_ptr; - return true; -} diff --git a/src/backend/codegen/slot_getattr_codegen.cc b/src/backend/codegen/slot_getattr_codegen.cc deleted file mode 100644 index fc776ca41a8519848ef4fb9897f4b93943026b08..0000000000000000000000000000000000000000 --- a/src/backend/codegen/slot_getattr_codegen.cc +++ /dev/null @@ -1,714 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// slot_getattr_codegen.cc -// -// @doc: -// Contains slot_getattr generator -// -//--------------------------------------------------------------------------- -#include -#include -#include - -#include "codegen/base_codegen.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/codegen_config.h" -#include "codegen/codegen_manager.h" -#include "codegen/slot_getattr_codegen.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/utils/utility.h" - -#include "llvm/IR/Argument.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Verifier.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "c.h" // NOLINT(build/include) -#include "executor/tuptable.h" -#include "utils/elog.h" -#include "access/htup.h" -#include "access/memtup.h" -#include "access/tupdesc.h" -#include "access/tupmacs.h" -#include "catalog/pg_attribute.h" - -extern void slot_deform_tuple(TupleTableSlot* slot, int nattr); -} - -namespace llvm { -class BasicBlock; -class Value; -} // namespace llvm - - -using gpcodegen::SlotGetAttrCodegen; - -constexpr char SlotGetAttrCodegen::kSlotGetAttrPrefix[]; - -std::unordered_map< - gpcodegen::CodegenManager*, SlotGetAttrCodegen::SlotGetAttrCodegenCache> - SlotGetAttrCodegen::codegen_cache_by_manager; - -SlotGetAttrCodegen* SlotGetAttrCodegen::GetCodegenInstance( - gpcodegen::CodegenManager* manager, - TupleTableSlot *slot, - int max_attr) { - - // TODO(krajaraman, frahman) : Refactor so creation happens through - // CodegenManager::CreateAndEnrollGenerator. In that case, we don't - // need to do this `if` condition here. - if (!CodegenConfig::IsGeneratorEnabled()) { - return nullptr; - } - - // Create an cache entry for this manager if it doesn't already exist - auto it = codegen_cache_by_manager[manager].find(slot); - - SlotGetAttrCodegen* generator = nullptr; - if (it != codegen_cache_by_manager[manager].end()) { - // For a slot already seen before, update max_attr value only - generator = it->second; - generator->max_attr_ = std::max(generator->max_attr_, max_attr); - } else { - // TODO(krajaraman, frahman) : Refactor so creation happens through - // CodegenManager::CreateAndEnrollGenerator. - // For a slot we haven't see before, create and add a new object - generator = new SlotGetAttrCodegen(manager, slot, max_attr); - codegen_cache_by_manager[manager].insert(std::make_pair(slot, generator)); - // Enroll this in the manager so that it can take ownership - manager->EnrollCodeGenerator(CodegenFuncLifespan_Parameter_Invariant, - generator); - } - - assert(nullptr != generator); - return generator; -} - -void SlotGetAttrCodegen::RemoveSelfFromCache() { - CodegenManager* manager = this->manager(); - - assert(manager != nullptr && - codegen_cache_by_manager.find(manager) != - codegen_cache_by_manager.end()); - std::unordered_map& cache_map = - codegen_cache_by_manager[manager]; - auto it = codegen_cache_by_manager[manager].find(slot_); - - assert(it != cache_map.end() && it->second == this); - // Delete this SlotGetAttr from it's manager - cache_map.erase(it); - - // Clean up manager data out of the map if this was the last instance - if (cache_map.empty()) { - codegen_cache_by_manager.erase(manager); - } -} - -SlotGetAttrCodegen::~SlotGetAttrCodegen() { - RemoveSelfFromCache(); -} - -bool SlotGetAttrCodegen::GenerateCodeInternal( - gpcodegen::GpCodegenUtils* codegen_utils) { - - // This function may be called multiple times, but it should generate code - // only once - if (IsGenerated()) { - return true; - } - - // Give the function a human readable name - std::string function_name = GetUniqueFuncName() + "_" + - std::to_string(reinterpret_cast(slot_)) + "_" + - std::to_string(max_attr_); - llvm::Function* function = CreateFunction(codegen_utils, - function_name); - - bool isGenerated = GenerateSlotGetAttr( - codegen_utils, slot_, max_attr_, function); - - if (isGenerated) { - elog(DEBUG1, "slot_getattr was generated successfully!"); - assert(nullptr != function); - llvm_function_ = function; - return true; - } else { - elog(DEBUG1, "slot_getattr generation failed!"); - llvm_function_ = nullptr; - return false; - } -} - -bool SlotGetAttrCodegen::GenerateSlotGetAttr( - gpcodegen::GpCodegenUtils* codegen_utils, - TupleTableSlot *slot, - int max_attr, - llvm::Function* slot_getattr_func) { - - // So looks like we're going to generate code - auto irb = codegen_utils->ir_builder(); - - // BasicBlock of function entry. - llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock( - "entry", slot_getattr_func); - // BasicBlock for checking correct slot - llvm::BasicBlock* slot_check_block = codegen_utils->CreateBasicBlock( - "slot_check", slot_getattr_func); - // BasicBlock for checking virtual tuple type. - llvm::BasicBlock* virtual_tuple_check_block = codegen_utils->CreateBasicBlock( - "virtual_tuple_check", slot_getattr_func); - // BasicBlock for checking memtuple type. - llvm::BasicBlock* memtuple_check_block = codegen_utils->CreateBasicBlock( - "memtuple_check", slot_getattr_func); - // BasicBlock for handling memtuple. - llvm::BasicBlock* memtuple_block = codegen_utils->CreateBasicBlock( - "memtuple", slot_getattr_func); - // BasicBlock for heap tuple check. - llvm::BasicBlock* heap_tuple_check_block = codegen_utils->CreateBasicBlock( - "heap_tuple_check", slot_getattr_func); - // BasicBlock for main. - llvm::BasicBlock* main_block = codegen_utils->CreateBasicBlock( - "main", slot_getattr_func); - // BasicBlock for final computations. - llvm::BasicBlock* final_block = codegen_utils->CreateBasicBlock( - "final", slot_getattr_func); - // BasicBlock for return - llvm::BasicBlock* return_block = codegen_utils->CreateBasicBlock( - "return", slot_getattr_func); - // BasicBlock for fall back. - llvm::BasicBlock* fallback_block = codegen_utils->CreateBasicBlock( - "fallback", slot_getattr_func); - - // External functions - llvm::Function* llvm_memset = - codegen_utils->GetOrRegisterExternalFunction(memset, "memset"); - llvm::Function* llvm_memtuple_getattr = - codegen_utils->GetOrRegisterExternalFunction(memtuple_getattr, - "memtuple_getattr"); - llvm::Function* llvm_slot_deform_tuple = - codegen_utils->GetOrRegisterExternalFunction(slot_deform_tuple, - "slot_deform_tuple"); - llvm::Function* llvm_att_align_nominal = - codegen_utils->GetOrRegisterExternalFunction(att_align_nominal_regular, - "att_align_nominal"); - - // Generation-time constants - llvm::Value* llvm_slot = codegen_utils->GetConstant(slot); - llvm::Value* llvm_max_attr = codegen_utils->GetConstant(max_attr); - - // Function arguments to slot_getattr - llvm::Value* llvm_slot_arg = ArgumentByPosition(slot_getattr_func, 0); - llvm::Value* llvm_attnum_arg = ArgumentByPosition(slot_getattr_func, 1); - llvm::Value* llvm_isnull_ptr_arg = ArgumentByPosition(slot_getattr_func, 2); - - // Entry block - // ----------- - - irb->SetInsertPoint(entry_block); -#ifdef CODEGEN_DEBUG - EXPAND_CREATE_ELOG(codegen_utils, - DEBUG1, - "Codegen'ed slot_getattr called!"); -#endif - // Retrieve slot's PRIVATE variables - llvm::Value* llvm_slot_PRIVATE_tts_isnull /* bool* */ = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_isnull)); - llvm::Value* llvm_slot_PRIVATE_tts_values /* Datum* */ = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_values)); - llvm::Value* llvm_slot_PRIVATE_tts_nvalid_ptr /* int* */ = - codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_nvalid); - llvm::Value* llvm_slot_tts_mt_bind /* MemTupleBinding* */ = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::tts_mt_bind)); - - // We start a sequence of checks to ensure that everything is fine and - // we do not need to fall back. - irb->CreateBr(slot_check_block); - - - // Slot check block - // ---------------- - irb->SetInsertPoint(slot_check_block); - // Compare slot given during code generation and the one passed - // in as an argument to slot_getattr - irb->CreateCondBr( - irb->CreateICmpEQ(llvm_slot, llvm_slot_arg), - virtual_tuple_check_block /* true */, - fallback_block /* false */); - - // Virtual tuple check block - // ------------------------- - - irb->SetInsertPoint(virtual_tuple_check_block); - llvm::Value* llvm_slot_PRIVATE_tts_flags_ptr = - codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_flags); - // (slot->PRIVATE_tts_flags & TTS_VIRTUAL) != 0 (= TupHasVirtualTuple(slot)) - llvm::Value* llvm_tuple_is_virtual_1 = irb->CreateICmpNE( - irb->CreateAnd( - irb->CreateLoad(llvm_slot_PRIVATE_tts_flags_ptr), - codegen_utils->GetConstant(TTS_VIRTUAL)), - codegen_utils->GetConstant(0)); - // slot->PRIVATE_tts_nvalid >= attnum - llvm::Value* llvm_tuple_is_virtual_2 = irb-> - CreateICmpSGE(irb->CreateLoad(llvm_slot_PRIVATE_tts_nvalid_ptr), - llvm_attnum_arg); - // TupHasVirtualTuple(slot) && slot->PRIVATE_tts_nvalid >= attnum - llvm::Value* llvm_tuple_is_virtual = irb->CreateAnd(llvm_tuple_is_virtual_1, - llvm_tuple_is_virtual_2); - // If it is indeed a virtual tuple, we must have already deformed this tuple, - // so go straight to returning the contained values - irb->CreateCondBr( - llvm_tuple_is_virtual, - return_block /* true */, - memtuple_check_block /* false */); - - - // Memtuple type check block - // ---------------------- - - irb->SetInsertPoint(memtuple_check_block); - - // slot->PRIVATE_tts_memtuple != NULL - llvm::Value* llvm_slot_PRIVATE_tts_memtuple = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_memtuple)); - llvm::Value* llvm_tuple_has_memtuple = irb->CreateICmpNE( - llvm_slot_PRIVATE_tts_memtuple, - codegen_utils->GetConstant((MemTuple) NULL)); - - irb->CreateCondBr( - llvm_tuple_has_memtuple, - memtuple_block /*true*/, heap_tuple_check_block /*false*/); - - // Memtuple Block - // -------------- - - irb->SetInsertPoint(memtuple_block); - // return memtuple_getattr(slot->PRIVATE_tts_memtuple, - // slot->tts_mt_bind, attnum, isnull); - llvm::Value* llvm_memtuple_ret = irb->CreateCall(llvm_memtuple_getattr, { - llvm_slot_PRIVATE_tts_memtuple, - llvm_slot_tts_mt_bind, - llvm_attnum_arg, - llvm_isnull_ptr_arg}); - irb->CreateRet(llvm_memtuple_ret); - - - // HeapTuple check block - // --------------------- - // We fall back if the given tuple is not a heaptuple. - - irb->SetInsertPoint(heap_tuple_check_block); - // In _slot_getsomeattrs, check if: TupGetHeapTuple(slot) != NULL - llvm::Value* llvm_slot_PRIVATE_tts_heaptuple = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_heaptuple)); - llvm::Value* llvm_tuple_has_heaptuple = irb->CreateICmpNE( - llvm_slot_PRIVATE_tts_heaptuple, - codegen_utils->GetConstant((HeapTuple) NULL)); - irb->CreateCondBr(llvm_tuple_has_heaptuple, - main_block /*true*/, - fallback_block /*false*/); - - // Main block - // ---------- - // We generate code for slot_deform_tuple. Note that we do not need to check - // the type of the tuple, since previous check guarantees that the we use the - // enrolled slot, which includes a heap tuple. - irb->SetInsertPoint(main_block); - - llvm::Value* llvm_heaptuple_t_data = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_slot_PRIVATE_tts_heaptuple, - &HeapTupleData::t_data)); - - llvm::Value* llvm_heaptuple_t_data_t_infomask = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_heaptuple_t_data, &HeapTupleHeaderData::t_infomask)); - - // Implementation for : {{{ - // attno = HeapTupleHeaderGetNatts(tuple->t_data); - // attno = Min(attno, attnum); - llvm::Value* llvm_heaptuple_t_data_t_infomask2 = - irb->CreateLoad(codegen_utils->GetPointerToMember( - llvm_heaptuple_t_data, - &HeapTupleHeaderData::t_infomask2)); - llvm::Value* llvm_attno_t0 = - irb->CreateZExt( - irb->CreateAnd(llvm_heaptuple_t_data_t_infomask2, - codegen_utils->GetConstant(HEAP_NATTS_MASK)), - codegen_utils->GetType()); - - llvm::Value* llvm_attno = - irb->CreateSelect( - irb->CreateICmpSLT(llvm_attno_t0, llvm_max_attr), - llvm_attno_t0, - llvm_max_attr); - // }}} - - llvm::Value* llvm_heaptuple_t_data_t_hoff = irb->CreateLoad( - codegen_utils->GetPointerToMember(llvm_heaptuple_t_data, - &HeapTupleHeaderData::t_hoff)); - - // tup->t_bits - llvm::Value* llvm_heaptuple_t_data_t_bits = - codegen_utils->GetPointerToMember( - llvm_heaptuple_t_data, - &HeapTupleHeaderData::t_bits); - - // Find the start of input data byte array - // same as tp in slot_deform_tuple (pointer to tuple data) - llvm::Value* llvm_tuple_data_ptr = irb->CreateInBoundsGEP( - llvm_heaptuple_t_data, {llvm_heaptuple_t_data_t_hoff}); - - // int off = 0; - llvm::Value* llvm_off_ptr = irb->CreateAlloca( - codegen_utils->GetType(), nullptr, "off"); - irb->CreateStore(codegen_utils->GetConstant(0), llvm_off_ptr); - - TupleDesc tupleDesc = slot->tts_tupleDescriptor; - Form_pg_attribute* att = tupleDesc->attrs; - - // For each attribute we use three blocks to i) check for null, - // ii) handle null case, and iii) handle not null case. - // Note that if an attribute cannot be null then we do not create - // the last two blocks. - llvm::BasicBlock* attribute_block = nullptr; - llvm::BasicBlock* is_null_block = nullptr; - llvm::BasicBlock* is_not_null_block = nullptr; - - // points to the attribute_block of the next attribute - llvm::BasicBlock* next_attribute_block = nullptr; - - // block of first attribute - attribute_block = codegen_utils->CreateBasicBlock( - "attribute_block_"+0, slot_getattr_func); - - irb->CreateBr(attribute_block); - - bool varlen_attrs_found = false; - int attnum = 0; - for (; attnum < max_attr; ++attnum) { - Form_pg_attribute thisatt = att[attnum]; - - // If any thisatt is varlen - if (thisatt->attlen < 0) { - // When we have variable length attributes, we can no longer benefit - // from codegen, since the next offset needs to be computed after the - // tuple is read into memory. - if (attnum < codegen_varlen_tolerance) { - // Also, if one of the first few attributes is varlen, might as well - // call slot_deform_tuple directly, instead of going through a codegen'd - // wrapper function. - return false; - } - varlen_attrs_found = true; - break; - } - - // ith attribute's block - // ---------- - // Check if attribute can be null. If yes, then create blocks - // to handle null and not null cases. - irb->SetInsertPoint(attribute_block); - - // Create the block of (attnum+1)th attribute and jump to it after - // you finish with current attribute. - next_attribute_block = codegen_utils->CreateBasicBlock( - "attribute_block_" + std::to_string(attnum+1), slot_getattr_func); - - llvm::Value* llvm_next_values_ptr = - irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_values, - {codegen_utils->GetConstant(attnum)}); - - // If attribute can be null, then create blocks to handle - // null and not null cases. - if (!thisatt->attnotnull) { - // Create blocks - is_null_block = codegen_utils->CreateBasicBlock( - "is_null_block_" + std::to_string(attnum), slot_getattr_func); - is_not_null_block = codegen_utils->CreateBasicBlock( - "is_not_null_block_" + std::to_string(attnum), - slot_getattr_func); - - llvm::Value* llvm_attnum = codegen_utils->GetConstant(attnum); - - // Check if attribute is null - // if (hasnulls && att_isnull(attnum, bp)) {{{ - // att_isnull(attnum, bp): !((BITS)[(ATT) >> 3] & (1<<((ATT) & 0x07))) {{ - // Expr_1: ((BITS)[(ATT) >> 3] - llvm::Value* llvm_expr_1 = irb->CreateLoad(irb->CreateGEP( - llvm_heaptuple_t_data_t_bits, - {codegen_utils->GetConstant(0), - irb->CreateAShr(llvm_attnum, codegen_utils->GetConstant(3))})); - // Expr_2: (1 << ((ATT) & 0x07)) - llvm::Value* llvm_expr_2 = irb->CreateTrunc( - irb->CreateShl(codegen_utils->GetConstant(1), - irb->CreateAnd(llvm_attnum, - codegen_utils->GetConstant(0x07))), - codegen_utils->GetType()); - // (Exp_1 & Expr_2) - llvm::Value* llvm_expr1_and_expr2 = irb->CreateICmpNE( - irb->CreateAnd(llvm_expr_1, llvm_expr_2), - codegen_utils->GetConstant(0)); - - // !(Expr_1 & Expr_2) - llvm::Value* llvm_att_isnull = irb->CreateNot(llvm_expr1_and_expr2, - "llvm_att_isnull"); - // }} - - llvm::Value* llvm_hasnulls = irb->CreateICmpNE( - irb->CreateAnd(llvm_heaptuple_t_data_t_infomask, - codegen_utils->GetConstant(HEAP_HASNULL)), - codegen_utils->GetConstant(0), - "llvm_hasnulls"); - - // hasnulls && att_isnull(attnum, bp) - llvm::Value* llvm_is_null = irb->CreateAnd( - llvm_hasnulls, llvm_att_isnull); - - irb->CreateCondBr( - llvm_is_null, - is_null_block, /* true */ - is_not_null_block /* false */); - //}}} End of if (hasnulls && att_isnull(attnum, bp)) - - // Is null block - // ---------------- - - irb->SetInsertPoint(is_null_block); - - // values[attnum] = (Datum) 0; {{{ - irb->CreateStore( - codegen_utils->GetConstant(0), - llvm_next_values_ptr); - // }}} - - // isnull[attnum] = true; {{{ - llvm::Value* llvm_isnull_ptr = - irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_isnull, - {llvm_attnum}); - irb->CreateStore( - codegen_utils->GetConstant(true), - llvm_isnull_ptr); - // }}} - - // Jump to next attribute - irb->CreateBr(next_attribute_block); - - // Is not null block - // ---------------- - - irb->SetInsertPoint(is_not_null_block); - } // End of if ( !thisatt->attnotnull ) - - // off = att_align_nominal(off, thisatt->attalign); - irb->CreateStore(irb->CreateCall( - llvm_att_align_nominal, {irb->CreateLoad(llvm_off_ptr), - codegen_utils->GetConstant(thisatt->attalign)}), - llvm_off_ptr); - - // values[attnum] = fetchatt(thisatt, tp + off) {{{ - llvm::Value* llvm_next_t_data_ptr = - irb->CreateInBoundsGEP(llvm_tuple_data_ptr, - {irb->CreateLoad(llvm_off_ptr)}); - - llvm::Value* llvm_colVal = nullptr; - if (thisatt->attbyval) { - // Load the value from the calculated input address. - switch (thisatt->attlen) { - case sizeof(char): - llvm_colVal = irb->CreateLoad(llvm_next_t_data_ptr); - break; - case sizeof(int16): - llvm_colVal = irb->CreateLoad( - codegen_utils->GetType(), - irb->CreateBitCast(llvm_next_t_data_ptr, - codegen_utils->GetType())); - break; - case sizeof(int32): - llvm_colVal = irb->CreateLoad( - codegen_utils->GetType(), - irb->CreateBitCast(llvm_next_t_data_ptr, - codegen_utils->GetType())); - break; - case sizeof(Datum): - llvm_colVal = irb->CreateLoad( - codegen_utils->GetType(), - irb->CreateBitCast(llvm_next_t_data_ptr, - codegen_utils->GetType())); - break; - default: - // We do not support other data type length, passed by value - elog(DEBUG1, - "We do not support other data type length, passed by value"); - return false; - } - } else { - // We do not support attributes by reference - elog(DEBUG1, "We do not support attributes by reference"); - return false; - } - - // store colVal into out_values[attnum] - irb->CreateStore( - irb->CreateZExt(llvm_colVal, codegen_utils->GetType()), - llvm_next_values_ptr); - - // }}} End of values[attnum] = fetchatt(thisatt, tp + off) - - // isnull[attnum] = false; {{{ - llvm::Value* llvm_next_isnull_ptr = - irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_isnull, - {codegen_utils->GetConstant(attnum)}); - irb->CreateStore( - codegen_utils->GetConstant(false), - llvm_next_isnull_ptr); - // }}} End of isnull[attnum] = false; - - // off += thisatt->attlen; - irb->CreateStore(irb->CreateAdd( - irb->CreateLoad(llvm_off_ptr), - codegen_utils->GetConstant(thisatt->attlen)), - llvm_off_ptr); - - // Jump to next attribute - irb->CreateBr(next_attribute_block); - - // Process next attribute - attribute_block = next_attribute_block; - } // end for - - // Next attribute block - // ---------------- - // Note that we have iterated over all attributes already, - // so simply jump to final block. - - irb->SetInsertPoint(next_attribute_block); - irb->CreateBr(final_block); - - - // Final block - // ---------------- - // Save the state for the next execution - - irb->SetInsertPoint(final_block); - - // slot->PRIVATE_tts_off = off; - llvm::Value* llvm_slot_PRIVATE_tts_off_ptr /* long* */ = - codegen_utils->GetPointerToMember( - llvm_slot, &TupleTableSlot::PRIVATE_tts_off); - irb->CreateStore( - codegen_utils->CreateCast( // NOLINT(runtime/int) - irb->CreateLoad(llvm_off_ptr)), llvm_slot_PRIVATE_tts_off_ptr); - - // slot->PRIVATE_tts_nvalid = attnum; - irb->CreateStore(codegen_utils->GetConstant(attnum), - llvm_slot_PRIVATE_tts_nvalid_ptr); - - if (varlen_attrs_found) { - // If we encountered any varlen attribute, we stop codegen from that - // attribute onwards and call slot_deform_tuple() directly to deform the - // rest of the tuple. - irb->CreateCall(llvm_slot_deform_tuple, { - llvm_slot_arg, - llvm_max_attr}); - } - - // End of slot_deform_tuple - - // _slot_getsomeattrs() after calling slot_deform_tuple {{{ - // for (; attno < attnum; attno++) - // { - // slot->PRIVATE_tts_values[attno] = (Datum) 0; - // slot->PRIVATE_tts_isnull[attno] = true; - // } - - // TODO(shardikar): Convert these to loops - llvm::Value* llvm_num_bytes = irb->CreateMul( - codegen_utils->GetConstant(sizeof(Datum)), - irb->CreateZExtOrTrunc( - irb->CreateSub(llvm_max_attr, llvm_attno), - codegen_utils->GetType())); - codegen_utils->ir_builder()->CreateCall( - llvm_memset, { - irb->CreateBitCast( - irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_values, - {llvm_attno}), - codegen_utils->GetType()), - codegen_utils->GetConstant(0), - llvm_num_bytes}); - - codegen_utils->ir_builder()->CreateCall( - llvm_memset, { - irb->CreateBitCast( - irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_isnull, - {llvm_attno}), - codegen_utils->GetType()), - codegen_utils->GetConstant(static_cast(true)), - llvm_num_bytes}); - - // TupSetVirtualTuple(slot); - irb->CreateStore( - irb->CreateOr( - irb->CreateLoad(llvm_slot_PRIVATE_tts_flags_ptr), - codegen_utils->GetConstant(TTS_VIRTUAL)), - llvm_slot_PRIVATE_tts_flags_ptr); - - // }}} - irb->CreateBr(return_block); - - // Return block - // ------------ - // slot_getattr() after calling _slot_getsomeattrs() {{{ - - irb->SetInsertPoint(return_block); - - // *isnull = slot->PRIVATE_tts_isnull[attnum-1]; - llvm::Value* llvm_isnull_from_slot_val = - irb->CreateLoad(irb->CreateInBoundsGEP( - llvm_slot_PRIVATE_tts_isnull, - {irb->CreateSub(llvm_attnum_arg, codegen_utils->GetConstant(1))})); - irb->CreateStore(llvm_isnull_from_slot_val, llvm_isnull_ptr_arg); - - // return slot->PRIVATE_tts_values[attnum-1]; - llvm::Value* llvm_value_from_slot_val = - irb->CreateLoad(irb->CreateInBoundsGEP( - llvm_slot_PRIVATE_tts_values, - {irb->CreateSub(llvm_attnum_arg, codegen_utils->GetConstant(1))})); - irb->CreateRet(llvm_value_from_slot_val); - - // Fall back block - // --------------- - // Note: We collect error code information, based on the block from which we - // fall back, and log it for debugging purposes. - irb->SetInsertPoint(fallback_block); - llvm::PHINode* llvm_error = irb-> - CreatePHI(codegen_utils->GetType(), 2); - llvm_error->addIncoming(codegen_utils->GetConstant("slot check failed"), - slot_check_block); - llvm_error->addIncoming(codegen_utils->GetConstant("tuple check failed"), - heap_tuple_check_block); - - EXPAND_CREATE_ELOG(codegen_utils, - DEBUG1, - "Falling back to regular slot_getattr, reason = %s", - llvm_error); - - codegen_utils->CreateFallback( - codegen_utils->GetOrRegisterExternalFunction(slot_getattr_regular, - "slot_getattr_regular"), - slot_getattr_func); - return true; -} diff --git a/src/backend/codegen/tests/clang_compiler_unittest.cc b/src/backend/codegen/tests/clang_compiler_unittest.cc deleted file mode 100644 index 7441e3eba6695932650f65b3f8b13d4cc49f5fe9..0000000000000000000000000000000000000000 --- a/src/backend/codegen/tests/clang_compiler_unittest.cc +++ /dev/null @@ -1,467 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// clang_compiler_unittest.cc -// -// @doc: -// Unittests for utils/clang_compiler.cc -// -// @test: -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include - -#include "codegen/utils/clang_compiler.h" - -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/instance_method_wrappers.h" -#include "gtest/gtest.h" -#include "llvm/ADT/Twine.h" -#include "llvm/IR/Module.h" - -namespace gpcodegen { - -namespace { - -enum class BoolEnum : bool { - kFalse, - kTrue -}; - -enum class IntEnum : int { - kCaseA, - kCaseB, - kCaseC -}; - -enum class UInt16Enum : std::uint16_t { - kCaseA, - kCaseB, - kCaseC -}; - -} // namespace - -// Test environment to handle global per-process initialization tasks for all -// tests. -class ClangCompilerTestEnvironment : public ::testing::Environment { - public: - virtual void SetUp() { - ASSERT_TRUE(CodegenUtils::InitializeGlobal()); - } -}; - -class ClangCompilerTest : public ::testing::Test { - protected: - virtual void SetUp() { - codegen_utils_.reset(new CodegenUtils("test_module")); - clang_compiler_.reset(new ClangCompiler(codegen_utils_.get())); - } - - // Check whether the auxiliary module specified by 'idx' in 'codegen_utils_' - // has debugging metadata attached. - bool AuxiliaryModuleHasDebugInfo(const std::size_t idx) { - // The "compile unit" (i.e. translation unit) level debugging info emitted - // by the clang frontend is called "llvm.dbg.cu". - return codegen_utils_->auxiliary_modules_.at(idx)->getNamedMetadata( - "llvm.dbg.cu") - != nullptr; - } - - // Helper method for CppTypeFromAnnotatedTypeTest. Checks that invoking - // ClangCompiler::CppTypeFromAnnotatedType() on the AnnotatedType produced - // by CodegenUtils::GetType() returns 'expected_cpp_type'. - template - void CheckCppTypeFromAnnotatedType(const std::string& expected_cpp_type) { - EXPECT_EQ(expected_cpp_type, - ClangCompiler::CppTypeFromAnnotatedType( - codegen_utils_->GetAnnotatedType())); - } - - template - void CheckGetLiteralConstant(const T original_value, - const std::string& expected_cpp_literal) { - EXPECT_EQ(expected_cpp_literal, - clang_compiler_->GetLiteralConstant(original_value)); - } - - std::unique_ptr codegen_utils_; - std::unique_ptr clang_compiler_; -}; - -static const char kBasicCompilationSource[] = -"unsigned factorial(unsigned arg) {\n" -" unsigned prod = 1;\n" -" for (; arg > 0; --arg) {\n" -" prod *= arg;\n" -" }\n" -" return prod;\n" -"}\n" -"\n" -"extern \"C\" unsigned binomial_coefficient(const unsigned n,\n" -" const unsigned k) {\n" -" return (factorial(n) / factorial(k)) / factorial(n - k);\n" -"}\n"; - -TEST_F(ClangCompilerTest, BasicCompilationTest) { - // Try compiling simple C++ source to an LLVM module. - EXPECT_TRUE(clang_compiler_->CompileCppSource(kBasicCompilationSource)); - - // Now do actual codegen and try and call the function. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - false)); - - unsigned (*binomial_coefficient)(const unsigned, const unsigned) - = codegen_utils_->GetFunctionPointer( - "binomial_coefficient"); - ASSERT_NE(binomial_coefficient, nullptr); - - EXPECT_EQ(21u, (*binomial_coefficient)(7u, 2u)); - EXPECT_EQ(252u, (*binomial_coefficient)(10u, 5u)); - EXPECT_EQ(1u, (*binomial_coefficient)(0u, 0u)); -} - -// Similar to above, but compile the two functions in separate modules. -static const char kMultiModuleFactorialSource[] = -"namespace codegen_test {\n" -"unsigned factorial(unsigned arg) {\n" -" unsigned prod = 1;\n" -" for (; arg > 0; --arg) {\n" -" prod *= arg;\n" -" }\n" -" return prod;\n" -"}\n" -"}\n"; - -static const char kMultiModuleBinomialCoeffecientSource[] = -"namespace codegen_test {\n" -"unsigned factorial(unsigned);\n" -"}\n" -"\n" -"using codegen_test::factorial;\n" -"\n" -"extern \"C\" unsigned binomial_coefficient(const unsigned n,\n" -" const unsigned k) {\n" -" return (factorial(n) / factorial(k)) / factorial(n - k);\n" -"}\n"; - -TEST_F(ClangCompilerTest, MultiModuleCompilationTest) { - EXPECT_TRUE(clang_compiler_->CompileCppSource(kMultiModuleFactorialSource)); - EXPECT_TRUE(clang_compiler_->CompileCppSource( - kMultiModuleBinomialCoeffecientSource)); - - // Now do actual codegen and try and call the function. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - false)); - - unsigned (*binomial_coefficient)(const unsigned, const unsigned) - = codegen_utils_->GetFunctionPointer( - "binomial_coefficient"); - ASSERT_NE(binomial_coefficient, nullptr); - - EXPECT_EQ(21u, (*binomial_coefficient)(7u, 2u)); - EXPECT_EQ(252u, (*binomial_coefficient)(10u, 5u)); - EXPECT_EQ(1u, (*binomial_coefficient)(0u, 0u)); -} - -TEST_F(ClangCompilerTest, CompilationFailureTest) { - EXPECT_FALSE(clang_compiler_->CompileCppSource("foo")); -} - -TEST_F(ClangCompilerTest, DebugInfoTest) { - // First, compile without debug info. - EXPECT_TRUE( - clang_compiler_->CompileCppSource(kBasicCompilationSource, false)); - EXPECT_FALSE(AuxiliaryModuleHasDebugInfo(0u)); - - // Now, compile another copy of the same module WITH debug info. - EXPECT_TRUE( - clang_compiler_->CompileCppSource(kBasicCompilationSource, true)); - EXPECT_TRUE(AuxiliaryModuleHasDebugInfo(1u)); -} - -TEST_F(ClangCompilerTest, CppTypeFromAnnotatedTypeTest) { - CheckCppTypeFromAnnotatedType("void"); - - // Bool with different CV-qualifiers. - CheckCppTypeFromAnnotatedType("bool"); - CheckCppTypeFromAnnotatedType("const bool"); - CheckCppTypeFromAnnotatedType("volatile bool"); - CheckCppTypeFromAnnotatedType("const volatile bool"); - - // char with different signedness. - CheckCppTypeFromAnnotatedType("char"); - CheckCppTypeFromAnnotatedType("char"); - CheckCppTypeFromAnnotatedType("unsigned char"); - CheckCppTypeFromAnnotatedType("const unsigned char"); - - // Built in integer types. - CheckCppTypeFromAnnotatedType("short"); // NOLINT(runtime/int) - CheckCppTypeFromAnnotatedType("int"); - CheckCppTypeFromAnnotatedType("long"); // NOLINT(runtime/int) - CheckCppTypeFromAnnotatedType("long long"); // NOLINT(runtime/int) - - // cstdint typedefs. - CheckCppTypeFromAnnotatedType("char"); - CheckCppTypeFromAnnotatedType("unsigned char"); - CheckCppTypeFromAnnotatedType("short"); - CheckCppTypeFromAnnotatedType("unsigned short"); - CheckCppTypeFromAnnotatedType("int"); - CheckCppTypeFromAnnotatedType("unsigned int"); - if (std::is_same::value) { // NOLINT(runtime/int) - CheckCppTypeFromAnnotatedType("long"); - } else { - CheckCppTypeFromAnnotatedType("long long"); - } - if (std::is_same::value) { // NOLINT(runtime/int) - CheckCppTypeFromAnnotatedType("unsigned long"); - } else { - CheckCppTypeFromAnnotatedType("unsigned long long"); - } - - // Enums map to their underlying integer types. - CheckCppTypeFromAnnotatedType("bool"); - CheckCppTypeFromAnnotatedType("int"); - CheckCppTypeFromAnnotatedType("unsigned short"); - - // Distinguish between char* and void*. - CheckCppTypeFromAnnotatedType("void*"); - CheckCppTypeFromAnnotatedType("char*"); - - // Pointer to arbitrary class or struct gets converted to void*. - CheckCppTypeFromAnnotatedType("void*"); - - // Also check with different combinations of CV-qualifiers. - CheckCppTypeFromAnnotatedType("const void*"); - CheckCppTypeFromAnnotatedType("void* const"); - CheckCppTypeFromAnnotatedType("const void* const"); - CheckCppTypeFromAnnotatedType("volatile void*"); - CheckCppTypeFromAnnotatedType("void* volatile"); - CheckCppTypeFromAnnotatedType( - "volatile void* volatile"); - CheckCppTypeFromAnnotatedType( - "const volatile void* const"); - - CheckCppTypeFromAnnotatedType("const char*"); - CheckCppTypeFromAnnotatedType("char* const"); - CheckCppTypeFromAnnotatedType("const char* const"); - CheckCppTypeFromAnnotatedType("volatile char*"); - CheckCppTypeFromAnnotatedType("char* volatile"); - CheckCppTypeFromAnnotatedType( - "volatile char* volatile"); - CheckCppTypeFromAnnotatedType( - "const volatile char* const"); - - CheckCppTypeFromAnnotatedType("const void*"); - CheckCppTypeFromAnnotatedType("void* const"); - CheckCppTypeFromAnnotatedType( - "const void* const"); - CheckCppTypeFromAnnotatedType("volatile void*"); - CheckCppTypeFromAnnotatedType("void* volatile"); - CheckCppTypeFromAnnotatedType( - "volatile void* volatile"); - CheckCppTypeFromAnnotatedType( - "const volatile void* const"); - - // Pointer to enum should map to pointer to underlying integer type. - CheckCppTypeFromAnnotatedType("const int*"); - - // References to built-in types. - CheckCppTypeFromAnnotatedType("char&"); - CheckCppTypeFromAnnotatedType("const char&"); - CheckCppTypeFromAnnotatedType("volatile char&"); - CheckCppTypeFromAnnotatedType("const volatile char&"); - - CheckCppTypeFromAnnotatedType("short&"); // NOLINT(runtime/int) - CheckCppTypeFromAnnotatedType("unsigned int&"); - - // Reference to enum. - CheckCppTypeFromAnnotatedType("int&"); - - // Special case: reference to class or struct is converted to void* const. - CheckCppTypeFromAnnotatedType("void* const"); - CheckCppTypeFromAnnotatedType("const void* const"); - - // Chains of multiple pointers/references. - CheckCppTypeFromAnnotatedType( - "const void* const** volatile*&"); - - // Similar for an enum. - CheckCppTypeFromAnnotatedType( - "const unsigned short* const** volatile*&"); - - // Similar for pointer to class. - CheckCppTypeFromAnnotatedType( - "const void* const** volatile*&"); -} - -TEST_F(ClangCompilerTest, GetLiteralConstantTest) { - // bool literals. - CheckGetLiteralConstant(false, "false"); - CheckGetLiteralConstant(true, "true"); - - // Various kinds of integer literals. - CheckGetLiteralConstant(-1234, "static_cast(-1234)"); - CheckGetLiteralConstant(5678u, "static_cast(5678u)"); - CheckGetLiteralConstant(static_cast(4242), - "static_cast(4242u)"); - if (std::is_same::value) { // NOLINT(runtime/int) - CheckGetLiteralConstant(INT64_C(1234567890), - "static_cast(1234567890l)"); - } else if (std::is_same::value) { // NOLINT(runtime/int) - CheckGetLiteralConstant(INT64_C(1234567890), - "static_cast(1234567890ll)"); - } - - // Enum literals (map to underlying integer literals). - CheckGetLiteralConstant(BoolEnum::kTrue, "true"); - CheckGetLiteralConstant(IntEnum::kCaseB, "static_cast(1)"); - CheckGetLiteralConstant(UInt16Enum::kCaseC, - "static_cast(2u)"); - - // Pointer literals. - CheckGetLiteralConstant(nullptr, "nullptr"); - CheckGetLiteralConstant(static_cast(nullptr), - "static_cast(nullptr)"); - CheckGetLiteralConstant( - this, - std::string("reinterpret_cast(") - + std::to_string(reinterpret_cast(this)) - + "ull)"); - - const int stack_var = 0; - CheckGetLiteralConstant( - &stack_var, - std::string("reinterpret_cast(") - + std::to_string(reinterpret_cast(&stack_var)) - + "ull)"); -} - -// Test external functions using an inner factorial function that is precompiled -// and an external binomial_coefficient function that is codegened. -namespace { - -unsigned factorial(unsigned arg) { - unsigned prod = 1; - for (; arg > 0; --arg) { - prod *= arg; - } - return prod; -} - -static const char kExternalFunctionBinomialCoefficientSource[] = -"extern \"C\" unsigned binomial_coefficient(const unsigned n,\n" -" const unsigned k) {\n" -" return (factorial(n) / factorial(k)) / factorial(n - k);" -"}\n"; - -} // namespace - -TEST_F(ClangCompilerTest, ExternalFunctionTest) { - codegen_utils_->GetOrRegisterExternalFunction(&factorial, "factorial"); - EXPECT_TRUE(clang_compiler_->CompileCppSource( - llvm::Twine(clang_compiler_->GenerateExternalFunctionDeclarations()) - .concat(kExternalFunctionBinomialCoefficientSource))); - - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - false)); - - unsigned (*binomial_coefficient)(const unsigned, const unsigned) - = codegen_utils_->GetFunctionPointer( - "binomial_coefficient"); - ASSERT_NE(binomial_coefficient, nullptr); - - EXPECT_EQ(21u, (*binomial_coefficient)(7u, 2u)); - EXPECT_EQ(252u, (*binomial_coefficient)(10u, 5u)); - EXPECT_EQ(1u, (*binomial_coefficient)(0u, 0u)); -} - -namespace { - -// Toy object used to test instance method invocation. -template -class Accumulator { - public: - explicit Accumulator(const T initial) - : total_(initial) { - } - - void Accumulate(const T arg) { - total_ += arg; - } - - T Get() const { - return total_; - } - - private: - T total_; -}; - -static const char kExternalMethodInvocationSource[] = -"extern \"C\" double AddWithAccumulator(const double arg1,\n" -" const double arg2,\n" -" const double arg3) {\n" -" void* accumulator = new_Accumulator(0.0);\n" -" Accumulator_Accumulate(accumulator, arg1);\n" -" Accumulator_Accumulate(accumulator, arg2);\n" -" Accumulator_Accumulate(accumulator, arg3);\n" -" const double result = Accumulator_Get(accumulator);\n" -" delete_Accumulator(accumulator);\n" -" return result;\n" -"}\n"; - -} // namespace - -TEST_F(ClangCompilerTest, ExternalMethodTest) { - codegen_utils_->GetOrRegisterExternalFunction( - &WrapNew, double>, - "new_Accumulator"); - codegen_utils_->GetOrRegisterExternalFunction( - &WrapDelete>, - "delete_Accumulator"); - codegen_utils_->GetOrRegisterExternalFunction( - &GPCODEGEN_WRAP_METHOD(&Accumulator::Accumulate), - "Accumulator_Accumulate"); - codegen_utils_->GetOrRegisterExternalFunction( - &GPCODEGEN_WRAP_METHOD(&Accumulator::Get), - "Accumulator_Get"); - - EXPECT_TRUE(clang_compiler_->CompileCppSource( - llvm::Twine(clang_compiler_->GenerateExternalFunctionDeclarations()) - .concat(kExternalMethodInvocationSource))); - - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - false)); - - double (*AddWithAccumulator)(const double, const double, const double) - = codegen_utils_->GetFunctionPointer< - decltype(AddWithAccumulator)>("AddWithAccumulator"); - ASSERT_NE(AddWithAccumulator, nullptr); - - EXPECT_EQ(1.2 + 2.3 + 3.4, (*AddWithAccumulator)(1.2, 2.3, 3.4)); - EXPECT_EQ(-4.5e20 + 6.2e-3 + 123.45e67, - (*AddWithAccumulator)(-4.5e20, 6.2e-3, 123.45e67)); -} - -} // namespace gpcodegen - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - AddGlobalTestEnvironment(new gpcodegen::ClangCompilerTestEnvironment); - return RUN_ALL_TESTS(); -} - -// EOF diff --git a/src/backend/codegen/tests/codegen_framework_unittest.cc b/src/backend/codegen/tests/codegen_framework_unittest.cc deleted file mode 100644 index 06949314c19503ce589bae30e4fa71da43dd7414..0000000000000000000000000000000000000000 --- a/src/backend/codegen/tests/codegen_framework_unittest.cc +++ /dev/null @@ -1,842 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// code_generator_unittest.cc -// -// @doc: -// Unit tests for codegen_manager.cc -// -// @test: -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gtest/gtest.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#undef newNode // undef newNode so it doesn't have name collision with llvm -#include "utils/elog.h" -#undef elog -#define elog(...) -} - -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/APInt.h" -#include "llvm/IR/Argument.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/Value.h" -#include "llvm/IR/Verifier.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/Support/Casting.h" - - -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/utils/utility.h" -#include "codegen/codegen_manager.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/codegen_interface.h" -#include "codegen/base_codegen.h" - -extern bool codegen_validate_functions; -using gpcodegen::GpCodegenUtils; -namespace gpcodegen { - -typedef int (*SumFunc) (int x, int y); -typedef void (*UncompilableFunc)(int x); -typedef int (*MulFunc) (int x, int y); - -template -using DatumCastFn = dest_type (*)(src_type); - -int SumFuncRegular(int x, int y) { - return x + y; -} - -void UncompilableFuncRegular(int x) { - return; -} - -int MulFuncRegular(int x, int y) { - return x * y; -} - -SumFunc sum_func_ptr = nullptr; -SumFunc failed_func_ptr = nullptr; -UncompilableFunc uncompilable_func_ptr = nullptr; -MulFunc mul_func_ptr = nullptr; - -class SumCodeGenerator : public BaseCodegen { - public: - explicit SumCodeGenerator(gpcodegen::CodegenManager* manager, - SumFunc regular_func_ptr, - SumFunc* ptr_to_regular_func_ptr) : - BaseCodegen(manager, - kAddFuncNamePrefix, - regular_func_ptr, - ptr_to_regular_func_ptr) { - } - - virtual ~SumCodeGenerator() = default; - - protected: - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final { - llvm::Function* add2_func - = CreateFunction(codegen_utils, GetUniqueFuncName()); - llvm::BasicBlock* add2_body = codegen_utils->CreateBasicBlock("body", - add2_func); - codegen_utils->ir_builder()->SetInsertPoint(add2_body); - llvm::Value* add2_sum = codegen_utils->ir_builder()->CreateAdd( - ArgumentByPosition(add2_func, 0), - ArgumentByPosition(add2_func, 1)); - codegen_utils->ir_builder()->CreateRet(add2_sum); - - return true; - } - - public: - static constexpr char kAddFuncNamePrefix[] = "SumFunc"; -}; - -class MulOverflowCodeGenerator : public BaseCodegen { - public: - explicit MulOverflowCodeGenerator(gpcodegen::CodegenManager* manager, - MulFunc regular_func_ptr, - MulFunc* ptr_to_regular_func_ptr) : - BaseCodegen(manager, - kMulFuncNamePrefix, - regular_func_ptr, - ptr_to_regular_func_ptr) { - } - - virtual ~MulOverflowCodeGenerator() = default; - - protected: - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final { - llvm::Function* mul2_func - = CreateFunction(codegen_utils, GetUniqueFuncName()); - llvm::BasicBlock* mul2_body = codegen_utils->CreateBasicBlock("body", - mul2_func); - llvm::BasicBlock* result_result_block = codegen_utils->CreateBasicBlock( - "return_result", mul2_func); - llvm::BasicBlock* return_overflow_block = codegen_utils->CreateBasicBlock( - "return_overflow", mul2_func); - - codegen_utils->ir_builder()->SetInsertPoint(mul2_body); - llvm::Value* arg0 = ArgumentByPosition(mul2_func, 0); - llvm::Value* arg1 = ArgumentByPosition(mul2_func, 1); - - llvm::Function* llvm_mul_overflow = llvm::Intrinsic::getDeclaration( - codegen_utils->module(), - llvm::Intrinsic::smul_with_overflow, - arg0->getType()); - llvm::Value* llvm_umul_results = codegen_utils->ir_builder()->CreateCall( - llvm_mul_overflow, {arg0, arg1}); - llvm::Value* llvm_overflow_flag = codegen_utils->ir_builder()-> - CreateExtractValue(llvm_umul_results, 1); - llvm::Value* llvm_results = codegen_utils->ir_builder()->CreateExtractValue( - llvm_umul_results, 0); - - codegen_utils->ir_builder()->CreateCondBr( - codegen_utils->ir_builder()->CreateICmpSLE( - llvm_overflow_flag, codegen_utils->GetConstant(true)), - return_overflow_block, - result_result_block); - - codegen_utils->ir_builder()->SetInsertPoint(return_overflow_block); - codegen_utils->ir_builder()->CreateRet(codegen_utils->GetConstant(1)); - - codegen_utils->ir_builder()->SetInsertPoint(result_result_block); - codegen_utils->ir_builder()->CreateRet(llvm_results); - mul2_func->dump(); - return true; - } - - public: - static constexpr char kMulFuncNamePrefix[] = "MulOverflowFunc"; -}; - -class FailingCodeGenerator : public BaseCodegen { - public: - explicit FailingCodeGenerator(gpcodegen::CodegenManager* manager, - SumFunc regular_func_ptr, - SumFunc* ptr_to_regular_func_ptr): - BaseCodegen(manager, - kFailingFuncNamePrefix, - regular_func_ptr, - ptr_to_regular_func_ptr) { - } - - virtual ~FailingCodeGenerator() = default; - - protected: - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final { - return false; - } - - private: - static constexpr char kFailingFuncNamePrefix[] = "SumFuncFailing"; -}; - -template -class UncompilableCodeGenerator : public BaseCodegen { - public: - explicit UncompilableCodeGenerator( - gpcodegen::CodegenManager* manager, - UncompilableFunc regular_func_ptr, - UncompilableFunc* ptr_to_regular_func_ptr) - : BaseCodegen(manager, - kUncompilableFuncNamePrefix, - regular_func_ptr, - ptr_to_regular_func_ptr) { - } - - virtual ~UncompilableCodeGenerator() = default; - - protected: - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final { - llvm::Function* dummy_func - = CreateFunction(codegen_utils, - GetUniqueFuncName()); - llvm::BasicBlock* dummy_func_body = codegen_utils->CreateBasicBlock("body", - dummy_func); - codegen_utils->ir_builder()->SetInsertPoint(dummy_func_body); - llvm::Value* int_value = codegen_utils->GetConstant(4); - codegen_utils->ir_builder()->CreateRet(int_value); - return GEN_SUCCESS; - } - - private: - static constexpr char kUncompilableFuncNamePrefix[] = "UncompilableFunc"; -}; - -template -class DatumToCppCastGenerator : - public BaseCodegen> { - using DatumCastTemplateFn = DatumCastFn; - - public: - explicit DatumToCppCastGenerator( - gpcodegen::CodegenManager* manager, - DatumCastTemplateFn regular_func_ptr) - : BaseCodegen(manager, - kDatumToCppCastFuncNamePrefix, - regular_func_ptr, - &swappable_function_holder) { - } - - virtual ~DatumToCppCastGenerator() = default; - - DatumCastTemplateFn swappable_function_holder; - - protected: - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final { - llvm::Function* cast_func - = this->template CreateFunction( - codegen_utils, this->GetUniqueFuncName()); - llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock("entry", - cast_func); - codegen_utils->ir_builder()->SetInsertPoint(entry_block); - llvm::Value* llvm_arg0 = ArgumentByPosition(cast_func, 0); - codegen_utils->ir_builder()->CreateRet( - codegen_utils->CreateDatumToCppTypeCast( - llvm_arg0)); - cast_func->dump(); - return true; - } - - private: - static constexpr char kDatumToCppCastFuncNamePrefix[] = "DatumToCppCastFunc"; -}; - -template -class CppToDatumCastGenerator : - public BaseCodegen> { - using DatumCastTemplateFn = DatumCastFn; - public: - explicit CppToDatumCastGenerator( - gpcodegen::CodegenManager* manager, - DatumCastTemplateFn regular_func_ptr) - : BaseCodegen(manager, - kCppToDatumCastFuncNamePrefix, - regular_func_ptr, - &swappable_function_holder) { - } - - virtual ~CppToDatumCastGenerator() = default; - - DatumCastTemplateFn swappable_function_holder; - - protected: - bool GenerateCodeInternal(gpcodegen::GpCodegenUtils* codegen_utils) final { - llvm::Function* cast_func - = this->template CreateFunction( - codegen_utils, this->GetUniqueFuncName()); - llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock("entry", - cast_func); - codegen_utils->ir_builder()->SetInsertPoint(entry_block); - llvm::Value* llvm_arg0 = ArgumentByPosition(cast_func, 0); - codegen_utils->ir_builder()->CreateRet( - codegen_utils->CreateCppTypeToDatumCast( - llvm_arg0, std::is_unsigned::value)); - cast_func->dump(); - return true; - } - - private: - static constexpr char kCppToDatumCastFuncNamePrefix[] = "CppToDatumCastFunc"; -}; - -constexpr char SumCodeGenerator::kAddFuncNamePrefix[]; -constexpr char FailingCodeGenerator::kFailingFuncNamePrefix[]; -constexpr char MulOverflowCodeGenerator::kMulFuncNamePrefix[]; -template -constexpr char -DatumToCppCastGenerator::kDatumToCppCastFuncNamePrefix[]; - -template -constexpr char -CppToDatumCastGenerator::kCppToDatumCastFuncNamePrefix[]; - -template -constexpr char -UncompilableCodeGenerator::kUncompilableFuncNamePrefix[]; - -// Test environment to handle global per-process initialization tasks for all -// tests. -class CodegenManagerTestEnvironment : public ::testing::Environment { - public: - virtual void SetUp() { - ASSERT_EQ(InitCodegen(), 1); - } -}; - -class CodegenManagerTest : public ::testing::Test { - protected: - virtual void SetUp() { - manager_.reset(new CodegenManager("CodegenManagerTest")); - codegen_validate_functions = true; - } - - template - void EnrollCodegen(FuncType reg_func, FuncType* ptr_to_chosen_func) { - ClassType* code_gen = new ClassType( - manager_.get(), reg_func, ptr_to_chosen_func); - ASSERT_TRUE(reg_func == *ptr_to_chosen_func); - ASSERT_TRUE(manager_->EnrollCodeGenerator( - CodegenFuncLifespan_Parameter_Invariant, - code_gen)); - } - - template - void CheckDatumCast(DatumCastFn CppToDatumReg, - DatumCastFn DatumToCppReg, - const std::vector& values) { - CppToDatumCastGenerator* cpp_datum_gen = - new CppToDatumCastGenerator( - manager_.get(), CppToDatumReg); - DatumCastFn& CppToDatumCgFn = - cpp_datum_gen->swappable_function_holder; - - - DatumToCppCastGenerator* datum_cpp_gen = - new DatumToCppCastGenerator( - manager_.get(), DatumToCppReg); - DatumCastFn& DatumToCppCgFn = - datum_cpp_gen->swappable_function_holder; - - ASSERT_TRUE(manager_->EnrollCodeGenerator( - CodegenFuncLifespan_Parameter_Invariant, cpp_datum_gen)); - - ASSERT_TRUE(manager_->EnrollCodeGenerator( - CodegenFuncLifespan_Parameter_Invariant, datum_cpp_gen)); - - EXPECT_EQ(2, manager_->GenerateCode()); - - ASSERT_TRUE(manager_->PrepareGeneratedFunctions()); - - ASSERT_TRUE(CppToDatumCgFn != CppToDatumReg); - ASSERT_TRUE(DatumToCppCgFn != DatumToCppReg); - - for (const CppType& v : values) { - Datum d_gpdb = CppToDatumReg(v); - Datum d_codegen = CppToDatumCgFn(v); - std::cout << "d_gpdb " << d_gpdb - << " d_codegen " << d_codegen << std::endl; - EXPECT_EQ(d_gpdb, d_codegen); - EXPECT_EQ(DatumToCppReg(d_gpdb), DatumToCppCgFn(d_codegen)); - } - } - - std::unique_ptr manager_; -}; - -TEST_F(CodegenManagerTest, TestGetters) { - sum_func_ptr = nullptr; - SumCodeGenerator* code_gen = new SumCodeGenerator(manager_.get(), - SumFuncRegular, - &sum_func_ptr); - - EXPECT_EQ(SumCodeGenerator::kAddFuncNamePrefix, - code_gen->GetOrigFuncName()); - // Static unique_counter will be zero to begin with. - // So uniqueFuncName return with suffix zero. - EXPECT_EQ(SumCodeGenerator::kAddFuncNamePrefix + std::to_string(0), - code_gen->GetUniqueFuncName()); - ASSERT_TRUE(manager_->EnrollCodeGenerator( - CodegenFuncLifespan_Parameter_Invariant, code_gen)); - ASSERT_FALSE(code_gen->IsGenerated()); - EXPECT_EQ(1, manager_->GenerateCode()); - ASSERT_TRUE(code_gen->IsGenerated()); -} - -TEST_F(CodegenManagerTest, EnrollCodeGeneratorTest) { - sum_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, &sum_func_ptr); - EXPECT_EQ(1, manager_->GetEnrollmentCount()); -} - -TEST_F(CodegenManagerTest, GenerateCodeTest) { - // With no generator it should return false - EXPECT_EQ(0, manager_->GenerateCode()); - - // Test if generation happens successfully - sum_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, &sum_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Test if generation fails with FailingCodeGenerator - failed_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, - &failed_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Test if generation pass with UncompiledCodeGenerator - uncompilable_func_ptr = nullptr; - EnrollCodegen, UncompilableFunc>( - UncompilableFuncRegular, &uncompilable_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); -} - -TEST_F(CodegenManagerTest, PrepareGeneratedFunctionsNoCompilationErrorTest) { - // Test if generation happens successfully - sum_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, &sum_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Test if generation fails with FailingCodeGenerator - failed_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, - &failed_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Make sure both the function pointers refer to regular versions - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - - // This should update function pointers to generated version, - // if generation was successful - ASSERT_TRUE(manager_->PrepareGeneratedFunctions()); - - // For sum_func_ptr, we successfully generated code. - // So, pointer should reflect that. - ASSERT_TRUE(SumFuncRegular != sum_func_ptr); - - // For failed_func_ptr, code generation was unsuccessful. - // So, pointer should not change. - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - - // Check generate SumFuncRegular works as expected; - EXPECT_EQ(3, sum_func_ptr(1, 2)); - - // Reset the manager, so that all the code generators go away - manager_.reset(nullptr); - - // The manager reset should have restored all the function pointers - // to point to regular version - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); -} - -TEST_F(CodegenManagerTest, UnCompilableFailedGenerationTest) { - // Test if generation happens successfully - sum_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, &sum_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Test if generation fails with FailingCodeGenerator - failed_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, - &failed_func_ptr); - - // Create uncompilable generator which fails in generation - // and produce broken function - uncompilable_func_ptr = nullptr; - EnrollCodegen, UncompilableFunc>( - UncompilableFuncRegular, &uncompilable_func_ptr); - - EXPECT_EQ(1, manager_->GenerateCode()); - - // Make sure the function pointers refer to regular versions - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - ASSERT_TRUE(UncompilableFuncRegular == uncompilable_func_ptr); - - // This should update function pointers to generated version, - // if generation was successful - ASSERT_TRUE(manager_->PrepareGeneratedFunctions()); - - // For sum_func_ptr, we successfully generated code. - // So, pointer should reflect that. - ASSERT_TRUE(SumFuncRegular != sum_func_ptr); - - // For failed_func_ptr, code generation was unsuccessful. - // So, pointer should not change. - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - - // For uncompilable_func_ptr, code generation was unsuccessful. - // So, pointer should not change. - ASSERT_TRUE(UncompilableFuncRegular == uncompilable_func_ptr); - - // Check generate SumFuncRegular works as expected; - EXPECT_EQ(3, sum_func_ptr(1, 2)); - - // Reset the manager, so that all the code generators go away - manager_.reset(nullptr); - - // The manager reset should have restored all the function pointers - // to point to regular version - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - ASSERT_TRUE(UncompilableFuncRegular == uncompilable_func_ptr); -} - -TEST_F(CodegenManagerTest, UnCompilablePassedGenerationTest) { - // Test if generation happens successfully - sum_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, &sum_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Test if generation fails with FailingCodeGenerator - failed_func_ptr = nullptr; - EnrollCodegen(SumFuncRegular, - &failed_func_ptr); - - // Create uncompilable generator which generate broken - // function and return success status on generation - uncompilable_func_ptr = nullptr; - EnrollCodegen, UncompilableFunc>( - UncompilableFuncRegular, &uncompilable_func_ptr); - - EXPECT_EQ(1, manager_->GenerateCode()); - - // Make sure both the function pointers refer to regular versions - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - ASSERT_TRUE(UncompilableFuncRegular == uncompilable_func_ptr); - - EXPECT_EQ(1, manager_->PrepareGeneratedFunctions()); - - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - ASSERT_TRUE(UncompilableFuncRegular == uncompilable_func_ptr); - - // Reset the manager, so that all the code generators go away - manager_.reset(nullptr); - - // The manager reset should have restored all the function pointers - // to point to regular version - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - ASSERT_TRUE(SumFuncRegular == failed_func_ptr); - ASSERT_TRUE(UncompilableFuncRegular == uncompilable_func_ptr); -} - -TEST_F(CodegenManagerTest, MulOverFlowTest) { - // Test if generation happens successfully - mul_func_ptr = nullptr; - EnrollCodegen(MulFuncRegular, - &mul_func_ptr); - EXPECT_EQ(1, manager_->GenerateCode()); - - - ASSERT_TRUE(MulFuncRegular == mul_func_ptr); - - // This should cause program to exit because of - // broken function - EXPECT_EQ(1, manager_->PrepareGeneratedFunctions()); - - EXPECT_EQ(6, mul_func_ptr(2, 3)); - - EXPECT_EQ(1, mul_func_ptr(2147483647, 3)); - - EXPECT_EQ(-6, mul_func_ptr(-2, 3)); - - // Reset the manager, so that all the code generators go away - manager_.reset(nullptr); -} - -TEST_F(CodegenManagerTest, ResetTest) { - sum_func_ptr = nullptr; - SumCodeGenerator* code_gen = new SumCodeGenerator(manager_.get(), - SumFuncRegular, - &sum_func_ptr); - - ASSERT_TRUE(manager_->EnrollCodeGenerator( - CodegenFuncLifespan_Parameter_Invariant, code_gen)); - EXPECT_EQ(1, manager_->GenerateCode()); - - // Make sure the function pointers refer to regular versions - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); - - // This should update function pointers to generated version, - // if generation was successful - ASSERT_TRUE(manager_->PrepareGeneratedFunctions()); - - // For sum_func_ptr, we successfully generated code. - // So, pointer should reflect that. - ASSERT_TRUE(SumFuncRegular != sum_func_ptr); - - code_gen->Reset(); - - // Make sure Reset set the function pointer back to regular version. - ASSERT_TRUE(SumFuncRegular == sum_func_ptr); -} - -TEST_F(CodegenManagerTest, TestDatumBoolCast) { - CheckDatumCast(BoolGetDatum, - DatumGetBool, - {true, false}); -} - -TEST_F(CodegenManagerTest, TestDatumCharCast) { - CheckDatumCast(CharGetDatum, - DatumGetChar, - {'a', 'A'}); -} - -TEST_F(CodegenManagerTest, TestDatumInt8Cast) { - std::vector values = {0, -0, 23, -23, - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(Int8GetDatum, - DatumGetInt8, - values); -} - -TEST_F(CodegenManagerTest, TestDatumUInt8Cast) { - std::vector values = {0, -0, 23, static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(UInt8GetDatum, - DatumGetUInt8, - values); -} - -TEST_F(CodegenManagerTest, TestDatumInt16Cast) { - std::vector values = {0, -0, 23, -23, - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(Int16GetDatum, - DatumGetInt16, - values); -} - -TEST_F(CodegenManagerTest, TestDatumUInt16Cast) { - std::vector values = {0, -0, 23, static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(UInt16GetDatum, - DatumGetUInt16, - values); -} - -TEST_F(CodegenManagerTest, TestDatumInt32Cast) { - std::vector values = {0, -0, 23, -23, - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(Int32GetDatum, - DatumGetInt32, - values); -} - -TEST_F(CodegenManagerTest, TestDatumUInt32Cast) { - std::vector values = {0, -0, 23, static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(UInt32GetDatum, - DatumGetUInt32, - values); -} - -TEST_F(CodegenManagerTest, TestDatumInt64Cast) { - std::vector values = {0, -0, 23, -23, - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(Int64GetDatum, - DatumGetInt64, - values); -} - -TEST_F(CodegenManagerTest, TestDatumUInt64Cast) { - std::vector values = {0, -0, 23, static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(UInt64GetDatum, - DatumGetUInt64, - values); -} - -TEST_F(CodegenManagerTest, TestDatumOidCast) { - std::vector values = {0, -0, 23, static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(ObjectIdGetDatum, - DatumGetObjectId, - values); -} - -TEST_F(CodegenManagerTest, TestDatumTxIdCast) { - std::vector values = {0, -0, 23, - static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(TransactionIdGetDatum, - DatumGetTransactionId, - values); -} - -TEST_F(CodegenManagerTest, TestDatumCmdIdCast) { - std::vector values = {0, -0, 23, - static_cast(-23), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::lowest() - }; - CheckDatumCast(CommandIdGetDatum, - DatumGetCommandId, - values); -} - -TEST_F(CodegenManagerTest, TestDatumPtrCast) { - struct DatumVoidPtr { - static Datum PointerGetDatumNoConst(void *p) { - return PointerGetDatum((const void*)p); - } - }; - std::vector values = {reinterpret_cast(23), - reinterpret_cast(-23), - reinterpret_cast(NULL)}; - CheckDatumCast(DatumVoidPtr::PointerGetDatumNoConst, - DatumGetPointer, - values); -} - -TEST_F(CodegenManagerTest, TestDatumCStringCast) { - struct DatumVoidPtr { - static Datum PointerGetDatumNoConst(char *p) { - return PointerGetDatum((const char*)p); - } - }; - std::vector values = {const_cast("dfdFD"), - const_cast(""), - static_cast(nullptr)}; - CheckDatumCast(DatumVoidPtr::PointerGetDatumNoConst, - DatumGetCString, - values); -} - -TEST_F(CodegenManagerTest, TestDatumNameCast) { - Name n1 = new nameData(); - Name n2 = new nameData(); - strncpy(n1->data, "iqbal", sizeof(n1->data) - 1); - strncpy(n2->data, "", sizeof(n2->data) - 1); - CheckDatumCast(NameGetDatum, - DatumGetName, - {n1, n2}); - delete n1; - delete n2; -} - -TEST_F(CodegenManagerTest, TestDatumFloatCast) { - std::vector values = {0.0, -0.0, 12.34, -12.34, - std::numeric_limits::min(), - std::numeric_limits::max(), - std::numeric_limits::lowest(), - std::numeric_limits::denorm_min(), - std::numeric_limits::infinity()}; - CheckDatumCast(Float4GetDatum, - DatumGetFloat4, - values); -} - -TEST_F(CodegenManagerTest, TestDatumDoubleCast) { - std::vector values = {0.0, -0.0, 12.34, -12.34, - std::numeric_limits::min(), - std::numeric_limits::max(), - std::numeric_limits::lowest(), - std::numeric_limits::denorm_min(), - std::numeric_limits::infinity()}; - CheckDatumCast(Float8GetDatum, - DatumGetFloat8, - values); -} - -TEST_F(CodegenManagerTest, TestDatumItemPtrCast) { - ItemPointer p1 = reinterpret_cast(23); - ItemPointer p2 = reinterpret_cast(42); - CheckDatumCast(ItemPointerGetDatum, - DatumGetItemPointer, - {p1, p2}); -} - -} // namespace gpcodegen - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - AddGlobalTestEnvironment(new gpcodegen::CodegenManagerTestEnvironment); - return RUN_ALL_TESTS(); -} - diff --git a/src/backend/codegen/tests/codegen_pg_func_generator_unittest.cc b/src/backend/codegen/tests/codegen_pg_func_generator_unittest.cc deleted file mode 100644 index e87dfbc883658452ee28817c94bdccf22de516ea..0000000000000000000000000000000000000000 --- a/src/backend/codegen/tests/codegen_pg_func_generator_unittest.cc +++ /dev/null @@ -1,388 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_pg_func_generator_unittest.cc -// -// @doc: -// Unit tests for PGFuncGenerator -// -// @test: -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gtest/gtest.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#undef newNode // undef newNode so it doesn't have name collision with llvm -#include "utils/elog.h" -#undef elog -#define elog(...) -} - -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/APInt.h" -#include "llvm/IR/Argument.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/Value.h" -#include "llvm/IR/Verifier.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/Support/Casting.h" - - -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/utils/utility.h" -#include "codegen/codegen_manager.h" -#include "codegen/codegen_wrapper.h" -#include "codegen/codegen_interface.h" -#include "codegen/base_codegen.h" -#include "codegen/pg_func_generator.h" -#include "codegen/pg_arith_func_generator.h" - - -namespace gpcodegen { - -class PGFuncGeneratorTestEnvironment : public ::testing::Environment { - public: - virtual void SetUp() { - ASSERT_TRUE(CodegenUtils::InitializeGlobal()); - } -}; - -class CodegenPGFuncGeneratorTest : public ::testing::Test { - protected: - virtual void SetUp() { - codegen_utils_.reset(new GpCodegenUtils("test_module")); - } - - std::unique_ptr codegen_utils_; -}; - - - -// Helper Variadic template classes for recursive checking the types in an array -// of llvm::Values* -template -class LLVMTypeChecker { -}; - -template<> -class LLVMTypeChecker<> { - public: - static void check(gpcodegen::GpCodegenUtils* codegen_utils, - std::vector::const_iterator val_iter) { - } -}; - -template -class LLVMTypeChecker { - public: - static void check(gpcodegen::GpCodegenUtils* codegen_utils, - std::vector::const_iterator val_iter) { - EXPECT_EQ((*val_iter)->getType(), codegen_utils->GetType()); - LLVMTypeChecker::check(codegen_utils, ++val_iter); - } - - static void check(gpcodegen::GpCodegenUtils* codegen_utils, - const std::vector & values) { - check(codegen_utils, values.begin()); - } -}; - -// Helper method to check -template -void CheckPGFuncArgPreProcessor(gpcodegen::GpCodegenUtils* codegen_utils) { - std::vector in_values; - std::vector out_values; - - for (int i=0; i< sizeof...(CppTypes); ++i) { - in_values.push_back(codegen_utils->GetConstant(i)); - } - - EXPECT_TRUE(pg_func_generator_detail::PGFuncArgPreProcessor - ::PreProcessArgs(codegen_utils, in_values, &out_values)); - EXPECT_EQ(sizeof...(CppTypes), out_values.size()); - LLVMTypeChecker::check(codegen_utils, out_values); -} - - -// Test PGFuncArgPreProcessor with 1, 2 and 3 arguments of various types -TEST_F(CodegenPGFuncGeneratorTest, PGFuncArgPreProcessorTest ) { - using VoidFn = void (*) (void); - - // Start of boiler plate - llvm::Function* dummy_fn = - codegen_utils_->CreateFunction("dummy_fn"); - - llvm::BasicBlock* main_block = - codegen_utils_->CreateBasicBlock("main", dummy_fn); - codegen_utils_->ir_builder()->SetInsertPoint(main_block); - // end of boiler plate - - // Test with one type - CheckPGFuncArgPreProcessor(codegen_utils_.get()); - // Test with two types - CheckPGFuncArgPreProcessor( - codegen_utils_.get()); - // Test with three types - CheckPGFuncArgPreProcessor( - codegen_utils_.get()); - // Test with different types - CheckPGFuncArgPreProcessor(codegen_utils_.get()); - - // More boiler plate - codegen_utils_->ir_builder()->CreateRetVoid(); - - EXPECT_FALSE(llvm::verifyFunction(*dummy_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare generated code for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - EXPECT_EQ(nullptr, codegen_utils_->module()); -} - -// Test PGGenericFuncGenerator with 2 arguments in a strict function -TEST_F(CodegenPGFuncGeneratorTest, - PGGenericFuncGeneratorNonStrictFuncTwoArgsTest) { - using AddFn = Datum (*) (Datum, Datum, bool, bool); - - llvm::Function* sum_fn = - codegen_utils_->CreateFunction("sum_fn"); - - llvm::BasicBlock* main_block = - codegen_utils_->CreateBasicBlock("main", sum_fn); - llvm::BasicBlock* error_block = - codegen_utils_->CreateBasicBlock("error", sum_fn); - - auto irb = codegen_utils_->ir_builder(); - - irb->SetInsertPoint(main_block); - - auto generator = std::unique_ptr( - new PGGenericFuncGenerator( - 1841, - "int4_sum", - &PGArithFuncGenerator::AddWithOverflow, - &PGArithFuncGenerator:: - CreateArgumentNullChecks, - false)); - - llvm::Value* result; - std::vector args = { - ArgumentByPosition(sum_fn, 0), - ArgumentByPosition(sum_fn, 1)}; - std::vector args_isNull = { - ArgumentByPosition(sum_fn, 2), - ArgumentByPosition(sum_fn, 3)}; - llvm::Value* llvm_isNull = irb->CreateAlloca( - codegen_utils_->GetType(), nullptr, "isNull"); - irb->CreateStore(codegen_utils_->GetConstant(false), llvm_isNull); - PGFuncGeneratorInfo pg_gen_info(sum_fn, error_block, args, - args_isNull); - - EXPECT_TRUE(generator->GenerateCode(codegen_utils_.get(), - pg_gen_info, &result, llvm_isNull)); - irb->CreateRet(result); - - irb->SetInsertPoint(error_block); - irb->CreateRet(codegen_utils_->GetConstant(0)); - - - EXPECT_FALSE(llvm::verifyFunction(*sum_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare generated code for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - EXPECT_EQ(nullptr, codegen_utils_->module()); - - AddFn fn = codegen_utils_->GetFunctionPointer("sum_fn"); - - Datum d1 = 1, d2 = 2; - EXPECT_EQ(3, fn(d1, d2, false, false)); - EXPECT_EQ(2, fn(d1, d2, true, false)); - EXPECT_EQ(1, fn(d1, d2, false, true)); - EXPECT_EQ(0, fn(d1, d2, true, true)); -} - -// Test PGGenericFuncGenerator with 2 arguments in a strict function -TEST_F(CodegenPGFuncGeneratorTest, - PGGenericFuncGeneratorStrictFuncTwoArgsTest) { - using DoubleFn = double (*) (Datum, Datum, bool, bool); - - llvm::Function* double_add_fn = - codegen_utils_->CreateFunction("double_add_fn"); - - llvm::BasicBlock* main_block = - codegen_utils_->CreateBasicBlock("main", double_add_fn); - llvm::BasicBlock* error_block = - codegen_utils_->CreateBasicBlock("error", double_add_fn); - - auto irb = codegen_utils_->ir_builder(); - - irb->SetInsertPoint(main_block); - - auto generator = std::unique_ptr( - new PGGenericFuncGenerator( - 218, - "float8pl", - &PGArithFuncGenerator::AddWithOverflow, - nullptr, - true)); - - llvm::Value* result; - std::vector args = { - ArgumentByPosition(double_add_fn, 0), - ArgumentByPosition(double_add_fn, 1)}; - std::vector args_isNull = { - ArgumentByPosition(double_add_fn, 2), - ArgumentByPosition(double_add_fn, 3)}; - llvm::Value* llvm_isNull = irb->CreateAlloca( - codegen_utils_->GetType(), nullptr, "isNull"); - irb->CreateStore(codegen_utils_->GetConstant(false), llvm_isNull); - PGFuncGeneratorInfo pg_gen_info(double_add_fn, error_block, args, - args_isNull); - - EXPECT_TRUE(generator->GenerateCode(codegen_utils_.get(), - pg_gen_info, &result, llvm_isNull)); - irb->CreateRet(result); - - irb->SetInsertPoint(error_block); - irb->CreateRet(codegen_utils_->GetConstant(0.0)); - - - EXPECT_FALSE(llvm::verifyFunction(*double_add_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare generated code for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - EXPECT_EQ(nullptr, codegen_utils_->module()); - - DoubleFn fn = codegen_utils_->GetFunctionPointer("double_add_fn"); - - double d1 = 1.0, d2 = 2.0; - EXPECT_EQ(3.0, fn(*reinterpret_cast(&d1), - *reinterpret_cast(&d2), - false, false)); - EXPECT_EQ(0.0, fn(*reinterpret_cast(&d1), - *reinterpret_cast(&d2), - true, false)); - EXPECT_EQ(0.0, fn(*reinterpret_cast(&d1), - *reinterpret_cast(&d2), - false, true)); - EXPECT_EQ(0.0, fn(*reinterpret_cast(&d1), - *reinterpret_cast(&d2), - true, true)); -} - -// A method of type PGFuncGenerator as expected by PGGenericFuncGenerator -// that generates instructions to add 1 to the first input argument. -template -bool GenerateAddOne( - gpcodegen::GpCodegenUtils* codegen_utils, - const PGFuncGeneratorInfo& pg_func_info, - llvm::Value** llvm_out_value) { - *llvm_out_value = codegen_utils->ir_builder()->CreateAdd( - pg_func_info.llvm_args[0], - codegen_utils->GetConstant(1)); - return true; -} - -// Test PGGenericFuncGenerator with 1 arguments -TEST_F(CodegenPGFuncGeneratorTest, PGGenericFuncGeneratorOneArgTest) { - using AddOneFn = int32_t (*) (Datum); - - llvm::Function* add_one_fn = - codegen_utils_->CreateFunction("add_one_fn"); - - llvm::BasicBlock* main_block = - codegen_utils_->CreateBasicBlock("main", add_one_fn); - llvm::BasicBlock* error_block = - codegen_utils_->CreateBasicBlock("error", add_one_fn); - - auto irb = codegen_utils_->ir_builder(); - - irb->SetInsertPoint(main_block); - - auto generator = std::unique_ptr( - new PGGenericFuncGenerator( - 0, - "", - &GenerateAddOne, - nullptr, // assume that this is a strict function - true)); - - llvm::Value* result = nullptr; - llvm::Value* llvm_isNull = irb->CreateAlloca( - codegen_utils_->GetType(), nullptr, "isNull"); - irb->CreateStore(codegen_utils_->GetConstant(false), llvm_isNull); - std::vector args = {ArgumentByPosition(add_one_fn, 0)}; - std::vector args_isNull = {codegen_utils_-> - GetConstant(false)}; // dummy - PGFuncGeneratorInfo pg_gen_info(add_one_fn, error_block, args, - args_isNull); - - EXPECT_TRUE(generator->GenerateCode(codegen_utils_.get(), - pg_gen_info, - &result, - llvm_isNull)); - irb->CreateRet(result); - - irb->SetInsertPoint(error_block); - irb->CreateRet(codegen_utils_->GetConstant(-1)); - - - EXPECT_FALSE(llvm::verifyFunction(*add_one_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare generated code for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - EXPECT_EQ(nullptr, codegen_utils_->module()); - - AddOneFn fn = codegen_utils_->GetFunctionPointer("add_one_fn"); - - EXPECT_EQ(1, fn(0)); - EXPECT_EQ(3, fn(2)); -} - -} // namespace gpcodegen - - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - AddGlobalTestEnvironment(new gpcodegen::PGFuncGeneratorTestEnvironment); - return RUN_ALL_TESTS(); -} - diff --git a/src/backend/codegen/tests/codegen_utils_unittest.cc b/src/backend/codegen/tests/codegen_utils_unittest.cc deleted file mode 100644 index e898f6cf09b8089bdc48bd23beb38704a47f6a18..0000000000000000000000000000000000000000 --- a/src/backend/codegen/tests/codegen_utils_unittest.cc +++ /dev/null @@ -1,3140 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// code_generator_unittest.cc -// -// @doc: -// Unit tests for utils/codegen_utils.cc -// -// @test: -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/annotated_type.h" -#include "codegen/utils/instance_method_wrappers.h" -#include "codegen/utils/utility.h" -#include "gtest/gtest.h" -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/APInt.h" -#include "llvm/IR/Argument.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/Value.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/Casting.h" - -namespace gpcodegen { - -namespace { - -// Dummy enums with various properties for test purposes. -enum SimpleEnum { - kSimpleEnumA, - kSimpleEnumB, - kSimpleEnumC -}; - -// The compiler must choose a signed representation if one of the cases is -// explicitly set to a negative value. -enum SignedSimpleEnum { - kSignedSimpleEnumA = -1, - kSignedSimpleEnumB, - kSignedSimpleEnumC -}; - -// C++11 strongly-typed enum. -enum class StronglyTypedEnum { - kCaseA, - kCaseB, - kCaseC -}; - -// C++11 strongly-typed enum that must be signed. -enum class SignedStronglyTypedEnum { - kCaseA = -1, - kCaseB, - kCaseC -}; - -// C++11 strongly-typed enum with explicit underlying type. -enum class StronglyTypedEnumUint64 : std::uint64_t { - kCaseA, - kCaseB, - kCaseC -}; - -// Dummy struct. -struct DummyStruct { - int int_field; - bool bool_field; - double double_field; - int int_array_field[5]; - bool bool_array_field[2]; -}; - -// A dummy struct with several char fields that map to LLVM's i8 type. Used to -// check that CodegenUtils::GetPointerToMember() works correctly when the type -// of a pointer to the field to be accessed is the same as the type used for -// underlying pointer arithmetic. -struct DummyStructWithCharFields { - char front_char; - char* char_ptr; - char back_char; -}; - -// A struct that nests other structs and includes a pointer to another instance -// of its own type. -struct Matryoshka { - DummyStructWithCharFields nested_dummy_struct_with_char_fields; - char non_nested_char; - int non_nested_int; - Matryoshka* ptr_to_peer; - DummyStruct nested_dummy_struct; -}; - -// Dummy abstract base class. -class DummyAbstractBaseClass { - public: - explicit DummyAbstractBaseClass(int payload) - : payload_(payload) { - } - - virtual ~DummyAbstractBaseClass() = 0; - - virtual int TransformPayload() const = 0; - - protected: - int payload_; -}; - -DummyAbstractBaseClass::~DummyAbstractBaseClass() { -} - -// One version of a derived class. -class Negater : public DummyAbstractBaseClass { - public: - explicit Negater(int payload) - : DummyAbstractBaseClass(payload) { - } - - ~Negater() override { - } - - int TransformPayload() const override { - return -payload_; - } -}; - -// Another version of a derived class. -class Squarer : public DummyAbstractBaseClass { - public: - explicit Squarer(int payload) - : DummyAbstractBaseClass(payload) { - } - - ~Squarer() override { - } - - int TransformPayload() const override { - return payload_ * payload_; - } -}; - -// All-static class that wraps a global int variable and has methods to set and -// get it. -class StaticIntWrapper { - public: - static void Set(const int value) { - wrapped_value_ = value; - } - - static int Get() { - return wrapped_value_; - } - - private: - // Undefined default constructor. Class is all-static and should not be - // instantiated. - StaticIntWrapper(); - - static int wrapped_value_; -}; - -int StaticIntWrapper::wrapped_value_; - -// Toy object used to test instance method invocation. -template -class Accumulator { - public: - explicit Accumulator(const T initial) - : total_(initial) { - } - - void Accumulate(const T arg) { - total_ += arg; - } - - const T Get() const { - return total_; - } - - private: - T total_; -}; - -// When we GetAnnotatedType(), do we expect 'is_long' to be true? -template -struct ExpectLong - : std::integral_constant< - bool, - std::is_same::value // NOLINT(runtime/int) - || std::is_same::value> {}; // NOLINT(runtime/int) - -// Partial specialization for enums. -template -struct ExpectLong::value>::type> - : ExpectLong::type> {}; - -// Similar for 'is_long_long'. -template -struct ExpectLongLong - : std::integral_constant< - bool, - std::is_same::value // NOLINT(runtime/int) - || std::is_same< - IntegerType, - unsigned long long>::value> {}; // NOLINT(runtime/int) - -template -struct ExpectLongLong::value>::type> - : ExpectLongLong::type> {}; - -} // namespace - -// Test environment to handle global per-process initialization tasks for all -// tests. -class CodegenUtilsTestEnvironment : public ::testing::Environment { - public: - virtual void SetUp() { - ASSERT_TRUE(CodegenUtils::InitializeGlobal()); - } -}; - -class CodegenUtilsTest : public ::testing::Test { - protected: - virtual void SetUp() { - codegen_utils_.reset(new CodegenUtils("test_module")); - } - - // Helper method for GetScalarTypeTest. Tests CodegenUtils::GetType() and - // CodegenUtils::GetAnnotatedType() for an 'IntegerType' and its - // const-qualified version. - template - void CheckGetIntegerType() { - llvm::Type* llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(sizeof(IntegerType) << 3)); - - // Check extra information from AnnotatedType. - AnnotatedType annotated_type - = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_EQ(std::is_unsigned::value, - annotated_type.explicitly_unsigned); - EXPECT_EQ(ExpectLong::value, annotated_type.is_long); - EXPECT_EQ(ExpectLongLong::value, annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_FALSE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Also check with const-qualifier. - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(sizeof(IntegerType) << 3)); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_EQ(std::is_unsigned::value, - annotated_type.explicitly_unsigned); - EXPECT_EQ(ExpectLong::value, annotated_type.is_long); - EXPECT_EQ(ExpectLongLong::value, annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_TRUE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - } - - // Helper method for GetScalarTypeTest. Tests CodegenUtils::GetType() for an - // 'EnumType' that is expected to map to 'EquivalentIntegerType'. - template - void CheckGetEnumType() { - llvm::Type* llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(sizeof(EquivalentIntegerType) << 3)); - - // Check extra information from AnnotatedType. - AnnotatedType annotated_type - = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_EQ(std::is_unsigned::value, - annotated_type.explicitly_unsigned); - EXPECT_EQ(ExpectLong::value, annotated_type.is_long); - EXPECT_EQ(ExpectLongLong::value, - annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_FALSE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Also check with const-qualifier. - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(sizeof(EquivalentIntegerType) << 3)); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_EQ(std::is_unsigned::value, - annotated_type.explicitly_unsigned); - EXPECT_EQ(ExpectLong::value, annotated_type.is_long); - EXPECT_EQ(ExpectLongLong::value, - annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_TRUE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Check that the Type used for the enum is the exact same as the Type that - // would be used for the underlying integer. - llvm::Type* int_llvm_type - = codegen_utils_->GetType(); - EXPECT_EQ(llvm_type, int_llvm_type); - } - - // Helper method for GetPointerTypeTest. Checks that the annotations produced - // by CodegenUtils::GetAnnotatedType() are as expected for a given - // 'PointerType' (which may also be a reference). - // - // NOTE(chasseur): This works with up to 2 levels of indirection (e.g. pointer - // to pointer) which is as far as we test. CodegenUtils::GetAnnotatedType() - // should work for arbitrarily deep chains of pointers, but this check method - // only looks at most 2 pointers deep. - template - static void CheckAnnotationsForPointer(const AnnotatedType& annotated_type) { - // Strip pointers and references to get the innermost scalar type. - typedef typename std::remove_pointer< - typename std::remove_pointer< - typename std::remove_reference::type>::type>::type - ScalarType; - - // Is this a void* or pointer to user-defined class or struct represented as - // a void*? - EXPECT_EQ(std::is_void::value - || std::is_class::value, - annotated_type.is_voidptr); - // Is the outermost type a pointer or a reference? - EXPECT_EQ(std::is_reference::value, - annotated_type.is_reference); - // Is the inner scalar type explicitly unsigned (i.e. an unsigned arithmetic - // type other than bool, OR an enum that is represented as such a type)? - EXPECT_EQ(annotated_type_detail::IsUnsignedAdapter::value, - annotated_type.explicitly_unsigned); - // Is the inner scalar type long or long long (or an enum that has long or - // long long as its underlying type)? - EXPECT_EQ(ExpectLong::type>::value, - annotated_type.is_long); - EXPECT_EQ(ExpectLongLong::type>::value, - annotated_type.is_long_long); - - // Check CV-qualifiers for chains of pointers and/or references. - if (std::is_reference::value) { - if (std::is_pointer< - typename std::remove_reference::type>::value) { - // Reference to pointer. - ASSERT_EQ(3u, annotated_type.is_const.size()); - - // Constness of innermost scalar type. - EXPECT_EQ(std::is_const::value, - annotated_type.is_const[0]); - // Constness of intermediate pointertype. - EXPECT_EQ(std::is_const< - typename std::remove_reference::type>::value, - annotated_type.is_const[1]); - // Outermost reference type can not have any cv-qualifiers. - EXPECT_FALSE(annotated_type.is_const[2]); - - // Similar for volatile qualifiers. - ASSERT_EQ(3u, annotated_type.is_volatile.size()); - EXPECT_EQ(std::is_volatile::value, - annotated_type.is_volatile[0]); - EXPECT_EQ(std::is_volatile< - typename std::remove_reference::type>::value, - annotated_type.is_volatile[1]); - EXPECT_FALSE(annotated_type.is_volatile[2]); - } else { - // Reference to scalar. - ASSERT_EQ(2u, annotated_type.is_const.size()); - // Constness of referent scalar type. - EXPECT_EQ(std::is_const::value, - annotated_type.is_const[0]); - // Reference type itself can not have any cv-qualifiers. - EXPECT_FALSE(annotated_type.is_const[1]); - - // Similar for volatile qualifiers. - ASSERT_EQ(2u, annotated_type.is_volatile.size()); - EXPECT_EQ(std::is_volatile::value, - annotated_type.is_volatile[0]); - EXPECT_FALSE(annotated_type.is_volatile[1]); - } - } else { - if (std::is_pointer< - typename std::remove_pointer::type>::value) { - // Pointer to pointer. - ASSERT_EQ(3u, annotated_type.is_const.size()); - - // Constness of innermost scalar type. - EXPECT_EQ(std::is_const::value, - annotated_type.is_const[0]); - // Constness of intermediate pointer-to-scalar type. - EXPECT_EQ(std::is_const< - typename std::remove_pointer::type>::value, - annotated_type.is_const[1]); - // Constness of outermost pointer-to-pointer-to-scalar type. - EXPECT_EQ(std::is_const::value, - annotated_type.is_const[2]); - - // Similar for volatile qualifiers. - ASSERT_EQ(3u, annotated_type.is_volatile.size()); - EXPECT_EQ(std::is_volatile::value, - annotated_type.is_volatile[0]); - EXPECT_EQ(std::is_volatile< - typename std::remove_pointer::type>::value, - annotated_type.is_volatile[1]); - EXPECT_EQ(std::is_volatile::value, - annotated_type.is_volatile[2]); - } else { - // Pointer to scalar. - ASSERT_EQ(2u, annotated_type.is_const.size()); - // Constness of referent scalar type. - EXPECT_EQ(std::is_const::value, - annotated_type.is_const[0]); - // Constness of pointer type itself. - EXPECT_EQ(std::is_const::value, - annotated_type.is_const[1]); - - // Similar for volatile qualifiers. - ASSERT_EQ(2u, annotated_type.is_volatile.size()); - EXPECT_EQ(std::is_volatile::value, - annotated_type.is_volatile[0]); - EXPECT_EQ(std::is_volatile::value, - annotated_type.is_volatile[1]); - } - } - } - - // Helper method for GetPointerTypeTest. Calls CodegenUtils::GetType() and - // CodegenUtils::GetAnnotatedType() for 4 versions of a pointer to - // 'PointedType' with various const-qualifiers (regular mutable pointer, - // pointer to const, const-pointer, and const-pointer to const) and invokes - // 'check_functor' on the returned llvm::Type*. check_functor's call operator - // should do the actual checks to make sure that the llvm::Type is as - // expected. - // - // This method also calls CheckAnnotationsForPointer() to check that the - // additional annotations for C++ type properties not captured by the LLVM - // type system are as expected for each checked pointer type. - template - void CheckAllPointerFlavors(const CheckFunctor& check_functor) { - check_functor(codegen_utils_->GetType()); - check_functor(codegen_utils_->GetType()); - check_functor(codegen_utils_->GetType()); - check_functor(codegen_utils_->GetType()); - - // Also check GetAnnotatedType(). - AnnotatedType annotated_type - = codegen_utils_->GetAnnotatedType(); - check_functor(annotated_type.llvm_type); - CheckAnnotationsForPointer(annotated_type); - - annotated_type = codegen_utils_->GetAnnotatedType(); - check_functor(annotated_type.llvm_type); - CheckAnnotationsForPointer(annotated_type); - - annotated_type = codegen_utils_->GetAnnotatedType(); - check_functor(annotated_type.llvm_type); - CheckAnnotationsForPointer(annotated_type); - - annotated_type - = codegen_utils_->GetAnnotatedType(); - check_functor(annotated_type.llvm_type); - CheckAnnotationsForPointer(annotated_type); - } - - - // Similar to CheckAllPointerFlavors(), but also checks the lvalue-reference - // types 'PointedType&' and 'const PointedType&', which should map to the - // same LLVM pointer type. - template - void CheckAllPointerAndReferenceFlavors(const CheckFunctor& check_functor) { - CheckAllPointerFlavors(check_functor); - - check_functor(codegen_utils_->GetType()); - check_functor(codegen_utils_->GetType()); - - // Also check GetAnnotatedType() for reference types. - AnnotatedType annotated_type - = codegen_utils_->GetAnnotatedType(); - check_functor(annotated_type.llvm_type); - CheckAnnotationsForPointer(annotated_type); - - annotated_type = codegen_utils_->GetAnnotatedType(); - check_functor(annotated_type.llvm_type); - CheckAnnotationsForPointer(annotated_type); - } - - // Helper method for GetPointerTypeTest. Tests the various different flavors - // of a pointer or reference to 'IntegerType'. - template - void CheckGetIntegerPointerType() { - auto integer_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isIntegerTy( - sizeof(IntegerType) << 3)); - }; - - CheckAllPointerAndReferenceFlavors< - IntegerType, - decltype(integer_pointer_check_lambda)>( - integer_pointer_check_lambda); - } - - // Helper method for GetPointerTypeTest. Tests various different flavors of - // a pointer or reference to 'EnumType', which is expected to map to - // 'EquivalentIntegerType'. - template - void CheckGetEnumPointerType() { - auto enum_pointer_check_lambda = [this](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isIntegerTy( - sizeof(EquivalentIntegerType) << 3)); - - llvm::Type* int_llvm_type - = codegen_utils_->GetType(); - EXPECT_EQ(llvm_type, int_llvm_type); - }; - - CheckAllPointerAndReferenceFlavors< - EnumType, - decltype(enum_pointer_check_lambda)>( - enum_pointer_check_lambda); - } - - // Helper method for GetArrayTypeTest. Tests the correct dimensionality for - // single and multi dimensional arrays, which is expected to contain elements - // of ElementType. The proper ElementType can be checked by a call to the - // appropriate ScalarCheckFunctor - template - void CheckGetMultiDimensionalArray( - const llvm::Type* llvm_type, - ScalarCheckFunctor check_functor) { - std::vector dimensions = { Ns... }; - - for ( size_t dim : dimensions ) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isArrayTy()); - ASSERT_EQ(llvm::dyn_cast( - llvm_type)->getNumElements(), dim); - llvm_type = llvm_type->getArrayElementType(); - } - // Check the correctness of the scalar element type - ASSERT_NE(llvm_type, nullptr); - check_functor(llvm_type); - } - - // Helper method for GetArrayTypeTest. Calls CodegenUtils::GetType() for - // one, two and three dimensional arrays of 'ElementType', calling - // CodegenUtils::CheckGetMultiDimensionalArray() with appropriate llvm type - // and ScalarCheckFunctor. - template - void CheckForVariousArrayDimensions(const ScalarCheckFunctor& check_functor) { - CheckGetMultiDimensionalArray( - codegen_utils_->GetType(), - check_functor); - CheckGetMultiDimensionalArray( - codegen_utils_->GetType(), - check_functor); - CheckGetMultiDimensionalArray( - codegen_utils_->GetType(), - check_functor); - } - - // Helper method for GetArrayTypeTest. Tests the various different flavors - // of an array of 'IntegerType'. - template - void CheckGetArrayOfIntegersType() { - auto integer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(sizeof(IntegerType) << 3)); - }; - - CheckForVariousArrayDimensions(integer_check_lambda); - } - - // Helper method for GetArrayTypeTest. Tests arrays of various flavors - // of pointers to 'PointedType'. - template - void CheckGetArrayOfPointersType(ScalarCheckFunctor check_functor) { - // Pointer flavors - CheckForVariousArrayDimensions(check_functor); - CheckForVariousArrayDimensions(check_functor); - CheckForVariousArrayDimensions(check_functor); - CheckForVariousArrayDimensions(check_functor); - - // TODO(shardikar) Add checks for annotated types here - } - - // Helper method for GetArrayTypeTest. Tests various different flavors of an - // array of 'EnumType', which is expected to map to an array of - // 'EquivalentIntegerType'. - template - void CheckGetArrayOfEnumsType() { - auto enum_check_lambda = [this](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(sizeof(EquivalentIntegerType) << 3)); - - llvm::Type* int_llvm_type - = codegen_utils_->GetType(); - EXPECT_EQ(llvm_type, int_llvm_type); - }; - - CheckForVariousArrayDimensions(enum_check_lambda); - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for a single 'integer_constant'. - template - void CheckGetSingleIntegerConstant(const IntegerType integer_constant) { - llvm::Constant* constant = codegen_utils_->GetConstant(integer_constant); - CheckGetSingleIntegerConstant(integer_constant, constant); - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for a single 'integer_constant'. - template - void CheckGetSingleIntegerConstant(const IntegerType integer_constant, - llvm::Constant* constant) { - // Check the type. - EXPECT_EQ(codegen_utils_->GetType(), constant->getType()); - - // Check the value. - const llvm::APInt& constant_apint = constant->getUniqueInteger(); - if (std::is_signed::value) { - // If signed, compare with the APInt's sign-extended representation. - EXPECT_TRUE(constant_apint.isSignedIntN(sizeof(IntegerType) << 3)); - EXPECT_EQ(integer_constant, - static_cast(constant_apint.getSExtValue())); - } else { - // If unsigned, compare with the APInt's zero-extended representation. - EXPECT_TRUE(constant_apint.isIntN(sizeof(IntegerType) << 3)); - EXPECT_EQ(integer_constant, - static_cast(constant_apint.getZExtValue())); - } - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for an 'IntegerType' with several values - // of the specified integer type (0, 1, 123, the maximum, and if signed, - // -1, -123, and the minimum). - template - void CheckGetIntegerConstant() { - CheckGetSingleIntegerConstant(0); - CheckGetSingleIntegerConstant(1); - CheckGetSingleIntegerConstant(123); - CheckGetSingleIntegerConstant( - std::numeric_limits::max()); - if (std::is_signed::value) { - CheckGetSingleIntegerConstant(-1); - CheckGetSingleIntegerConstant(-123); - CheckGetSingleIntegerConstant( - std::numeric_limits::min()); - } - } - - // Helper method for CreateCastTest. Test CreateCast - // for Integer types - template - void CheckIntegerCast(const IntegerDestType integer_constant) { - llvm::Constant* constant = codegen_utils_->GetConstant( - static_cast(integer_constant)); - llvm::Constant* casted_constant = - llvm::dyn_cast( - codegen_utils_->CreateCast( - constant)); - CheckGetSingleIntegerConstant(integer_constant, casted_constant); - } - - // Helper method for CreateCastTest. Tests - // CodegenUtils::CreateCast() for an 'IntegerType' with several values - // of the specified integer type (0, 1, 123, the maximum, and if signed, - // -1, -123, and the minimum). - template - void CheckIntegerCast() { - CheckIntegerCast(0); - CheckIntegerCast(1); - CheckIntegerCast(123); - CheckIntegerCast( - static_cast( - std::numeric_limits::max())); - if (std::is_signed::value || - std::is_signed::value) { - IntegerSrcType src_value = -1; - CheckIntegerCast( - static_cast(src_value)); - src_value = -123; - CheckIntegerCast( - static_cast(src_value)); - src_value = std::numeric_limits::min(); - CheckIntegerCast( - static_cast(src_value)); - } - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for a single 'fp_constant'. - template - void CheckGetSingleFloatingPointConstant( - const FloatingPointType fp_constant) { - llvm::Constant* constant = codegen_utils_->GetConstant(fp_constant); - CheckGetSingleFloatingPointConstant(fp_constant, constant); - } - - template - void CheckGetSingleFloatingPointConstant( - const FloatingPointType fp_constant, - llvm::Constant* constant) { // Check the type. - EXPECT_EQ(codegen_utils_->GetType(), - constant->getType()); - - // Check the value. - llvm::ConstantFP* constant_as_fp - = llvm::dyn_cast(constant); - ASSERT_NE(constant_as_fp, nullptr); - if (std::is_same::value) { - EXPECT_EQ(fp_constant, - constant_as_fp->getValueAPF().convertToDouble()); - } else if (std::is_same::value) { - EXPECT_EQ(fp_constant, - constant_as_fp->getValueAPF().convertToFloat()); - } else { - ASSERT_TRUE(false) - << "Can not check value of floating point constant for a type that " - << "is not float or double."; - } - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for a 'FloatingPointType' with several values - // of the specified floating point type (positive and negative zero, positive - // and negative 12.34, the minimum and maximum possible normalized values, - // the highest-magnitude negative value, the smallest possible nonzero - // denormalized value, and infinity). - template - void CheckGetFloatingPointConstant() { - CheckGetSingleFloatingPointConstant(0.0); - CheckGetSingleFloatingPointConstant(-0.0); - CheckGetSingleFloatingPointConstant(12.34); - CheckGetSingleFloatingPointConstant(-12.34); - CheckGetSingleFloatingPointConstant( - std::numeric_limits::min()); - CheckGetSingleFloatingPointConstant( - std::numeric_limits::max()); - CheckGetSingleFloatingPointConstant( - std::numeric_limits::lowest()); - CheckGetSingleFloatingPointConstant( - std::numeric_limits::denorm_min()); - CheckGetSingleFloatingPointConstant( - std::numeric_limits::infinity()); - } - - // Helper method for CreateCastTest. Test CreateCast - // for Floating Point types - template - void CheckFloatingPointCast(const FloatDestType fp_constant) { - llvm::Constant* constant = codegen_utils_->GetConstant( - static_cast(fp_constant)); - llvm::Constant* casted_constant = - llvm::dyn_cast( - codegen_utils_->CreateCast(constant)); - CheckGetSingleFloatingPointConstant(fp_constant, casted_constant); - } - - // Helper method for CreateCastTest. Tests - // CodegenUtils::CreateCast() for a 'FloatSrcType' with several values - // of the specified floating point type (positive and negative zero, positive - // and negative 12.34, the minimum and maximum possible normalized values, - // the highest-magnitude negative value, the smallest possible nonzero - // denormalized value, and infinity). - template - void CheckFloatingPointCast() { - std::vector src_values = {0.0, -0.0, 12.34, -12.34, - std::numeric_limits::min(), - std::numeric_limits::max(), - std::numeric_limits::lowest(), - std::numeric_limits::denorm_min(), - std::numeric_limits::infinity()}; - for (const FloatSrcType& src_value : src_values) { - CheckFloatingPointCast( - static_cast(src_value)); - } - } - - // Helper method for CreateCastTest. Tests - // CodegenUtils::CreateCast() from an 'IntegerType' to 64-bit float - template - void CheckIntegerTo64FloatCast(const double double_constant) { - llvm::Constant* constant = codegen_utils_->GetConstant( - static_cast(double_constant)); - llvm::Constant* casted_constant = - llvm::dyn_cast( - codegen_utils_->CreateCast( - constant)); - CheckGetSingleFloatingPointConstant(double_constant, casted_constant); - } - - // Helper method for CreateCastTest. Tests - // CodegenUtils::CreateCast() for an 'IntegerType' with several values - // of the specified integer type (0, 1, 123, the maximum, and if signed, - // -1, -123, and the minimum) to 64-bit float. - template - void CheckIntegerTo64FloatCast() { - CheckIntegerTo64FloatCast(static_cast(0)); - CheckIntegerTo64FloatCast(static_cast(1)); - CheckIntegerTo64FloatCast(static_cast(123)); - CheckIntegerTo64FloatCast( - static_cast( - std::numeric_limits::max())); - if (std::is_signed::value) { - IntegerSrcType src_value = -1; - CheckIntegerTo64FloatCast( - static_cast(src_value)); - src_value = -123; - CheckIntegerTo64FloatCast( - static_cast(src_value)); - src_value = std::numeric_limits::min(); - CheckIntegerTo64FloatCast( - static_cast(src_value)); - } - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for a single 'enum_constant'. - template - void CheckGetSingleEnumConstant(const EnumType enum_constant) { - llvm::Constant* constant = codegen_utils_->GetConstant(enum_constant); - - // Check the type. - EXPECT_EQ(codegen_utils_->GetType(), constant->getType()); - - // Check the value (implicitly converted to the underlying integer type). - const llvm::APInt& constant_apint = constant->getUniqueInteger(); - if (std::is_signed::type>::value) { - // If signed, compare with the APInt's sign-extended representation. - EXPECT_TRUE(constant_apint.isSignedIntN( - sizeof(typename std::underlying_type::type) << 3)); - EXPECT_EQ(static_cast::type>( - enum_constant), - static_cast::type>( - constant_apint.getSExtValue())); - } else { - // If unsigned, compare with the APInt's zero-extended representation. - EXPECT_TRUE(constant_apint.isIntN( - sizeof(typename std::underlying_type::type) << 3)); - EXPECT_EQ(static_cast::type>( - enum_constant), - static_cast::type>( - constant_apint.getZExtValue())); - } - } - - // Helper method for GetScalarConstantTest. Tests - // CodegenUtils::GetConstant() for an 'EnumType' by calling - // CheckGetSingleEnumConstant() for each constant listed in 'enum_constants'. - template - void CheckGetEnumConstants(std::initializer_list enum_constants) { - for (const EnumType enum_constant : enum_constants) { - CheckGetSingleEnumConstant(enum_constant); - } - } - - // Helper method for GetPointerConstantTest and - // GetPointerToMemberConstantTest. Generates a unique function name for a - // global variable accessor function based on 'idx'. - static std::string GlobalConstantAccessorName(std::size_t idx) { - static constexpr char kPrefix[] = "global_accessor_"; - char print_buffer[sizeof(kPrefix) + (sizeof(idx) << 1) + 1]; - int written = std::snprintf(print_buffer, - sizeof(print_buffer), - "%s%zx", - kPrefix, - idx); - assert(static_cast(written) < sizeof(print_buffer)); - return print_buffer; - } - - // Helper method for GetPointerConstantTest and - // GetPointerToMemberConstantTest. Calls all of the constant-accessor - // functions generated during the test and verifies that they return the - // expected addresses. - void FinishCheckingGlobalConstantPointers( - const std::vector& pointer_check_addresses) { - for (std::size_t idx = 0; idx < pointer_check_addresses.size(); ++idx) { - std::uintptr_t (*check_fn)() - = codegen_utils_->GetFunctionPointer( - GlobalConstantAccessorName(idx)); - EXPECT_EQ(pointer_check_addresses[idx], (*check_fn)()); - } - } - - // Helper method for GetPointerConstantTest. Tests - // CodegenUtils::GetConstant() for 'ptr_constant'. Verifies that the - // pointer Constant returned is the expected type and generates an accessor - // function that returns the address of the pointer constant, recording the - // expected address in '*pointer_check_addresses'. After all of the - // invocations of this method in a test, CodegenUtils::PrepareForExecution() - // should be called to compile the accessor functions, then - // FinishCheckingGlobalConstantPointers() should be called to make sure that - // the accessor functions return the expected addresses. - template - void CheckGetSinglePointerConstant( - PointerType ptr_constant, - std::vector* pointer_check_addresses) { - llvm::Constant* constant = codegen_utils_->GetConstant(ptr_constant); - - // Check type. - EXPECT_EQ(codegen_utils_->GetType(), constant->getType()); - - if (ptr_constant == nullptr) { - // Expect a NULL literal. - EXPECT_TRUE(constant->isNullValue()); - } else { - // Expect a GlobalVariable. This will be mapped to the actual external - // address when CodegenUtils::PrepareForExecution() is called. For now, - // we generate a function that returns the (constant) address of the - // GlobalVariable. Later on, we will compile all such functions and check - // that they return the expected addresses. - EXPECT_TRUE(llvm::isa(constant)); - typedef std::uintptr_t (*GloablAccessorFn) (); - llvm::Function* global_accessor_fn - = codegen_utils_->CreateFunction( - GlobalConstantAccessorName(pointer_check_addresses->size())); - llvm::BasicBlock* global_accessor_fn_body - = codegen_utils_->CreateBasicBlock("body", global_accessor_fn); - codegen_utils_->ir_builder()->SetInsertPoint(global_accessor_fn_body); - llvm::Value* global_addr = codegen_utils_->ir_builder()->CreatePtrToInt( - constant, - codegen_utils_->GetType()); - codegen_utils_->ir_builder()->CreateRet(global_addr); - - // Verify function is well-formed. - EXPECT_FALSE(llvm::verifyFunction(*global_accessor_fn)); - - pointer_check_addresses->emplace_back( - reinterpret_cast(ptr_constant)); - } - } - - // Helper method for GetPointerConstantTest. Tests - // CodegenUtils::GetConstant() by calling CheckGetSinglePointerConstant() - // for a 'ScalarType*' pointer pointing to NULL, to stack memory, and to heap - // memory, and for a 'ScalarType**' pointer pointing to NULL, to stack - // memory, and to heap memory. - template - void CheckGetPointerToScalarConstant( - std::vector* pointer_check_addresses) { - ScalarType* bare_ptr = nullptr; - ScalarType stack_variable; - std::unique_ptr heap_variable(new ScalarType); - - // Note that CheckGetSinglePointerConstant() will create functions that - // refer to global variables based on pointers that will no longer be valid - // outside of the scope of this method. This is safe in this particular case - // only because the generated functions do nothing but take the address of - // the global variable in question (they do not dereference it). - CheckGetSinglePointerConstant(bare_ptr, pointer_check_addresses); - CheckGetSinglePointerConstant(&stack_variable, pointer_check_addresses); - CheckGetSinglePointerConstant(heap_variable.get(), pointer_check_addresses); - - // Also check pointer-to-pointer. - ScalarType** bare_ptr_to_ptr = nullptr; - CheckGetSinglePointerConstant(bare_ptr_to_ptr, pointer_check_addresses); - bare_ptr_to_ptr = &bare_ptr; - CheckGetSinglePointerConstant(bare_ptr_to_ptr, pointer_check_addresses); - std::unique_ptr heap_ptr(new ScalarType*); - CheckGetSinglePointerConstant(heap_ptr.get(), pointer_check_addresses); - } - - // Helper method for ExternalFunctionTest. Registers 'external_function' in - // 'codegen_utils_' and creates an LLVM wrapper function for it named - // 'wrapper_function_name'. The wrapper function has the same type-signature - // as 'external_function' and simply forwards its arguments as-is to - // 'external_function' and returns back the same return value. - template - void MakeWrapperFunction( - ReturnType (*external_function)(ArgumentTypes...), - const std::string& wrapper_function_name) { - // Register 'external_function' in 'codegen_utils_' and check that it has - // the expected type-signature. - llvm::Function* llvm_external_function - = codegen_utils_->GetOrRegisterExternalFunction(external_function); - ASSERT_NE(llvm_external_function, nullptr); - EXPECT_EQ( - (codegen_utils_->GetFunctionType() - ->getPointerTo()), - llvm_external_function->getType()); - - // Create a wrapper function in 'codegen_utils_' with the same - // type-signature that merely forwards its arguments to the external - // function as-is. - typedef ReturnType (*WrapperFn) (ArgumentTypes...); - llvm::Function* wrapper_function - = codegen_utils_->CreateFunction( - wrapper_function_name); - - llvm::BasicBlock* wrapper_function_body - = codegen_utils_->CreateBasicBlock("wrapper_fn_body", - wrapper_function); - - codegen_utils_->ir_builder()->SetInsertPoint(wrapper_function_body); - std::vector forwarded_args; - for (llvm::Argument& arg : wrapper_function->args()) { - forwarded_args.push_back(&arg); - } - llvm::CallInst* call = codegen_utils_->ir_builder()->CreateCall( - llvm_external_function, - forwarded_args); - - // Return the result of the call, or void if the function returns void. - if (std::is_same::value) { - codegen_utils_->ir_builder()->CreateRetVoid(); - } else { - codegen_utils_->ir_builder()->CreateRet(call); - } - - // Check that the wrapper function is well-formed. LLVM verification - // functions return false if no errors are detected. - EXPECT_FALSE(llvm::verifyFunction(*wrapper_function)); - } - - // Helper method for GetPointerToMemberConstantTest. Verifies that - // CodegenUtils::GetPointerToMember() functions correctly for - // 'pointers_to_members' by checking the type of the pointer generated when - // accessing a member from a constant pointer to StructType and generating an - // accessor function the returns the address of the member, recording the - // expected address in '*pointer_check_addresses'. After all of the - // invocations of this method in a test, CodegenUtils::PrepareForExecution() - // should be called to compile the accessor functions, then - // FinishCheckingGlobalConstantPointers() should be called to make sure that - // the accessor functions return the expected addresses. - template - void CheckGetPointerToMemberConstant( - std::vector* pointer_check_addresses, - const StructType* external_struct, - const std::size_t expected_offset, - PointerToMemberTypes&&... pointers_to_members) { - llvm::Constant* llvm_ptr_to_struct - = codegen_utils_->GetConstant(external_struct); - - // Generate a function that returns the (constant) address of the member - // field inside the struct. - typedef std::uintptr_t (*GlobalAccessorFn) (); - llvm::Function* global_member_accessor_fn - = codegen_utils_->CreateFunction( - GlobalConstantAccessorName(pointer_check_addresses->size())); - llvm::BasicBlock* global_member_accessor_fn_body - = codegen_utils_->CreateBasicBlock("body", global_member_accessor_fn); - codegen_utils_->ir_builder()->SetInsertPoint( - global_member_accessor_fn_body); - llvm::Value* member_ptr = codegen_utils_->GetPointerToMember( - llvm_ptr_to_struct, - std::forward(pointers_to_members)...); - llvm::Value* member_address = codegen_utils_->ir_builder()->CreatePtrToInt( - member_ptr, - codegen_utils_->GetType()); - codegen_utils_->ir_builder()->CreateRet(member_address); - - // Verify accessor function is well-formed. - EXPECT_FALSE(llvm::verifyFunction(*global_member_accessor_fn)); - - pointer_check_addresses->emplace_back( - reinterpret_cast(external_struct) + expected_offset); - } - - // Helper method for GetPointerToMemberTest. Generates an accessor function - // that takes a StructType* pointer and returns the value of the member - // variable indicated by 'pointers_to_members'. - template - void MakeStructMemberAccessorFunction( - const std::string& function_name, - PointerToMemberTypes&&... pointers_to_members) { - typedef MemberType (*AccessorFn) (const StructType*); - llvm::Function* accessor_function - = codegen_utils_->CreateFunction( - function_name); - llvm::BasicBlock* accessor_function_body - = codegen_utils_->CreateBasicBlock("accessor_fn_body", - accessor_function); - codegen_utils_->ir_builder()->SetInsertPoint(accessor_function_body); - - // Get pointer to member. - llvm::Value* member_ptr = codegen_utils_->GetPointerToMember( - ArgumentByPosition(accessor_function, 0), - std::forward(pointers_to_members)...); - // Actually load the value from the pointer. - llvm::Value* member_value - = codegen_utils_->ir_builder()->CreateLoad(member_ptr); - // Return the loaded value. - codegen_utils_->ir_builder()->CreateRet(member_value); - - // Check that the accessor function is well-formed. LLVM verification - // functions return false if no errors are detected. - EXPECT_FALSE(llvm::verifyFunction(*accessor_function)); - } - - // Helper method for GetPointerToMemberTest for accessing an element of an - // array that is a member of structure. Generates an accessor function that - // takes a StructType* pointer and an index into the array and returns the - // value of the member variable at the index indicated by - // 'pointers_to_members'. - template - void MakeStructArrayMemberElementAccessorFunction( - const std::string& function_name, - PointerToMemberTypes&&... pointers_to_members) { - typedef typename std::remove_extent< - OneDimArrayMemberType>::type ElementType; - typedef ElementType (*AccessorFn) (const StructType*, size_t index); - llvm::Function* accessor_function - = codegen_utils_->CreateFunction( - function_name); - llvm::BasicBlock* accessor_function_body - = codegen_utils_->CreateBasicBlock("accessor_fn_body", - accessor_function); - codegen_utils_->ir_builder()->SetInsertPoint(accessor_function_body); - - // Get pointer to member that is the array. - llvm::Value* array_member_ptr = codegen_utils_->GetPointerToMember( - ArgumentByPosition(accessor_function, 0), - std::forward(pointers_to_members)...); - // Get pointer to the index in that array - llvm::Value* element_ptr = codegen_utils_->ir_builder()->CreateGEP( - array_member_ptr, { - codegen_utils_->GetConstant(0), - ArgumentByPosition(accessor_function, 1) }); - // Actually load the value from the pointer. - llvm::Value* element_value - = codegen_utils_->ir_builder()->CreateLoad(element_ptr); - // Return the loaded value. - codegen_utils_->ir_builder()->CreateRet(element_value); - - // Check that the accessor function is well-formed. LLVM verification - // functions return false if no errors are detected. - EXPECT_FALSE(llvm::verifyFunction(*accessor_function)); - } - - // Helper method for ProjectScalarArrayTest. Generate an array with random - // value for given size and return the pointer to it. Caller is responsible - // for deleting the memory. - template - InputType* RandomInputArrayGenerator(size_t input_size) { - InputType* input = new InputType[input_size]; - for (size_t idx = 0; idx < input_size; ++idx) { - unsigned int seed = idx; - input[idx] = rand_r(&seed) % ((2 ^ (sizeof(InputType) * 8)) - 1); - } - return input; - } - - // Helper method for ProjectScalarArrayTest. Generate an array with random - // value for given range and size. Caller is responsible for deleting the - // memory - size_t* RandomProjectionIndicesGenerator(size_t how_many, size_t range) { - size_t* projection_indices = new size_t[how_many]; - for (size_t idx = 0; idx < how_many; ++idx) { - unsigned int seed = idx; - projection_indices[idx] = rand_r(&seed) % range; - } - return projection_indices; - } - - // Helper method for ProjectScalarArrayTest. Generate an IR module for - // projecting element from scalar array for given type and indices / offsets. - template - void GenerateScalarArrayProjectionFunction( - const std::string& project_func_name, - size_t* projection_indices, size_t projection_count) { - typedef void (*ProjectScalarFn) (InputType*, InputType*); - llvm::Function* project_scalar_function - = codegen_utils_->CreateFunction( - project_func_name); - - // BasicBlocks for function entry. - llvm::BasicBlock* entry_block = codegen_utils_->CreateBasicBlock( - "entry", project_scalar_function); - llvm::Value* input_array = ArgumentByPosition(project_scalar_function, 0); - llvm::Value* output_array = ArgumentByPosition(project_scalar_function, 1); - codegen_utils_->ir_builder()->SetInsertPoint(entry_block); - - // Build loop unrolled projection code. - for (size_t idx = 0; idx < projection_count; ++idx) { - // The next address of the input array where we need to read. - llvm::Value* next_address = - codegen_utils_->ir_builder()->CreateInBoundsGEP(input_array, - {codegen_utils_->GetConstant(projection_indices[idx])}); - - // Load the value from the calculated input address. - llvm::LoadInst* load_instruction = - codegen_utils_->ir_builder()->CreateLoad(next_address, "input"); - - // Find the output address where we need to write our projected element. - llvm::Value* next_output_address = - codegen_utils_->ir_builder()->CreateInBoundsGEP(output_array, - {codegen_utils_->GetConstant(idx)}); - - // Store the projecetd element into the output address. - codegen_utils_->ir_builder()-> CreateStore( - load_instruction, next_output_address, "output"); - } - codegen_utils_->ir_builder()->CreateRetVoid(); - } - - // Helper method for ProjectScalarArrayTest. For given type generate random - // array, random index and then project using IR module. - template - void ProjectScalarArrayTestHelper() { - // Input and project array size - const int input_size = 100; - const int projection_count = 10; - - InputType* input_array = RandomInputArrayGenerator(input_size); - size_t* proj_indices = RandomProjectionIndicesGenerator(projection_count, - input_size); - InputType* output_array = new InputType[projection_count]; - memset(output_array, -1, projection_count * sizeof(InputType)); - GenerateScalarArrayProjectionFunction("func_project", - proj_indices, projection_count); - - // Prepare for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, true)); - - void (*project_scalar_function_compiled)(InputType*, InputType*) - = codegen_utils_->GetFunctionPointer< - decltype(project_scalar_function_compiled)>("func_project"); - - // Call the generated projection function - (*project_scalar_function_compiled)(input_array, output_array); - - // Check if all the projected elements are correctly placed in the output. - for (size_t idx = 0; idx < projection_count; ++idx) { - EXPECT_EQ(input_array[proj_indices[idx]], output_array[idx]); - } - - delete[] input_array; - delete[] proj_indices; - delete[] output_array; - } - - // Helper method for CreateMakeTupleTest. This method creates LLVM function of - // type int(*)(MemberType... args). This LLVM function will create tuple using - // passed arguments. Then it checks if the value of the member in the tuple - // matches with arguments passed in functions. If the check fails, it will - // return the argument index. - template - void MakeTupleFunc(const std::string& func_name) { - auto irb = codegen_utils_->ir_builder(); - using LLVMFuncType = int(*)(MemberTypes... args); - - llvm::Function* check_tuple_fn = - codegen_utils_->CreateFunction( - func_name); - - irb->SetInsertPoint(codegen_utils_->CreateBasicBlock( - "main", check_tuple_fn)); - llvm::BasicBlock* return_block = codegen_utils_->CreateBasicBlock( - "return_block", check_tuple_fn); - llvm::Value* llvm_ret_ptr = irb->CreateAlloca( - codegen_utils_->GetType(), nullptr, "ret_ptr"); - irb->CreateStore(codegen_utils_->GetConstant(200), llvm_ret_ptr); - std::vector members; - llvm::Function::arg_iterator itr = check_tuple_fn->arg_begin(); - for (; itr != check_tuple_fn->arg_end(); ++itr) { - members.push_back(&(*itr)); - } - llvm::Value* llvm_struct_ptr = codegen_utils_->CreateMakeTuple( - members, "tuple_struct"); - - llvm::Value* llvm_struct_a = irb->CreateLoad(llvm_struct_ptr); - - for (size_t idx = 0; idx < members.size(); ++idx) { - llvm::BasicBlock* next_block = codegen_utils_->CreateBasicBlock( - "member_" + std::to_string(idx), check_tuple_fn); - llvm::Value* llvm_mem_val = irb->CreateExtractValue(llvm_struct_a, idx); - irb->CreateStore(codegen_utils_->GetConstant(idx), llvm_ret_ptr); - llvm::Type* arg_type = members[idx]->getType(); - ASSERT_TRUE(arg_type->isIntegerTy() || - arg_type->isFloatingPointTy()); - llvm::Value* llvm_comp_res = nullptr; - if (arg_type->isIntegerTy()) { - llvm_comp_res = irb->CreateICmpEQ(members[idx], llvm_mem_val); - } else { - llvm_comp_res = irb->CreateFCmpOEQ(members[idx], llvm_mem_val); - } - irb->CreateCondBr( - llvm_comp_res, - next_block /* true */, - return_block /* false */); - irb->SetInsertPoint(next_block); - } - irb->CreateStore(codegen_utils_->GetConstant( - members.size()), llvm_ret_ptr); - irb->CreateBr(return_block); - irb->SetInsertPoint(return_block); - irb->CreateRet(irb->CreateLoad(llvm_ret_ptr)); - } - - std::unique_ptr codegen_utils_; -}; - -typedef CodegenUtilsTest CodegenUtilsDeathTest; - -TEST_F(CodegenUtilsTest, InitializationTest) { - EXPECT_NE(codegen_utils_->ir_builder(), nullptr); - ASSERT_NE(codegen_utils_->module(), nullptr); - EXPECT_EQ(std::string("test_module"), - codegen_utils_->module()->getModuleIdentifier()); -} - -TEST_F(CodegenUtilsTest, GetScalarTypeTest) { - // Check void. - llvm::Type* llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isVoidTy()); - - AnnotatedType annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isVoidTy()); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_FALSE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Check bool (represented as i1 in LLVM IR). - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(1)); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isIntegerTy(1)); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_FALSE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(1)); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isIntegerTy(1)); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_TRUE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Check 32-bit float. - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isFloatTy()); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isFloatTy()); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_FALSE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isFloatTy()); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isFloatTy()); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_TRUE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Check 64-bit double. - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isDoubleTy()); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isDoubleTy()); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_FALSE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - llvm_type = codegen_utils_->GetType(); - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isDoubleTy()); - - annotated_type = codegen_utils_->GetAnnotatedType(); - ASSERT_NE(annotated_type.llvm_type, nullptr); - EXPECT_TRUE(annotated_type.llvm_type->isDoubleTy()); - EXPECT_FALSE(annotated_type.is_voidptr); - EXPECT_FALSE(annotated_type.is_reference); - EXPECT_FALSE(annotated_type.explicitly_unsigned); - EXPECT_FALSE(annotated_type.is_long); - EXPECT_FALSE(annotated_type.is_long_long); - ASSERT_EQ(1u, annotated_type.is_const.size()); - EXPECT_TRUE(annotated_type.is_const.front()); - ASSERT_EQ(1u, annotated_type.is_volatile.size()); - EXPECT_FALSE(annotated_type.is_volatile.front()); - - // Check C built-in integral types. - CheckGetIntegerType(); - CheckGetIntegerType(); // NOLINT(runtime/int) - CheckGetIntegerType(); - CheckGetIntegerType(); // NOLINT(runtime/int) - CheckGetIntegerType(); // NOLINT(runtime/int) - - // Check explicitly signed versions of C built-in integral types. Note that - // integer types in LLVM do not have a signedness property, so - // signed/unsigned versions of a type in C/C++ have the same representation - // in LLVM IR. - CheckGetIntegerType(); - CheckGetIntegerType(); // NOLINT(runtime/int) - CheckGetIntegerType(); - CheckGetIntegerType(); // NOLINT(runtime/int) - CheckGetIntegerType(); // NOLINT(runtime/int) - - // Check explicitly unsigned versions of C built-in integral types. - CheckGetIntegerType(); - CheckGetIntegerType(); // NOLINT(runtime/int) - CheckGetIntegerType(); - CheckGetIntegerType(); // NOLINT(runtime/int) - CheckGetIntegerType(); // NOLINT(runtime/int) - - // Check cstdint typedefs. - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - CheckGetIntegerType(); - - // Check cstddef typedefs. - CheckGetIntegerType(); - CheckGetIntegerType(); - - // Check enums. - CheckGetEnumType::type>(); - CheckGetEnumType::type>(); - CheckGetEnumType::type>(); - CheckGetEnumType::type>(); - CheckGetEnumType(); -} - -TEST_F(CodegenUtilsTest, GetPointerTypeTest) { - // Check void*. Void pointers are a special case, because convention in the - // LLVM type system is to use i8* (equivalent to char* in C) for all - // "untyped" pointers. - auto void_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isIntegerTy(8)); - }; - // Unlike other types, we check only pointers, not references, because there - // is no such thing as void&. - CheckAllPointerFlavors( - void_pointer_check_lambda); - - // Check bool* (bool is represented as i1 in LLVM IR). - auto bool_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isIntegerTy(1)); - }; - CheckAllPointerAndReferenceFlavors< - bool, - decltype(bool_pointer_check_lambda)>( - bool_pointer_check_lambda); - - // Check float*. - auto float_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isFloatTy()); - }; - CheckAllPointerAndReferenceFlavors< - float, - decltype(float_pointer_check_lambda)>( - float_pointer_check_lambda); - - // Check double*. - auto double_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isDoubleTy()); - }; - CheckAllPointerAndReferenceFlavors< - double, - decltype(double_pointer_check_lambda)>( - double_pointer_check_lambda); - - // Check pointers to C built-in integral types. - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - - // Check pointers to explicitly signed versions of C built-in integral types. - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - - // Check pointers to unsigned versions of C built-in integral types. - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - CheckGetIntegerPointerType(); // NOLINT(runtime/int) - - // Check pointers to cstdint typedefs. - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - - // Check pointers to cstddef typedefs. - CheckGetIntegerPointerType(); - CheckGetIntegerPointerType(); - - // Check pointers to enums. - CheckGetEnumPointerType< - SimpleEnum, - std::underlying_type::type>(); - CheckGetEnumPointerType< - SignedSimpleEnum, - std::underlying_type::type>(); - CheckGetEnumPointerType< - StronglyTypedEnum, - std::underlying_type::type>(); - CheckGetEnumPointerType< - SignedStronglyTypedEnum, - std::underlying_type::type>(); - CheckGetEnumPointerType(); - - // Pointers and references to structs and classes get transformed to untyped - // pointers (i8* in LLVM, equivalent to void* in C++). We can reuse - // 'void_pointer_check_lambda' here. - CheckAllPointerAndReferenceFlavors< - DummyStruct, - decltype(void_pointer_check_lambda)>( - void_pointer_check_lambda); - CheckAllPointerAndReferenceFlavors< - DummyAbstractBaseClass, - decltype(void_pointer_check_lambda)>( - void_pointer_check_lambda); - CheckAllPointerAndReferenceFlavors< - Negater, - decltype(void_pointer_check_lambda)>( - void_pointer_check_lambda); - CheckAllPointerAndReferenceFlavors< - Squarer, - decltype(void_pointer_check_lambda)>( - void_pointer_check_lambda); - - // Pointer to pointer and reference to pointer. - auto pointer_to_pointer_to_int_check_lambda - = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - ASSERT_TRUE(llvm_type->getPointerElementType()->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->getPointerElementType() - ->isIntegerTy(sizeof(int) << 3)); - }; - CheckAllPointerAndReferenceFlavors< - int*, - decltype(pointer_to_pointer_to_int_check_lambda)>( - pointer_to_pointer_to_int_check_lambda); - - // Also check void** and the like. - auto pointer_to_pointer_to_void_check_lambda - = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - ASSERT_TRUE(llvm_type->getPointerElementType()->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->getPointerElementType() - ->isIntegerTy(8)); - }; - CheckAllPointerAndReferenceFlavors< - void*, - decltype(pointer_to_pointer_to_void_check_lambda)>( - pointer_to_pointer_to_void_check_lambda); - - // Check pointer-to-pointer and reference-to-pointer for struct and class - // types as well. As above, the last-level pointer becomes a generic untyped - // pointer (i8* in LLVM, equivalent to void* in C++). - CheckAllPointerAndReferenceFlavors< - DummyStruct*, - decltype(pointer_to_pointer_to_void_check_lambda)>( - pointer_to_pointer_to_void_check_lambda); - CheckAllPointerAndReferenceFlavors< - DummyAbstractBaseClass*, - decltype(pointer_to_pointer_to_void_check_lambda)>( - pointer_to_pointer_to_void_check_lambda); - CheckAllPointerAndReferenceFlavors< - Negater*, - decltype(pointer_to_pointer_to_void_check_lambda)>( - pointer_to_pointer_to_void_check_lambda); - CheckAllPointerAndReferenceFlavors< - Squarer*, - decltype(pointer_to_pointer_to_void_check_lambda)>( - pointer_to_pointer_to_void_check_lambda); -} - -TEST_F(CodegenUtilsTest, GetArrayTypeTest) { - // Check void*. Void pointers are a special case, because convention in the - // LLVM type system is to use i8* (equivalent to char* in C) for all - // "untyped" pointers. - auto void_pointer_array_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isIntegerTy(8)); - }; - // Unlike other types, we check only pointers, not references, because there - // is no such thing as void&. - CheckForVariousArrayDimensions( - void_pointer_array_check_lambda); - - // Check bool* (bool is represented as i1 in LLVM IR). - auto bool_pointer_array_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - ASSERT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isIntegerTy(1)); - }; - CheckForVariousArrayDimensions< - bool*, - decltype(bool_pointer_array_check_lambda)>( - bool_pointer_array_check_lambda); - - // Check bool - auto bool_array_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isIntegerTy(1)); - }; - CheckForVariousArrayDimensions< - bool, - decltype(bool_array_check_lambda)>( - bool_array_check_lambda); - - // Check float. - auto float_array_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isFloatTy()); - }; - CheckForVariousArrayDimensions< - float, - decltype(float_array_check_lambda)>( - float_array_check_lambda); - - // Check double. - auto double_array_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isDoubleTy()); - }; - CheckForVariousArrayDimensions< - double, - decltype(double_array_check_lambda)>( - double_array_check_lambda); - - // Check arrays of some C built-in integral types. - CheckGetArrayOfIntegersType(); - CheckGetArrayOfIntegersType(); // NOLINT(runtime/int) - CheckGetArrayOfIntegersType(); - CheckGetArrayOfIntegersType(); // NOLINT(runtime/int) - CheckGetArrayOfIntegersType(); // NOLINT(runtime/int) - - // Check arrays of explicitly signed and unsigned versions of int - CheckGetArrayOfIntegersType(); - CheckGetArrayOfIntegersType(); - - // Check arrays of cstddef typedefs. - CheckGetArrayOfIntegersType(); - CheckGetArrayOfIntegersType(); - - // Check arrays of enums. - CheckGetArrayOfEnumsType< - SimpleEnum, - std::underlying_type::type>(); - CheckGetArrayOfEnumsType(); - - // Check arrays of pointers to structs and classes - // Pointers and references to structs and classes get transformed to untyped - // pointers (i8* in LLVM, equivalent to void* in C++). We can reuse - // 'void_pointer_check_lambda' here. - CheckForVariousArrayDimensions< - DummyStruct*, - decltype(void_pointer_array_check_lambda)>( - void_pointer_array_check_lambda); - CheckForVariousArrayDimensions< - DummyAbstractBaseClass*, - decltype(void_pointer_array_check_lambda)>( - void_pointer_array_check_lambda); - CheckForVariousArrayDimensions< - Squarer*, - decltype(void_pointer_array_check_lambda)>( - void_pointer_array_check_lambda); - - // Check arrays of various flavors of pointers to integers - auto int_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()-> - isIntegerTy(sizeof(int) << 3)); - }; - CheckGetArrayOfPointersType(int_pointer_check_lambda); - - // Check arrays of various flavors of pointers to pointers to integers - auto char_pointer_to_pointer_check_lambda = [](const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()-> - getPointerElementType()->isIntegerTy(sizeof(char) << 3)); - }; - CheckGetArrayOfPointersType(char_pointer_to_pointer_check_lambda); - - // Check pointers to arrays - auto int_pointer_to_array_of_ints_check_lambda = []( - const llvm::Type* llvm_type) { - ASSERT_NE(llvm_type, nullptr); - EXPECT_TRUE(llvm_type->isPointerTy()); - EXPECT_TRUE(llvm_type->getPointerElementType()->isArrayTy()); - ASSERT_EQ(llvm::dyn_cast( - llvm_type->getPointerElementType())->getNumElements(), 5); - EXPECT_TRUE(llvm_type->getPointerElementType()-> - getArrayElementType()->isIntegerTy(sizeof(int) << 3)); - }; - int_pointer_to_array_of_ints_check_lambda( - codegen_utils_->GetType()); -} - -TEST_F(CodegenUtilsTest, GetScalarConstantTest) { - // Check bool constants. - llvm::Constant* constant = codegen_utils_->GetConstant(false); - // Verify the constant's type. - EXPECT_EQ(codegen_utils_->GetType(), constant->getType()); - // Verify the constant's value. - EXPECT_TRUE(constant->isZeroValue()); - - constant = codegen_utils_->GetConstant(true); - EXPECT_EQ(codegen_utils_->GetType(), constant->getType()); - EXPECT_TRUE(constant->isOneValue()); - - // Check the C built-in integer types, and their explicitly signed and - // unsigned versions. - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); // NOLINT(runtime/int) - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); // NOLINT(runtime/int) - CheckGetIntegerConstant(); // NOLINT(runtime/int) - - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); // NOLINT(runtime/int) - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); // NOLINT(runtime/int) - CheckGetIntegerConstant(); // NOLINT(runtime/int) - - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); // NOLINT(runtime/int) - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); // NOLINT(runtime/int) - CheckGetIntegerConstant(); // NOLINT(runtime/int) - - // Check cstdint typedefs. - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - - // Check cstddef typedefs. - CheckGetIntegerConstant(); - CheckGetIntegerConstant(); - - // Check floating-point types. - CheckGetFloatingPointConstant(); - CheckGetFloatingPointConstant(); - - // Check enums. - CheckGetEnumConstants( - {kSimpleEnumA, kSimpleEnumB, kSimpleEnumC}); - CheckGetEnumConstants( - {kSignedSimpleEnumA, kSignedSimpleEnumB, kSignedSimpleEnumC}); - CheckGetEnumConstants( - {StronglyTypedEnum::kCaseA, StronglyTypedEnum::kCaseB, - StronglyTypedEnum::kCaseC}); - CheckGetEnumConstants( - {SignedStronglyTypedEnum::kCaseA, SignedStronglyTypedEnum::kCaseB, - SignedStronglyTypedEnum::kCaseC}); - CheckGetEnumConstants( - {StronglyTypedEnumUint64::kCaseA, StronglyTypedEnumUint64::kCaseB, - StronglyTypedEnumUint64::kCaseC}); -} - -TEST_F(CodegenUtilsTest, CreateCastTest) { - // Check different integer types - // signed to signed with ext / trunc - CheckIntegerCast(); - CheckIntegerCast(); - - // unsigned to unsigned with ext / trunc - CheckIntegerCast(); - CheckIntegerCast(); - - // signed to unsigned with ext / trunc - CheckIntegerCast(); - CheckIntegerCast(); - - // unsigned to signed with ext / trunc - CheckIntegerCast(); - CheckIntegerCast(); - - // integer type of same size - CheckIntegerCast(); - CheckIntegerCast(); - - // Check floating-point types. - CheckFloatingPointCast(); - CheckFloatingPointCast(); - - // Floating type of same size - CheckFloatingPointCast(); - CheckFloatingPointCast(); - - // signed integer to 64-bit float - CheckIntegerTo64FloatCast(); - CheckIntegerTo64FloatCast(); - - // unsigned integer to 64-bit float - CheckIntegerTo64FloatCast(); - CheckIntegerTo64FloatCast(); -} - -TEST_F(CodegenUtilsTest, GetPointerConstantTest) { - // Remember the addresses of pointer constants, in order, that we expect check - // functions to return. - std::vector pointer_check_addresses; - - // Check void* pointers. - const void* voidptr = nullptr; - CheckGetSinglePointerConstant(voidptr, &pointer_check_addresses); - voidptr = this; - CheckGetSinglePointerConstant(voidptr, &pointer_check_addresses); - const void** voidptrptr = nullptr; - CheckGetSinglePointerConstant(voidptrptr, &pointer_check_addresses); - voidptrptr = &voidptr; - CheckGetSinglePointerConstant(voidptrptr, &pointer_check_addresses); - - // Check pointers to C++ built-in scalar types. - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - CheckGetPointerToScalarConstant( // NOLINT(runtime/int) - &pointer_check_addresses); - - // Check pointers to cstdint typedefs. - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( - &pointer_check_addresses); - CheckGetPointerToScalarConstant( - &pointer_check_addresses); - CheckGetPointerToScalarConstant( - &pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - - // Check pointers to cstddef typedefs. - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - - // Check pointers to enums. - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant(&pointer_check_addresses); - CheckGetPointerToScalarConstant( - &pointer_check_addresses); - CheckGetPointerToScalarConstant( - &pointer_check_addresses); - - // Check pointers to struct. - DummyStruct dummy_struct; - const DummyStruct* dummy_struct_ptr = nullptr; - CheckGetSinglePointerConstant(dummy_struct_ptr, &pointer_check_addresses); - dummy_struct_ptr = &dummy_struct; - CheckGetSinglePointerConstant(dummy_struct_ptr, &pointer_check_addresses); - - // Check pointers to an abstract base class and a concrete class. - const DummyAbstractBaseClass* dummy_abstract_ptr = nullptr; - CheckGetSinglePointerConstant(dummy_abstract_ptr, &pointer_check_addresses); - - Negater dummy_concrete_object(42); - const Negater* dummy_concrete_ptr = nullptr; - CheckGetSinglePointerConstant(dummy_concrete_ptr, &pointer_check_addresses); - dummy_abstract_ptr = &dummy_concrete_object; - CheckGetSinglePointerConstant(dummy_abstract_ptr, &pointer_check_addresses); - dummy_concrete_ptr = &dummy_concrete_object; - CheckGetSinglePointerConstant(dummy_concrete_ptr, &pointer_check_addresses); - - // The various invocations of CheckGetSinglePointerConstant() above created a - // bunch of accessor functions that we will now compile and use to check that - // the addresses of global variables are as expected. - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - ASSERT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - FinishCheckingGlobalConstantPointers(pointer_check_addresses); -} - -TEST_F(CodegenUtilsTest, GetFunctionTypeTest) { - // Simple function with no parameters that returns void. - llvm::FunctionType* fn_type = codegen_utils_->GetFunctionType(); - ASSERT_NE(fn_type, nullptr); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getReturnType()); - EXPECT_EQ(0u, fn_type->getNumParams()); - - // Function that takes a few different scalar parameters and returns double. - fn_type = codegen_utils_->GetFunctionType(); - ASSERT_NE(fn_type, nullptr); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getReturnType()); - ASSERT_EQ(4u, fn_type->getNumParams()); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getParamType(0)); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getParamType(1)); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getParamType(2)); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getParamType(3)); - - // A mix of pointer and reference parameters. - fn_type = codegen_utils_->GetFunctionType(); - ASSERT_NE(fn_type, nullptr); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getReturnType()); - ASSERT_EQ(4u, fn_type->getNumParams()); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getParamType(0)); - EXPECT_EQ(codegen_utils_->GetType(), fn_type->getParamType(1)); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getParamType(2)); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getParamType(3)); - - // Pointers and references to user-defined structs and classes. - fn_type = codegen_utils_->GetFunctionType(); - ASSERT_NE(fn_type, nullptr); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getReturnType()); - ASSERT_EQ(3u, fn_type->getNumParams()); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getParamType(0)); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getParamType(1)); - EXPECT_EQ(codegen_utils_->GetType(), - fn_type->getParamType(2)); -} - -TEST_F(CodegenUtilsTest, TrivialCompilationTest) { - // Create an IR function that takes no arguments and returns int. - typedef int (*SimpleFn) (); - llvm::Function* simple_fn - = codegen_utils_->CreateFunction("simple_fn"); - - // Construct a single BasicBlock for the function's body. - llvm::BasicBlock* simple_fn_body - = codegen_utils_->CreateBasicBlock("simple_fn_body", simple_fn); - - // Create a return instruction that returns the constant value '42'. - codegen_utils_->ir_builder()->SetInsertPoint(simple_fn_body); - codegen_utils_->ir_builder()->CreateRet( - codegen_utils_->GetConstant(42)); - - // Check that the function and the module are both well-formed (note that the - // LLVM verification functions return false to indicate success). - EXPECT_FALSE(llvm::verifyFunction(*simple_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare generated code for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - EXPECT_EQ(nullptr, codegen_utils_->module()); - typedef void (*VoidFn)(); - typedef int (*IntFn)(); - // Try looking up function names that don't exist. - EXPECT_EQ(nullptr, codegen_utils_->GetFunctionPointer("foo")); - EXPECT_EQ(nullptr, - codegen_utils_->GetFunctionPointer("simple_fn_body")); - - // Cast to the actual function type and call the generated function. - int (*function_ptr)() - = codegen_utils_->GetFunctionPointer("simple_fn"); - ASSERT_NE(function_ptr, nullptr); - - EXPECT_EQ(42, (*function_ptr)()); -} - -TEST_F(CodegenUtilsTest, ExternalFunctionTest) { - // Test a function with overloads for different types (we will explicitly use - // the version of std::fabs() for doubles). - MakeWrapperFunction(&std::fabs, - "fabs_double_wrapper"); - - // Test a function that takes a pointer to a struct as an argument. - MakeWrapperFunction(&std::mktime, "mktime_wrapper"); - - // Test a static method of a class that returns void. - MakeWrapperFunction(&StaticIntWrapper::Set, "StaticIntWrapper::Set_wrapper"); - - // Check that the module is well-formed and prepare the generated wrapper - // functions for execution. - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - - // Try calling std::fabs() through the generated wrapper. - double (*fabs_double_wrapper)(double) // NOLINT(readability/casting) - = codegen_utils_->GetFunctionPointer( - "fabs_double_wrapper"); - ASSERT_NE(fabs_double_wrapper, nullptr); - EXPECT_EQ(12.34, (*fabs_double_wrapper)(12.34)); - EXPECT_EQ(56.78, (*fabs_double_wrapper)(-56.78)); - - // Try calling std::mktime() through the generated wrapper. - std::time_t (*mktime_wrapper)(std::tm*) - = codegen_utils_->GetFunctionPointer( - "mktime_wrapper"); - ASSERT_NE(mktime_wrapper, nullptr); - std::tm broken_time = {}; - broken_time.tm_year = 1900 - 1871; - broken_time.tm_mon = 2; - broken_time.tm_mday = 18; - EXPECT_EQ(std::mktime(&broken_time), (*mktime_wrapper)(&broken_time)); - - StaticIntWrapper::Set(0); - void (*static_int_set_wrapper)(const int) - = codegen_utils_->GetFunctionPointer( - "StaticIntWrapper::Set_wrapper"); - ASSERT_NE(static_int_set_wrapper, nullptr); - (*static_int_set_wrapper)(42); - EXPECT_EQ(42, StaticIntWrapper::Get()); -} - -TEST_F(CodegenUtilsTest, VariadicExternalFunctionTest) { - // Test a function with overloads an external function that contains - // variable length arguments : printf and sprintf - - // Register printf with takes variable arguments past char* - llvm::Function* llvm_printf_function = - codegen_utils_->GetOrRegisterExternalFunction(printf); - ASSERT_NE(llvm_printf_function, nullptr); - ASSERT_EQ((codegen_utils_-> - GetFunctionType(true)->getPointerTo()), - llvm_printf_function->getType()); - - // Register sprintf with takes variable arguments past char*, char* - llvm::Function* llvm_sprintf_function = - codegen_utils_->GetOrRegisterExternalFunction(sprintf); - ASSERT_NE(llvm_sprintf_function, nullptr); - ASSERT_EQ((codegen_utils_-> - GetFunctionType(true)->getPointerTo()), - llvm_sprintf_function->getType()); - - char sprintf_with_three_args_buffer[16], sprintf_with_four_args_buffer[16]; - - // Create a simple function that calls sprintf with 3 and 4 arguments and uses - // the buffers allocated above - typedef void (*SprintfTestType)(); - llvm::Function* sprintf_test = - codegen_utils_->CreateFunction("sprintf_test"); - llvm::BasicBlock* main_block = - codegen_utils_->CreateBasicBlock("main", sprintf_test); - - codegen_utils_->ir_builder()->SetInsertPoint(main_block); - codegen_utils_->ir_builder()->CreateCall( - llvm_printf_function, { - codegen_utils_->GetConstant("%s %d"), - codegen_utils_->GetConstant("Zero is another way of saying "), - codegen_utils_->GetConstant(0) - }); - codegen_utils_->ir_builder()->CreateCall( - llvm_sprintf_function, { - codegen_utils_->GetConstant(sprintf_with_three_args_buffer), - codegen_utils_->GetConstant("%d"), - codegen_utils_->GetConstant(42) - }); - codegen_utils_->ir_builder()->CreateCall( - llvm_sprintf_function, { - codegen_utils_->GetConstant(sprintf_with_four_args_buffer), - codegen_utils_->GetConstant("%d %s"), - codegen_utils_->GetConstant(51), - codegen_utils_->GetConstant("fifty-one") - }); - codegen_utils_->ir_builder()->CreateRetVoid(); - - // Check that the module is well-formed and prepare the generated wrapper - // functions for execution. - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - - SprintfTestType llvm_sprintf_test = - codegen_utils_->GetFunctionPointer("sprintf_test"); - llvm_sprintf_test(); - - ASSERT_EQ(strcmp(sprintf_with_three_args_buffer, "42"), 0); - ASSERT_EQ(strcmp(sprintf_with_four_args_buffer, "51 fifty-one"), 0); -} - -TEST_F(CodegenUtilsTest, RecursionTest) { - // Test a version of the factorial function that works by recursion. - typedef unsigned (*FactorialRecursiveFn) (unsigned); - llvm::Function* factorial_recursive - = codegen_utils_->CreateFunction( - "factorial_recursive"); - - // Create a BasicBlock for the function's entry point that will branch to a - // base case and a recursive case. - llvm::BasicBlock* entry = codegen_utils_->CreateBasicBlock( - "entry", - factorial_recursive); - llvm::BasicBlock* base_case = codegen_utils_->CreateBasicBlock( - "base_case", - factorial_recursive); - llvm::BasicBlock* recursive_case = codegen_utils_->CreateBasicBlock( - "recursive_case", - factorial_recursive); - - ASSERT_EQ(1u, factorial_recursive->arg_size()); - llvm::Value* argument = ArgumentByPosition(factorial_recursive, 0); - - // Check if we have reached the base-case (argument == 0) and conditionally - // branch. - codegen_utils_->ir_builder()->SetInsertPoint(entry); - llvm::Value* arg_is_zero = codegen_utils_->ir_builder()->CreateICmpEQ( - argument, - codegen_utils_->GetConstant(0u)); - codegen_utils_->ir_builder()->CreateCondBr(arg_is_zero, - base_case, - recursive_case); - - // Base case: 0! = 1. - codegen_utils_->ir_builder()->SetInsertPoint(base_case); - codegen_utils_->ir_builder()->CreateRet(codegen_utils_->GetConstant(1u)); - - // Recursive case: N! = N * (N - 1)! - codegen_utils_->ir_builder()->SetInsertPoint(recursive_case); - - std::vector recursive_call_args; - recursive_call_args.push_back(codegen_utils_->ir_builder()->CreateSub( - argument, - codegen_utils_->GetConstant(1u))); - llvm::Value* child_result = codegen_utils_->ir_builder()->CreateCall( - factorial_recursive, - recursive_call_args); - - llvm::Value* product = codegen_utils_->ir_builder()->CreateMul(argument, - child_result); - codegen_utils_->ir_builder()->CreateRet(product); - - // Verify function and module. - EXPECT_FALSE(llvm::verifyFunction(*factorial_recursive)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - unsigned (*factorial_recursive_compiled)(unsigned) - = codegen_utils_->GetFunctionPointer< - decltype(factorial_recursive_compiled)>("factorial_recursive"); - - // Test out the compiled function. - EXPECT_EQ(1u, (*factorial_recursive_compiled)(0u)); - EXPECT_EQ(1u, (*factorial_recursive_compiled)(0u)); - EXPECT_EQ(1u * 2u * 3u * 4u * 5u * 6u * 7u, - (*factorial_recursive_compiled)(7u)); -} - -TEST_F(CodegenUtilsTest, SwitchTest) { - // Test that generates IR code with a SWITCH statement. - // It takes a char as input and returns 1 if input = 'A', - // 2 if input is 'B'; -1 otherwise. - typedef int (*SwitchFn) (char); - llvm::Function* switch_function - = codegen_utils_->CreateFunction( - "switch_function"); - - // BasicBlocks for function entry, for each case of switch instruction, - // for the default case, and for function termination - // where an integer is returned. - llvm::BasicBlock* entry_block = codegen_utils_->CreateBasicBlock( - "entry", - switch_function); - - llvm::BasicBlock* A_block = codegen_utils_->CreateBasicBlock( - "A_block", - switch_function); - - llvm::BasicBlock* B_block = codegen_utils_->CreateBasicBlock( - "B_block", - switch_function); - - llvm::BasicBlock* default_block = codegen_utils_->CreateBasicBlock( - "default", - switch_function); - - llvm::BasicBlock* return_block = codegen_utils_->CreateBasicBlock( - "return", - switch_function); - - llvm::Value* argument = ArgumentByPosition(switch_function, 0); - - // Switch instruction is located in the entry point. - codegen_utils_->ir_builder()->SetInsertPoint(entry_block); - llvm::SwitchInst* switch_instruction - = codegen_utils_->ir_builder()->CreateSwitch(argument, default_block, 3); - - // Add switch cases. - llvm::ConstantInt* val_a = static_cast( - codegen_utils_->GetConstant('A')); - ASSERT_TRUE(llvm::isa(val_a)); - switch_instruction->addCase(val_a, A_block); - - llvm::ConstantInt* val_b = static_cast( - codegen_utils_->GetConstant('B')); - ASSERT_TRUE(llvm::isa(val_b)); - switch_instruction->addCase(val_b, B_block); - - // All switch cases jump to return block. - codegen_utils_->ir_builder()->SetInsertPoint(default_block); - codegen_utils_->ir_builder()->CreateBr(return_block); - - codegen_utils_->ir_builder()->SetInsertPoint(A_block); - codegen_utils_->ir_builder()->CreateBr(return_block); - - codegen_utils_->ir_builder()->SetInsertPoint(B_block); - codegen_utils_->ir_builder()->CreateBr(return_block); - - // Add incoming edges from switch cases to return block, - // where each case sends to return block the proper value. - codegen_utils_->ir_builder()->SetInsertPoint(return_block); - llvm::PHINode* return_node = codegen_utils_->ir_builder()->CreatePHI( - codegen_utils_->GetType(), 3); - return_node->addIncoming(codegen_utils_->GetConstant(-1), default_block); - return_node->addIncoming(codegen_utils_->GetConstant(1), A_block); - return_node->addIncoming(codegen_utils_->GetConstant(2), B_block); - codegen_utils_->ir_builder()->CreateRet(return_node); - - // Verify function and module. - EXPECT_FALSE(llvm::verifyFunction(*switch_function)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - - int (*switch_function_compiled)(char) // NOLINT(readability/casting) - = codegen_utils_->GetFunctionPointer( - "switch_function"); - - // Test out the compiled function. - EXPECT_EQ(1, (*switch_function_compiled)('A')); - EXPECT_EQ(2, (*switch_function_compiled)('B')); - EXPECT_EQ(-1, (*switch_function_compiled)('C')); -} - -TEST_F(CodegenUtilsTest, ProjectScalarIntArrayTest) { - ProjectScalarArrayTestHelper(); -} - -TEST_F(CodegenUtilsTest, ProjectScalarInt16ArrayTest) { - ProjectScalarArrayTestHelper(); -} - -TEST_F(CodegenUtilsTest, ProjectScalarInt64ArrayTest) { - ProjectScalarArrayTestHelper(); -} - -TEST_F(CodegenUtilsTest, ProjectScalarCharArrayTest) { - ProjectScalarArrayTestHelper(); -} - -TEST_F(CodegenUtilsTest, IterationTest) { - // Test a version of the factorial function works with an iterative loop. - typedef unsigned (*FactorialIterFn) (unsigned); - llvm::Function* factorial_iterative - = codegen_utils_->CreateFunction( - "factorial_iterative"); - - // BasicBlocks for function entry, for the start of the loop where the - // termination condition is checked, for the loop body where running variables - // are updated, and for function termination where the computed product is - // returned. - llvm::BasicBlock* entry = codegen_utils_->CreateBasicBlock( - "entry", - factorial_iterative); - llvm::BasicBlock* loop_start = codegen_utils_->CreateBasicBlock( - "loop_start", - factorial_iterative); - llvm::BasicBlock* loop_computation = codegen_utils_->CreateBasicBlock( - "loop_computation", - factorial_iterative); - llvm::BasicBlock* terminus = codegen_utils_->CreateBasicBlock( - "terminus", - factorial_iterative); - - ASSERT_EQ(1u, factorial_iterative->arg_size()); - llvm::Value* argument = ArgumentByPosition(factorial_iterative, 0); - - // Entry point unconditionally enters the loop. Note that we can't just make - // "loop_start" the entry point for the function, because it has PHI-nodes - // that need to be assigned based on predecessor BasicBlocks. - codegen_utils_->ir_builder()->SetInsertPoint(entry); - codegen_utils_->ir_builder()->CreateBr(loop_start); - - // Create PHI nodes to represent the current factor (starting at the - // argument's value and counting down to zero) and the current product - // (starting at one and getting multiplied for each iteration of the loop). - codegen_utils_->ir_builder()->SetInsertPoint(loop_start); - - llvm::PHINode* current_factor = codegen_utils_->ir_builder()->CreatePHI( - codegen_utils_->GetType(), 2); - current_factor->addIncoming(argument, entry); - - llvm::PHINode* current_product = codegen_utils_->ir_builder()->CreatePHI( - codegen_utils_->GetType(), 2); - current_product->addIncoming(codegen_utils_->GetConstant(1u), entry); - - // If 'current_factor' has reached zero, break out of the loop. Otherwise - // proceed to "loop_computation" to compute the factor and the product for the - // next iteration. - llvm::Value* current_factor_is_zero - = codegen_utils_->ir_builder()->CreateICmpEQ( - current_factor, - codegen_utils_->GetConstant(0u)); - codegen_utils_->ir_builder()->CreateCondBr(current_factor_is_zero, - terminus, - loop_computation); - - // Compute values for the next iteration of the loop and go back to - // "loop_start". - codegen_utils_->ir_builder()->SetInsertPoint(loop_computation); - llvm::Value* next_factor = codegen_utils_->ir_builder()->CreateSub( - current_factor, - codegen_utils_->GetConstant(1u)); - llvm::Value* next_product = codegen_utils_->ir_builder()->CreateMul( - current_factor, - current_product); - codegen_utils_->ir_builder()->CreateBr(loop_start); - - // Add incoming edges to the PHI nodes in "loop_start" for the newly-computed - // values. - current_factor->addIncoming(next_factor, loop_computation); - current_product->addIncoming(next_product, loop_computation); - - // Terminus just returns the computed product. - codegen_utils_->ir_builder()->SetInsertPoint(terminus); - codegen_utils_->ir_builder()->CreateRet(current_product); - - // Verify function and module. - EXPECT_FALSE(llvm::verifyFunction(*factorial_iterative)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - unsigned (*factorial_iterative_compiled)(unsigned) - = codegen_utils_->GetFunctionPointer< - decltype(factorial_iterative_compiled)>("factorial_iterative"); - - // Test out the compiled function. - EXPECT_EQ(1u, (*factorial_iterative_compiled)(0u)); - EXPECT_EQ(1u, (*factorial_iterative_compiled)(0u)); - EXPECT_EQ(1u * 2u * 3u * 4u * 5u * 6u * 7u, - (*factorial_iterative_compiled)(7u)); -} - -// Macro that provides some syntactic sugar for a call to -// CheckGetPointerToMemberConstant(). Automates deduction of the expected -// member type and calculation of the expected offset within the struct. -#define GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(struct_ptr, element_name) \ - CheckGetPointerToMemberConstant< \ - std::remove_referenceelement_name)>::type>( \ - &pointer_check_addresses, \ - struct_ptr, \ - offsetof(std::remove_pointer::type, \ - element_name), \ - &std::remove_pointer::type::element_name) - -// Similar to above, but tests accesing a field nested inside a struct member of -// the top-level struct. -#define GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( \ - struct_ptr, \ - top_element_name, \ - nested_element_name) \ - CheckGetPointerToMemberConstant< \ - std::remove_referencetop_element_name.nested_element_name)>::type>( \ - &pointer_check_addresses, \ - struct_ptr, \ - offsetof(std::remove_pointer::type, \ - top_element_name) \ - + offsetof(std::remove_reference< \ - decltype((struct_ptr)->top_element_name)>::type, \ - nested_element_name), \ - &std::remove_pointer::type::top_element_name, \ - &std::remove_referencetop_element_name)>::type \ - ::nested_element_name) - -// Test for CodegenUtils::GetPointerToMember() with constant pointers to -// external structs. -TEST_F(CodegenUtilsTest, GetPointerToMemberConstantTest) { - // Remember the addresses of pointer constants, in order, that we expect check - // functions to return. - std::vector pointer_check_addresses; - - // Test a struct on the stack. - DummyStruct stack_struct; - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(&stack_struct, int_field); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(&stack_struct, bool_field); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(&stack_struct, double_field); - - // Also works without specifying any members at all. This trivially gives a - // pointer to the original struct. - CheckGetPointerToMemberConstant(&pointer_check_addresses, - &stack_struct, - 0); - - // And on the heap. - std::unique_ptr heap_struct(new DummyStruct); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(heap_struct.get(), int_field); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(heap_struct.get(), bool_field); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(heap_struct.get(), double_field); - CheckGetPointerToMemberConstant(&pointer_check_addresses, - heap_struct.get(), - 0); - - // A NULL pointer also works, since CodegenUtils::GetPointerToMember() only - // does address computation and doesn't dereference anything. - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - static_cast(nullptr), - int_field); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - static_cast(nullptr), - bool_field); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - static_cast(nullptr), - double_field); - CheckGetPointerToMemberConstant( - &pointer_check_addresses, - static_cast(nullptr), - 0); - - // Also test a struct with char and char* fields to make sure that there is no - // confusion when the pointer-to-member type is the same as the pointer type - // used for the underlying address computation. - DummyStructWithCharFields stack_struct_with_char_fields; - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(&stack_struct_with_char_fields, - front_char); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(&stack_struct_with_char_fields, - char_ptr); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT(&stack_struct_with_char_fields, - back_char); - - // Also test a struct that nests other structs. - Matryoshka stack_matryoshka; - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct_with_char_fields); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - &stack_matryoshka, - non_nested_char); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - &stack_matryoshka, - non_nested_int); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - &stack_matryoshka, - ptr_to_peer); - GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct); - - // Test accessing fields inside nested structs with a single call to - // GetPointerToMember(). - GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct_with_char_fields, - front_char); - GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct_with_char_fields, - char_ptr); - GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct_with_char_fields, - back_char); - GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct, - int_field); - GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct, - bool_field); - GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT( - &stack_matryoshka, - nested_dummy_struct, - double_field); - - // Now we compile and call the various constant-accessor functions that were - // generated in the course of this test, checking that they return the - // expected addresses of member fields. - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - ASSERT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - FinishCheckingGlobalConstantPointers(pointer_check_addresses); -} - -#undef GPCODEGEN_TEST_GET_POINTER_TO_NESTED_STRUCT_ELEMENT -#undef GPCODEGEN_TEST_GET_POINTER_TO_STRUCT_ELEMENT - -TEST_F(CodegenUtilsTest, GetPointerToMemberTest) { - // Create some accessor functions that load the value of fields in a struct - // passed in as a pointer. - MakeStructMemberAccessorFunction( - "Get_DummyStruct::int_field", - &DummyStruct::int_field); - MakeStructMemberAccessorFunction( - "Get_DummyStruct::bool_field", - &DummyStruct::bool_field); - MakeStructMemberAccessorFunction( - "Get_DummyStruct::double_field", - &DummyStruct::double_field); - MakeStructArrayMemberElementAccessorFunction( - "Get_DummyStruct::int_array_field", - &DummyStruct::int_array_field); - MakeStructArrayMemberElementAccessorFunction( - "Get_DummyStruct::bool_array_field", - &DummyStruct::bool_array_field); - - // Check that module is well-formed, then compile. - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - - int (*Get_DummyStruct_int_field)(const DummyStruct*) - = codegen_utils_->GetFunctionPointer< - decltype(Get_DummyStruct_int_field)>("Get_DummyStruct::int_field"); - ASSERT_NE(Get_DummyStruct_int_field, nullptr); - - bool (*Get_DummyStruct_bool_field)(const DummyStruct*) - = codegen_utils_->GetFunctionPointer< - decltype(Get_DummyStruct_bool_field)>("Get_DummyStruct::bool_field"); - ASSERT_NE(Get_DummyStruct_bool_field, nullptr); - - double (*Get_DummyStruct_double_field)(const DummyStruct*) - = codegen_utils_->GetFunctionPointer< - decltype(Get_DummyStruct_double_field)>("Get_DummyStruct::double_field"); - ASSERT_NE(Get_DummyStruct_double_field, nullptr); - - int (*Get_DummyStruct_int_array_field)(const DummyStruct*, size_t index) - = codegen_utils_->GetFunctionPointer< - decltype(Get_DummyStruct_int_array_field)>( - "Get_DummyStruct::int_array_field"); - ASSERT_NE(Get_DummyStruct_int_array_field, nullptr); - - bool (*Get_DummyStruct_bool_array_field)(const DummyStruct*, size_t index) - = codegen_utils_->GetFunctionPointer< - decltype(Get_DummyStruct_bool_array_field)>( - "Get_DummyStruct::bool_array_field"); - ASSERT_NE(Get_DummyStruct_bool_array_field, nullptr); - - // Call generated accessor function and make sure they read values from the - // passed-in struct pointer properly. - DummyStruct test_struct{42, true, -12.34, {1, 2, 3, 4, 5}, {true, false} }; - - EXPECT_EQ(42, (*Get_DummyStruct_int_field)(&test_struct)); - EXPECT_EQ(true, (*Get_DummyStruct_bool_field)(&test_struct)); - EXPECT_EQ(-12.34, (*Get_DummyStruct_double_field)(&test_struct)); - EXPECT_EQ(1, (*Get_DummyStruct_int_array_field)(&test_struct, 0)); - EXPECT_EQ(3, (*Get_DummyStruct_int_array_field)(&test_struct, 2)); - EXPECT_EQ(5, (*Get_DummyStruct_int_array_field)(&test_struct, 4)); - EXPECT_TRUE((*Get_DummyStruct_bool_array_field)(&test_struct, 0)); - EXPECT_FALSE((*Get_DummyStruct_bool_array_field)(&test_struct, 1)); - - // Modify and read again. - test_struct.int_field = -123; - test_struct.bool_field = false; - test_struct.double_field = 1e100; - test_struct.int_array_field[0] = 56; - test_struct.int_array_field[1] = 58; - test_struct.int_array_field[3] = 55; - test_struct.bool_array_field[0] = false; - test_struct.bool_array_field[1] = true; - - EXPECT_EQ(-123, (*Get_DummyStruct_int_field)(&test_struct)); - EXPECT_FALSE((*Get_DummyStruct_bool_field)(&test_struct)); - EXPECT_EQ(1e100, (*Get_DummyStruct_double_field)(&test_struct)); - EXPECT_EQ(56, (*Get_DummyStruct_int_array_field)(&test_struct, 0)); - EXPECT_EQ(58, (*Get_DummyStruct_int_array_field)(&test_struct, 1)); - EXPECT_EQ(55, (*Get_DummyStruct_int_array_field)(&test_struct, 3)); - EXPECT_FALSE((*Get_DummyStruct_bool_array_field)(&test_struct, 0)); - EXPECT_TRUE((*Get_DummyStruct_bool_array_field)(&test_struct, 1)); -} - -TEST_F(CodegenUtilsTest, OptimizationTest) { - // Create an ultra-simple function that just adds 2 ints. We expect this to be - // automatically inlined at call sites during optimization. - typedef int (*Add2Fn) (int, int); - llvm::Function* add2_func - = codegen_utils_->CreateFunction("add2"); - llvm::BasicBlock* add2_body = codegen_utils_->CreateBasicBlock("body", - add2_func); - codegen_utils_->ir_builder()->SetInsertPoint(add2_body); - llvm::Value* add2_sum = codegen_utils_->ir_builder()->CreateAdd( - ArgumentByPosition(add2_func, 0), - ArgumentByPosition(add2_func, 1)); - codegen_utils_->ir_builder()->CreateRet(add2_sum); - - // Create another function that adds 3 ints by making 2 calls to add2. - typedef int (*Add3Fn) (int, int, int); - llvm::Function* add3_func - = codegen_utils_->CreateFunction("add3"); - llvm::BasicBlock* add3_body = codegen_utils_->CreateBasicBlock("body", - add3_func); - codegen_utils_->ir_builder()->SetInsertPoint(add3_body); - llvm::Value* add3_sum1 = codegen_utils_->ir_builder()->CreateCall( - add2_func, - {ArgumentByPosition(add3_func, 0), ArgumentByPosition(add3_func, 1)}); - llvm::Value* add3_sum2 = codegen_utils_->ir_builder()->CreateCall( - add2_func, - {add3_sum1, ArgumentByPosition(add3_func, 2)}); - codegen_utils_->ir_builder()->CreateRet(add3_sum2); - - // Before optimization, function memory-access characteristics are not known. - EXPECT_FALSE(add2_func->doesNotAccessMemory()); - EXPECT_FALSE(add3_func->doesNotAccessMemory()); - - // Apply basic optimizations. - EXPECT_TRUE(codegen_utils_->Optimize(CodegenUtils::OptimizationLevel::kLess, - CodegenUtils::SizeLevel::kNormal, - false)); - - // Analysis passes should have marked both functions "readnone" since they do - // not access any external memory. - EXPECT_TRUE(add2_func->doesNotAccessMemory()); - EXPECT_TRUE(add3_func->doesNotAccessMemory()); - - // We expect the tiny add2 function to be inlined into add3. We iterate - // through the instructions in add3's body and check that none are calls. - for (const llvm::Instruction& instruction : *add3_body) { - EXPECT_NE(instruction.getOpcode(), llvm::Instruction::Call); - } - - // Now, actually compile machine code from the optimized IR and call it. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kLess, - false)); - int (*add3_compiled)(int, int, int) - = codegen_utils_->GetFunctionPointer("add3"); - EXPECT_EQ(758, (*add3_compiled)(12, -67, 813)); -} - -// Test code-generation used with instance methods of a statically compiled C++ -// class. -TEST_F(CodegenUtilsTest, CppClassObjectTest) { - // Register method wrappers for Accumulator - llvm::Function* new_accumulator_double - = codegen_utils_->GetOrRegisterExternalFunction( - &WrapNew, double>); - llvm::Function* delete_accumulator_double - = codegen_utils_->GetOrRegisterExternalFunction( - &WrapDelete>); - llvm::Function* accumulator_double_accumulate - = codegen_utils_->GetOrRegisterExternalFunction( - &GPCODEGEN_WRAP_METHOD(&Accumulator::Accumulate)); - llvm::Function* accumulator_double_get - = codegen_utils_->GetOrRegisterExternalFunction( - &GPCODEGEN_WRAP_METHOD(&Accumulator::Get)); - - typedef double (*AccumulateTestFn) (double); - llvm::Function* accumulate_test_fn - = codegen_utils_->CreateFunction("accumulate_test_fn"); - llvm::BasicBlock* body - = codegen_utils_->CreateBasicBlock("body", accumulate_test_fn); - codegen_utils_->ir_builder()->SetInsertPoint(body); - - // Make a new accumulator object, forwarding the function's argument to the - // constructor. - llvm::Value* accumulator_ptr = codegen_utils_->ir_builder()->CreateCall( - new_accumulator_double, - {ArgumentByPosition(accumulate_test_fn, 0)}); - - // Add a few constants to the accumulator via the wrapped instance method. - codegen_utils_->ir_builder()->CreateCall( - accumulator_double_accumulate, - {accumulator_ptr, codegen_utils_->GetConstant(1.0)}); - codegen_utils_->ir_builder()->CreateCall( - accumulator_double_accumulate, - {accumulator_ptr, codegen_utils_->GetConstant(2.0)}); - codegen_utils_->ir_builder()->CreateCall( - accumulator_double_accumulate, - {accumulator_ptr, codegen_utils_->GetConstant(3.0)}); - codegen_utils_->ir_builder()->CreateCall( - accumulator_double_accumulate, - {accumulator_ptr, codegen_utils_->GetConstant(4.0)}); - - // Read out the accumulated value. - llvm::Value* retval = codegen_utils_->ir_builder()->CreateCall( - accumulator_double_get, - {accumulator_ptr}); - - // Delete the accumulator object. - codegen_utils_->ir_builder()->CreateCall( - delete_accumulator_double, - {accumulator_ptr}); - - // Return the accumulated value. - codegen_utils_->ir_builder()->CreateRet(retval); - - // Check that function and module are well-formed, then compile. - EXPECT_FALSE(llvm::verifyFunction(*accumulate_test_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - - double (*accumulate_test_fn_compiled)(double) // NOLINT(readability/casting) - = codegen_utils_->GetFunctionPointer< - decltype(accumulate_test_fn_compiled)>("accumulate_test_fn"); - - // Actually invoke the function and make sure that the wrapped behavior of - // Accumulator is as expected. - EXPECT_EQ(42.0, (*accumulate_test_fn_compiled)(32.0)); - EXPECT_EQ(-12.75, (*accumulate_test_fn_compiled)(-22.75)); -} - -// Test GetOrRegisterExternalFunction to return the right llvm::Function if -// previously registered or else register it anew -TEST_F(CodegenUtilsTest, GetOrRegisterExternalFunctionTest) { - // Test previous unregistered function - EXPECT_EQ(nullptr, codegen_utils_->module()->getFunction("floor")); - llvm::Function* floor_func = codegen_utils_->GetOrRegisterExternalFunction( - floor, "floor"); - EXPECT_EQ(floor_func, codegen_utils_->module()->getFunction("floor")); - - // Test previous registered Non vararg function - llvm::Function* expected_fabs_func = codegen_utils_-> - GetOrRegisterExternalFunction(ceil); - llvm::Function* fabs_func = codegen_utils_-> - GetOrRegisterExternalFunction(ceil); - - EXPECT_EQ(expected_fabs_func, fabs_func); - - // Test previously registered vararg function - llvm::Function* expected_fprintf_func = codegen_utils_-> - GetOrRegisterExternalFunction(fprintf); - llvm::Function* fprintf_func = codegen_utils_-> - GetOrRegisterExternalFunction(fprintf); - - EXPECT_EQ(expected_fprintf_func, fprintf_func); -} - -// Utility method to compute the number of calls in an llvm::Function* -int GetLLVMFunctionCallCount(llvm::Function* F) { - return std::count_if(llvm::inst_begin(F), llvm::inst_end(F), - [] (llvm::Instruction& i)-> bool { - return (llvm::dyn_cast(&i)); - }); -} - -// Test InlineFunction -TEST_F(CodegenUtilsTest, InlineFunctionTest) { - auto irb = codegen_utils_->ir_builder(); - - typedef int (*AddConstToIntFn) (int); - - // Create a simple adds 1 to a number and returns the new value - llvm::Function* add_one_fn = - codegen_utils_->CreateFunction("add_one"); - irb->SetInsertPoint(codegen_utils_->CreateBasicBlock("main", add_one_fn)); - irb->CreateRet(irb->CreateAdd(ArgumentByPosition(add_one_fn, 0), - codegen_utils_->GetConstant(1))); - - // Create another simple function add_two which calls add_one twice - llvm::Function* add_two_fn = - codegen_utils_->CreateFunction("add_two"); - irb->SetInsertPoint(codegen_utils_->CreateBasicBlock("main", add_two_fn)); - llvm::CallInst* first_call = - irb->CreateCall(add_one_fn, {ArgumentByPosition(add_two_fn, 0)}); - llvm::CallInst* second_call = irb->CreateCall(add_one_fn, {first_call}); - irb->CreateRet(second_call); - - - EXPECT_EQ(GetLLVMFunctionCallCount(add_two_fn), 2); - - EXPECT_TRUE(codegen_utils_->InlineFunction(first_call)); - EXPECT_EQ(GetLLVMFunctionCallCount(add_two_fn), 1); - - EXPECT_FALSE(codegen_utils_->InlineFunction(first_call)); - - EXPECT_TRUE(codegen_utils_->InlineFunction(second_call)); - EXPECT_EQ(GetLLVMFunctionCallCount(add_two_fn), 0); - - // Compiled module - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, false)); - AddConstToIntFn compiled_add_two_fn = - codegen_utils_->GetFunctionPointer("add_two"); - - // Test normal functionality, even after inlining - EXPECT_TRUE(nullptr != compiled_add_two_fn); - EXPECT_EQ(compiled_add_two_fn(5), 7); - EXPECT_EQ(compiled_add_two_fn(-5), -3); -} - -// Test CreateMakeTuple in CodegenUtils. -TEST_F(CodegenUtilsTest, CreateMakeTupleTest) { - MakeTupleFunc( - "MakeTupleFunc"); - using MakeTupleFuncType = - int (*)(int64_t, bool, int32_t, float, int16_t, double); - - int total_arg = 6; - - // Compiled module - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, false)); - MakeTupleFuncType compiled_tuple_fn = - codegen_utils_->GetFunctionPointer("MakeTupleFunc"); - - EXPECT_EQ(total_arg, compiled_tuple_fn(42, false, 23, 36.23, 12, 25.23)); - EXPECT_EQ(total_arg, compiled_tuple_fn(std::numeric_limits::max(), - true, - std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::max())); - EXPECT_EQ(total_arg, compiled_tuple_fn(std::numeric_limits::min(), - true, - std::numeric_limits::min(), - std::numeric_limits::min(), - std::numeric_limits::min(), - std::numeric_limits::min())); -} - - -#ifdef CODEGEN_DEBUG - -TEST_F(CodegenUtilsDeathTest, WrongFunctionTypeTest) { - // Create a function identical to the one in TrivialCompilationTest, but try - // GetFunctionPointer() with the wrong type-signature. - typedef int (*SimpleFn) (); - llvm::Function* simple_fn - = codegen_utils_->CreateFunction("simple_fn"); - llvm::BasicBlock* simple_fn_body - = codegen_utils_->CreateBasicBlock("simple_fn_body", simple_fn); - codegen_utils_->ir_builder()->SetInsertPoint(simple_fn_body); - codegen_utils_->ir_builder()->CreateRet( - codegen_utils_->GetConstant(42)); - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - typedef float (*FloatFn) (); - EXPECT_DEATH(codegen_utils_->GetFunctionPointer("simple_fn"), ""); -} - -TEST_F(CodegenUtilsDeathTest, ModifyExternalFunctionTest) { - // Register an external function, then try to add a BasicBlock to it. - llvm::Function* external_function - = codegen_utils_->GetOrRegisterExternalFunction(&std::mktime); - - EXPECT_DEATH(codegen_utils_->CreateBasicBlock("body", external_function), - ""); -} - -TEST_F(CodegenUtilsDeathTest, GetPointerToMemberFromNullBasePointerTest) { - // Set up a dummy function and BasicBlock to hold instructions. - typedef void (*DummFn) (); - llvm::Function* dummy_fn - = codegen_utils_->CreateFunction("dummy_fn"); - llvm::BasicBlock* dummy_fn_body - = codegen_utils_->CreateBasicBlock("dummy_fn_body", dummy_fn); - codegen_utils_->ir_builder()->SetInsertPoint(dummy_fn_body); - - EXPECT_DEATH(codegen_utils_->GetPointerToMember(nullptr, - &DummyStruct::int_field), - ""); -} - -TEST_F(CodegenUtilsDeathTest, GetPointerToMemberFromWrongTypeBasePointerTest) { - // Set up a dummy function and BasicBlock to hold instructions. - typedef void (*DummyFn) (); - llvm::Function* dummy_fn - = codegen_utils_->CreateFunction("dummy_fn"); - llvm::BasicBlock* dummy_fn_body - = codegen_utils_->CreateBasicBlock("dummy_fn_body", dummy_fn); - codegen_utils_->ir_builder()->SetInsertPoint(dummy_fn_body); - - const int external_int = 42; - llvm::Value* external_int_ptr = codegen_utils_->GetConstant(&external_int); - - // Pointers to structs are expected to be represented as i8*, but here we are - // passing an i32* pointer. - EXPECT_DEATH(codegen_utils_->GetPointerToMember(external_int_ptr, - &DummyStruct::int_field), - ""); -} - -#endif // CODEGEN_DEBUG - -} // namespace gpcodegen - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - AddGlobalTestEnvironment(new gpcodegen::CodegenUtilsTestEnvironment); - return RUN_ALL_TESTS(); -} - -// EOF diff --git a/src/backend/codegen/tests/gp_codegen_utils_unittest.cc b/src/backend/codegen/tests/gp_codegen_utils_unittest.cc deleted file mode 100644 index cd297d84e3636ef2478cf6f0f52eb790a249b5dd..0000000000000000000000000000000000000000 --- a/src/backend/codegen/tests/gp_codegen_utils_unittest.cc +++ /dev/null @@ -1,115 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// gp_codegen_utils_unittest.cc -// -// @doc: -// Unit tests for utils/codegen_utils.cc -// -// @test: -// -//--------------------------------------------------------------------------- -#include "gtest/gtest.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#undef newNode // undef newNode so it doesn't have name collision with llvm -#include "utils/palloc.h" -#include "utils/memutils.h" -} - -#include "codegen/utils/gp_codegen_utils.h" - -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" - - -namespace gpcodegen { - -// Test environment to handle global per-process initialization tasks for all -// tests. -class GpCodegenUtilsTestEnvironment : public ::testing::Environment { - public: - virtual void SetUp() { - ASSERT_TRUE(GpCodegenUtils::InitializeGlobal()); - } -}; - -class GpCodegenUtilsTest : public ::testing::Test { - protected: - virtual void SetUp() { - codegen_utils_.reset(new GpCodegenUtils("test_module")); - } - - // Helper method to test memory allocation. - void CheckCreatePalloc() { - // Test scenario: i) Create a memory context and set it as the current. - // ii) Create and call a function that allocates memory in the current - // memory context, and iii) check if the size of the current memory - // context has been increased. - typedef void (*AllocateMemoryFn) (); - llvm::Function* allocate_memory_fn - = codegen_utils_->CreateFunction("AllocateMemory"); - llvm::BasicBlock* allocate_memory_fn_body - = codegen_utils_->CreateBasicBlock("body", allocate_memory_fn); - codegen_utils_->ir_builder()->SetInsertPoint(allocate_memory_fn_body); - - EXPAND_CREATE_PALLOC(codegen_utils_, 10000); - - codegen_utils_->ir_builder()->CreateRetVoid(); - - // Verify function is well-formed. - EXPECT_FALSE(llvm::verifyFunction(*allocate_memory_fn)); - EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); - - // Prepare generated code for execution. - EXPECT_TRUE(codegen_utils_->PrepareForExecution( - CodegenUtils::OptimizationLevel::kNone, - true)); - - // Cast to the actual function type. - void (*function_ptr)() = codegen_utils_-> - GetFunctionPointer("AllocateMemory"); - ASSERT_NE(function_ptr, nullptr); - - // Create a memory context and set it as the current memory context. - MemoryContext memCtx = - AllocSetContextCreate(nullptr, - "memContext", - ALLOCSET_SMALL_MINSIZE, - ALLOCSET_SMALL_INITSIZE, - ALLOCSET_SMALL_MAXSIZE); - MemoryContextSwitchTo(memCtx); - - EXPECT_EQ(0, MemoryContextGetCurrentSpace(memCtx)); - (*function_ptr)(); - EXPECT_LT(10000, MemoryContextGetCurrentSpace(memCtx)); - } - - std::unique_ptr codegen_utils_; -}; - -TEST_F(GpCodegenUtilsTest, InitializationTest) { - EXPECT_NE(codegen_utils_->ir_builder(), nullptr); - ASSERT_NE(codegen_utils_->module(), nullptr); - EXPECT_EQ(std::string("test_module"), - codegen_utils_->module()->getModuleIdentifier()); -} - -TEST_F(GpCodegenUtilsTest, MemoryAllocationTest) { - CheckCreatePalloc(); -} -} // namespace gpcodegen - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - AddGlobalTestEnvironment(new gpcodegen::GpCodegenUtilsTestEnvironment); - return RUN_ALL_TESTS(); -} - -// EOF - diff --git a/src/backend/codegen/tests/instance_method_wrappers_unittest.cc b/src/backend/codegen/tests/instance_method_wrappers_unittest.cc deleted file mode 100644 index abe6bb1fc15932dfbf2f3363a12e7f4e7c9f1ad0..0000000000000000000000000000000000000000 --- a/src/backend/codegen/tests/instance_method_wrappers_unittest.cc +++ /dev/null @@ -1,459 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// instance_method_wrappers_unittest.cc -// -// @doc: -// Unittests for instance method wrapper feature of codegen. -// -// @test: -// -//--------------------------------------------------------------------------- - -#include -#include - -#include "codegen/utils/instance_method_wrappers.h" -#include "gtest/gtest.h" - -namespace gpcodegen { - -namespace { - -class AbstractBase { - public: - static std::size_t constructor_call_count_; - static std::size_t destructor_call_count_; - - explicit AbstractBase(const int int_payload) - : int_payload_(int_payload) { - ++constructor_call_count_; - } - - virtual ~AbstractBase() = 0; - - // A non-overloaded method of the base class. - int AddPayload(const int arg) const { - return arg + int_payload_; - } - - // Overloaded method of base class. - int SubPayload(const int arg) const { - return arg - int_payload_; - } - - double SubPayload(const double arg) const { - return arg - int_payload_; - } - - double SubPayload(const double arg_a, const double arg_b) const { - return arg_a + arg_b - int_payload_; - } - - // Non-overloaded virtual method of base class. - virtual int MulPayload(const int arg) const = 0; - - // Overloaded virtual method of base class. - virtual int DivPayload(const int arg) const = 0; - virtual double DivPayload(const double arg) const = 0; - - // Zero-argument method. - int GetPayload() const { - return int_payload_; - } - - // Overloaded zero-argument method (overloaded on the const-ness of the - // implicit 'this' pointer). - bool ThisConst() { - return false; - } - - bool ThisConst() const { - return true; - } - - // Overloaded prefix-increment operator. - AbstractBase& operator++() { - ++int_payload_; - return *this; - } - - // Operator with multiple overloads. - int operator+(const int arg) const { - return int_payload_ + arg; - } - - double operator+(const double arg) const { - return int_payload_ + arg; - } - - protected: - int int_payload_; -}; - -std::size_t AbstractBase::constructor_call_count_ = 0; -std::size_t AbstractBase::destructor_call_count_ = 0; - -AbstractBase::~AbstractBase() { - ++destructor_call_count_; -} - -// Template for derived classes. -template -class Derived : public AbstractBase { - public: - static std::size_t constructor_call_count_; - static std::size_t destructor_call_count_; - - explicit Derived(const int int_payload) - : AbstractBase(int_payload) { - ++constructor_call_count_; - } - - // Copy constructor. - Derived(const Derived& original) = default; - - // Move constructor. Although not strictly necessary, we zero out the original - // so that we can check that it was indeed moved-from. - Derived(Derived&& original) // NOLINT(build/c++11) - : Derived(original.int_payload_) { - original.int_payload_ = 0; - } - - ~Derived() override { - ++destructor_call_count_; - } - - int MulPayload(const int arg) const override { - return flip_sign ? -arg * int_payload_ - : arg * int_payload_; - } - - int DivPayload(const int arg) const override { - return flip_sign ? -arg / int_payload_ - : arg / int_payload_; - } - - double DivPayload(const double arg) const override { - return flip_sign ? -arg / int_payload_ - : arg / int_payload_; - } -}; - -template -std::size_t Derived::constructor_call_count_ = 0; - -template -std::size_t Derived::destructor_call_count_ = 0; - -typedef Derived DerivedA; -typedef Derived DerivedB; - -} // namespace - -class InstanceMethodWrappersTest : public ::testing::Test { - protected: - virtual void SetUp() { - // Zero out global counters before each test. - AbstractBase::constructor_call_count_ = 0; - AbstractBase::destructor_call_count_ = 0; - DerivedA::constructor_call_count_ = 0; - DerivedA::destructor_call_count_ = 0; - DerivedB::constructor_call_count_ = 0; - DerivedB::destructor_call_count_ = 0; - } -}; - -// Simple test for wrapped new and delete. -TEST_F(InstanceMethodWrappersTest, NewDeleteTest) { - DerivedA* instance_a = WrapNew(42); - ASSERT_NE(instance_a, nullptr); - EXPECT_EQ(42, instance_a->GetPayload()); - EXPECT_EQ(1u, AbstractBase::constructor_call_count_); - EXPECT_EQ(1u, DerivedA::constructor_call_count_); - EXPECT_EQ(0u, DerivedB::constructor_call_count_); - - DerivedB* instance_b = WrapNew(123); - ASSERT_NE(instance_b, nullptr); - EXPECT_EQ(123, instance_b->GetPayload()); - EXPECT_EQ(2u, AbstractBase::constructor_call_count_); - EXPECT_EQ(1u, DerivedA::constructor_call_count_); - EXPECT_EQ(1u, DerivedB::constructor_call_count_); - - WrapDelete(instance_a); - EXPECT_EQ(1u, AbstractBase::destructor_call_count_); - EXPECT_EQ(1u, DerivedA::destructor_call_count_); - EXPECT_EQ(0u, DerivedB::destructor_call_count_); - - WrapDelete(instance_b); - EXPECT_EQ(2u, AbstractBase::destructor_call_count_); - EXPECT_EQ(1u, DerivedA::destructor_call_count_); - EXPECT_EQ(1u, DerivedB::destructor_call_count_); -} - -// Similar to NewDeleteTest, but tests placement-new and invocation of a -// destructor without the delete operator. -TEST_F(InstanceMethodWrappersTest, PlacementNewDeleteTest) { - void* instance_a_mem = std::malloc(sizeof(DerivedA)); - void* instance_b_mem = std::malloc(sizeof(DerivedB)); - - DerivedA* instance_a = WrapPlacementNew(instance_a_mem, 42); - ASSERT_NE(instance_a, nullptr); - EXPECT_EQ(instance_a_mem, instance_a); - EXPECT_EQ(42, instance_a->GetPayload()); - EXPECT_EQ(1u, AbstractBase::constructor_call_count_); - EXPECT_EQ(1u, DerivedA::constructor_call_count_); - EXPECT_EQ(0u, DerivedB::constructor_call_count_); - - DerivedB* instance_b = WrapPlacementNew(instance_b_mem, 123); - ASSERT_NE(instance_b, nullptr); - EXPECT_EQ(instance_b_mem, instance_b); - EXPECT_EQ(123, instance_b->GetPayload()); - EXPECT_EQ(2u, AbstractBase::constructor_call_count_); - EXPECT_EQ(1u, DerivedA::constructor_call_count_); - EXPECT_EQ(1u, DerivedB::constructor_call_count_); - - WrapDestructor(instance_a); - EXPECT_EQ(1u, AbstractBase::destructor_call_count_); - EXPECT_EQ(1u, DerivedA::destructor_call_count_); - EXPECT_EQ(0u, DerivedB::destructor_call_count_); - - WrapDestructor(instance_b); - EXPECT_EQ(2u, AbstractBase::destructor_call_count_); - EXPECT_EQ(1u, DerivedA::destructor_call_count_); - EXPECT_EQ(1u, DerivedB::destructor_call_count_); - - std::free(instance_a_mem); - std::free(instance_b_mem); -} - -// Test copy-construction. -TEST_F(InstanceMethodWrappersTest, CopyTest) { - DerivedA original(42); - - DerivedA* copy = WrapNew(original); - ASSERT_NE(copy, nullptr); - EXPECT_NE(&original, copy); - EXPECT_EQ(42, copy->GetPayload()); - - // Also try copy constructor with placement-new. - void* placement_copy_mem = std::malloc(sizeof(DerivedA)); - DerivedA* placement_copy = WrapPlacementNew(placement_copy_mem, - original); - ASSERT_NE(placement_copy, nullptr); - EXPECT_EQ(placement_copy_mem, placement_copy); - EXPECT_EQ(42, placement_copy->GetPayload()); - - WrapDelete(copy); - WrapDestructor(placement_copy); - std::free(placement_copy_mem); -} - -// Test move-construction. -TEST_F(InstanceMethodWrappersTest, MoveTest) { - DerivedA original(42); - - DerivedA* moved = WrapNewMove(&original); - ASSERT_NE(moved, nullptr); - EXPECT_EQ(42, moved->GetPayload()); - - // Verify that the original was moved-from and not copied. - EXPECT_EQ(0, original.GetPayload()); - - // Also check placement-new with move semantics. - void* moved_2_mem = std::malloc(sizeof(DerivedA)); - DerivedA* moved_2 = WrapPlacementNewMove(moved_2_mem, moved); - ASSERT_NE(moved_2, nullptr); - EXPECT_EQ(moved_2_mem, moved_2); - EXPECT_EQ(42, moved_2->GetPayload()); - - EXPECT_EQ(0, moved->GetPayload()); - - WrapDelete(moved); - WrapDestructor(moved_2); - std::free(moved_2_mem); -} - -TEST_F(InstanceMethodWrappersTest, SimpleMethodTest) { - DerivedA instance_a1(42); - DerivedA instance_a2(123); - DerivedB instance_b(-12); - - // Invoke a simple non-overloaded method via a wrapper. - EXPECT_EQ(45, - (GPCODEGEN_WRAP_METHOD(&AbstractBase::AddPayload) - (&instance_a1, 3))); - EXPECT_EQ(126, - (GPCODEGEN_WRAP_METHOD(&AbstractBase::AddPayload) - (&instance_a2, 3))); - EXPECT_EQ(-9, - (GPCODEGEN_WRAP_METHOD(&AbstractBase::AddPayload) - (&instance_b, 3))); - - // Should also work fine if we refer to the derived classes rather than the - // base. - EXPECT_EQ(45, - (GPCODEGEN_WRAP_METHOD(&DerivedA::AddPayload) - (&instance_a1, 3))); - EXPECT_EQ(126, - (GPCODEGEN_WRAP_METHOD(&DerivedA::AddPayload) - (&instance_a2, 3))); - EXPECT_EQ(-9, - (GPCODEGEN_WRAP_METHOD(&DerivedB::AddPayload) - (&instance_b, 3))); -} - -TEST_F(InstanceMethodWrappersTest, OverloadedMethodTest) { - DerivedA instance_a1(42); - DerivedA instance_a2(123); - DerivedB instance_b(-12); - - // Overloaded methods distinguished only by their const-qualifier. - EXPECT_FALSE((GPCODEGEN_WRAP_OVERLOADED_METHOD_ZERO_ARGS( - bool, - AbstractBase, - ThisConst,)( // NOLINT(whitespace/comma) - &instance_a1))); - EXPECT_TRUE((GPCODEGEN_WRAP_OVERLOADED_METHOD_ZERO_ARGS( - bool, - AbstractBase, - ThisConst, - const)( - &instance_a1))); - - // Overloads which take arguments. - EXPECT_EQ(58, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - int, - AbstractBase, - SubPayload, - const, - int)( - &instance_a1, - 100))); - - EXPECT_EQ(2.125, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - double, - AbstractBase, - SubPayload, - const, - double)( - &instance_a2, - 125.125))); - - EXPECT_EQ(124.75, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - double, - AbstractBase, - SubPayload, - const, - double, - double)( - &instance_b, - 12.5, - 100.25))); -} - -TEST_F(InstanceMethodWrappersTest, VirtualMethodTest) { - DerivedA instance_a1(42); - DerivedA instance_a2(123); - DerivedB instance_b(-12); - - // Invoke virtual method of base class on instances of derived classes. - EXPECT_EQ(84, - (GPCODEGEN_WRAP_METHOD(&AbstractBase::MulPayload) - (&instance_a1, 2))); - EXPECT_EQ(369, - (GPCODEGEN_WRAP_METHOD(&AbstractBase::MulPayload) - (&instance_a2, 3))); - EXPECT_EQ(48, - (GPCODEGEN_WRAP_METHOD(&AbstractBase::MulPayload) - (&instance_b, 4))); - - // Should also work if we refer to the derived classes than the base. - EXPECT_EQ(84, - (GPCODEGEN_WRAP_METHOD(&DerivedA::MulPayload)(&instance_a1, 2))); - EXPECT_EQ(369, - (GPCODEGEN_WRAP_METHOD(&DerivedA::MulPayload)(&instance_a2, 3))); - EXPECT_EQ(48, - (GPCODEGEN_WRAP_METHOD(&DerivedB::MulPayload)(&instance_b, 4))); -} - -TEST_F(InstanceMethodWrappersTest, OverloadedVirtualMethodTest) { - DerivedA instance_a1(42); - DerivedA instance_a2(123); - DerivedB instance_b(-12); - - // Invoke virtual method with overloads. - EXPECT_EQ(1, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - int, - AbstractBase, - DivPayload, - const, - int)( - &instance_a1, - 63))); - - EXPECT_EQ(1.5, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - double, - AbstractBase, - DivPayload, - const, - double)( - &instance_a1, - 63.0))); - - EXPECT_EQ(-1.75, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - double, - AbstractBase, - DivPayload, - const, - double)( - &instance_b, - -21.0))); -} - -TEST_F(InstanceMethodWrappersTest, OperatorOverloadTest) { - DerivedA instance(42); - - // Simple overloaded operator. - AbstractBase& incremented - = GPCODEGEN_WRAP_METHOD(&AbstractBase::operator++)(&instance); - EXPECT_EQ(&instance, &incremented); - EXPECT_EQ(43, instance.GetPayload()); - - // Operator with multiple differently-typed overloads. - EXPECT_EQ(45, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - int, - AbstractBase, - operator+, - const, - int)( - &instance, - 2))); - EXPECT_EQ(0.5, - (GPCODEGEN_WRAP_OVERLOADED_METHOD( - double, - AbstractBase, - operator+, - const, - double)( - &instance, - -42.5))); -} - -} // namespace gpcodegen - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} - -// EOF diff --git a/src/backend/codegen/utils/clang_compiler.cc b/src/backend/codegen/utils/clang_compiler.cc deleted file mode 100644 index 91aa9732f7be6166aab2414afb055f826c40f0ce..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/clang_compiler.cc +++ /dev/null @@ -1,317 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// clang_compiler.cc -// -// @doc: -// Tool for compiling in-memory C++ source code snippets into LLVM -// modules which can then be codegened. -// -// @test: -// Unittests in tests/clang_compiler_unittest.cc -// -// -//--------------------------------------------------------------------------- - -#include "codegen/utils/clang_compiler.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/annotated_type.h" - -#ifdef CODEGEN_HAVE_TEMPORARY_FILE -#include "codegen/utils/temporary_file.h" -#endif - -#include "clang/CodeGen/CodeGenAction.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" - -namespace gpcodegen { - -namespace { - -// Adapter for clang::EmitLLVMOnlyAction that saves the generated module before -// the action is destroyed by clang::tooling::runToolOnCodeWithArgs(). -class CompileModuleAction : public clang::EmitLLVMOnlyAction { - public: - CompileModuleAction(llvm::LLVMContext* context, - std::unique_ptr* target_module) - : clang::EmitLLVMOnlyAction(context), - target_module_(target_module) { - } - - ~CompileModuleAction() override { - *target_module_ = takeModule(); - } - - private: - std::unique_ptr* target_module_; -}; - -// Make sure that widths of built-in integer types are what we expect for -// ScalarCppTypeFromLLVMType() to work properly. -static_assert(sizeof(char) == 1, - "Builtin char type is not 1 byte"); -static_assert(sizeof(short) == 2, // NOLINT(runtime/int) - "Builtin short type is not 2 bytes"); -static_assert(sizeof(int) == 4, - "Builtin int type is not 4 bytes"); -static_assert(sizeof(long) == 8 // NOLINT(runtime/int) - || sizeof(long long) == 8, // NOLINT(runtime/int) - "Neither of the builtin types long or long long are 8 bytes"); - -// Map a basic scalar type from LLVM to C++. -std::string ScalarCppTypeFromLLVMType(const llvm::Type& llvm_type) { - switch (llvm_type.getTypeID()) { - case llvm::Type::VoidTyID: - return "void"; - case llvm::Type::FloatTyID: - return "float"; - case llvm::Type::DoubleTyID: - return "double"; - case llvm::Type::IntegerTyID: { - const unsigned bit_width - = static_cast(llvm_type).getBitWidth(); - switch (bit_width) { - case 1: - return "bool"; - case 8: - return "char"; - case 16: - return "short"; - case 32: - return "int"; - case 64: - return (sizeof(long) == 8) // NOLINT(runtime/int) - ? "long" : "long long"; - default: - std::fprintf( - stderr, - "Unexpected bit width (=%u) for llvm::IntegerType in " - "gpcodegen::ClangCompiler::CppTypeFromAnnotatedType\n", - bit_width); - std::exit(1); - } - } - default: { - std::fprintf( - stderr, - "FATAL ERROR: Unhandled llvm::Type::TypeID in " - "gpcodegen::ClangCompiler::CppTypeFromAnnotatedType\n"); - std::exit(1); - } - } -} - -// Internal implementation of ClangCompiler::CppTypeFromAnnotatedType(). -// 'annotated_type' is the AnnotatedType being converted to a C++ type. -// 'current_llvm_type' is the LLVM Type being converted by this particular call -// to CppTypeFromAnnotatedTypeImpl() (this starts as 'annotated_type.llvm_type' -// for the first invocation of this function, then recursively descends through -// pointer types, if any). 'idx' is the position of the current type in the -// chain of pointers and cv-qualifiers represented by 'annotated_type.is_const' -// and 'annotated_type.is_volatile'. 'idx' starts at the last element of those -// vectors (the outermost pointer or reference, if any) and descends as this -// function is called recursively. -std::string CppTypeFromAnnotatedTypeImpl( - const AnnotatedType& annotated_type, - const llvm::Type& current_llvm_type, - const std::vector::size_type idx) { - if (idx == 0) { - // We are at the base scalar type. - std::string cpp_type_name; - - // Qualifiers for base scalar type. - if (annotated_type.is_const[0]) { - cpp_type_name.append("const "); - } - if (annotated_type.is_volatile[0]) { - cpp_type_name.append("volatile "); - } - if (annotated_type.explicitly_unsigned) { - cpp_type_name.append("unsigned "); - } - - if (annotated_type.is_voidptr) { - // Note that we just emit the base type "void" here, while the caller - // will add the "*" to indicate a pointer. - cpp_type_name.append("void"); - } else if (annotated_type.is_long) { - cpp_type_name.append("long"); - } else if (annotated_type.is_long_long) { - cpp_type_name.append("long long"); - } else { - cpp_type_name.append(ScalarCppTypeFromLLVMType(current_llvm_type)); - } - - return cpp_type_name; - } else { - // We are dealing with a pointer or reference type. Recurse to get the - // referent type. - std::string cpp_type_name(CppTypeFromAnnotatedTypeImpl( - annotated_type, - *current_llvm_type.getPointerElementType(), - idx - 1)); - - if (annotated_type.is_reference - && (idx == annotated_type.is_const.size() - 1)) { - if ((annotated_type.is_voidptr) && (idx == 1u)) { - // This is a single-level reference (no inner pointers), but it has been - // converted to void* because it is a reference to a class or struct. - cpp_type_name.append("* const"); - } else { - // If this is the outermost of a chain of pointers/references and it is - // a reference, and the reference has NOT been converted to void* (e.g. - // for a reference to a class or struct), make a reference. - cpp_type_name.push_back('&'); - } - } else { - // Make a pointer. - cpp_type_name.push_back('*'); - } - - // Add cv-qualifiers for pointer. - if (annotated_type.is_const[idx]) { - cpp_type_name.append(" const"); - } - if (annotated_type.is_volatile[idx]) { - cpp_type_name.append(" volatile"); - } - - return cpp_type_name; - } -} - -} // namespace - -bool ClangCompiler::CompileCppSource(const llvm::Twine& source_code, - const bool debug) { - std::unique_ptr compiled_module; - - bool run_ok = false; - if (debug) { -#ifdef CODEGEN_HAVE_TEMPORARY_FILE - // Dump source code to temporary file so that the debugger can find it. - std::string source_prefix(TemporaryFile::TemporaryDirectoryPath()); - source_prefix.append("/codegen_cpp_src_"); - - TemporaryFile source_dump(source_prefix.c_str()); - if (!(source_dump.Open() - && source_dump.WriteTwine(source_code) - && source_dump.Flush())) { - return false; - } -#endif - - // Run with additional flags: "-g" to generate debug symbols, "-x c++" to - // treat temporary file's contents as C++ even though it doesn't have a - // recognized suffix. - run_ok = clang::tooling::runToolOnCodeWithArgs( - new CompileModuleAction(&(code_generator_->context_), - &compiled_module), - source_code, - {"-std=c++11", "-Wno-c99-extensions", "-g", "-x", "c++"}, -#ifdef CODEGEN_HAVE_TEMPORARY_FILE - source_dump.Filename()); -#else - "debugsrc.cc"); -#endif - } else { - run_ok = clang::tooling::runToolOnCodeWithArgs( - new CompileModuleAction(&(code_generator_->context_), - &compiled_module), - source_code, - {"-std=c++11", "-Wno-c99-extensions"}); - } - - if (run_ok) { - assert(compiled_module); - code_generator_->auxiliary_modules_.emplace_back( - std::move(compiled_module)); - } - - return run_ok; -} - -std::string ClangCompiler::CppTypeFromAnnotatedType( - const AnnotatedType& annotated_type) { - return CppTypeFromAnnotatedTypeImpl(annotated_type, - *annotated_type.llvm_type, - annotated_type.is_const.size() - 1); -} - -std::string ClangCompiler::GenerateExternalFunctionDeclarations() const { - std::string declarations; - for (const CodegenUtils::NamedExternalFunction& external_fn - : code_generator_->named_external_functions_) { - // Mark extern "C" to avoid name-mangling. - declarations.append("extern \"C\" "); - - // The function's return type. - declarations.append(CppTypeFromAnnotatedType(external_fn.return_type)); - declarations.push_back(' '); - - // The function's name. - declarations.append(external_fn.name); - declarations.push_back('('); - - // The types of the function's arguments, separated by commas. - if (!external_fn.argument_types.empty()) { - declarations.append(CppTypeFromAnnotatedType( - external_fn.argument_types.front())); - } - for (std::vector::size_type idx = 1; - idx < external_fn.argument_types.size(); - ++idx) { - declarations.append(", "); - declarations.append(CppTypeFromAnnotatedType( - external_fn.argument_types[idx])); - } - - declarations.append(");\n"); - } - - return declarations; -} - -namespace clang_compiler_detail { - -std::string HexDouble(const double value) { - static constexpr std::size_t kHexDoubleBufferSize = - 1 // Minus sign "-" - + 2 // 0x - + 1 // First digit - + 1 // Decimal point "." - + 13 // 52 bits of mantissa, represented as hexadecimal digits - + 1 // "p" specifier for binary exponent - + 1 // Exponent sign "+" or "-" - + 4 // 11 bits of binary exponent written in decimal - + 1; // Null-terminator - char buffer[kHexDoubleBufferSize] = {}; - int written = std::snprintf(buffer, - sizeof(buffer), - "%.13a", - value); - assert((written >= 0) - && (static_cast(written) < sizeof(buffer))); - return std::string(buffer); -} - -} // namespace clang_compiler_detail - -} // namespace gpcodegen - -// EOF diff --git a/src/backend/codegen/utils/codegen_utils.cc b/src/backend/codegen/utils/codegen_utils.cc deleted file mode 100644 index eb1653f13b9503d5399fb32f7b57abcebf11d4c3..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/codegen_utils.cc +++ /dev/null @@ -1,415 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_utils.cc -// -// @doc: -// Object that manages runtime code generation for a single LLVM module. -// -// @test: -// Unittests in tests/code_generator_unittest.cc -// -// -//--------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "codegen/utils/codegen_utils.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/Analysis/TargetTransformInfo.h" -#include "llvm/ExecutionEngine/ExecutionEngine.h" -// DO NOT REMOVE: including the MCJIT.h header forces the MCJIT engine to be -// linked in when using static libraries. -#include "llvm/ExecutionEngine/MCJIT.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalValue.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/Value.h" -#include "llvm/Support/CodeGen.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" - -namespace llvm { class FunctionType; } - -namespace gpcodegen { - -namespace { - -// Almost-trivial conversion from Codegen enum for optimization level to the -// LLVM equivalent. -inline llvm::CodeGenOpt::Level OptLevelCodegenToLLVM( - const CodegenUtils::OptimizationLevel codegen_opt_level) { - switch (codegen_opt_level) { - case CodegenUtils::OptimizationLevel::kNone: - return llvm::CodeGenOpt::None; - case CodegenUtils::OptimizationLevel::kLess: - return llvm::CodeGenOpt::Less; - case CodegenUtils::OptimizationLevel::kDefault: - return llvm::CodeGenOpt::Default; - case CodegenUtils::OptimizationLevel::kAggressive: - return llvm::CodeGenOpt::Aggressive; - default: { - std::fprintf(stderr, - "FATAL: Unrecognized CodegenUtils::OptimizationLevel\n"); - std::exit(1); - } - } -} - -} // namespace - -constexpr char CodegenUtils::kExternalVariableNamePrefix[]; -constexpr char CodegenUtils::kExternalFunctionNamePrefix[]; - -CodegenUtils::CodegenUtils(llvm::StringRef module_name) - : ir_builder_(context_), - module_(new llvm::Module(module_name, context_)), - external_variable_counter_(0), - external_function_counter_(0) { -} - -bool CodegenUtils::InitializeGlobal() { - // These LLVM initialization routines return true if there is no native - // target available, false if initialization was OK. So, if all 3 return - // false, then initialization is fine. - return !llvm::InitializeNativeTarget() - && !llvm::InitializeNativeTargetAsmPrinter() - && !llvm::InitializeNativeTargetAsmParser(); -} - -llvm::AllocaInst* CodegenUtils::CreateMakeTuple( - const std::vector& members, const std::string& name) { - std::vector argument_types(members.size(), nullptr); - std::transform(members.begin(), - members.end(), - argument_types.begin(), - [](llvm::Value* arg) { - assert(nullptr != arg); return arg->getType();}); - llvm::StructType* llvm_struct_type = llvm::StructType::create( - context_, argument_types, name.empty() ? "" : name + "_type"); - llvm::AllocaInst* llvm_struct_ptr = ir_builder()->CreateAlloca( - llvm_struct_type, - nullptr, - name); - for (size_t member_idx = 0; member_idx < members.size(); ++member_idx) { - llvm::Value* llvm_mem_ptr = ir_builder()->CreateInBoundsGEP( - llvm_struct_type, - llvm_struct_ptr, - // This doesn't work if type is not int32_t. - {GetConstant(0), GetConstant(member_idx)}); - ir_builder()->CreateStore(members[member_idx], llvm_mem_ptr); - } - return llvm_struct_ptr; -} - -bool CodegenUtils::Optimize(const OptimizationLevel generic_opt_level, - const SizeLevel size_level, - const bool optimize_for_host_cpu) { - // This method's implementation is loosely based on the LLVM "opt" - // command-line tool ('tools/opt/opt.cpp' in the LLVM source). - - if (module_.get() == nullptr) { - // No Module to optimize (probably already compiled). - return false; - } - - // Get info about the target machine. - llvm::Triple native_triple(llvm::sys::getProcessTriple()); - - std::string error_string; - const llvm::Target* native_target = llvm::TargetRegistry::lookupTarget( - native_triple.getTriple(), - error_string); - if (native_target == nullptr) { - // Couldn't find this platform in the TargetRegistry. - return false; - } - - std::unique_ptr native_target_machine( - native_target->createTargetMachine( - native_triple.getTriple(), - optimize_for_host_cpu ? llvm::sys::getHostCPUName() : "", - // CPU features string is empty, so feature flags will be - // automatically inferred based on CPU model. - "", - llvm::TargetOptions(), - llvm::Reloc::Default, - llvm::CodeModel::Default, - OptLevelCodegenToLLVM(generic_opt_level))); - if (!native_target_machine) { - // Couldn't create TargetMachine. - return false; - } - - // Set the module's triple and data layout to that of the target machine. - module_->setTargetTriple(native_triple.getTriple()); - module_->setDataLayout(native_target_machine->createDataLayout()); - - // Module-level and function-level pass managers to hold the transformation - // passes that will be run over code. - // - // TODO(chasseur): The new pass-manager infrastructure is a work-in-progress - // and doesn't yet have an easy way to automatically populate all the passes - // from a given optimization level, so we use the legacy PassManager classes - // instead. Once the new PassManagers are a bit more mature, we will probably - // want to switch to using them. - llvm::legacy::PassManager pass_manager; - llvm::legacy::FunctionPassManager function_pass_manager(module_.get()); - - // Add TargetLibraryInfo pass (provides information about built-in functions - // and vectorizability for the target machine.). - llvm::TargetLibraryInfoImpl target_library_info(native_triple); - pass_manager.add(new llvm::TargetLibraryInfoWrapperPass(target_library_info)); - - // Add internal analysis passes from the target machine. - pass_manager.add(llvm::createTargetTransformInfoWrapperPass( - native_target_machine->getTargetIRAnalysis())); - if (generic_opt_level > OptimizationLevel::kNone) { - function_pass_manager.add(llvm::createTargetTransformInfoWrapperPass( - native_target_machine->getTargetIRAnalysis())); - } - - // PassManagerBuilder handles automatically populating our pass managers with - // the standard set of optimizations at each optimization level. - llvm::PassManagerBuilder pass_builder; - pass_builder.OptLevel = static_cast(generic_opt_level); - pass_builder.SizeLevel = static_cast(size_level); - - // Add an inlining pass at -O1 and above. - if (generic_opt_level > OptimizationLevel::kNone) { - pass_builder.Inliner = llvm::createFunctionInliningPass( - static_cast(generic_opt_level), - static_cast(size_level)); - } - - // Add vectorization passes at -O2 and above. - if (generic_opt_level > OptimizationLevel::kLess) { - pass_builder.LoopVectorize = true; - pass_builder.SLPVectorize = true; - } - - // Have the PassManagerBuilder actually fill in the pass managers. - pass_builder.populateFunctionPassManager(function_pass_manager); - pass_builder.populateModulePassManager(pass_manager); - - // Run function-level passes. - function_pass_manager.doInitialization(); - for (llvm::Function &function : *module_) { - function_pass_manager.run(function); - } - function_pass_manager.doFinalization(); - - // Run module-level passes. - pass_manager.run(*module_); - - return true; -} - -bool CodegenUtils::PrepareForExecution(const OptimizationLevel cpu_opt_level, - const bool optimize_for_host_cpu) { - if (engine_.get() != nullptr) { - // This method was already called successfully. - return false; - } - - if (module_.get() == nullptr) { - // No Module to compile. - return false; - } - - llvm::EngineBuilder builder(std::move(module_)); - builder.setEngineKind(llvm::EngineKind::JIT); - builder.setOptLevel(OptLevelCodegenToLLVM(cpu_opt_level)); - if (optimize_for_host_cpu) { - builder.setMCPU(llvm::sys::getHostCPUName()); - } - - engine_.reset(builder.create()); - if (engine_.get() == nullptr) { - return false; - } - - // Add auxiliary modules generated by companion tools to the ExecutionEngine. - for (std::unique_ptr& auxiliary_module : auxiliary_modules_) { - engine_->addModule(std::move(auxiliary_module)); - } - - // Map global variables (i.e. pointer constants) and registered external - // functions to their actual locations in memory. - // - // Note that on OSX, C symbol names all have a leading underscore prepended to - // them. We must replicate this when adding global mappings to the - // ExecutionEngine so that names are properly resolved. - for (const std::pair& external_global_variable - : external_global_variables_) { - engine_->addGlobalMapping( -#ifdef __APPLE__ - std::string(1, '_') + external_global_variable.first, -#else // !__APPLE__ - external_global_variable.first, -#endif - external_global_variable.second); - } - - // Map registered external functions to their actual locations in memory. - for (const std::pair& external_function - : external_functions_) { - engine_->addGlobalMapping( -#ifdef __APPLE__ - std::string(1, '_') + external_function.second, -#else // !__APPLE__ - external_function.second, -#endif - external_function.first); - } - - return true; -} - -void CodegenUtils::PrintUnderlyingModules(llvm::raw_ostream& out) { - // Print the main module - out << "==== MAIN MODULE ====" << "\n"; - out.flush(); - module()->print(out, nullptr); - - // Print auxiliary modules - out << "==== AUXILIARY MODULES ====" << "\n"; - out.flush(); - for (std::unique_ptr& auxiliary_module : auxiliary_modules_) { - auxiliary_module->print(out, nullptr); - } - out << "==== END MODULES ====" << "\n\n"; - out.flush(); -} - -llvm::GlobalVariable* CodegenUtils::AddExternalGlobalVariable( - llvm::Type* type, - const void* address) { - external_global_variables_.emplace_back( - GenerateExternalVariableName(), - reinterpret_cast(address)); - - return new llvm::GlobalVariable(*module_, - type, - false, - llvm::GlobalValue::ExternalLinkage, - nullptr, - external_global_variables_.back().first); -} - -void CodegenUtils::CheckFunctionType( - const std::string& function_name, - const llvm::FunctionType* expected_function_type) { - // Look up function in the ExecutionEngine if PrepareForExecution() has - // already been called, or in the Module if it has not. - const llvm::Function* function - = engine_ ? engine_->FindFunctionNamed(function_name.c_str()) - : module_->getFunction(function_name); - - if (function != nullptr) { - assert(expected_function_type == function->getFunctionType()); - } -} - -std::string CodegenUtils::GenerateExternalVariableName() { - char print_buffer[sizeof(kExternalVariableNamePrefix) - + (sizeof(unsigned) << 1) + 1] = {}; - int chars_printed = std::snprintf(print_buffer, - sizeof(print_buffer), - "%s%X", - kExternalVariableNamePrefix, - ++external_variable_counter_); - assert(static_cast(chars_printed) < sizeof(print_buffer)); - return std::string(print_buffer); -} - -std::string CodegenUtils::GenerateExternalFunctionName() { - // Prefix for generated names is only 10 chars. Up to 16 chars (including - // null-terminator) are typically stored inline for C++ standard library - // implementations that use the short-string optimization for std::string - // (e.g. LLVM libc++ and GNU libstdc++ from GCC 5 or higher). This gives us - // 5 chars worth of space that we can use for unique names without strings - // ever having to allocate external storage on the heap. Since we number using - // hexadecimal characters, this means we can register up to 1M (2^20) - // external functions without ever touching external memory for strings. That - // ought to be enough for anybody. ;) - - char print_buffer[sizeof(kExternalFunctionNamePrefix) - + (sizeof(unsigned) << 1) + 1] = {}; - int chars_printed = std::snprintf(print_buffer, - sizeof(print_buffer), - "%s%X", - kExternalFunctionNamePrefix, - ++external_function_counter_); - assert(static_cast(chars_printed) < sizeof(print_buffer)); - return std::string(print_buffer); -} - -llvm::Value* CodegenUtils::GetPointerToMemberImpl( - llvm::Value* base_ptr, - llvm::Type* cast_type, - const std::size_t cumulative_offset) { - // Sanity checks: base_ptr must be non-NULL. - assert(base_ptr != nullptr); - - // Either this is a trivial invocation of GetPointerToMember() with zero - // pointer-to-member arguments, or we are actually doing something meaningful - // and base_ptr should be a pointer-to-struct represented as i8*. - assert(((cast_type == nullptr) && (cumulative_offset == 0u)) - || (base_ptr->getType()->isPointerTy() - && base_ptr->getType()->getPointerElementType()->isIntegerTy(8))); - - llvm::Value* offset_pointer = base_ptr; - if (cumulative_offset != 0u) { - // Use the GEP (get element pointer) instruction to do the address - // computation in LLVM. - offset_pointer = ir_builder_.CreateInBoundsGEP( - GetType(), // Use LLVM i8 type as the basis for array indexing. - base_ptr, - GetConstant(cumulative_offset)); - } - - if (cast_type == nullptr) { - return offset_pointer; - } else { - // Cast the pointer to the appropriate type. - return ir_builder_.CreateBitCast(offset_pointer, cast_type); - } -} - -llvm::Value* CodegenUtils::CreateIntrinsicInstrCall( - llvm::Intrinsic::ID Id, - llvm::ArrayRef Tys, - llvm::Value* arg0, - llvm::Value* arg1) { - llvm::Function* llvm_intr_func = llvm::Intrinsic::getDeclaration(module(), - Id, - Tys); - return ir_builder()->CreateCall(llvm_intr_func, {arg0, arg1}); -} - -} // namespace gpcodegen - -// EOF diff --git a/src/backend/codegen/utils/examples/CMakeLists.txt b/src/backend/codegen/utils/examples/CMakeLists.txt deleted file mode 100644 index 3b4f4996aa8ba0480d84ea74620f73de0a89e6d4..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/examples/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2015-2016 Pivotal Software, Inc. -# -# CMakeLists.txt -# Cmake configuration for building GPDB codegen examples. -# - - -cmake_minimum_required(VERSION 2.8.12) - -# Example 1: Materialize Tuple -add_executable(materialize_tuple_ex materialize_tuple_ex.cc) -target_link_libraries(materialize_tuple_ex gpcodegen) diff --git a/src/backend/codegen/utils/examples/README.md b/src/backend/codegen/utils/examples/README.md deleted file mode 100644 index 62edfcec00e59a7d9f0f96c85d56633f559b562c..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/examples/README.md +++ /dev/null @@ -1,20 +0,0 @@ -README -====== - -1. Configure Cmake with `-D build_examples=ON`. -``` - mkdir build - cd build - cmake -D build_examples=ON ../ -``` - -2. Build codegen. -``` - make -``` - -3. Run individual examples as: -``` - ./example/materialize_tuple_ex -``` - diff --git a/src/backend/codegen/utils/examples/materialize_tuple_ex.cc b/src/backend/codegen/utils/examples/materialize_tuple_ex.cc deleted file mode 100644 index c7dfd632fa51a4a0afeaba46494ffc269a0d1bd2..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/examples/materialize_tuple_ex.cc +++ /dev/null @@ -1,236 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// materialize_tuple_ex.cc -// -// @doc: -// Contains an example that implements the MaterializeTuple() function that -// uses type information available at runtime to generate an output tuple -// in a buffer. -// -// @test: -// -// -//--------------------------------------------------------------------------- - -#include - -#include "codegen/utils/codegen_utils.h" -#include "codegen/utils/utility.h" -#include "llvm/IR/Verifier.h" - -namespace { - -typedef void (*MaterializeTupleFunction)(char *); - -enum Types { - kBool = 0, // 1 byte - kInt // 4 bytes -}; - -const int kTupleSize = 9; -const int kNumSlots = 3; -const int kOffsets[] = {0, 4, 5}; -const Types kTypes[] = {Types::kInt, Types::kBool, Types::kInt}; - -bool ParseBool() { return true; } -int ParseInt() { return 6513; } - -void PrintTuple(char *tuple, int len) { - for (int i = 0; i < len; ++i) { - std::printf("%02x ", static_cast(tuple[i])); - } - std::printf("\n"); -} - -void testMaterializeTuple(MaterializeTupleFunction func) { - char *tuple = new char[kTupleSize](); - PrintTuple(tuple, kTupleSize); - func(tuple); - PrintTuple(tuple, kTupleSize); - delete[] tuple; -} - -void materializeTuple(char *tuple) { - for (int i = 0; i < kNumSlots; ++i) { - char *slot = tuple + kOffsets[i]; - switch (kTypes[i]) { - case Types::kBool: - *slot = ParseBool(); - break; - case Types::kInt: - *reinterpret_cast(slot) = ParseInt(); - break; - } - } -} - -// The following method tries to faithfully reproduce the semantics of the -// MaterializeTuple function (implemented natively above). -// -// The reader should note that this possible misses the point of codegen in -// that since we know all the types and offsets at the time of code generation, -// we can actually unroll the loop and eliminate any branching by type here. -// -// This implementation does illustrate the basic blocks and branches necessary -// when implementing a for loop, a switch construct, type conversion and -// external -// -MaterializeTupleFunction -GenerateCodeForMaterializeTuple(gpcodegen::CodegenUtils *codegen_utils) { - auto irb = codegen_utils->ir_builder(); - llvm::Function *mt_function = - codegen_utils->CreateFunction("materializeTuple"); - - // "Constants" - llvm::Value *kNumSlots_value = codegen_utils->GetConstant(kNumSlots); - llvm::Value *kOffsets_array = codegen_utils->GetConstant(kOffsets); - llvm::Value *kTypes_array = codegen_utils->GetConstant(kTypes); - llvm::Value *bool_type = codegen_utils->GetConstant(Types::kBool); - llvm::Value *int_type = codegen_utils->GetConstant(Types::kInt); - // "External functions" - llvm::Function *parseInt_f = - codegen_utils->RegisterExternalFunction(ParseInt); - llvm::Function *parseBool_f = - codegen_utils->RegisterExternalFunction(ParseBool); - - /* - * - * +-------+ - * | entry | - * +-------+ - * | - * V - * +------------+ - * +->| loop_start | - * | +------------+ - * | | +-------------+ - * | V +---->| switch_bool |--+ - * | +-----------+ | +-------------+ | - * | | loop_main |----| | - * | +-----------+ | +------------+ | - * | +---->| switch_int |---| - * | +-------------+ +------------+ | - * +--| switch_term |<-------------------------+ - * +-------------+ - * | - * V - * +-----------+ - * | loop_term | - * +-----------+ - */ - - // "Blocks" - llvm::BasicBlock *entry = - codegen_utils->CreateBasicBlock("entry", mt_function); - llvm::BasicBlock *loop_start = - codegen_utils->CreateBasicBlock("loop_start", mt_function); - llvm::BasicBlock *loop_main = - codegen_utils->CreateBasicBlock("loop_main", mt_function); - llvm::BasicBlock *switch_term = - codegen_utils->CreateBasicBlock("switch_term", mt_function); - llvm::BasicBlock *switch_bool = - codegen_utils->CreateBasicBlock("switch_bool", mt_function); - llvm::BasicBlock *switch_int = - codegen_utils->CreateBasicBlock("switch_int", mt_function); - llvm::BasicBlock *loop_term = - codegen_utils->CreateBasicBlock("loop_term", mt_function); - - // "Input Arguments" - llvm::Value *tuple_arg = - (llvm::Value *)gpcodegen::ArgumentByPosition(mt_function, 0); - - // entry block - irb->SetInsertPoint(entry); - irb->CreateBr(loop_start); - - // loop-start block - irb->SetInsertPoint(loop_start); - llvm::PHINode *kOffsets_index = irb->CreatePHI(codegen_utils->GetType(), - 2 /* num of incoming edges */); - kOffsets_index->addIncoming(codegen_utils->GetConstant(0), entry); - - // kOffsets_index == kNumSlots_value - irb->CreateCondBr(irb->CreateICmpEQ(kOffsets_index, kNumSlots_value), - loop_term, loop_main); - - // loop-main block - irb->SetInsertPoint(loop_main); - // Type offset_value = kOffsets_array[kOffsets_index] - llvm::Value *offset_value = - irb->CreateLoad(codegen_utils->ir_builder()->CreateInBoundsGEP( - codegen_utils->GetType(), kOffsets_array, kOffsets_index)); - // Type type_value = kTypes_array[kOffsets_index] - llvm::Value *type_value = - irb->CreateLoad(codegen_utils->ir_builder()->CreateInBoundsGEP( - codegen_utils->GetType(), kTypes_array, kOffsets_index)); - // char* slot_ptr = tuple_arg[offset_value] - llvm::Value *slot_ptr = irb->CreateInBoundsGEP( - codegen_utils->GetType(), tuple_arg, offset_value); - - // switch(type) - llvm::SwitchInst *switch_ins = irb->CreateSwitch(type_value, switch_term, 3); - switch_ins->addCase(static_cast(bool_type), switch_bool); - switch_ins->addCase(static_cast(int_type), switch_int); - - { // case kBool: - irb->SetInsertPoint(switch_bool); - // *slot_ptr = (char) parseBool_f() - llvm::Value *parsed_value = irb->CreateCall(parseBool_f, {}); - irb->CreateStore( - irb->CreateZExt(parsed_value, codegen_utils->GetType()), - slot_ptr); - irb->CreateBr(switch_term); - } - - { // case kInt: - irb->SetInsertPoint(switch_int); - // *(int*)slot_ptr = parseInt_f() - llvm::Value *parsed_value = irb->CreateCall(parseInt_f, {}); - irb->CreateStore( - parsed_value, - irb->CreateBitCast(slot_ptr, codegen_utils->GetType())); - irb->CreateBr(switch_term); - } - - // switch-term block - irb->SetInsertPoint(switch_term); - - // kOffsets_index++ - llvm::Value *next_kOffsets_index = - irb->CreateAdd(kOffsets_index, codegen_utils->GetConstant(1)); - irb->CreateBr(loop_start); - kOffsets_index->addIncoming(next_kOffsets_index, switch_term); - - // loop term block - irb->SetInsertPoint(loop_term); - irb->CreateRetVoid(); - - // Verification - assert(!llvm::verifyFunction(*mt_function)); - assert(!llvm::verifyModule(*codegen_utils->module())); - - bool prepare_ok = codegen_utils->PrepareForExecution( - gpcodegen::CodegenUtils::OptimizationLevel::kDefault, true); - assert(prepare_ok); - - return codegen_utils->GetFunctionPointer("materializeTuple"); -} -} // namespace - -int main() { - bool init_ok = gpcodegen::CodegenUtils::InitializeGlobal(); - assert(init_ok); - - std::printf("Testing static compiled version:\n"); - gpcodegen::CodegenUtils codegen_utils("materializeTuple"); - testMaterializeTuple(materializeTuple); - - std::printf("Testing JIT compiled version:\n"); - auto materializeTupleCompiled = - GenerateCodeForMaterializeTuple(&codegen_utils); - testMaterializeTuple(materializeTupleCompiled); - return 0; -} diff --git a/src/backend/codegen/utils/gp_assert.cc b/src/backend/codegen/utils/gp_assert.cc deleted file mode 100644 index 19f16163c9304b52907d94070f34b77da2dd9a6b..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/gp_assert.cc +++ /dev/null @@ -1,47 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// gp_assert.cc -// -// @doc: -// Implementation of lower level assert methods to override C++ assert() -// functionality to pass any errors to GPDB. -// -//--------------------------------------------------------------------------- - -#ifdef CODEGEN_GPDB_ASSERT_HANDLING -extern "C" { -#include "postgres.h" // NOLINT(build/include) - -// Overload assert handling from LLVM, and pass any error messages to GPDB. -// LLVM has a custom implementation of __assert_rtn, only compiled -// on OSX. To prevent naming conflicts when building GPDB, LLVM should -// be built with -DLLVM_ENABLE_CRASH_OVERRIDES=off -// -// (Refer http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20130826/186121.html) -#ifdef __APPLE__ -void __assert_rtn(const char *func, - const char *file, - int line, - const char *expr) { -#elif __linux__ -void __assert_fail(const char * expr, - const char * file, - unsigned int line, - const char * func) { -#endif - - if (!func) { - func = ""; - } - - if ((assert_enabled) && (nullptr != expr)) { - ExceptionalCondition(expr, ("Failed C++ assertion"), file, line); - } - - // Normal execution beyond this point is unsafe -} -} -#endif // CODEGEN_GPDB_ASSERT_HANDLING diff --git a/src/backend/codegen/utils/gp_codegen_utils.cc b/src/backend/codegen/utils/gp_codegen_utils.cc deleted file mode 100644 index d2bcdb018fa03f3d996be9be9d7033f205461336..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/gp_codegen_utils.cc +++ /dev/null @@ -1,92 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// gp_codegen_utils.cc -// -// @doc: -// Object that extends the functionality of CodegenUtils by adding GPDB -// specific functionality and utilities to aid in the runtime code generation -// for a LLVM module -// -// @test: -// Unittests in tests/code_generator_unittest.cc -// -// -//--------------------------------------------------------------------------- - -#include "codegen/utils/gp_codegen_utils.h" - -namespace gpcodegen { - -llvm::Value* GpCodegenUtils::CreateCppTypeToDatumCast( - llvm::Value* value, bool is_src_unsigned) { - assert(nullptr != value); - - llvm::Type* llvm_dest_type = GetType(); - unsigned dest_size = llvm_dest_type->getScalarSizeInBits(); - assert(sizeof(Datum) << 3 == dest_size); - - llvm::Type* llvm_src_type = value->getType(); - unsigned src_size = llvm_src_type->getScalarSizeInBits(); - // If the source value is pointer type, just convert to int. - if (llvm_src_type->isPointerTy()) { - return ir_builder()->CreatePtrToInt(value, llvm_dest_type); - } - - assert(0 != src_size); - assert(dest_size >= src_size); - - llvm::Value* llvm_int_value = value; - // Convert given value to int type to do ext / trunc - if (!llvm_src_type->isIntegerTy()) { - // Get src type as integer type with same as size - llvm::Type* llvm_src_as_int_type = llvm::IntegerType::get(*context(), - src_size); - llvm_int_value = ir_builder()->CreateBitCast( - value, llvm_src_as_int_type); - } - - llvm::Value* llvm_casted_value = llvm_int_value; - if (src_size < dest_size) { - // If a given src type is integer but not a bool and unsigned flag is set to - // false, then do signed extension. - if (llvm_src_type->isIntegerTy() && - src_size > 1 && - !is_src_unsigned) { - llvm_casted_value = ir_builder()->CreateSExt(llvm_int_value, - llvm_dest_type); - } else { - // Datum doesn't do signed extension if src type is not integer, not bool - // and given type is unsigned. - llvm_casted_value = ir_builder()->CreateZExt(llvm_int_value, - llvm_dest_type); - } - } - return llvm_casted_value; -} - -llvm::Value* GpCodegenUtils::CreatePalloc(Size size, - const char* file, - const char *func, - int line) { - llvm::Function* llvm_memory_context_alloc_impl = - GetOrRegisterExternalFunction(MemoryContextAllocImpl, - "MemoryContextAllocImpl"); - // Define llvm_memory_context_alloc_impl as a system memory allocation - // function that returns a pointer to allocated storage. - llvm_memory_context_alloc_impl->setDoesNotAlias(0 /* return value */); - llvm::Value* llvm_current_memory_context = - ir_builder()->CreateLoad(GetConstant(&CurrentMemoryContext)); - return ir_builder()->CreateCall(llvm_memory_context_alloc_impl, { - llvm_current_memory_context, - GetConstant(size), - GetConstant(file), - GetConstant(func), - GetConstant(line)}); -} - -} // namespace gpcodegen - -// EOF diff --git a/src/backend/codegen/utils/temporary_file.cc b/src/backend/codegen/utils/temporary_file.cc deleted file mode 100644 index 3bbe01924eecadcd0c0a4c29d5300d1313200aa0..0000000000000000000000000000000000000000 --- a/src/backend/codegen/utils/temporary_file.cc +++ /dev/null @@ -1,110 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright 2016 Pivotal Software, Inc. -// -// @filename: -// temporary_file.cc -// -// @doc: -// A lightweight interface to a temporary file created by POSIX mkstemp(). -// -// @test: -// -//--------------------------------------------------------------------------- - -#include "codegen/utils/temporary_file.h" - -#include -#include -#include -#include -#include -#include - -namespace gpcodegen { - -TemporaryFile::TemporaryFile(const char* prefix) - : filename_buffer_(nullptr), - fd_(-1) { - // Fill in '*filename_buffer_' with the prefix string followed by 6 'X' - // characters and a null-terminator. - const std::size_t prefix_len = std::strlen(prefix); - filename_buffer_ = static_cast(std::malloc(prefix_len + 7)); - std::memcpy(filename_buffer_, prefix, prefix_len); - std::memset(filename_buffer_ + prefix_len, 'X', 6); - filename_buffer_[prefix_len + 6] = '\0'; -} - -TemporaryFile::~TemporaryFile() { - if (IsOpen()) { - close(fd_); - } - std::free(filename_buffer_); -} - -const char* TemporaryFile::TemporaryDirectoryPath() { - // Lookup sequence: - // 1. TMPDIR environment variable. - // 2. P_tmpdir macro. - // 3. "/tmp" if all else fails. - const char* tmpdir_env = std::getenv("TMPDIR"); - if (tmpdir_env != nullptr) { - return tmpdir_env; - } - -#ifdef P_tmpdir - if (P_tmpdir != nullptr) { - return P_tmpdir; - } -#endif - - static constexpr char kDefaultTemporaryDirectoryPath[] = "/tmp"; - return kDefaultTemporaryDirectoryPath; -} - -bool TemporaryFile::Open() { - if (fd_ != -1) { - // Already open. - return true; - } - - fd_ = mkstemp(filename_buffer_); - return (fd_ != -1); -} - -bool TemporaryFile::Write(const void* buffer, - const std::size_t buffer_size) { - if (!IsOpen()) { - return false; - } - - std::size_t bytes_written = 0; - while (bytes_written < buffer_size) { - const ssize_t write_result = write( - fd_, - static_cast(buffer) + bytes_written, - buffer_size - bytes_written); - if (write_result < 0) { - if (errno == EINTR) { - // Write was interrupted by a signal, try again. - continue; - } else { - return false; - } - } else { - bytes_written += write_result; - } - } - - return true; -} - -bool TemporaryFile::Flush() { - if (!IsOpen()) { - return false; - } - - return fsync(fd_) == 0; -} - -} // namespace gpcodegen diff --git a/src/backend/codegen/var_expr_tree_generator.cc b/src/backend/codegen/var_expr_tree_generator.cc deleted file mode 100644 index d9b1afdffa9f60c5de0f7c63b12585ee1fc985d7..0000000000000000000000000000000000000000 --- a/src/backend/codegen/var_expr_tree_generator.cc +++ /dev/null @@ -1,105 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// var_expr_tree_generator.cc -// -// @doc: -// Object that generator code for variable expression. -// -//--------------------------------------------------------------------------- -#include -#include -#include -#include - -#include "codegen/expr_tree_generator.h" -#include "codegen/utils/gp_codegen_utils.h" -#include "codegen/var_expr_tree_generator.h" - -#include "llvm/IR/Constant.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" - -extern "C" { -#include "postgres.h" // NOLINT(build/include) -#include "nodes/execnodes.h" -#include "executor/tuptable.h" -#include "nodes/nodes.h" -#include "nodes/primnodes.h" -} - -namespace llvm { -class Value; -} // namespace llvm - -using gpcodegen::VarExprTreeGenerator; -using gpcodegen::ExprTreeGenerator; -using gpcodegen::GpCodegenUtils; - -bool VarExprTreeGenerator::VerifyAndCreateExprTree( - const ExprState* expr_state, - ExprTreeGeneratorInfo* gen_info, - std::unique_ptr* expr_tree) { - assert(nullptr != expr_state && - nullptr != expr_state->expr && - T_Var == nodeTag(expr_state->expr) && - nullptr != expr_tree && - nullptr != gen_info); - expr_tree->reset(new VarExprTreeGenerator(expr_state)); - gen_info->max_attr = std::max(gen_info->max_attr, - reinterpret_cast(expr_state->expr)->varattno); - return true; -} - -VarExprTreeGenerator::VarExprTreeGenerator(const ExprState* expr_state) : - ExprTreeGenerator(expr_state, ExprTreeNodeType::kVar) { -} - -bool VarExprTreeGenerator::GenerateCode(GpCodegenUtils* codegen_utils, - const ExprTreeGeneratorInfo& gen_info, - llvm::Value** llvm_out_value, - llvm::Value* const llvm_isnull_ptr) { - assert(nullptr != llvm_out_value); - assert(nullptr != llvm_isnull_ptr); - *llvm_out_value = nullptr; - Var* var_expr = reinterpret_cast(expr_state()->expr); - int attnum = var_expr->varattno; - auto irb = codegen_utils->ir_builder(); - - // slot = econtext->ecxt_scantuple; {{{ - // At code generation time, slot is NULL. - // For that reason, we keep a double pointer to slot and at execution time - // we load slot. - TupleTableSlot **ptr_to_slot_ptr = nullptr; - switch (var_expr->varno) { - case INNER: /* get the tuple from the inner node */ - ptr_to_slot_ptr = &gen_info.econtext->ecxt_innertuple; - break; - - case OUTER: /* get the tuple from the outer node */ - ptr_to_slot_ptr = &gen_info.econtext->ecxt_outertuple; - break; - - default: /* get the tuple from the relation being scanned */ - ptr_to_slot_ptr = &gen_info.econtext->ecxt_scantuple; - break; - } - - llvm::Value *llvm_slot = irb->CreateLoad( - codegen_utils->GetConstant(ptr_to_slot_ptr)); - //}}} - - llvm::Value *llvm_variable_varattno = codegen_utils-> - GetConstant(attnum); - - assert(nullptr != gen_info.llvm_slot_getattr_func); - // retrieve variable - *llvm_out_value = irb->CreateCall( - gen_info.llvm_slot_getattr_func, { - llvm_slot, - llvm_variable_varattno, - llvm_isnull_ptr}); - return true; -} diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e4b8002220b2ad9339847aa93437e3a64b1a5116..4fe4ef0e7cdbf7b522c5df02f49545eccdc741a6 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -85,9 +85,6 @@ static void ExplainDXL(Query *query, ExplainStmt *stmt, const char *queryString, ParamListInfo params, TupOutputState *tstate); #endif -#ifdef USE_CODEGEN -static void ExplainCodegen(PlanState *planstate, TupOutputState *tstate); -#endif static double elapsed_time(instr_time *starttime); static void explain_outNode(StringInfo str, Plan *plan, PlanState *planstate, @@ -120,6 +117,7 @@ static void explain_partition_selector(PartitionSelector *ps, Plan *parent, StringInfo str, int indent, ExplainState *es); + /* * ExplainQuery - * execute an EXPLAIN command @@ -317,32 +315,6 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt, "Utility statements have no plan structure"); } -#ifdef USE_CODEGEN -/* - * ExplainCodegen - - * given a PlanState tree, traverse its nodes, collect any accumulated - * explain strings from the state's CodegenManager, and print to EXPLAIN - * output - * NB: This method does not recurse into sub plans at this point. - */ -static void -ExplainCodegen(PlanState *planstate, TupOutputState *tstate) { - if (NULL == planstate) { - return; - } - - Assert(NULL != tstate); - - ExplainCodegen(planstate->lefttree, tstate); - - char* str = CodeGeneratorManagerGetExplainString(planstate->CodegenManager); - Assert(NULL != str); - do_text_output_oneline(tstate, str); - - ExplainCodegen(planstate->righttree, tstate); -} -#endif - /* * ExplainOnePlan - * given a planned query, execute it if needed, and then print @@ -416,25 +388,13 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainStmt *stmt, else eflags = EXEC_FLAG_EXPLAIN_ONLY; - queryDesc->plannedstmt->query_mem = ResourceManagerGetQueryMemoryLimit(queryDesc->plannedstmt); -#ifdef USE_CODEGEN - if (stmt->codegen && codegen && Gp_segment == -1) - { - eflags |= EXEC_FLAG_EXPLAIN_CODEGEN; - } -#endif + queryDesc->plannedstmt->query_mem = ResourceManagerGetQueryMemoryLimit( + queryDesc->plannedstmt); /* call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, eflags); -#ifdef USE_CODEGEN - if (stmt->codegen && codegen && Gp_segment == -1) - { - ExplainCodegen(queryDesc->planstate, tstate); - } -#endif - /* Execute the plan for statistics if asked for */ if (stmt->analyze) { diff --git a/src/backend/executor/execHHashagg.c b/src/backend/executor/execHHashagg.c index 3147fa3f2a86c2e7f25b4c271d6be32c28331b1c..48fe706993ecb2e2aaa5170a2f13ad158bd94f85 100644 --- a/src/backend/executor/execHHashagg.c +++ b/src/backend/executor/execHHashagg.c @@ -938,7 +938,7 @@ agg_hash_initial_pass(AggState *aggstate) } /* Advance the aggregates */ - call_AdvanceAggregates(aggstate, hashtable->groupaggs->aggs, &(aggstate->mem_manager)); + advance_aggregates(aggstate, hashtable->groupaggs->aggs, &(aggstate->mem_manager)); hashtable->num_tuples++; diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index a3b2e70e177231a02f0d1efca5a89260d867836b..3e5c48b41a6f69760c2aa12ec01992eff0ac04c4 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -132,8 +132,6 @@ #include "pg_trace.h" #include "tcop/tcopprot.h" -#include "codegen/codegen_wrapper.h" - /* flags bits for planstate walker */ #define PSW_IGNORE_INITPLAN 0x01 @@ -156,12 +154,6 @@ static CdbVisitOpt planstate_walk_kids(PlanState *planstate, void *context, int flags); -static void - EnrollQualList(PlanState *result); - -static void - EnrollProjInfoTargetList(PlanState *result, ProjectionInfo *ProjInfo); - /* * setSubplanSliceId * Set the slice id info for the given subplan. @@ -224,13 +216,6 @@ ExecInitNode(Plan *node, EState *estate, int eflags) MemoryAccountIdType curMemoryAccountId = MEMORY_OWNER_TYPE_Undefined; - StringInfo codegenManagerName = makeStringInfo(); - - appendStringInfo(codegenManagerName, "%s-%d-%d", "execProcnode", node->plan_node_id, node->type); - void *CodegenManager = CodeGeneratorManagerCreate(codegenManagerName->data); - - START_CODE_GENERATOR_MANAGER(CodegenManager); - { int localMotionId = LocallyExecutingSliceIndex(estate); @@ -367,34 +352,6 @@ ExecInitNode(Plan *node, EState *estate, int eflags) { result = (PlanState *) ExecInitTableScan((TableScan *) node, estate, eflags); - - /* - * Enroll ExecVariableList in codegen_manager - */ - if (NULL != result) - { - ScanState *scanState = (ScanState *) result; - ProjectionInfo *projInfo = result->ps_ProjInfo; - if (NULL != scanState && - scanState->tableType == TableTypeHeap && - NULL != projInfo && - projInfo->pi_directMap && - NULL != projInfo->pi_targetlist) - { - enroll_ExecVariableList_codegen(ExecVariableList, - &projInfo->ExecVariableList_gen_info.ExecVariableList_fn, projInfo, scanState->ss_ScanTupleSlot); - } - } - - /* - * Enroll targetlist & quals' expression evaluation functions - * in codegen_manager - */ - EnrollQualList(result); - if (NULL !=result) - { - EnrollProjInfoTargetList(result, result->ps_ProjInfo); - } } END_MEMORY_ACCOUNT(); break; @@ -656,22 +613,6 @@ ExecInitNode(Plan *node, EState *estate, int eflags) { result = (PlanState *) ExecInitAgg((Agg *) node, estate, eflags); - /* - * Enroll targetlist & quals' expression evaluation functions - * in codegen_manager - */ - EnrollQualList(result); - if (NULL != result) - { - AggState* aggstate = (AggState*)result; - for (int aggno = 0; aggno < aggstate->numaggs; aggno++) - { - AggStatePerAgg peraggstate = &aggstate->peragg[aggno]; - EnrollProjInfoTargetList(result, peraggstate->evalproj); - } - enroll_AdvanceAggregates_codegen(advance_aggregates, - &aggstate->AdvanceAggregates_gen_info.AdvanceAggregates_fn, - aggstate); } } END_MEMORY_ACCOUNT(); break; @@ -844,121 +785,10 @@ ExecInitNode(Plan *node, EState *estate, int eflags) if (result != NULL) { SAVE_EXECUTOR_MEMORY_ACCOUNT(result, curMemoryAccountId); - result->CodegenManager = CodegenManager; - /* - * Generate code only if current node is not alien or - * if it is from 'explain codegen` / `explain analyze codegen` query - */ - bool isExplainCodegenOnMaster = (Gp_segment == -1) && - (eflags & EXEC_FLAG_EXPLAIN_CODEGEN) && - (eflags & EXEC_FLAG_EXPLAIN_ONLY); - - bool isExplainAnalyzeCodegenOnMaster = (Gp_segment == -1) && - (eflags & EXEC_FLAG_EXPLAIN_CODEGEN) && - !(eflags & EXEC_FLAG_EXPLAIN_ONLY); - - if (!isAlienPlanNode || - isExplainAnalyzeCodegenOnMaster || - isExplainCodegenOnMaster) - { - (void) CodeGeneratorManagerGenerateCode(CodegenManager); - if (isExplainAnalyzeCodegenOnMaster || - isExplainCodegenOnMaster) - { - CodeGeneratorManagerAccumulateExplainString(CodegenManager); - } - if (!isExplainCodegenOnMaster) - { - (void) CodeGeneratorManagerPrepareGeneratedFunctions(CodegenManager); - } - } } - } - END_CODE_GENERATOR_MANAGER(); - return result; } -/* ---------------------------------------------------------------- - * EnrollQualList - * - * Enroll Qual List's expr state from PlanState for codegen. - * ---------------------------------------------------------------- - */ -void -EnrollQualList(PlanState *result) -{ -#ifdef USE_CODEGEN - if (NULL == result || - NULL == result->qual) - { - return; - } - - ListCell *l; - - foreach(l, result->qual) - { - ExprState *exprstate = (ExprState *) lfirst(l); - - enroll_ExecEvalExpr_codegen(exprstate->evalfunc, - &exprstate->evalfunc, - exprstate, - result->ps_ExprContext, - result - ); - } - -#endif -} - -/* ---------------------------------------------------------------- - * EnrollProjInfoTargetList - * - * Enroll Targetlist from ProjectionInfo to Codegen - * ---------------------------------------------------------------- - */ -void -EnrollProjInfoTargetList(PlanState *result, ProjectionInfo *ProjInfo) -{ -#ifdef USE_CODEGEN - if (NULL == ProjInfo || - NULL == ProjInfo->pi_targetlist) - { - return; - } - if (ProjInfo->pi_isVarList) - { - /* - * Skip generating expression evaluation for VAR elements in the - * target list since ExecVariableList will take of that - * TODO(shardikar) Re-evaluate this condition once we codegen - * ExecTargetList - */ - return; - } - - ListCell *l; - - foreach(l, ProjInfo->pi_targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(l); - - if (NULL == gstate->arg || - NULL == gstate->arg->evalfunc) - { - continue; - } - enroll_ExecEvalExpr_codegen(gstate->arg->evalfunc, - &gstate->arg->evalfunc, - gstate->arg, - ProjInfo->pi_exprContext, - result); - } -#endif -} - - /* ---------------------------------------------------------------- * ExecSliceDependencyNode * @@ -1013,8 +843,6 @@ ExecProcNode(PlanState *node) { TupleTableSlot *result = NULL; - START_CODE_GENERATOR_MANAGER(node->CodegenManager); - { START_MEMORY_ACCOUNT(node->plan->memoryAccountId); { @@ -1257,8 +1085,6 @@ ExecProcNode(PlanState *node) } END_MEMORY_ACCOUNT(); - } - END_CODE_GENERATOR_MANAGER(); return result; } @@ -1837,16 +1663,6 @@ ExecEndNode(PlanState *node) break; } - if (codegen) - { - /* - * if codegen guc is true, then assert if CodegenManager is NULL - */ - Assert(NULL != node->CodegenManager); - CodeGeneratorManagerDestroy(node->CodegenManager); - node->CodegenManager = NULL; - } - estate->currentSliceIdInPlan = origSliceIdInPlan; estate->currentExecutingSliceId = origExecutingSliceId; } diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 4f44754f21cca38df9b87c672908edeecc59c55b..a8573b7be5f7dbe8e7dccee9b20b8dddc2ec90aa 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -6176,10 +6176,7 @@ ExecCleanTargetListLength(List *targetlist) * of *isDone = ExprEndResult signifies end of the set of tuple. * We assume that *isDone has been initialized to ExprSingleResult by caller. */ -#ifndef USE_CODEGEN -static -#endif -bool +static bool ExecTargetList(List *targetlist, ExprContext *econtext, Datum *values, diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 1d1bdf564f121a882cf72f08db1c72df8fea71c7..1049be58b8bfb13a0528f3274c184cb0bec71356 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -15,7 +15,6 @@ *------------------------------------------------------------------------- */ #include "postgres.h" -#include "codegen/codegen_wrapper.h" #include "executor/executor.h" #include "miscadmin.h" diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index d2bafc385f26664838c83cebe6c692bb92696056..cb22be8db7a6f9300d4a653943cb121e201b8a0d 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -558,9 +558,6 @@ ExecGetResultType(PlanState *planstate) return slot->tts_tupleDescriptor; } -extern void -ExecVariableList(ProjectionInfo *projInfo, Datum *values, bool *isnull); - /* ---------------- * ExecBuildProjectionInfo * diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index bf0855866d6dd29af0f33a068989feabf58bb200..50aa666ac4d1a1bc8da7688a56286830023ee5b7 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1316,7 +1316,7 @@ agg_retrieve_direct(AggState *aggstate) if (!aggstate->has_partial_agg) { has_partial_agg = true; - call_AdvanceAggregates(aggstate, pergroup, &(aggstate->mem_manager)); + advance_aggregates(aggstate, pergroup, &(aggstate->mem_manager)); } /* Reset per-input-tuple context after each tuple */ @@ -1390,7 +1390,7 @@ agg_retrieve_direct(AggState *aggstate) { has_partial_agg = true; tmpcontext->ecxt_outertuple = outerslot; - call_AdvanceAggregates(aggstate, pergroup, &(aggstate->mem_manager)); + advance_aggregates(aggstate, pergroup, &(aggstate->mem_manager)); } passthru_ready = true; @@ -1434,7 +1434,7 @@ agg_retrieve_direct(AggState *aggstate) ResetExprContext(tmpcontext); tmpcontext->ecxt_outertuple = outerslot; - call_AdvanceAggregates(aggstate, perpassthru, &(aggstate->mem_manager)); + advance_aggregates(aggstate, perpassthru, &(aggstate->mem_manager)); } /* diff --git a/src/backend/mock.mk b/src/backend/mock.mk index a826ade65c9e48c4737542cd1da8904b5f7be48c..1706f3cc45aa9d1e3d3c952f5db18c77390da2a7 100644 --- a/src/backend/mock.mk +++ b/src/backend/mock.mk @@ -104,12 +104,6 @@ ifeq ($(enable_orca),yes) MOCK_OBJS+=$(top_srcdir)/src/test/unit/mock/gpopt_mock.o endif -# No test programs in GPDB currently exercise codegen, so -# mock that instead of linking with the real library. -ifeq ($(enable_codegen),yes) -MOCK_OBJS+=$(top_srcdir)/src/test/unit/mock/gpcodegen_mock.o -endif - # $(OBJFILES) contains %/objfiles.txt, because src/backend/Makefile will # create it with rule=objfiles.txt, which is not expected in postgres rule. # It actually uses expand_subsys to obtain the .o file list. But here we diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e7876b52556cb88e67d66cc7232e1edf8092300b..a4a657d14ce15542b9de5f56f9c998f7eb013c0a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -373,7 +373,6 @@ static Node *makeIsNotDistinctFromNode(Node *expr, int position); %type opt_freeze opt_default opt_ordered opt_recheck %type opt_rootonly_all %type opt_dxl -%type codegen %type opt_binary opt_oids copy_delimiter %type copy_from opt_program skip_external_partition @@ -611,7 +610,7 @@ static Node *makeIsNotDistinctFromNode(Node *expr, int position); %token ACTIVE - CODEGEN CONTAINS CPU_RATE_LIMIT CREATEEXTTABLE CUBE + CONTAINS CPU_RATE_LIMIT CREATEEXTTABLE CUBE DECODE DENY DISTRIBUTED DXL @@ -2647,7 +2646,7 @@ reloption_list: ; /* This should match def_elem and also allow qualified names */ -reloption_elem: +reloption_elem: ColLabel '=' def_arg { $$ = makeDefElem($1, (Node *) $3); @@ -4043,7 +4042,7 @@ tab_part_val: tab_part_val_no_paran { $$ = $1; } $$ = makeTypeCast($2, $5, @4); } ; - + TabPartitionBoundarySpecValList: tab_part_val { $$ = list_make1($1); } | TabPartitionBoundarySpecValList ',' @@ -4058,7 +4057,7 @@ OptTabPartitionRangeInclusive: TabPartitionBoundarySpecStart: START - '(' TabPartitionBoundarySpecValList ')' + '(' TabPartitionBoundarySpecValList ')' OptTabPartitionRangeInclusive { PartitionRangeItem *n = makeNode(PartitionRangeItem); @@ -4074,7 +4073,7 @@ TabPartitionBoundarySpecStart: TabPartitionBoundarySpecEnd: END_P - '(' TabPartitionBoundarySpecValList ')' + '(' TabPartitionBoundarySpecValList ')' OptTabPartitionRangeInclusive { PartitionRangeItem *n = makeNode(PartitionRangeItem); @@ -4346,7 +4345,7 @@ TabPartitionByType: OptTabPartitionBy: PARTITION BY - TabPartitionByType '(' columnList ')' + TabPartitionByType '(' columnList ')' opt_list_subparts OptTabPartitionSpec { @@ -4441,11 +4440,11 @@ list_subparts: TabSubPartitionBy { $$ = $1; } TabSubPartitionBy: SUBPARTITION BY - TabPartitionByType '(' columnList ')' + TabPartitionByType '(' columnList ')' { PartitionBy *n = makeNode(PartitionBy); n->partType = $3; - n->keys = $5; + n->keys = $5; n->subPart = NULL; n->partSpec = NULL; n->partDepth = 0; @@ -9216,7 +9215,7 @@ opt_name_list: * *****************************************************************************/ -ExplainStmt: EXPLAIN opt_analyze opt_verbose opt_dxl opt_force codegen ExplainableStmt +ExplainStmt: EXPLAIN opt_analyze opt_verbose opt_dxl opt_force ExplainableStmt { ExplainStmt *n = makeNode(ExplainStmt); n->analyze = $2; @@ -9226,8 +9225,7 @@ ExplainStmt: EXPLAIN opt_analyze opt_verbose opt_dxl opt_force codegen Explainab ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot use force with explain statement") )); - n->codegen = $6; - n->query = $7; + n->query = $6; $$ = (Node *)n; } ; @@ -9258,11 +9256,6 @@ opt_analyze: | /* EMPTY */ { $$ = FALSE; } ; -codegen: - CODEGEN { $$ = TRUE; } - | /* EMPTY */ { $$ = FALSE; } - ; - /***************************************************************************** * * QUERY: @@ -13442,7 +13435,6 @@ unreserved_keyword: | CLASS | CLOSE | CLUSTER - | CODEGEN | COMMENT | COMMIT | COMMITTED diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index ccd681ace13cc4290188dfe904efb4464606c454..b507e98be19c259b42b323319283d5457912d133 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -3076,8 +3076,21 @@ int8_invsum(PG_FUNCTION_ARGS) NumericGetDatum(oldsum), newval)); } -inline -Datum intfloat_avg_accum_decum(IntFloatAvgTransdata *transdata, float8 newval, bool acc) +/* + * Routines for avg int type. The transition datatype is a int64 for count, and a float8 for sum. + */ + +typedef struct IntFloatAvgTransdata +{ + int32 _len; /* len for varattrib, do not touch directly */ +#if 1 + int32 pad; /* pad so int64 and float64 will be 8 bytes aligned */ +#endif + int64 count; + float8 sum; +} IntFloatAvgTransdata; + +static inline Datum intfloat_avg_accum_decum(IntFloatAvgTransdata *transdata, float8 newval, bool acc) { if(transdata == NULL || VARSIZE(transdata) != sizeof(IntFloatAvgTransdata)) { diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index a685c0c5830607ef76ca2fff420a4068b10959c3..a062ee7512880f25f34b58dd3df9cc45c6a5ebcd 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -65,7 +65,6 @@ #include "utils/tqual.h" #include "utils/session_state.h" -#include "codegen/codegen_wrapper.h" static HeapTuple GetDatabaseTuple(const char *dbname); @@ -577,9 +576,6 @@ BaseInit(void) InitFileAccess(); smgrinit(); InitBufferPoolAccess(); - - /* Initialize llvm library if USE_CODEGEN is defined */ - init_codegen(); } /* diff --git a/src/backend/utils/misc/guc_gp.c b/src/backend/utils/misc/guc_gp.c index aaef2995931dcf5eaf8c521d0d09b0c1f1ff54ae..8eb3c8bc11c59e9ea67708b96dda71bc637b1c7d 100644 --- a/src/backend/utils/misc/guc_gp.c +++ b/src/backend/utils/misc/guc_gp.c @@ -73,9 +73,6 @@ static const char *assign_gp_workfile_compress_algorithm(const char *newval, boo static const char *assign_optimizer_minidump(const char *newval, bool doit, GucSource source); static bool assign_optimizer(bool newval, bool doit, GucSource source); -static bool assign_codegen(bool newval, bool doit, GucSource source); -static bool assign_codegen_optimization_level(int newval, bool doit, - GucSource source); static bool assign_dispatch_log_stats(bool newval, bool doit, GucSource source); static bool assign_gp_hashagg_default_nbatches(int newval, bool doit, GucSource source); @@ -460,19 +457,6 @@ bool optimizer_enable_space_pruning; bool optimizer_analyze_root_partition; bool optimizer_analyze_midlevel_partition; -/** - * GUCs related to code generation. - **/ -bool init_codegen; -bool codegen; -bool codegen_validate_functions; -bool codegen_exec_variable_list; -bool codegen_slot_getattr; -bool codegen_exec_eval_expr; -bool codegen_advance_aggregate; -int codegen_varlen_tolerance; -int codegen_optimization_level; - /* System Information */ static int gp_server_version_num; static char *gp_server_version_string; @@ -524,14 +508,6 @@ static const struct config_enum_entry optimizer_log_failure_options[] = { {NULL, 0} }; -static const struct config_enum_entry codegen_optimization_level_options[] = { - {"none", CODEGEN_OPTIMIZATION_LEVEL_NONE}, - {"less", CODEGEN_OPTIMIZATION_LEVEL_LESS}, - {"default", CODEGEN_OPTIMIZATION_LEVEL_DEFAULT}, - {"aggressive", CODEGEN_OPTIMIZATION_LEVEL_AGGRESSIVE}, - {NULL, 0} -}; - static const struct config_enum_entry optimizer_cost_model_options[] = { {"legacy", OPTIMIZER_GPDB_LEGACY}, {"calibrated", OPTIMIZER_GPDB_CALIBRATED}, @@ -2785,102 +2761,6 @@ struct config_bool ConfigureNamesBool_gp[] = false, NULL, NULL }, - { - {"init_codegen", PGC_POSTMASTER, DEVELOPER_OPTIONS, - gettext_noop("Enable just-in-time code generation."), - NULL, - GUC_NOT_IN_SAMPLE - }, - &init_codegen, -#ifdef USE_CODEGEN - true, -#else - false, -#endif - assign_codegen, NULL - }, - - { - {"codegen", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Perform just-in-time code generation."), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen, - false, - assign_codegen, NULL - }, - - { - {"codegen_validate_functions", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Perform verify for generated functions to catch any error before compiling"), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE - }, - &codegen_validate_functions, -#if defined(USE_ASSERT_CHECKING) && defined(USE_CODEGEN) - true, /* true by default on debug builds. */ -#else - false, -#endif - assign_codegen, NULL - }, - { - {"codegen_exec_variable_list", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Enable codegen for ExecVariableList"), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen_exec_variable_list, -#ifdef USE_CODEGEN - true, -#else - false, -#endif - assign_codegen, NULL - }, - { - {"codegen_slot_getattr", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Enable codegen for slot_get_attr"), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen_slot_getattr, -#ifdef USE_CODEGEN - true, -#else - false, -#endif - assign_codegen, NULL - }, - { - {"codegen_exec_eval_expr", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Enable codegen for ExecEvalExpr"), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen_exec_eval_expr, -#ifdef USE_CODEGEN - true, -#else - false, -#endif - assign_codegen, NULL - }, - { - {"codegen_advance_aggregate", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Enable codegen for AdvanceAggregate"), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen_advance_aggregate, -#ifdef USE_CODEGEN - true, -#else - false, -#endif - assign_codegen, NULL - }, { {"vmem_process_interrupt", PGC_USERSET, DEVELOPER_OPTIONS, gettext_noop("Checks for interrupts before reserving VMEM"), @@ -4109,21 +3989,6 @@ struct config_int ConfigureNamesInt_gp[] = INDEX_CHECK_NONE, 0, INDEX_CHECK_ALL, NULL, NULL }, - { - {"codegen_varlen_tolerance", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Minimum number of initial fixed length attributes in the table to generate code for deforming tuples."), - NULL, - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen_varlen_tolerance, -#ifdef USE_CODEGEN - 5, -#else - 0, -#endif - 0, INT_MAX, NULL, NULL - }, - { {"dtx_phase2_retry_count", PGC_SUSET, DEVELOPER_OPTIONS, gettext_noop("Maximum number of retries during two phase commit after which master PANICs."), @@ -4805,21 +4670,6 @@ struct config_enum ConfigureNamesEnum_gp[] = PASSWORD_HASH_MD5, password_hash_algorithm_options, NULL, NULL }, - { - {"codegen_optimization_level", PGC_USERSET, DEVELOPER_OPTIONS, - gettext_noop("Sets optimizer level to use when compiling generated code."), - gettext_noop("Valid values are none, less, default, aggressive."), - GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_GPDB_ADDOPT - }, - &codegen_optimization_level, -#ifdef USE_CODEGEN - CODEGEN_OPTIMIZATION_LEVEL_DEFAULT, -#else - CODEGEN_OPTIMIZATION_LEVEL_NONE, -#endif - codegen_optimization_level_options, assign_codegen_optimization_level, NULL - }, - { {"optimizer_cost_model", PGC_USERSET, DEVELOPER_OPTIONS, gettext_noop("Set optimizer cost model."), @@ -4914,21 +4764,6 @@ assign_pljava_classpath_insecure(bool newval, bool doit, GucSource source) return true; } -static bool -assign_codegen_optimization_level(int val, bool assign, GucSource source) { -#ifndef USE_CODEGEN - if (val != CODEGEN_OPTIMIZATION_LEVEL_NONE) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Code generation is not supported by this build"))); - return false; - } -#endif - - return true; -} - static const char * assign_optimizer_minidump(const char *val, bool assign, GucSource source) { @@ -4989,19 +4824,6 @@ assign_optimizer(bool newval, bool doit, GucSource source) return true; } -static bool -assign_codegen(bool newval, bool doit, GucSource source) -{ -#ifndef USE_CODEGEN - if (newval) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Code generation is not supported by this build"))); -#endif - - return true; -} - static bool assign_dispatch_log_stats(bool newval, bool doit, GucSource source) { diff --git a/src/include/Makefile b/src/include/Makefile index 20a8674f913230db880bc6491f40ddd87c2d28cf..e4a94003298031a68f94dea9923c03a09fd5b7d3 100644 --- a/src/include/Makefile +++ b/src/include/Makefile @@ -17,7 +17,7 @@ all: pg_config.h pg_config_os.h check_oids # Subdirectories containing headers for server-side dev -SUBDIRS = access bootstrap catalog cdb codegen commands executor gpmon lib libpq \ +SUBDIRS = access bootstrap catalog cdb commands executor gpmon lib libpq \ mb nodes optimizer parser postmaster regex replication rewrite storage tcop \ snowball snowball/libstemmer tsearch tsearch/dicts utils \ port port/atomics port/win32 port/win32_msvc port/win32_msvc/sys \ diff --git a/src/include/codegen/codegen_wrapper.h b/src/include/codegen/codegen_wrapper.h deleted file mode 100644 index 7254af2880dc78b1f0e11ac5a5e1ef6476bc62ed..0000000000000000000000000000000000000000 --- a/src/include/codegen/codegen_wrapper.h +++ /dev/null @@ -1,294 +0,0 @@ -//--------------------------------------------------------------------------- -// Greenplum Database -// Copyright (C) 2016 Pivotal Software, Inc. -// -// @filename: -// codegen_wrapper.h -// -// @doc: -// C wrappers for initialization of code generation. -// -//--------------------------------------------------------------------------- -#ifndef CODEGEN_WRAPPER_H_ -#define CODEGEN_WRAPPER_H_ - -#include - -#include "pg_config.h" -#include "c.h" - -#ifndef __cplusplus -#include "postgres.h" -#else -typedef int64 Datum; -#endif - -/* - * Code that needs to be shared irrespective of whether USE_CODEGEN is enabled or not. - */ -struct TupleTableSlot; -struct ProjectionInfo; -struct ExprContext; -struct ExprState; -struct PlanState; -struct AggState; -struct MemoryManagerContainer; -struct AggStatePerGroupData; -/* - * Enum used to mimic ExprDoneCond in ExecEvalExpr function pointer. - */ -typedef enum tmp_enum{ - TmpResult -}tmp_enum; - -typedef void (*AdvanceAggregatesFn) (struct AggState *aggstate, /*struct AggStatePerGroup*/struct AggStatePerGroupData *pergroup, struct MemoryManagerContainer *mem_manager); -typedef void (*ExecVariableListFn) (struct ProjectionInfo *projInfo, Datum *values, bool *isnull); -typedef Datum (*ExecEvalExprFn) (struct ExprState *expression, struct ExprContext *econtext, bool *isNull, /*ExprDoneCond*/ tmp_enum *isDone); -typedef Datum (*SlotGetAttrFn) (struct TupleTableSlot *slot, int attnum, bool *isnull); - -#ifndef USE_CODEGEN - -#define InitCodegen() ((void) 1) -#define CodeGeneratorManagerCreate(module_name) ((void *) NULL) -#define CodeGeneratorManagerGenerateCode(manager) ((unsigned int) 1) -#define CodeGeneratorManagerPrepareGeneratedFunctions(manager) ((unsigned int) 1) -#define CodeGeneratorManagerNotifyParameterChange(manager) ((unsigned int) 1) -#define CodeGeneratorManagerAccumulateExplainString(manager) ((void) 1) -#define CodeGeneratorManagerGetExplainString(manager) ((char *) NULL) -#define CodeGeneratorManagerDestroy(manager) ((void) 1) -#define GetActiveCodeGeneratorManager() ((void *) NULL) -#define SetActiveCodeGeneratorManager(manager) ((void) 1) - -#define START_CODE_GENERATOR_MANAGER(newManager) -#define END_CODE_GENERATOR_MANAGER() - -#define init_codegen() -#define call_ExecVariableList(projInfo, values, isnull) ExecVariableList(projInfo, values, isnull) -#define enroll_ExecVariableList_codegen(regular_func, ptr_to_chosen_func, proj_info, slot) -#define call_AdvanceAggregates(aggstate, pergroup, mem_manager) advance_aggregates(aggstate, pergroup, mem_manager) -#define enroll_AdvanceAggregates_codegen(regular_func, ptr_to_chosen_func, aggstate) -#else - -/* - * @brief Life span of Code generator instance - * - * @note Each code generator is responsible to generate code for one specific function. - * Each generated function has a life span to indicate to the manager about when to - * invalidate and regenerate this function. The enroller is responsible to know - * how long a generated code should be valid. - * - * - */ -typedef enum CodegenFuncLifespan -{ - // Does not depend on parameter changes - CodegenFuncLifespan_Parameter_Invariant, - // Has to be regenerated as the parameter changes - CodegenFuncLifespan_Parameter_Variant -} CodegenFuncLifespan; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Forward extern declaration of code generated functions if code gen is enabled - */ -extern void ExecVariableList(struct ProjectionInfo *projInfo, Datum *values, bool *isnull); - - -/* - * Do one-time global initialization of LLVM library. Returns 1 - * on success, 0 on error. - */ -unsigned int -InitCodegen(); - -/* - * Creates a manager for an operator - */ -void* -CodeGeneratorManagerCreate(const char* module_name); - -/* - * Calls all the registered CodegenInterface to generate code - */ -unsigned int -CodeGeneratorManagerGenerateCode(void* manager); - -/* - * Compiles and prepares all the Codegen function pointers. Returns - * number of successfully generated functions - */ -unsigned int -CodeGeneratorManagerPrepareGeneratedFunctions(void* manager); - -/* - * Notifies a manager that the underlying operator has a parameter change - */ -unsigned int -CodeGeneratorManagerNotifyParameterChange(void* manager); - -/* - * Destroys a manager for an operator - */ -void -CodeGeneratorManagerDestroy(void* manager); - -/* - * Accumulate the explain string with a dump of all the underlying LLVM modules - */ -void -CodeGeneratorManagerAccumulateExplainString(void* manager); - -/* - * Return a copy in CurrentMemoryContext of the previously accumulated explain - * string - */ -char* -CodeGeneratorManagerGetExplainString(void* manager); - -/* - * Get the active code generator manager - */ -void* -GetActiveCodeGeneratorManager(); - -/* - * Set the active code generator manager - */ -void -SetActiveCodeGeneratorManager(void* manager); - -/* - * Wrapper function for slot_getattr. - */ -Datum -slot_getattr_regular(struct TupleTableSlot *slot, int attnum, bool *isnull); - -/* - * Wrapper function for att_align_nominal. - */ -int -att_align_nominal_regular(int cur_offset, char attalign); - -/* - * Wrapper function for SET_VARSIZE. - */ -void -SET_VARSIZE_regular(void* ptr, size_t len); - -/* - * Wrapper function for VARSIZE. - */ -uint32 -VARSIZE_regular(void* ptr); - -/* - * returns the pointer to the ExecVariableList - */ -void* -ExecVariableListCodegenEnroll(ExecVariableListFn regular_func_ptr, - ExecVariableListFn* ptr_to_regular_func_ptr, - struct ProjectionInfo* proj_info, - struct TupleTableSlot* slot); - -/* - * Enroll and returns the pointer to ExecEvalExprGenerator - */ -void* -ExecEvalExprCodegenEnroll(ExecEvalExprFn regular_func_ptr, - ExecEvalExprFn* ptr_to_regular_func_ptr, - struct ExprState *exprstate, - struct ExprContext *econtext, - struct PlanState* plan_state); - -/* - * Enroll and returns the pointer to AdvanceAggregateGenerator - */ -void* -AdvanceAggregatesCodegenEnroll(AdvanceAggregatesFn regular_func_ptr, - AdvanceAggregatesFn* ptr_to_regular_func_ptr, - struct AggState *aggstate); - -#ifdef __cplusplus -} // extern "C" -#endif - -/* - * START_CODE_GENERATOR_MANAGER would switch to the specified code generator manager, - * saving the oldCodeGeneratorManager. Must be paired with END_CODE_GENERATOR_MANAGER - */ -#define START_CODE_GENERATOR_MANAGER(newManager) \ - do { \ - void *oldManager = NULL; \ - if (codegen) { \ - Assert(newManager != NULL); \ - oldManager = GetActiveCodeGeneratorManager(); \ - SetActiveCodeGeneratorManager(newManager);\ - } \ -/* - * END_CODE_GENERATOR_MANAGER would restore the previous code generator manager that was - * active at the time of START_CODE_GENERATOR_MANAGER call - */ -#define END_CODE_GENERATOR_MANAGER() \ - if (codegen) { \ - SetActiveCodeGeneratorManager(oldManager);\ - } \ - } while (0); - - -/* - * Initialize LLVM library - */ -#define init_codegen() \ - if (init_codegen) { \ - if (InitCodegen() == 0) { \ - ereport(FATAL, \ - (errcode(ERRCODE_INTERNAL_ERROR), \ - errmsg("failed to initialize LLVM library"), \ - errhint("LLVM library for code generation failed " \ - "to initialize. You may wish to disable " \ - "code generation by turning off the " \ - "\"codegen\" GUC."))); \ - } \ - } \ - -/* - * Call ExecVariableList using function pointer ExecVariableList_fn. - * Function pointer may point to regular version or generated function - */ -#define call_ExecVariableList(projInfo, values, isnull) \ - projInfo->ExecVariableList_gen_info.ExecVariableList_fn(projInfo, values, isnull) - -/* - * Call AdvanceAggregates using function pointer AdvanceAggregates_fn. - * Function pointer may point to regular version or generated function - */ -#define call_AdvanceAggregates(aggstate, pergroup, mem_manager) \ - aggstate->AdvanceAggregates_gen_info.AdvanceAggregates_fn(aggstate, pergroup, mem_manager) - -/* - * Enrollment macros - * The enrollment process also ensures that the generated function pointer - * is set to the regular version initially - */ -#define enroll_ExecVariableList_codegen(regular_func, ptr_to_regular_func_ptr, proj_info, slot) \ - proj_info->ExecVariableList_gen_info.code_generator = ExecVariableListCodegenEnroll( \ - regular_func, ptr_to_regular_func_ptr, proj_info, slot); \ - Assert(proj_info->ExecVariableList_gen_info.ExecVariableList_fn == regular_func); \ - -#define enroll_ExecEvalExpr_codegen(regular_func, ptr_to_regular_func_ptr, exprstate, econtext, plan_state) \ - exprstate->ExecEvalExpr_code_generator = ExecEvalExprCodegenEnroll( \ - (ExecEvalExprFn)regular_func, (ExecEvalExprFn*)ptr_to_regular_func_ptr, exprstate, econtext, plan_state); \ - Assert(exprstate->evalfunc == regular_func); \ - -#define enroll_AdvanceAggregates_codegen(regular_func, ptr_to_regular_func_ptr, aggstate) \ - aggstate->AdvanceAggregates_gen_info.code_generator = AdvanceAggregatesCodegenEnroll( \ - regular_func, ptr_to_regular_func_ptr, aggstate); \ - Assert(aggstate->AdvanceAggregates_gen_info.AdvanceAggregates_fn == regular_func); \ - -#endif //USE_CODEGEN - -#endif // CODEGEN_WRAPPER_H_ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ac01b2e017f15881df670f2f0d916554024a950a..7efe10c53d041be2efe7f47bc2c7c26629489a69 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -51,7 +51,6 @@ struct ChunkTransportState; /* #include "cdb/cdbinterconnect.h" */ #define EXEC_FLAG_REWIND 0x0002 /* expect rescan */ #define EXEC_FLAG_BACKWARD 0x0004 /* need backward scan */ #define EXEC_FLAG_MARK 0x0008 /* need mark/restore */ -#define EXEC_FLAG_EXPLAIN_CODEGEN 0x0010 /* EXPLAIN CODEGEN */ /* @@ -318,9 +317,6 @@ extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); extern int ExecTargetListLength(List *targetlist); -#ifdef USE_CODEGEN -extern bool ExecTargetList(List *targetlist, ExprContext *econtext, Datum *values, bool *isnull, ExprDoneCond *itemIsDone, ExprDoneCond *isDone); -#endif extern int ExecCleanTargetListLength(List *targetlist); extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone); diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 3fdd7554f38155fe4ad96eddb5960c91ed51f965..c36a120217297ec19bc2fd891c5a1b60db517c2a 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -19,7 +19,6 @@ #include "access/heapam.h" #include "access/memtup.h" #include "storage/buf.h" -#include "codegen/codegen_wrapper.h" /*---------- * The executor stores tuples in a "tuple table" which is a List of diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 925d07de45a2c36444aec2b045a705e0950fa3b5..7211610268ac56e57a816629d4badacf7206800d 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -209,14 +209,6 @@ typedef struct ReturnSetInfo TupleDesc setDesc; /* actual descriptor for returned tuples */ } ReturnSetInfo; -typedef struct ExecVariableListCodegenInfo -{ - /* Pointer to store ExecVariableListCodegen from Codegen */ - void* code_generator; - /* Function pointer that points to either regular or generated slot_deform_tuple */ - ExecVariableListFn ExecVariableList_fn; -} ExecVariableListCodegenInfo; - /* ---------------- * ProjectionInfo node information * @@ -271,10 +263,6 @@ typedef struct ProjectionInfo int pi_lastInnerVar; int pi_lastOuterVar; int pi_lastScanVar; - -#ifdef USE_CODEGEN - ExecVariableListCodegenInfo ExecVariableList_gen_info; -#endif } ProjectionInfo; /* ---------------- @@ -810,11 +798,6 @@ struct ExprState NodeTag type; Expr *expr; /* associated Expr node */ ExprStateEvalFunc evalfunc; /* routine to run to execute node */ - -#ifdef USE_CODEGEN - void *ExecEvalExpr_code_generator; -#endif - }; /* ---------------- @@ -1393,9 +1376,6 @@ typedef struct PlanState ExprContext *ps_ExprContext; /* node's expression-evaluation context */ ProjectionInfo *ps_ProjInfo; /* info for doing tuple projection */ - /* The manager manages all the code generators and generation process */ - void *CodegenManager; - /* * EXPLAIN ANALYZE statistics collection */ @@ -2411,15 +2391,6 @@ typedef struct SortState * expressions and run the aggregate transition functions. * ------------------------- */ - -typedef struct AdvanceAggregatesCodegenInfo -{ - /* Pointer to store AdvanceAggregatesCodegen from Codegen */ - void* code_generator; - /* Function pointer that points to either regular or generated advance_aggregates */ - AdvanceAggregatesFn AdvanceAggregates_fn; -} AdvanceAggregatesCodegenInfo; - /* these structs are private in nodeAgg.c: */ typedef struct AggStatePerAggData *AggStatePerAgg; typedef struct AggStatePerGroupData *AggStatePerGroup; @@ -2478,9 +2449,6 @@ typedef struct AggState /* set if the operator created workfiles */ bool workfiles_created; -#ifdef USE_CODEGEN - AdvanceAggregatesCodegenInfo AdvanceAggregates_gen_info; -#endif } AggState; /* ---------------- diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6f06019b751d096682fbccd8225cab0701415659..1c406de606b6620bd3825e917a97ec91446dd449 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2807,7 +2807,6 @@ typedef struct ExplainStmt bool verbose; /* print plan info */ bool analyze; /* get statistics by executing plan */ bool dxl; /* display plan in dxl format */ - bool codegen; /* display generated IR codegen */ } ExplainStmt; /* ---------------------- diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 968bd191b132ce9c7ed97bc6eb0ee4dda3361de5..a92110e6bb43ba9ed5b649b9ea35c1b2e48b2e7c 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -78,7 +78,6 @@ PG_KEYWORD("class", CLASS, UNRESERVED_KEYWORD) PG_KEYWORD("close", CLOSE, UNRESERVED_KEYWORD) PG_KEYWORD("cluster", CLUSTER, UNRESERVED_KEYWORD) PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD) -PG_KEYWORD("codegen", CODEGEN, UNRESERVED_KEYWORD) PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD) PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD) PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD) diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 750613009d85d46f26b78ba6f63c6bdc7c2625e8..ed9dd5a1de820fec8552343f4fc7146203232eb0 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -881,9 +881,6 @@ /* Define to 1 to build with Bonjour support. (--with-bonjour) */ #undef USE_BONJOUR -/* Define to 1 to enable code generation. (--enable-codegen) */ -#undef USE_CODEGEN - /* Define to 1 to build with libcurl support. (--with-libcurl) */ #undef USE_CURL diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 3a5cae9bdba2c2c4fa8a64aadb7bd58b3de6412b..698e428f63c14126cae91469e6c34e72732ec468 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -492,20 +492,6 @@ extern bool optimizer_analyze_midlevel_partition; extern bool optimizer_use_gpdb_allocators; -/** - * GUCs related to code generation. - **/ -#define CODEGEN_OPTIMIZATION_LEVEL_NONE 0 -#define CODEGEN_OPTIMIZATION_LEVEL_LESS 1 -#define CODEGEN_OPTIMIZATION_LEVEL_DEFAULT 2 -#define CODEGEN_OPTIMIZATION_LEVEL_AGGRESSIVE 3 - -extern bool init_codegen; -extern bool codegen; -extern bool codegen_validate_functions; -extern int codegen_varlen_tolerance; -extern int codegen_optimization_level; - /** * Enable logging of DPE match in optimizer. */ diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h index 753ad2cb6f9582a3e2cd578b2a96344e6b9af7db..d9e0a2d4c8740e13304f5c721ab6b3909ba7c894 100644 --- a/src/include/utils/numeric.h +++ b/src/include/utils/numeric.h @@ -90,21 +90,4 @@ extern float8 numeric_li_fraction(Numeric x, Numeric x0, Numeric x1, bool *eq_bounds, bool *eq_abscissas); extern Numeric numeric_li_value(float8 f, Numeric y0, Numeric y1); - -/* - * Routines for avg int type. The transition datatype is a int64 for count, and a float8 for sum. - */ - -typedef struct IntFloatAvgTransdata -{ - int32 _len; /* len for varattrib, do not touch directly */ -#if 1 - int32 pad; /* pad so int64 and float64 will be 8 bytes aligned */ -#endif - int64 count; - float8 sum; -} IntFloatAvgTransdata; - -extern Datum intfloat_avg_accum_decum(IntFloatAvgTransdata *transdata, float8 newval, bool acc); - #endif /* _PG_NUMERIC_H_ */ diff --git a/src/test/regress/expected/null.out b/src/test/regress/expected/null.out deleted file mode 100644 index 5ad1bdfebd7129d033c941d3c4dc79375fb73342..0000000000000000000000000000000000000000 --- a/src/test/regress/expected/null.out +++ /dev/null @@ -1,149 +0,0 @@ --- check behavior when values are NULL with date type -create table date_null (a int4, b date); -NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table. -HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -insert into date_null values (1, '1957-04-09'); -insert into date_null values (2, NULL); -select a from date_null where b <= date '1957-04-10'; - a ---- - 1 -(1 row) - -select a from date_null where b <= date '1957-04-10' - interval '90' day; - a ---- -(0 rows) - --- check behavior when values are NULL with float8 type -create table float8_null (a float8, b float8, c float8); -NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table. -HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -insert into float8_null values (1, NULL, NULL); -select a from float8_null where b <=a; - a ---- -(0 rows) - -select a from float8_null where b*a <=0; - a ---- -(0 rows) - -select a from float8_null where b*c <=0; - a ---- -(0 rows) - -select a+b from float8_null; - ?column? ----------- - -(1 row) - -select b+c from float8_null; - ?column? ----------- - -(1 row) - -select a-b from float8_null; - ?column? ----------- - -(1 row) - -select b-c from float8_null; - ?column? ----------- - -(1 row) - --- check behavior when values are NULL with int4 type -create table int4_null (a int4, b int4, c int4); -NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table. -HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -insert into int4_null values (1, NULL, NULL); -select a from int4_null where b <=a; - a ---- -(0 rows) - -select a from int4_null where b*a <=0; - a ---- -(0 rows) - -select a from int4_null where b*c <=0; - a ---- -(0 rows) - -select a+b from int4_null; - ?column? ----------- - -(1 row) - -select b+c from int4_null; - ?column? ----------- - -(1 row) - -select a-b from int4_null; - ?column? ----------- - -(1 row) - -select b-c from int4_null; - ?column? ----------- - -(1 row) - --- check behavior when values are NULL with int8 type -create table int8_null (a int8, b int8, c int8); -NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table. -HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -insert into int8_null values (1, NULL, NULL); -select a from int8_null where b <=a; - a ---- -(0 rows) - -select a from int8_null where b*a <=0; - a ---- -(0 rows) - -select a from int8_null where b*c <=0; - a ---- -(0 rows) - -select a+b from int8_null; - ?column? ----------- - -(1 row) - -select b+c from int8_null; - ?column? ----------- - -(1 row) - -select a-b from int8_null; - ?column? ----------- - -(1 row) - -select b-c from int8_null; - ?column? ----------- - -(1 row) - diff --git a/src/test/regress/greenplum_schedule b/src/test/regress/greenplum_schedule index ef52e12c85a79c4a616b5bf68194df3c377c301a..92fba51ec488d37aa1a502f2e653675031a16dc2 100755 --- a/src/test/regress/greenplum_schedule +++ b/src/test/regress/greenplum_schedule @@ -14,7 +14,7 @@ # to the segments, and therefore has to run in smaller groups to avoid # hitting max_connections limit on segments. # -test: gp_tablespace gp_aggregates gp_metadata variadic_parameters default_parameters function_extensions spi gp_xml pgoptions shared_scan null +test: gp_tablespace gp_aggregates gp_metadata variadic_parameters default_parameters function_extensions spi gp_xml pgoptions shared_scan test: spi_processed64bit test: leastsquares opr_sanity_gp decode_expr bitmapscan bitmapscan_ao case_gp limit_gp notin percentile join_gp union_gp gpcopy gp_create_table gp_create_view window_views diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 474599d3b7c75ac2eae70ba5ee34a0d0aa434ae1..0d0aff3d87b4d22638e7824f30ded3cbeb484611 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -2282,11 +2282,11 @@ should_exclude_test(char *test) } /* - * @brief Check whether a feature (i.e., optimizer or codegen) is on or off. + * @brief Check whether a feature (e.g. optimizer) is on or off. * If the input feature is optimizer, then set the global * variable "optimizer_enabled" accordingly. * - * @param feature_name Name of the feature to be checked (i.e., optimizer or codegen) + * @param feature_name Name of the feature to be checked (e.g. optimizer) * @param feature_value Expected value when the feature is enabled (i.e., on or group) * @param on_msg Message to be printed when the feature is enabled * @param off_msg Message to be printed when the feature is disabled @@ -2804,11 +2804,6 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc "Resource group enabled. Using resource group answer files whenever possible", "Resource group disabled. Using default answer files"); - /* - * Find out if codegen is on or off - */ - check_feature_status("codegen", "on", "Codegen enabled", "Codegen disabled"); - /* * Ready to run the tests */ diff --git a/src/test/regress/sql/null.sql b/src/test/regress/sql/null.sql deleted file mode 100644 index 73f9c1d278b0d99af8e6364ca92ac1b680942e10..0000000000000000000000000000000000000000 --- a/src/test/regress/sql/null.sql +++ /dev/null @@ -1,39 +0,0 @@ --- check behavior when values are NULL with date type -create table date_null (a int4, b date); -insert into date_null values (1, '1957-04-09'); -insert into date_null values (2, NULL); -select a from date_null where b <= date '1957-04-10'; -select a from date_null where b <= date '1957-04-10' - interval '90' day; - --- check behavior when values are NULL with float8 type -create table float8_null (a float8, b float8, c float8); -insert into float8_null values (1, NULL, NULL); -select a from float8_null where b <=a; -select a from float8_null where b*a <=0; -select a from float8_null where b*c <=0; -select a+b from float8_null; -select b+c from float8_null; -select a-b from float8_null; -select b-c from float8_null; - --- check behavior when values are NULL with int4 type -create table int4_null (a int4, b int4, c int4); -insert into int4_null values (1, NULL, NULL); -select a from int4_null where b <=a; -select a from int4_null where b*a <=0; -select a from int4_null where b*c <=0; -select a+b from int4_null; -select b+c from int4_null; -select a-b from int4_null; -select b-c from int4_null; - --- check behavior when values are NULL with int8 type -create table int8_null (a int8, b int8, c int8); -insert into int8_null values (1, NULL, NULL); -select a from int8_null where b <=a; -select a from int8_null where b*a <=0; -select a from int8_null where b*c <=0; -select a+b from int8_null; -select b+c from int8_null; -select a-b from int8_null; -select b-c from int8_null; diff --git a/src/test/unit/mock/gpcodegen_mock.c b/src/test/unit/mock/gpcodegen_mock.c deleted file mode 100644 index 54c94500f4a4ea9de7cf24d49cfe59f6a1bccdf0..0000000000000000000000000000000000000000 --- a/src/test/unit/mock/gpcodegen_mock.c +++ /dev/null @@ -1,122 +0,0 @@ -#include "postgres.h" - -#include "codegen/codegen_wrapper.h" - -// Do one-time global initialization of LLVM library. Returns 0 -// on success, nonzero on error. -unsigned int -InitCodegen() -{ - elog(ERROR, "mock implementation of InitCodegen called"); - return 0; -} - -// creates a manager for an operator -void* -CodeGeneratorManagerCreate(const char* module_name) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_Create called"); - return NULL; -} - -// calls all the registered CodegenInterface to generate code -unsigned int -CodeGeneratorManagerGenerateCode(void* manager) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_GenerateCode called"); - return 1; -} - -// compiles and prepares all the code gened function pointers -unsigned int -CodeGeneratorManagerPrepareGeneratedFunctions(void* manager) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_PrepareGeneratedFunctions called"); - return 1; -} - -// notifies a manager that the underlying operator has a parameter change -unsigned int -CodeGeneratorManagerNotifyParameterChange(void* manager) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_NotifyParameterChange called"); - return 1; -} - -// destroys a manager for an operator -void -CodeGeneratorManagerDestroy(void* manager) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_Destroy called"); -} - -/* - * Accumulate the explain string with a dump of all the underlying LLVM modules - */ -void -CodeGeneratorManagerAccumulateExplainString(void* manager) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_AccumulateExplainString called"); -} - -/* - * Return a copy in CurrentMemoryContext of the previously accumulated explain - * string - */ -char* -CodeGeneratorManagerGetExplainString(void* manager) -{ - elog(ERROR, "mock implementation of CodeGeneratorManager_GetExplainString called"); - return NULL; -} - -// get the active code generator manager -void* -GetActiveCodeGeneratorManager() -{ - elog(ERROR, "mock implementation of GetActiveCodeGeneratorManager called"); - return NULL; -} - -// set the active code generator manager -void -SetActiveCodeGeneratorManager(void* manager) -{ - elog(ERROR, "mock implementation of SetActiveCodeGeneratorManager called"); -} - -// returns the pointer to the ExecVariableListGenerator -void* -ExecVariableListCodegenEnroll(ExecVariableListFn regular_func_ptr, - ExecVariableListFn* ptr_to_regular_func_ptr, - struct ProjectionInfo* proj_info, - struct TupleTableSlot* slot) -{ - *ptr_to_regular_func_ptr = regular_func_ptr; - elog(ERROR, "mock implementation of ExecVariableListEnroll called"); - return NULL; -} - -// Enroll and returns the pointer to ExecEvalExprGenerator -void* -ExecEvalExprCodegenEnroll(ExecEvalExprFn regular_func_ptr, - ExecEvalExprFn* ptr_to_regular_func_ptr, - struct ExprState *exprstate, - struct ExprContext *econtext, - struct PlanState* planstate) -{ - *ptr_to_regular_func_ptr = regular_func_ptr; - elog(ERROR, "mock implementation of ExecEvalExprCodegenEnroll called"); - return NULL; -} - -// Enroll and returns the pointer to AdvanceAggregateGenerator -void* -AdvanceAggregatesCodegenEnroll(AdvanceAggregatesFn regular_func_ptr, - AdvanceAggregatesFn* ptr_to_regular_func_ptr, - struct AggState *aggstate) { - *ptr_to_regular_func_ptr = regular_func_ptr; - elog(ERROR, "mock implementation of AdvanceAggregatesCodegenEnroll called"); - return NULL; -} -