forked from OSchip/llvm-project
Add initial version of Polly
This version is equivalent to commit ba26ebece8f5be84e9bd6315611d412af797147e in the old git repository. llvm-svn: 130476
This commit is contained in:
parent
011eae7512
commit
758053788b
|
@ -0,0 +1,164 @@
|
|||
# Check if this is a in tree build.
|
||||
if (NOT DEFINED LLVM_MAIN_SRC_DIR)
|
||||
project(Polly)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
# Where is LLVM installed?
|
||||
set(LLVM_INSTALL_ROOT "" CACHE PATH "Root of LLVM install.")
|
||||
# Check if the LLVM_INSTALL_ROOT valid.
|
||||
if( NOT EXISTS ${LLVM_INSTALL_ROOT}/include/llvm )
|
||||
message(FATAL_ERROR "LLVM_INSTALL_ROOT (${LLVM_INSTALL_ROOT}) is not a valid LLVM installation.")
|
||||
endif(NOT EXISTS ${LLVM_INSTALL_ROOT}/include/llvm)
|
||||
# Add the llvm header path.
|
||||
include_directories(${LLVM_INSTALL_ROOT}/include/)
|
||||
|
||||
# Get the system librarys that will link into LLVM.
|
||||
function(get_system_libs return_var)
|
||||
# Returns in `return_var' a list of system libraries used by LLVM.
|
||||
if( NOT MSVC )
|
||||
if( MINGW )
|
||||
set(system_libs ${system_libs} imagehlp psapi)
|
||||
elseif( CMAKE_HOST_UNIX )
|
||||
if( HAVE_LIBDL )
|
||||
set(system_libs ${system_libs} ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD )
|
||||
set(system_libs ${system_libs} pthread)
|
||||
endif()
|
||||
endif( MINGW )
|
||||
endif( NOT MSVC )
|
||||
set(${return_var} ${system_libs} PARENT_SCOPE)
|
||||
endfunction(get_system_libs)
|
||||
|
||||
# Now set the header paths.
|
||||
execute_process(COMMAND "${LLVM_INSTALL_ROOT}/bin/llvm-config" --includedir
|
||||
OUTPUT_VARIABLE LLVM_INCLUDE_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
include_directories( ${LLVM_INCLUDE_DIR} )
|
||||
|
||||
# And then set the cxx flags.
|
||||
execute_process(COMMAND "${LLVM_INSTALL_ROOT}/bin/llvm-config" --cxxflags
|
||||
OUTPUT_VARIABLE LLVM_CXX_FLAGS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS})
|
||||
endif(NOT DEFINED LLVM_MAIN_SRC_DIR)
|
||||
|
||||
set(POLLY_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(POLLY_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Add appropriate flags for GCC
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
# FIXME: Turn off exceptions, RTTI:
|
||||
# -fno-exceptions -fno-rtti
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -fno-exceptions -fno-rtti")
|
||||
endif ()
|
||||
|
||||
# Add path for custom modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${POLLY_SOURCE_DIR}/cmake")
|
||||
|
||||
FIND_PACKAGE(Cloog REQUIRED)
|
||||
FIND_PACKAGE(Isl REQUIRED)
|
||||
FIND_PACKAGE(Gmp REQUIRED)
|
||||
|
||||
option(POLLY_ENABLE_OPENSCOP "Enable Openscop library for scop import/export" ON)
|
||||
if (POLLY_ENABLE_OPENSCOP)
|
||||
FIND_PACKAGE(OpenScop)
|
||||
endif(POLLY_ENABLE_OPENSCOP)
|
||||
|
||||
option(POLLY_ENABLE_SCOPLIB "Enable SCoPLib library for scop import/export" ON)
|
||||
if (POLLY_ENABLE_SCOPLIB)
|
||||
FIND_PACKAGE(SCoPLib)
|
||||
endif(POLLY_ENABLE_SCOPLIB)
|
||||
|
||||
INCLUDE_DIRECTORIES( ${CLOOG_INCLUDE_DIR} )
|
||||
INCLUDE_DIRECTORIES( ${ISL_INCLUDE_DIR} )
|
||||
INCLUDE_DIRECTORIES( ${GMP_INCLUDE_DIR} )
|
||||
|
||||
# Support OpenScop export/import if the library is available.
|
||||
if (OPENSCOP_FOUND)
|
||||
INCLUDE_DIRECTORIES( ${OPENSCOP_INCLUDE_DIR} )
|
||||
endif(OPENSCOP_FOUND)
|
||||
if (SCOPLIB_FOUND)
|
||||
INCLUDE_DIRECTORIES( ${SCOPLIB_INCLUDE_DIR} )
|
||||
endif(SCOPLIB_FOUND)
|
||||
|
||||
macro(add_polly_library name)
|
||||
set(srcs ${ARGN})
|
||||
if(MSVC_IDE OR XCODE)
|
||||
file( GLOB_RECURSE headers *.h *.td *.def)
|
||||
set(srcs ${srcs} ${headers})
|
||||
string( REGEX MATCHALL "/[^/]+" split_path ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
list( GET split_path -1 dir)
|
||||
file( GLOB_RECURSE headers
|
||||
../../include/polly${dir}/*.h)
|
||||
set(srcs ${srcs} ${headers})
|
||||
endif(MSVC_IDE OR XCODE)
|
||||
if (MODULE)
|
||||
set(libkind MODULE)
|
||||
elseif (SHARED_LIBRARY)
|
||||
set(libkind SHARED)
|
||||
else()
|
||||
set(libkind)
|
||||
endif()
|
||||
add_library( ${name} ${libkind} ${srcs} )
|
||||
if( LLVM_COMMON_DEPENDS )
|
||||
add_dependencies( ${name} ${LLVM_COMMON_DEPENDS} )
|
||||
endif( LLVM_COMMON_DEPENDS )
|
||||
if( LLVM_USED_LIBS )
|
||||
foreach(lib ${LLVM_USED_LIBS})
|
||||
target_link_libraries( ${name} ${lib} )
|
||||
endforeach(lib)
|
||||
endif( LLVM_USED_LIBS )
|
||||
|
||||
target_link_libraries( ${name} ${CLOOG_LIBRARY} ${ISL_LIBRARY} ${GMP_LIBRARY})
|
||||
if (OPENSCOP_FOUND)
|
||||
target_link_libraries( ${name} ${OPENSCOP_LIBRARY})
|
||||
endif(OPENSCOP_FOUND)
|
||||
if (SCOPLIB_FOUND)
|
||||
target_link_libraries( ${name} ${SCOPLIB_LIBRARY})
|
||||
endif(SCOPLIB_FOUND)
|
||||
|
||||
if( LLVM_LINK_COMPONENTS )
|
||||
llvm_config(${name} ${LLVM_LINK_COMPONENTS})
|
||||
endif( LLVM_LINK_COMPONENTS )
|
||||
get_system_libs(llvm_system_libs)
|
||||
if( llvm_system_libs )
|
||||
target_link_libraries(${name} ${llvm_system_libs})
|
||||
endif( llvm_system_libs )
|
||||
|
||||
if(MSVC)
|
||||
get_target_property(cflag ${name} COMPILE_FLAGS)
|
||||
if(NOT cflag)
|
||||
set(cflag "")
|
||||
endif(NOT cflag)
|
||||
set(cflag "${cflag} /Za")
|
||||
set_target_properties(${name} PROPERTIES COMPILE_FLAGS ${cflag})
|
||||
endif(MSVC)
|
||||
install(TARGETS ${name}
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX})
|
||||
endmacro(add_polly_library)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lib/JSON/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include
|
||||
)
|
||||
|
||||
install(DIRECTORY include
|
||||
DESTINATION .
|
||||
PATTERN ".svn" EXCLUDE
|
||||
)
|
||||
|
||||
add_definitions( -D_GNU_SOURCE )
|
||||
|
||||
add_subdirectory(include)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(tools)
|
||||
# TODO: docs.
|
||||
|
||||
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/polly/Config/config.h.cmake
|
||||
${POLLY_BINARY_DIR}/include/polly/Config/config.h )
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
This file is a partial list of people who have contributed to Polly.
|
||||
If you have contributed a patch or made some other contribution to
|
||||
LLVM, please submit a patch to this file to add yourself, and it will be
|
||||
done!
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
||||
(S).
|
||||
|
||||
N: Raghesh Aloor
|
||||
E: raghesh.a@gmail.com
|
||||
D: OpenMP code generation
|
||||
D: Google Summer of Code student 2011
|
||||
|
||||
N: Tobias Grosser
|
||||
E: tobias@grosser.es
|
||||
W: http://www.grosser.es
|
||||
D: Co-founder, design of the overall architecture
|
||||
|
||||
N: Andreas Simbuerger
|
||||
E: simbuerg@fim.uni-passau.de
|
||||
D: Profiling infrastructure
|
||||
|
||||
N: Hongbin Zheng
|
||||
E: etherzhhb@gmail.com
|
||||
D: Co-founder
|
||||
D: scop detection, automake/cmake infrastructure, scopinfo, scoppasses, ...
|
||||
D: Google Summer of Code student 2010
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
==============================================================================
|
||||
Polly Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2009-2011 Polly Team
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
Polly Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the Polly Team, copyright holders, nor the names of
|
||||
its contributors may be used to endorse or promote products derived from
|
||||
this Software without specific prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
Copyrights and Licenses for Third Party Software Distributed with LLVM:
|
||||
==============================================================================
|
||||
The Polly software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the Polly Distribution, and nothing in any of the other
|
||||
licenses gives permission to use the names of the Polly Team or promote products
|
||||
derived from this Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
jsoncpp lib/JSON
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
##===- projects/polly/Makefile -----------------------------*- Makefile -*-===##
|
||||
#
|
||||
# This is a polly Makefile for a project that uses LLVM.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
#
|
||||
# Indicates our relative path to the top of the project's root directory.
|
||||
#
|
||||
LEVEL = .
|
||||
DIRS = lib test tools
|
||||
EXTRA_DIST = include
|
||||
|
||||
#
|
||||
# Include the Master Makefile that knows how to build all.
|
||||
#
|
||||
include $(LEVEL)/Makefile.common
|
|
@ -0,0 +1,34 @@
|
|||
#===-- Makefile.common - Common make rules for Polly -------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
#
|
||||
# Configuration file to set paths specific to local installation of LLVM
|
||||
#
|
||||
PROJECT_NAME := polly
|
||||
PROJ_VERSION := 0.9
|
||||
# Set this variable to the top of the LLVM source tree.
|
||||
LLVM_SRC_ROOT = @LLVM_SRC@
|
||||
|
||||
# Set the name of the project here
|
||||
|
||||
# (this is *not* the same as OBJ_ROOT as defined in LLVM's Makefile.config).
|
||||
LLVM_OBJ_ROOT = @LLVM_OBJ@
|
||||
|
||||
PROJ_SRC_ROOT := $(subst //,/,@abs_top_srcdir@)
|
||||
|
||||
# Set the root directory of this project's object files
|
||||
PROJ_OBJ_ROOT := $(subst //,/,@abs_top_builddir@)
|
||||
|
||||
ifndef LLVM_OBJ_ROOT
|
||||
include $(LEVEL)/Makefile.config
|
||||
else
|
||||
include $(PROJ_OBJ_ROOT)/Makefile.config
|
||||
endif
|
||||
|
||||
# Include LLVM's Master Makefile.
|
||||
include $(LLVM_SRC_ROOT)/Makefile.common
|
|
@ -0,0 +1,40 @@
|
|||
#===-- Makefile.config - Local configuration for LLVM ------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
#
|
||||
# This file is included by Makefile.common. It defines paths and other
|
||||
# values specific to a particular installation of LLVM.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
# Set the root directory of this polly's object files
|
||||
POLLY_SRC_ROOT := $(subst //,/,@abs_top_srcdir@)
|
||||
|
||||
# Set this variable to the top level directory where LLVM was built
|
||||
POLLY_OBJ_ROOT := $(subst //,/,@abs_top_builddir@)
|
||||
|
||||
# Set the root directory of this project's install prefix
|
||||
PROJ_INSTALL_ROOT := @prefix@
|
||||
|
||||
# Set the C++ flags
|
||||
ifeq (@GXX@,yes)
|
||||
POLLY_CXXFLAGS := "-fno-common -Woverloaded-virtual -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings"
|
||||
endif
|
||||
|
||||
# Do us work with scoplib?
|
||||
OPENSCOP_FOUND := @openscop_found@
|
||||
SCOPLIB_FOUND := @scoplib_found@
|
||||
|
||||
# Set include directroys
|
||||
POLLY_INC := @gmp_inc@ @isl_inc@ \
|
||||
@cloog_inc@ @openscop_inc@ @scoplib_inc@ \
|
||||
-I$(POLLY_SRC_ROOT)/lib/JSON/include
|
||||
|
||||
POLLY_LD := @gmp_ld@ @isl_ld@ @cloog_ld@ @openscop_ld@ @scoplib_ld@ @scoplib_rpath@
|
||||
|
||||
POLLY_LIB := @gmp_lib@ @isl_lib@ @cloog_lib@ @openscop_lib@ @scoplib_lib@
|
|
@ -0,0 +1,12 @@
|
|||
Polly - Polyhedral optimizations for LLVM
|
||||
|
||||
Polly uses a mathematical representation, the polyhedral model, to represent and
|
||||
transform loops and other control flow structures. Using an abstract
|
||||
representation it is possible to reason about transformations in a more general
|
||||
way and to use highly optimized linear programming libraries to figure out the
|
||||
optimal loop structure. These transformations can be used to do constant
|
||||
propagation through arrays, remove dead loop iterations, optimize loops for
|
||||
cache locality, optimize arrays, apply advanced automatic parallelization, drive
|
||||
vectorization, or they can be used to do software pipelining.
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh
|
||||
die () {
|
||||
echo "$@" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
test -d autoconf && test -f autoconf/configure.ac && cd autoconf
|
||||
test -f configure.ac || die "Can't find 'autoconf' dir; please cd into it first"
|
||||
autoconf --version | egrep '2\.[5-6][0-9]' > /dev/null
|
||||
if test $? -ne 0 ; then
|
||||
die "Your autoconf was not detected as being 2.5x"
|
||||
fi
|
||||
cwd=`pwd`
|
||||
if test -d ../../../autoconf/m4 ; then
|
||||
cd ../../../autoconf/m4
|
||||
llvm_m4=`pwd`
|
||||
cd $cwd
|
||||
elif test -d ../../llvm/autoconf/m4 ; then
|
||||
cd ../../llvm/autoconf/m4
|
||||
llvm_m4=`pwd`
|
||||
cd $cwd
|
||||
else
|
||||
die "Can't find the LLVM autoconf/m4 directory. polly should be checked out to projects directory"
|
||||
fi
|
||||
echo "Regenerating aclocal.m4 with aclocal"
|
||||
rm -f aclocal.m4
|
||||
aclocal -I $llvm_m4 -I "$llvm_m4/.." -I $(pwd)/m4 || die "aclocal failed"
|
||||
echo "Regenerating configure with autoconf 2.5x"
|
||||
autoconf --force --warnings=all -o ../configure configure.ac || die "autoconf failed"
|
||||
cd ..
|
||||
echo "Regenerating config.h.in with autoheader"
|
||||
autoheader --warnings=all -I autoconf -I autoconf/m4 -I $llvm_m4 -I "$llvm_m4/.." autoconf/configure.ac || die "autoheader failed"
|
||||
exit 0
|
|
@ -0,0 +1,24 @@
|
|||
------------------------------------------------------------------------------
|
||||
Autoconf Files
|
||||
------------------------------------------------------------------------------
|
||||
All autoconf files are licensed under the LLVM license with the following
|
||||
additions:
|
||||
|
||||
llvm/autoconf/install-sh:
|
||||
This script is licensed under the LLVM license, with the following
|
||||
additional copyrights and restrictions:
|
||||
|
||||
Copyright 1991 by the Massachusetts Institute of Technology
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this software and its
|
||||
documentation for any purpose is hereby granted without fee, provided that
|
||||
the above copyright notice appear in all copies and that both that
|
||||
copyright notice and this permission notice appear in supporting
|
||||
documentation, and that the name of M.I.T. not be used in advertising or
|
||||
publicity pertaining to distribution of the software without specific,
|
||||
written prior permission. M.I.T. makes no representations about the
|
||||
suitability of this software for any purpose. It is provided "as is"
|
||||
without express or implied warranty.
|
||||
|
||||
Please see the source files for additional copyrights.
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
dnl find_lib_and_headers(name, verify-header, library-name, requirded?)
|
||||
dnl Export
|
||||
dnl name_inc in -I"include-path" form
|
||||
dnl name_lib in -l"library-name" form
|
||||
dnl name_ld in -L"library-path" form
|
||||
dnl name_found set to "yes" if found
|
||||
|
||||
AC_DEFUN([find_lib_and_headers],
|
||||
[
|
||||
AC_LANG_PUSH(C++)
|
||||
OLD_CXXFLAGS=$CXXFLAGS;
|
||||
OLD_LDFLAGS=$LDFLAGS;
|
||||
OLD_LIBS=$LIBS;
|
||||
|
||||
LIBS="$LIBS -l$3";
|
||||
|
||||
# Get include path and lib path
|
||||
AC_ARG_WITH([$1],
|
||||
[AS_HELP_STRING([--with-$1], [prefix of $1 ])],
|
||||
[given_inc_path="$withval/include"; CXXFLAGS="-I$given_inc_path $CXXFLAGS";
|
||||
given_lib_path="$withval/lib"; LDFLAGS="-L$given_lib_path $LDFLAGS"],
|
||||
[given_inc_path=inc_not_give_$1;
|
||||
given_lib_path=lib_not_give_$1]
|
||||
)
|
||||
# Check for library and headers works
|
||||
AC_MSG_CHECKING([for $1 in $given_inc_path, $given_lib_path])
|
||||
# try to compile a file that includes a header of the library
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <$2>]], [[;]])],
|
||||
[AC_MSG_RESULT([ok])
|
||||
AC_SUBST([$1_found],["yes"])
|
||||
AS_IF([test "x$given_inc_path" != "xinc_not_give_$1"],
|
||||
[AC_SUBST([$1_inc],["-I$given_inc_path"])])
|
||||
AC_SUBST([$1_lib],["-l$3"])
|
||||
AS_IF([test "x$given_lib_path" != "xlib_not_give_$1"],
|
||||
[AC_SUBST([$1_ld],["-L$given_lib_path"])])],
|
||||
[AS_IF([test "x$4" = "xrequired"],
|
||||
[AC_MSG_ERROR([$1 required but not found])],
|
||||
[AC_MSG_RESULT([not found])])]
|
||||
)
|
||||
|
||||
# reset original CXXFLAGS
|
||||
CXXFLAGS=$OLD_CXXFLAGS
|
||||
LDFLAGS=$OLD_LDFLAGS;
|
||||
LIBS=$OLD_LIBS
|
||||
AC_LANG_POP(C++)
|
||||
])
|
||||
|
||||
#
|
||||
# Provide the arguments and other processing needed for an LLVM project
|
||||
#
|
||||
AC_DEFUN([LLVM_CONFIG_PROJECT],
|
||||
[AC_ARG_WITH([llvmsrc],
|
||||
AS_HELP_STRING([--with-llvmsrc],[Location of LLVM Source Code]),
|
||||
[llvm_src="$withval"],[llvm_src="]$1["])
|
||||
AC_SUBST(LLVM_SRC,$llvm_src)
|
||||
AC_ARG_WITH([llvmobj],
|
||||
AS_HELP_STRING([--with-llvmobj],[Location of LLVM Object Code]),
|
||||
[llvm_obj="$withval"],[llvm_obj="]$2["])
|
||||
AC_SUBST(LLVM_OBJ,$llvm_obj)
|
||||
AC_CONFIG_COMMANDS([setup],,[llvm_src="${LLVM_SRC}"])
|
||||
])
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
|||
dnl **************************************************************************
|
||||
dnl * Initialize
|
||||
dnl **************************************************************************
|
||||
AC_INIT([Polly],[0.01],[polly-dev@googlegroups.com])
|
||||
|
||||
dnl Identify where LLVM source tree is
|
||||
LLVM_SRC_ROOT="`(cd $srcdir/../..; pwd)`"
|
||||
LLVM_OBJ_ROOT="`(cd ../..; pwd)`"
|
||||
|
||||
dnl Tell autoconf that this is an LLVM project being configured
|
||||
dnl This provides the --with-llvmsrc and --with-llvmobj options
|
||||
LLVM_CONFIG_PROJECT($LLVM_SRC_ROOT,$LLVM_OBJ_ROOT)
|
||||
|
||||
dnl Tell autoconf that the auxilliary files are actually located in
|
||||
dnl the LLVM autoconf directory, not here.
|
||||
AC_CONFIG_AUX_DIR($LLVM_SRC/autoconf)
|
||||
|
||||
dnl Indicate that we require autoconf 2.59 or later. Ths is needed because we
|
||||
dnl use some autoconf macros only available in 2.59.
|
||||
AC_PREREQ(2.59)
|
||||
|
||||
dnl Verify that the source directory is valid
|
||||
AC_CONFIG_SRCDIR(["lib/Analysis/ScopInfo.cpp"])
|
||||
|
||||
dnl Configure a common Makefile
|
||||
AC_CONFIG_FILES(Makefile.config)
|
||||
AC_CONFIG_FILES(Makefile.common)
|
||||
|
||||
dnl Configure project makefiles
|
||||
dnl List every Makefile that exists within your source tree
|
||||
|
||||
dnl Quit if the source directory has already been configured.
|
||||
dnl NOTE: This relies upon undocumented autoconf behavior.
|
||||
if test ${srcdir} != "." ; then
|
||||
if test -f ${srcdir}/include/polly/Config/config.h ; then
|
||||
AC_MSG_ERROR([Already configured in ${srcdir}])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_DEFINE([CLOOG_INT_GMP], [1], [Use gmp for isl])
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Determine which system we are building on
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Check for programs.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl AC_PROG_CPP
|
||||
dnl AC_PROG_CC(gcc)
|
||||
dnl AC_PROG_CXX(g++)
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Check for libraries.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Checks for header files.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Checks for typedefs, structures, and compiler characteristics.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Checks for library functions.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Enable various compile-time options
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Set the location of various third-party software packages
|
||||
dnl **************************************************************************
|
||||
dnl Find Gmp
|
||||
find_lib_and_headers([gmp], [gmp.h], [gmp], [required])
|
||||
|
||||
dnl Find Isl
|
||||
find_lib_and_headers([isl], [isl/config.h], [isl], [required])
|
||||
|
||||
dnl Find cloog
|
||||
saved_CXXFLAGS=$CXXFLAGS
|
||||
CXXFLAGS="$CXXFLAGS $isl_inc"
|
||||
find_lib_and_headers([cloog], [cloog/isl/cloog.h], [cloog-isl], [required])
|
||||
CXXFLAGS=$saved_CXXFLAGS
|
||||
|
||||
|
||||
dnl Check if Scoplib there
|
||||
find_lib_and_headers([openscop], [openscop/scop.h], [openscop])
|
||||
AS_IF([test "x$openscop_found" = "xyes"],
|
||||
[AC_DEFINE([OPENSCOP_FOUND],[1],[Define if openscop found])])
|
||||
|
||||
dnl Check if Scoplib there
|
||||
find_lib_and_headers([scoplib], [scoplib/scop.h], [scoplib])
|
||||
AS_IF([test "x$scoplib_found" = "xyes"],
|
||||
[AC_DEFINE([SCOPLIB_FOUND],[1],[Define if scoplib found])])
|
||||
|
||||
if test "x$scoplib_found" = "xyes"; then :
|
||||
scoplib_rpath="-Wl,-rpath=$given_lib_path"
|
||||
else
|
||||
scoplib_rpath=""
|
||||
fi
|
||||
|
||||
AC_SUBST(scoplib_rpath)
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Create the output files
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl This must be last
|
||||
AC_CONFIG_HEADERS(include/polly/Config/config.h)
|
||||
AC_OUTPUT
|
|
@ -0,0 +1,65 @@
|
|||
dnl **************************************************************************
|
||||
dnl * Initialize
|
||||
dnl **************************************************************************
|
||||
AC_INIT([Polly],[0.01],[etherzhhb@gmail.com grosser@fim.uni-passau.de ojomojo@gmail.com])
|
||||
|
||||
dnl Identify where LLVM source tree is
|
||||
LLVM_SRC_ROOT="../.."
|
||||
LLVM_OBJ_ROOT="../.."
|
||||
dnl Tell autoconf that the auxilliary files are actually located in
|
||||
dnl the LLVM autoconf directory, not here.
|
||||
AC_CONFIG_AUX_DIR($LLVM_SRC_ROOT/autoconf)
|
||||
|
||||
dnl Tell autoconf that this is an LLVM project being configured
|
||||
dnl This provides the --with-llvmsrc and --with-llvmobj options
|
||||
LLVM_CONFIG_PROJECT($LLVM_SRC_ROOT,$LLVM_OBJ_ROOT)
|
||||
|
||||
dnl Verify that the source directory is valid
|
||||
AC_CONFIG_SRCDIR(["Makefile.common.in"])
|
||||
|
||||
dnl Configure a common Makefile
|
||||
AC_CONFIG_FILES(Makefile.common)
|
||||
|
||||
dnl Configure project makefiles
|
||||
dnl List every Makefile that exists within your source tree
|
||||
AC_CONFIG_MAKEFILE(Makefile)
|
||||
AC_CONFIG_MAKEFILE(lib/Makefile)
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Determine which system we are building on
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Check for programs.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Check for libraries.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Checks for header files.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Checks for typedefs, structures, and compiler characteristics.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Checks for library functions.
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Enable various compile-time options
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Set the location of various third-party software packages
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl **************************************************************************
|
||||
dnl * Create the output files
|
||||
dnl **************************************************************************
|
||||
|
||||
dnl This must be last
|
||||
AC_OUTPUT
|
|
@ -0,0 +1,46 @@
|
|||
dnl find_lib_and_headers(name, verify-header, library-name, requirded?)
|
||||
dnl Export
|
||||
dnl name_inc in -I"include-path" form
|
||||
dnl name_lib in -l"library-name" form
|
||||
dnl name_ld in -L"library-path" form
|
||||
dnl name_found set to "yes" if found
|
||||
|
||||
AC_DEFUN([find_lib_and_headers],
|
||||
[
|
||||
AC_LANG_PUSH(C++)
|
||||
OLD_CXXFLAGS=$CXXFLAGS;
|
||||
OLD_LDFLAGS=$LDFLAGS;
|
||||
OLD_LIBS=$LIBS;
|
||||
|
||||
LIBS="$LIBS -l$3";
|
||||
|
||||
# Get include path and lib path
|
||||
AC_ARG_WITH([$1],
|
||||
[AS_HELP_STRING([--with-$1], [prefix of $1 ])],
|
||||
[given_inc_path="$withval/include"; CXXFLAGS="-I$given_inc_path $CXXFLAGS";
|
||||
given_lib_path="$withval/lib"; LDFLAGS="-L$given_lib_path $LDFLAGS"],
|
||||
[given_inc_path=inc_not_give_$1;
|
||||
given_lib_path=lib_not_give_$1]
|
||||
)
|
||||
# Check for library and headers works
|
||||
AC_MSG_CHECKING([for $1 in $given_inc_path, $given_lib_path])
|
||||
# try to compile a file that includes a header of the library
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <$2>]], [[;]])],
|
||||
[AC_MSG_RESULT([ok])
|
||||
AC_SUBST([$1_found],["yes"])
|
||||
AS_IF([test "x$given_inc_path" != "xinc_not_give_$1"],
|
||||
[AC_SUBST([$1_inc],["-I$given_inc_path"])])
|
||||
AC_SUBST([$1_lib],["-l$3"])
|
||||
AS_IF([test "x$given_lib_path" != "xlib_not_give_$1"],
|
||||
[AC_SUBST([$1_ld],["-L$given_lib_path"])])],
|
||||
[AS_IF([test "x$4" = "xrequired"],
|
||||
[AC_MSG_ERROR([$1 required but not found])],
|
||||
[AC_MSG_RESULT([not found])])]
|
||||
)
|
||||
|
||||
# reset original CXXFLAGS
|
||||
CXXFLAGS=$OLD_CXXFLAGS
|
||||
LDFLAGS=$OLD_LDFLAGS;
|
||||
LIBS=$OLD_LIBS
|
||||
AC_LANG_POP(C++)
|
||||
])
|
|
@ -0,0 +1,19 @@
|
|||
FIND_PATH(CLOOG_INCLUDE_DIR cloog/isl/cloog.h)
|
||||
|
||||
FIND_LIBRARY(CLOOG_LIBRARY NAMES cloog-isl)
|
||||
|
||||
IF (CLOOG_INCLUDE_DIR AND CLOOG_LIBRARY)
|
||||
SET(CLOOG_FOUND TRUE)
|
||||
ENDIF (CLOOG_INCLUDE_DIR AND CLOOG_LIBRARY)
|
||||
|
||||
|
||||
IF (CLOOG_FOUND)
|
||||
IF (NOT CLOOG_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found Cloog: ${CLOOG_LIBRARY}")
|
||||
ENDIF (NOT CLOOG_FIND_QUIETLY)
|
||||
ELSE (CLOOG_FOUND)
|
||||
IF (CLOOG_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find Cloog")
|
||||
ENDIF (CLOOG_FIND_REQUIRED)
|
||||
ENDIF (CLOOG_FOUND)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
FIND_PATH(GMP_INCLUDE_DIR gmp.h)
|
||||
|
||||
FIND_LIBRARY(GMP_LIBRARY NAMES gmp)
|
||||
|
||||
IF (GMP_INCLUDE_DIR AND GMP_LIBRARY)
|
||||
SET(GMP_FOUND TRUE)
|
||||
ENDIF (GMP_INCLUDE_DIR AND GMP_LIBRARY)
|
||||
|
||||
|
||||
IF (GMP_FOUND)
|
||||
IF (NOT GMP_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found GMP: ${GMP_LIBRARY}")
|
||||
ENDIF (NOT GMP_FIND_QUIETLY)
|
||||
ELSE (GMP_FOUND)
|
||||
IF (GMP_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find GMP")
|
||||
ENDIF (GMP_FIND_REQUIRED)
|
||||
ENDIF (GMP_FOUND)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
FIND_PATH(ISL_INCLUDE_DIR isl/set.h)
|
||||
|
||||
FIND_LIBRARY(ISL_LIBRARY NAMES isl)
|
||||
|
||||
IF (ISL_INCLUDE_DIR AND ISL_LIBRARY)
|
||||
SET(ISL_FOUND TRUE)
|
||||
ENDIF (ISL_INCLUDE_DIR AND ISL_LIBRARY)
|
||||
|
||||
|
||||
IF (ISL_FOUND)
|
||||
IF (NOT Isl_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found Isl: ${ISL_LIBRARY}")
|
||||
ENDIF (NOT Isl_FIND_QUIETLY)
|
||||
ELSE (ISL_FOUND)
|
||||
IF (Isl_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find Isl")
|
||||
ENDIF (Isl_FIND_REQUIRED)
|
||||
ENDIF (ISL_FOUND)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
FIND_PATH(OPENSCOP_INCLUDE_DIR openscop/scop.h)
|
||||
|
||||
FIND_LIBRARY(OPENSCOP_LIBRARY NAMES openscop)
|
||||
|
||||
IF (OPENSCOP_INCLUDE_DIR AND OPENSCOP_LIBRARY)
|
||||
SET(OPENSCOP_FOUND TRUE)
|
||||
ENDIF (OPENSCOP_INCLUDE_DIR AND OPENSCOP_LIBRARY)
|
||||
|
||||
|
||||
IF (OPENSCOP_FOUND)
|
||||
IF (NOT Isl_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found OpenScop: ${OPENSCOP_LIBRARY}")
|
||||
ENDIF (NOT Isl_FIND_QUIETLY)
|
||||
ELSE (OPENSCOP_FOUND)
|
||||
IF (Isl_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find OpenScop")
|
||||
ENDIF (Isl_FIND_REQUIRED)
|
||||
ENDIF (OPENSCOP_FOUND)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
FIND_PATH(SCOPLIB_INCLUDE_DIR scoplib/scop.h)
|
||||
|
||||
FIND_LIBRARY(SCOPLIB_LIBRARY NAMES scoplib)
|
||||
|
||||
IF (SCOPLIB_INCLUDE_DIR AND SCOPLIB_LIBRARY)
|
||||
SET(SCOPLIB_FOUND TRUE)
|
||||
ENDIF (SCOPLIB_INCLUDE_DIR AND SCOPLIB_LIBRARY)
|
||||
|
||||
|
||||
IF (SCOPLIB_FOUND)
|
||||
IF (NOT Isl_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found SCoPLib: ${SCOPLIB_LIBRARY}")
|
||||
ENDIF (NOT Isl_FIND_QUIETLY)
|
||||
ELSE (SCOPLIB_FOUND)
|
||||
IF (Isl_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find SCoPLib")
|
||||
ENDIF (Isl_FIND_REQUIRED)
|
||||
ENDIF (SCOPLIB_FOUND)
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1>SAMPLE PROJECT DOCUMENTATION</h1>
|
||||
<p>This is just a placeholder</p>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,149 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="401.6861"
|
||||
height="238.80142"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="polly.png">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.32759489"
|
||||
inkscape:cx="105.19472"
|
||||
inkscape:cy="-140.99869"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="862"
|
||||
inkscape:window-height="879"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="19"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-180.51353,-120.73426)">
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot3053"
|
||||
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
|
||||
id="flowRegion3055"><rect
|
||||
id="rect3057"
|
||||
width="234.05714"
|
||||
height="123.29796"
|
||||
x="115.98367"
|
||||
y="215.39891" /></flowRegion><flowPara
|
||||
id="flowPara3059"></flowPara></flowRoot> <text
|
||||
xml:space="preserve"
|
||||
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:OpenSymbol;-inkscape-font-specification:OpenSymbol"
|
||||
x="173.45306"
|
||||
y="355.41525"
|
||||
id="text3061"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3063"
|
||||
x="173.45306"
|
||||
y="355.41525"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:URW Bookman L;-inkscape-font-specification:URW Bookman L" /></text>
|
||||
<g
|
||||
id="g3981">
|
||||
<g
|
||||
id="g3971">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:188.40333557px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#253ba3;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="166.2375"
|
||||
y="263.02325"
|
||||
id="text3069"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(0.97718415,1.0233486)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3071"
|
||||
x="166.2375"
|
||||
y="263.02325">Po</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:188.40333557px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#25a34d;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="401.41913"
|
||||
y="255.32637"
|
||||
id="text3073"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(0.97718415,1.0233486)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3075"
|
||||
x="401.41913"
|
||||
y="255.32637">L </tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:188.40333557px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#a38c25;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="489.90826"
|
||||
y="262.77887"
|
||||
id="text3077"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(0.97718415,1.0233486)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3079"
|
||||
x="489.90826"
|
||||
y="262.77887">y</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:188.40333557px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#a3257a;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="369.58545"
|
||||
y="282.15436"
|
||||
id="text3081"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(0.97718415,1.0233486)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3083"
|
||||
x="369.58545"
|
||||
y="282.15436">L</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
transform="scale(0.97718415,1.0233486)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text3085"
|
||||
y="343.51309"
|
||||
x="214.77977"
|
||||
style="font-size:188.40333557px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-size:37.68066788px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Liberation Mono;-inkscape-font-specification:Liberation Mono"
|
||||
y="343.51309"
|
||||
x="214.77977"
|
||||
id="tspan3087"
|
||||
sodipodi:role="line">Polyhedral LLVM</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.7 KiB |
|
@ -0,0 +1 @@
|
|||
add_subdirectory(polly)
|
|
@ -0,0 +1,9 @@
|
|||
if( MSVC_IDE OR XCODE )
|
||||
# Creates a dummy target containing all headers for the benefit of
|
||||
# Visual Studio users.
|
||||
file(GLOB_RECURSE headers *.h)
|
||||
add_library(polly_headers_do_not_build EXCLUDE_FROM_ALL
|
||||
# We need at least one source file:
|
||||
${POLLY_SOURCE_DIR}/lib/Support/GICHelper.cpp
|
||||
${headers})
|
||||
endif()
|
|
@ -0,0 +1,63 @@
|
|||
//===- CLooG.h - CLooG interface --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// CLooG[1] interface.
|
||||
//
|
||||
// The CLooG interface takes a Scop and generates a CLooG AST (clast). This
|
||||
// clast can either be returned directly or it can be pretty printed to stdout.
|
||||
//
|
||||
// A typical clast output looks like this:
|
||||
//
|
||||
// for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) {
|
||||
// bb2(c2);
|
||||
// }
|
||||
//
|
||||
// [1] http://www.cloog.org/ - The Chunky Loop Generator
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_CLOOG_H
|
||||
#define POLLY_CLOOG_H
|
||||
#include "polly/ScopPass.h"
|
||||
|
||||
#define CLOOG_INT_GMP 1
|
||||
#include "cloog/cloog.h"
|
||||
|
||||
struct clast_name;
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
}
|
||||
|
||||
namespace polly {
|
||||
class Scop;
|
||||
class Cloog;
|
||||
|
||||
class CloogInfo : public ScopPass {
|
||||
Cloog *C;
|
||||
Scop *scop;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
CloogInfo() : ScopPass(ID), C(0) {}
|
||||
|
||||
/// Write a .cloog input file
|
||||
void dump(FILE *F);
|
||||
|
||||
/// Print a source code representation of the program.
|
||||
void pprint(llvm::raw_ostream &OS);
|
||||
|
||||
/// Create the CLooG AST from this program.
|
||||
const struct clast_stmt *getClast();
|
||||
|
||||
bool runOnScop(Scop &S);
|
||||
void printScop(llvm::raw_ostream &OS) const;
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
}
|
||||
#endif /* POLLY_CLOOG_H */
|
|
@ -0,0 +1,19 @@
|
|||
//===- polly/Config.h ------------ Configuration of Polly -------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Configuration of Polly.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef POLLY_CONFIG_H
|
||||
#define POLLY_CONFIG_H
|
||||
|
||||
#cmakedefine OPENSCOP_FOUND
|
||||
#cmakedefine SCOPLIB_FOUND
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
/* include/polly/Config/config.h.in. Generated from autoconf/configure.ac by autoheader. */
|
||||
|
||||
/* Use gmp for isl */
|
||||
#undef CLOOG_INT_GMP
|
||||
|
||||
/* Define if openscop found */
|
||||
#undef OPENSCOP_FOUND
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define if scoplib found */
|
||||
#undef SCOPLIB_FOUND
|
|
@ -0,0 +1,79 @@
|
|||
//===------ polly/Dependences.h - Polyhedral dependency analysis *- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Calculate the data dependency relations for a Scop using ISL.
|
||||
//
|
||||
// The integer set library (ISL) from Sven, has a integrated dependency analysis
|
||||
// to calculate data dependences. This pass takes advantage of this and
|
||||
// calculate those dependences a Scop.
|
||||
//
|
||||
// The dependences in this pass are exact in terms that for a specific read
|
||||
// statement instance only the last write statement instance is returned. In
|
||||
// case of may writes a set of possible write instances is returned. This
|
||||
// analysis will never produce redundant dependences.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_DEPENDENCES_H
|
||||
#define POLLY_DEPENDENCES_H
|
||||
|
||||
#include "polly/ScopPass.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
struct isl_union_map;
|
||||
struct isl_union_set;
|
||||
struct isl_map;
|
||||
struct isl_set;
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace polly {
|
||||
|
||||
class Scop;
|
||||
class ScopStmt;
|
||||
|
||||
class Dependences : public ScopPass {
|
||||
|
||||
isl_union_map *must_dep, *may_dep;
|
||||
isl_union_map *must_no_source, *may_no_source;
|
||||
|
||||
isl_union_map *war_dep;
|
||||
isl_union_map *waw_dep;
|
||||
|
||||
isl_union_map *sink;
|
||||
isl_union_map *must_source;
|
||||
isl_union_map *may_source;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
typedef std::map<ScopStmt*, isl_map*> StatementToIslMapTy;
|
||||
|
||||
Dependences();
|
||||
bool isValidScattering(StatementToIslMapTy *NewScatterings);
|
||||
|
||||
/// @brief Check if a dimension of the Scop can be executed in parallel.
|
||||
///
|
||||
/// @param loopDomain The subset of the scattering space that is executed in
|
||||
/// parallel.
|
||||
/// @param parallelDimension The scattering dimension that is being executed
|
||||
/// in parallel.
|
||||
///
|
||||
/// @return bool Returns true, if executing parallelDimension in parallel is
|
||||
/// valid for the scattering domain subset given.
|
||||
bool isParallelDimension(isl_set *loopDomain, unsigned parallelDimension);
|
||||
|
||||
bool runOnScop(Scop &S);
|
||||
void printScop(raw_ostream &OS) const;
|
||||
virtual void releaseMemory();
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
} // End polly namespace.
|
||||
|
||||
#endif
|
|
@ -0,0 +1,105 @@
|
|||
//===- polly/LinkAllPasses.h ------------ Reference All Passes ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This header file pulls in all transformation and analysis passes for tools
|
||||
// like opt and bugpoint that need this functionality.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_LINKALLPASSES_H
|
||||
#define POLLY_LINKALLPASSES_H
|
||||
|
||||
#include "polly/Config/config.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace llvm {
|
||||
class Pass;
|
||||
class PassInfo;
|
||||
class RegionPass;
|
||||
}
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace polly {
|
||||
Pass *createAffSCEVItTesterPass();
|
||||
Pass *createCloogExporterPass();
|
||||
Pass *createCloogInfoPass();
|
||||
Pass *createCodeGenerationPass();
|
||||
Pass *createCodePreperationPass();
|
||||
Pass *createDependencesPass();
|
||||
Pass *createDOTOnlyPrinterPass();
|
||||
Pass *createDOTOnlyViewerPass();
|
||||
Pass *createDOTPrinterPass();
|
||||
Pass *createDOTViewerPass();
|
||||
Pass *createIndependentBlocksPass();
|
||||
Pass *createInterchangePass();
|
||||
Pass *createJSONExporterPass();
|
||||
Pass *createJSONImporterPass();
|
||||
Pass *createRegionSimplifyPass();
|
||||
Pass *createScopInfoPass();
|
||||
|
||||
#ifdef OPENSCOP_FOUND
|
||||
Pass *createScopExporterPass();
|
||||
Pass *createScopImporterPass();
|
||||
#endif
|
||||
|
||||
#ifdef SCOPLIB_FOUND
|
||||
Pass *createPoccPass();
|
||||
Pass *createScopLibExporterPass();
|
||||
Pass *createScopLibImporterPass();
|
||||
#endif
|
||||
|
||||
extern char &IndependentBlocksID;
|
||||
extern char &CodePreperationID;
|
||||
}
|
||||
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
struct PollyForcePassLinking {
|
||||
PollyForcePassLinking() {
|
||||
// We must reference the passes in such a way that compilers will not
|
||||
// delete it all as dead code, even with whole program optimization,
|
||||
// yet is effectively a NO-OP. As the compiler isn't smart enough
|
||||
// to know that getenv() never returns -1, this will do the job.
|
||||
if (std::getenv("bar") != (char*) -1)
|
||||
return;
|
||||
|
||||
createAffSCEVItTesterPass();
|
||||
createCloogExporterPass();
|
||||
createCloogInfoPass();
|
||||
createCodeGenerationPass();
|
||||
createCodePreperationPass();
|
||||
createDependencesPass();
|
||||
createDOTOnlyPrinterPass();
|
||||
createDOTOnlyViewerPass();
|
||||
createDOTPrinterPass();
|
||||
createDOTViewerPass();
|
||||
createIndependentBlocksPass();
|
||||
createInterchangePass();
|
||||
createJSONExporterPass();
|
||||
createJSONImporterPass();
|
||||
createRegionSimplifyPass();
|
||||
createScopInfoPass();
|
||||
|
||||
#ifdef OPENSCOP_FOUND
|
||||
createScopExporterPass();
|
||||
createScopImporterPass();
|
||||
#endif
|
||||
#ifdef SCOPLIB_FOUND
|
||||
createPoccPass();
|
||||
createScopLibExporterPass();
|
||||
createScopLibImporterPass();
|
||||
#endif
|
||||
|
||||
}
|
||||
} PollyForcePassLinking; // Force link by creating a global definition.
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,157 @@
|
|||
//===- MayAliasSet.h - May-alias Set for Base Pointers ---------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines two classes: MayAliasSet and MayAliasSetInfo.
|
||||
// MayAliasSet contains the base pointers of access functions in SCoP that
|
||||
// may/must alias each others. And MayAliasSetInfo will compute and hold these
|
||||
// MayAliasSets in every SCoP in a function.
|
||||
//
|
||||
// The difference between MayAliasSet and the original LLVM AliasSet is that
|
||||
// the LLVM AliasSets are disjoint, but MayAliasSets are not.
|
||||
//
|
||||
// Suppose we have the following LLVM IR:
|
||||
// define void @f(i32* noalias nocapture %a, i32* noalias nocapture %b)nounwind{
|
||||
// bb.nph:
|
||||
// %0 = tail call i32 (...)* @rnd() nounwind
|
||||
// %1 = icmp eq i32 %0, 0
|
||||
// %ptr0 = select i1 %1, i32* %b, i32* %a
|
||||
// %2 = load i32* %ptr0, align 4
|
||||
// %3 = load i32* %a, align 4
|
||||
// %4 = load i32* %b, align 4
|
||||
// ret void
|
||||
// }
|
||||
//
|
||||
// The LLVM AliasSetTracker constructs only one LLVM AliasSet that contains
|
||||
// ptr0, a and b, but MayAliasSetInfo is supposed to build two MayAliasSets:
|
||||
// {a, ptr0} and {b, ptr0}.
|
||||
//
|
||||
// Take the above LLVM IR for example, the MayAliasSetInfo builds two set:
|
||||
// A: {a, ptr0} and B: {b, ptr0} and constructs base pointer to MayAliasSet
|
||||
// mapping like:
|
||||
// a -> A
|
||||
// b -> B
|
||||
// ptr0 -> A, B
|
||||
//
|
||||
// After that, SCoPInfo pass will build a access function for each MayAliasSet,
|
||||
// so "%2 = load i32* %ptr0, align 4" will be translated to "read A" and
|
||||
// "read B", while "%3 = load i32* %a, align 4" will be translated to "read A",
|
||||
// and "%4 = load i32* %b, align 4" will be translated to "read B". This means
|
||||
// we can treat the MayAliasSet as the identifier of the virtual array of memory
|
||||
// access in SCoPs.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_MAY_ALIAS_SET_H
|
||||
#define POLLY_MAY_ALIAS_SET_H
|
||||
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include <map>
|
||||
|
||||
namespace llvm {
|
||||
class Value;
|
||||
class AliasAnalysis;
|
||||
class raw_ostream;
|
||||
}
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace polly {
|
||||
class MayAliasSetInfo;
|
||||
class TempScop;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief MayAliasSet of pointers in SCoPs.
|
||||
///
|
||||
/// Note: Pointers in MayAliasSet only must-alias with each other now.
|
||||
class MayAliasSet {
|
||||
// DO NOT IMPLEMENT
|
||||
MayAliasSet(const MayAliasSet &);
|
||||
// DO NOT IMPLEMENT
|
||||
const MayAliasSet &operator=(const MayAliasSet &);
|
||||
|
||||
// TODO: Use CallbackVH to update the set when some base pointers are deleted
|
||||
// by some pass.
|
||||
SmallPtrSet<const Value*, 8> MustAliasPtrs;
|
||||
|
||||
MayAliasSet() {}
|
||||
|
||||
friend class MayAliasSetInfo;
|
||||
public:
|
||||
|
||||
/// @name Must Alias Pointer Iterators
|
||||
///
|
||||
/// These iterators iterate over all must alias pointers in the set.
|
||||
//@{
|
||||
typedef SmallPtrSetIterator<const Value*> const_iterator;
|
||||
const_iterator mustalias_begin() const { return MustAliasPtrs.begin(); }
|
||||
const_iterator mustalias_end() const { return MustAliasPtrs.end(); }
|
||||
//@}
|
||||
|
||||
/// @brief Add a must alias pointer to this set.
|
||||
///
|
||||
/// @param V The pointer to add.
|
||||
void addMustAliasPtr(const Value* V) { MustAliasPtrs.insert(V); }
|
||||
|
||||
void print(raw_ostream &OS) const;
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief Compute and manage the may-alias sets in a TempSCoP or SCoP.
|
||||
class MayAliasSetInfo {
|
||||
// DO NOT IMPLEMENT
|
||||
MayAliasSetInfo(const MayAliasSetInfo &);
|
||||
// DO NOT IMPLEMENT
|
||||
const MayAliasSetInfo &operator=(const MayAliasSetInfo &);
|
||||
|
||||
SpecificBumpPtrAllocator<MayAliasSet> MayASAllocator;
|
||||
|
||||
// Mapping the pointers to their may-alias sets.
|
||||
typedef std::multimap<const Value*, MayAliasSet*> MayAliasSetMapType;
|
||||
MayAliasSetMapType BasePtrMap;
|
||||
|
||||
public:
|
||||
MayAliasSetInfo() {}
|
||||
|
||||
/// @name MayAliasSet Iterators
|
||||
///
|
||||
/// These iterators iterate over all may-alias sets referring to a base
|
||||
/// pointer.
|
||||
//@{
|
||||
typedef MayAliasSetMapType::iterator alias_iterator;
|
||||
typedef MayAliasSetMapType::const_iterator const_alias_iterator;
|
||||
|
||||
alias_iterator alias_begin(const Value *BasePtr) {
|
||||
return BasePtrMap.lower_bound(BasePtr);
|
||||
}
|
||||
|
||||
alias_iterator alias_end(const Value *BasePtr) {
|
||||
return BasePtrMap.upper_bound(BasePtr);
|
||||
}
|
||||
|
||||
const_alias_iterator alias_begin(const Value *BasePtr) const {
|
||||
return BasePtrMap.lower_bound(BasePtr);
|
||||
}
|
||||
|
||||
const_alias_iterator alias_end(const Value *BasePtr) const {
|
||||
return BasePtrMap.upper_bound(BasePtr);
|
||||
}
|
||||
//@}
|
||||
|
||||
|
||||
/// @brief Build MayAliasSets in a SCoP.
|
||||
///
|
||||
/// @param Scop The SCoP to build MayAliasSets in.
|
||||
/// @param AA The AliasAnalaysis provides the alias information.
|
||||
void buildMayAliasSets(TempScop &Scop, AliasAnalysis &AA);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,272 @@
|
|||
//===--- ScopDetection.h - Detect Scops -------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Detect the maximal Scops of a function.
|
||||
//
|
||||
// A static control part (Scop) is a subgraph of the control flow graph (CFG)
|
||||
// that only has statically known control flow and can therefore be described
|
||||
// within the polyhedral model.
|
||||
//
|
||||
// Every Scop fullfills these restrictions:
|
||||
//
|
||||
// * It is a single entry single exit region
|
||||
//
|
||||
// * Only affine linear bounds in the loops
|
||||
//
|
||||
// Every natural loop in a Scop must have a number of loop iterations that can
|
||||
// be described as an affine linear function in surrounding loop iterators or
|
||||
// parameters. (A parameter is a scalar that does not change its value during
|
||||
// execution of the Scop).
|
||||
//
|
||||
// * Only comparisons of affine linear expressions in conditions
|
||||
//
|
||||
// * All loops and conditions perfectly nested
|
||||
//
|
||||
// The control flow needs to be structured such that it could be written using
|
||||
// just 'for' and 'if' statements, without the need for any 'goto', 'break' or
|
||||
// 'continue'.
|
||||
//
|
||||
// * Side effect free functions call
|
||||
//
|
||||
// Only function calls and intrinsics that do not have side effects are allowed
|
||||
// (readnone).
|
||||
//
|
||||
// The Scop detection finds the largest Scops by checking if the largest
|
||||
// region is a Scop. If this is not the case, its canonical subregions are
|
||||
// checked until a region is a Scop. It is now tried to extend this Scop by
|
||||
// creating a larger non canonical region.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_SCOP_DETECTION_H
|
||||
#define POLLY_SCOP_DETECTION_H
|
||||
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Analysis/AliasSetTracker.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace llvm {
|
||||
class RegionInfo;
|
||||
class Region;
|
||||
class LoopInfo;
|
||||
class Loop;
|
||||
class ScalarEvolution;
|
||||
class SCEV;
|
||||
class SCEVAddRecExpr;
|
||||
class CallInst;
|
||||
class Instruction;
|
||||
class AliasAnalysis;
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace polly {
|
||||
typedef std::set<const SCEV*> ParamSetType;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief Pass to detect the maximal static control parts (Scops) of a
|
||||
/// function.
|
||||
class ScopDetection : public FunctionPass {
|
||||
//===--------------------------------------------------------------------===//
|
||||
// DO NOT IMPLEMENT
|
||||
ScopDetection(const ScopDetection &);
|
||||
// DO NOT IMPLEMENT
|
||||
const ScopDetection &operator=(const ScopDetection &);
|
||||
|
||||
/// @brief Analysis passes used.
|
||||
//@{
|
||||
ScalarEvolution* SE;
|
||||
LoopInfo *LI;
|
||||
RegionInfo *RI;
|
||||
AliasAnalysis *AA;
|
||||
//@}
|
||||
|
||||
/// @brief Context variables for SCoP detection.
|
||||
struct DetectionContext {
|
||||
Region &CurRegion; // The region to check.
|
||||
AliasSetTracker AST; // The AliasSetTracker to hold the alias information.
|
||||
bool Verifying; // If we are in the verification phase?
|
||||
DetectionContext(Region &R, AliasAnalysis &AA, bool Verify)
|
||||
: CurRegion(R), AST(AA), Verifying(Verify) {}
|
||||
};
|
||||
|
||||
// Remember the valid regions
|
||||
typedef std::set<const Region*> RegionSet;
|
||||
RegionSet ValidRegions;
|
||||
|
||||
// Try to expand the region R. If R can be expanded return the expanded
|
||||
// region, NULL otherwise.
|
||||
Region *expandRegion(Region &R);
|
||||
|
||||
// Find the Scops in this region tree.
|
||||
void findScops(Region &R);
|
||||
|
||||
/// @brief Check if all basic block in the region are valid.
|
||||
///
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if all blocks in R are valid, false otherwise.
|
||||
bool allBlocksValid(DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check the exit block of a region is valid.
|
||||
///
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if the exit of R is valid, false otherwise.
|
||||
bool isValidExit(DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if a region is a Scop.
|
||||
///
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if R is a Scop, false otherwise.
|
||||
bool isValidRegion(DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if a call instruction can be part of a Scop.
|
||||
///
|
||||
/// @param CI The call instruction to check.
|
||||
/// @return True if the call instruction is valid, false otherwise.
|
||||
static bool isValidCallInst(CallInst &CI);
|
||||
|
||||
/// @brief Check if a memory access can be part of a Scop.
|
||||
///
|
||||
/// @param Inst The instruction accessing the memory.
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if the memory access is valid, false otherwise.
|
||||
bool isValidMemoryAccess(Instruction &Inst, DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if an instruction has any non trivial scalar dependencies
|
||||
/// as part of a Scop.
|
||||
///
|
||||
/// @param Inst The instruction to check.
|
||||
/// @param RefRegion The region in respect to which we check the access
|
||||
/// function.
|
||||
///
|
||||
/// @return True if the instruction has scalar dependences, false otherwise.
|
||||
bool hasScalarDependency(Instruction &Inst, Region &RefRegion) const;
|
||||
|
||||
/// @brief Check if an instruction can be part of a Scop.
|
||||
///
|
||||
/// @param Inst The instruction to check.
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if the instruction is valid, false otherwise.
|
||||
bool isValidInstruction(Instruction &I, DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if the BB can be part of a Scop.
|
||||
///
|
||||
/// @param BB The basic block to check.
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if the basic block is valid, false otherwise.
|
||||
bool isValidBasicBlock(BasicBlock &BB, DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if the control flow in a basic block is valid.
|
||||
///
|
||||
/// @param BB The BB to check the control flow.
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if the BB contains only valid control flow.
|
||||
bool isValidCFG(BasicBlock &BB, DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if the SCEV expression is a valid affine function
|
||||
///
|
||||
/// @param S The SCEV expression to be checked
|
||||
/// @param RefRegion The reference scope to check SCEV, it help to find out
|
||||
/// induction variables and parameters
|
||||
/// @param BasePtr If S represents a memory access, BasePtr should contain
|
||||
/// a valid memory location to which the base address of the
|
||||
/// memory access will be stored.
|
||||
///
|
||||
/// @return True if the SCEV expression is affine, false otherwise
|
||||
///
|
||||
bool isValidAffineFunction(const SCEV *S, Region &RefRegion,
|
||||
Value **BasePtr = 0) const;
|
||||
|
||||
/// @brief Is a loop valid with respect to a given region.
|
||||
///
|
||||
/// @param L The loop to check.
|
||||
/// @param Context The context of scop detection.
|
||||
///
|
||||
/// @return True if the loop is valid in the region.
|
||||
bool isValidLoop(Loop *L, DetectionContext &Context) const;
|
||||
|
||||
/// @brief Check if a function is an OpenMP subfunction.
|
||||
///
|
||||
/// An OpenMP subfunction is not valid for Scop detection.
|
||||
///
|
||||
/// @param F The function to check.
|
||||
///
|
||||
/// @return True if the function is not an OpenMP subfunction.
|
||||
bool isValidFunction(llvm::Function &F);
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
explicit ScopDetection() : FunctionPass(ID) {}
|
||||
|
||||
/// @brief Get the RegionInfo stored in this pass.
|
||||
///
|
||||
/// This was added to give the DOT printer easy access to this information.
|
||||
RegionInfo *getRI() const { return RI; }
|
||||
|
||||
/// @brief Is the region is the maximum region of a Scop?
|
||||
///
|
||||
/// @param R The Region to test if it is maximum.
|
||||
///
|
||||
/// @return Return true if R is the maximum Region in a Scop, false otherwise.
|
||||
bool isMaxRegionInScop(const Region &R) const;
|
||||
|
||||
/// @name Maximum Region In Scops Iterators
|
||||
///
|
||||
/// These iterators iterator over all maximum region in Scops of this
|
||||
/// function.
|
||||
//@{
|
||||
typedef RegionSet::iterator iterator;
|
||||
typedef RegionSet::const_iterator const_iterator;
|
||||
|
||||
iterator begin() { return ValidRegions.begin(); }
|
||||
iterator end() { return ValidRegions.end(); }
|
||||
|
||||
const_iterator begin() const { return ValidRegions.begin(); }
|
||||
const_iterator end() const { return ValidRegions.end(); }
|
||||
//@}
|
||||
|
||||
/// @brief Remove a region and its children from valid region set.
|
||||
///
|
||||
/// @param R The region to remove.
|
||||
void forgetScop(const Region &R) {
|
||||
assert(isMaxRegionInScop(R) && "R is not a Scop!");
|
||||
ValidRegions.erase(&R);
|
||||
}
|
||||
|
||||
/// @brief Verify if all valid Regions in this Function are still valid
|
||||
/// after some transformations.
|
||||
void verifyAnalysis() const;
|
||||
|
||||
/// @brief Verify if R is still a valid part of Scop after some
|
||||
/// transformations.
|
||||
///
|
||||
/// @param R The Region to verify.
|
||||
void verifyRegion(const Region &R) const;
|
||||
|
||||
/// @name FunctionPass interface
|
||||
//@{
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
virtual void releaseMemory();
|
||||
virtual bool runOnFunction(Function &F);
|
||||
virtual void print(raw_ostream &OS, const Module *) const;
|
||||
//@}
|
||||
};
|
||||
|
||||
} //end namespace polly
|
||||
|
||||
#endif
|
|
@ -0,0 +1,578 @@
|
|||
//===------ polly/ScopInfo.h - Create Scops from LLVM IR --------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Create a polyhedral description for a static control flow region.
|
||||
//
|
||||
// The pass creates a polyhedral description of the Scops detected by the Scop
|
||||
// detection derived from their LLVM-IR code.
|
||||
//
|
||||
// This represantation is shared among several tools in the polyhedral
|
||||
// community, which are e.g. CLooG, Pluto, Loopo, Graphite.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_SCOP_INFO_H
|
||||
#define POLLY_SCOP_INFO_H
|
||||
|
||||
#include "llvm/Analysis/RegionPass.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace llvm {
|
||||
class SCEV;
|
||||
class ScalarEvolution;
|
||||
class SCEVAddRecExpr;
|
||||
class Loop;
|
||||
class LoopInfo;
|
||||
class Type;
|
||||
class PHINode;
|
||||
}
|
||||
|
||||
struct isl_map;
|
||||
struct isl_basic_map;
|
||||
struct isl_set;
|
||||
struct isl_ctx;
|
||||
struct isl_dim;
|
||||
struct isl_constraint;
|
||||
|
||||
namespace polly {
|
||||
|
||||
class Scop;
|
||||
class ScopStmt;
|
||||
class ScopInfo;
|
||||
class TempScop;
|
||||
class SCEVAffFunc;
|
||||
class Comparison;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief Represent memory accesses in statements.
|
||||
class MemoryAccess {
|
||||
// DO NOT IMPLEMENT
|
||||
MemoryAccess(const MemoryAccess &);
|
||||
// DO NOT IMPLEMENT
|
||||
const MemoryAccess &operator=(const MemoryAccess &);
|
||||
|
||||
public:
|
||||
/// @brief The access type of a memory access
|
||||
///
|
||||
/// There are three kind of access types:
|
||||
///
|
||||
/// * A read access
|
||||
///
|
||||
/// A certain set of memory locations are read and may be used for internal
|
||||
/// calculations.
|
||||
///
|
||||
/// * A write access
|
||||
///
|
||||
/// A certain set of memory locactions is definitely written. The old value is
|
||||
/// replaced by a newly calculated value. The old value is not read or used at
|
||||
/// all.
|
||||
///
|
||||
/// * A may write access
|
||||
///
|
||||
/// A certain set of memory locactions may be written. The memory location may
|
||||
/// contain a new value if there is actually a write or the old value may
|
||||
/// remain, if no write happens.
|
||||
enum AccessType {
|
||||
Read,
|
||||
Write,
|
||||
MayWrite
|
||||
};
|
||||
|
||||
private:
|
||||
isl_map *AccessRelation;
|
||||
enum AccessType Type;
|
||||
|
||||
const Value* BaseAddr;
|
||||
std::string BaseName;
|
||||
isl_basic_map *createBasicAccessMap(ScopStmt *Statement);
|
||||
void setBaseName();
|
||||
ScopStmt *statement;
|
||||
|
||||
public:
|
||||
// @brief Create an affine memory access.
|
||||
//
|
||||
// @param AffFunc The access function.
|
||||
// @param Statement The Statement that contains this access.
|
||||
// @param SE The ScalarEvolution analysis.
|
||||
MemoryAccess(const SCEVAffFunc &AffFunc, ScopStmt *Statement);
|
||||
|
||||
// @brief Create a read all access.
|
||||
//
|
||||
// @param BaseAddress The base address of the memory accessed.
|
||||
// @param Statement The Statement that contains this access.
|
||||
MemoryAccess(const Value *BaseAddress, ScopStmt *Statement);
|
||||
|
||||
~MemoryAccess();
|
||||
|
||||
/// @brief Is this a read memory access?
|
||||
bool isRead() const { return Type == MemoryAccess::Read; }
|
||||
|
||||
isl_map *getAccessFunction() { return AccessRelation; }
|
||||
isl_map *getAccessFunction() const { return AccessRelation; }
|
||||
|
||||
/// @brief Get an isl string representing this access function.
|
||||
std::string getAccessFunctionStr() const;
|
||||
|
||||
const Value *getBaseAddr() const {
|
||||
return BaseAddr;
|
||||
}
|
||||
|
||||
const std::string &getBaseName() const {
|
||||
return BaseName;
|
||||
}
|
||||
|
||||
/// @brief Get the stride of this memory access in the specified domain
|
||||
/// subset.
|
||||
isl_set *getStride(const isl_set *domainSubset) const;
|
||||
|
||||
/// @brief Is consecutive memory accessed for a given
|
||||
/// statement instance set?
|
||||
bool isStrideOne(const isl_set *domainSubset) const;
|
||||
|
||||
/// @brief Is always the same memory accessed for a given
|
||||
/// statement instance set?
|
||||
bool isStrideZero(const isl_set *domainSubset) const;
|
||||
|
||||
/// @brief Get the statement that contains this memory access.
|
||||
ScopStmt *getStatement() const { return statement; }
|
||||
|
||||
/// @brief Print the MemoryAccess.
|
||||
///
|
||||
/// @param OS The output stream the MemoryAccess is printed to.
|
||||
void print(raw_ostream &OS) const;
|
||||
|
||||
/// @brief Print the MemoryAccess to stderr.
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief Statement of the Scop
|
||||
///
|
||||
/// A Scop statement represents an instruction in the Scop.
|
||||
///
|
||||
/// It is further described by its iteration domain, its schedule and its data
|
||||
/// accesses.
|
||||
/// At the moment every statement represents a single basic block of LLVM-IR.
|
||||
class ScopStmt {
|
||||
//===-------------------------------------------------------------------===//
|
||||
// DO NOT IMPLEMENT
|
||||
ScopStmt(const ScopStmt &);
|
||||
// DO NOT IMPLEMENT
|
||||
const ScopStmt &operator=(const ScopStmt &);
|
||||
|
||||
|
||||
/// Polyhedral description
|
||||
//@{
|
||||
|
||||
/// The Scop containing this ScopStmt
|
||||
Scop &Parent;
|
||||
|
||||
/// The iteration domain describes the set of iterations for which this
|
||||
/// statement is executed.
|
||||
///
|
||||
/// Example:
|
||||
/// for (i = 0; i < 100 + b; ++i)
|
||||
/// for (j = 0; j < i; ++j)
|
||||
/// S(i,j);
|
||||
///
|
||||
/// 'S' is executed for different values of i and j. A vector of all
|
||||
/// induction variables around S (i, j) is called iteration vector.
|
||||
/// The domain describes the set of possible iteration vectors.
|
||||
///
|
||||
/// In this case it is:
|
||||
///
|
||||
/// Domain: 0 <= i <= 100 + b
|
||||
/// 0 <= j <= i
|
||||
///
|
||||
/// A pair of statment and iteration vector (S, (5,3)) is called statment
|
||||
/// instance.
|
||||
isl_set *Domain;
|
||||
|
||||
/// The scattering map describes the execution order of the statement
|
||||
/// instances.
|
||||
///
|
||||
/// A statement and its iteration domain do not give any information about the
|
||||
/// order in time in which the different statement instances are executed.
|
||||
/// This information is provided by the scattering.
|
||||
///
|
||||
/// The scattering maps every instance of each statement into a multi
|
||||
/// dimensional scattering space. This space can be seen as a multi
|
||||
/// dimensional clock.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// <S,(5,4)> may be mapped to (5,4) by this scattering:
|
||||
///
|
||||
/// s0 = i (Year of execution)
|
||||
/// s1 = j (Day of execution)
|
||||
///
|
||||
/// or to (9, 20) by this scattering:
|
||||
///
|
||||
/// s0 = i + j (Year of execution)
|
||||
/// s1 = 20 (Day of execution)
|
||||
///
|
||||
/// The order statement instances are executed is defined by the
|
||||
/// scattering vectors they are mapped to. A statement instance
|
||||
/// <A, (i, j, ..)> is executed before a statement instance <B, (i', ..)>, if
|
||||
/// the scattering vector of A is lexicographic smaller than the scattering
|
||||
/// vector of B.
|
||||
isl_map *Scattering;
|
||||
|
||||
/// The memory accesses of this statement.
|
||||
///
|
||||
/// The only side effects of a statement are its memory accesses.
|
||||
typedef SmallVector<MemoryAccess*, 8> MemoryAccessVec;
|
||||
MemoryAccessVec MemAccs;
|
||||
std::map<const Instruction*, MemoryAccess*> InstructionToAccess;
|
||||
|
||||
//@}
|
||||
|
||||
/// The BasicBlock represented by this statement.
|
||||
BasicBlock *BB;
|
||||
|
||||
/// @brief Whether this statement is a reduction.
|
||||
bool IsReduction;
|
||||
|
||||
/// @brief The loop induction variables surrounding the statement.
|
||||
///
|
||||
/// This information is only needed for final code generation.
|
||||
std::vector<PHINode*> IVS;
|
||||
|
||||
std::string BaseName;
|
||||
|
||||
/// Build the statment.
|
||||
//@{
|
||||
isl_set *toUpperLoopBound(const SCEVAffFunc &UpperBound, isl_dim *dim,
|
||||
unsigned BoundedDimension) const;
|
||||
isl_set *toConditionSet(const Comparison &Cmp, isl_dim *dim) const;
|
||||
void addConditionsToDomain(TempScop &tempScop, const Region &CurRegion);
|
||||
void buildIterationDomainFromLoops(TempScop &tempScop);
|
||||
void buildIterationDomain(TempScop &tempScop, const Region &CurRegion);
|
||||
void buildScattering(SmallVectorImpl<unsigned> &Scatter);
|
||||
void buildAccesses(TempScop &tempScop, const Region &CurRegion);
|
||||
//@}
|
||||
|
||||
/// Create the ScopStmt from a BasicBlock.
|
||||
ScopStmt(Scop &parent, TempScop &tempScop, const Region &CurRegion,
|
||||
BasicBlock &bb, SmallVectorImpl<Loop*> &NestLoops,
|
||||
SmallVectorImpl<unsigned> &Scatter);
|
||||
|
||||
/// Create the finalization statement.
|
||||
ScopStmt(Scop &parent, SmallVectorImpl<unsigned> &Scatter);
|
||||
|
||||
friend class Scop;
|
||||
public:
|
||||
|
||||
~ScopStmt();
|
||||
|
||||
/// @brief Get an isl_ctx pointer.
|
||||
isl_ctx *getIslContext();
|
||||
|
||||
/// @brief Get the iteration domain of this ScopStmt.
|
||||
///
|
||||
/// @return The iteration domain of this ScopStmt.
|
||||
isl_set *getDomain() const { return Domain; }
|
||||
|
||||
/// @brief Get an isl string representing this domain.
|
||||
std::string getDomainStr() const;
|
||||
|
||||
/// @brief Get the scattering function of this ScopStmt.
|
||||
///
|
||||
/// @return The scattering function of this ScopStmt.
|
||||
isl_map *getScattering() const { return Scattering; }
|
||||
void setScattering(isl_map *scattering) { Scattering = scattering; }
|
||||
|
||||
/// @brief Get an isl string representing this scattering.
|
||||
std::string getScatteringStr() const;
|
||||
|
||||
/// @brief Get the BasicBlock represented by this ScopStmt.
|
||||
///
|
||||
/// @return The BasicBlock represented by this ScopStmt.
|
||||
BasicBlock *getBasicBlock() const { return BB; }
|
||||
|
||||
MemoryAccess &getAccessFor(const Instruction *Inst) {
|
||||
return *InstructionToAccess[Inst];
|
||||
}
|
||||
|
||||
void setBasicBlock(BasicBlock *Block) { BB = Block; }
|
||||
|
||||
typedef MemoryAccessVec::iterator memacc_iterator;
|
||||
memacc_iterator memacc_begin() { return MemAccs.begin(); }
|
||||
memacc_iterator memacc_end() { return MemAccs.end(); }
|
||||
|
||||
unsigned getNumParams() const;
|
||||
unsigned getNumIterators() const;
|
||||
unsigned getNumScattering() const;
|
||||
|
||||
Scop *getParent() { return &Parent; }
|
||||
const Scop *getParent() const { return &Parent; }
|
||||
|
||||
const char *getBaseName() const;
|
||||
/// @brief Get the induction variable for a dimension.
|
||||
///
|
||||
/// @param Dimension The dimension of the induction variable
|
||||
/// @return The induction variable at a certain dimension.
|
||||
const PHINode *getInductionVariableForDimension(unsigned Dimension) const;
|
||||
|
||||
/// @brief Return the SCEV for a loop dimension.
|
||||
const SCEVAddRecExpr *getSCEVForDimension(unsigned Dimension) const;
|
||||
|
||||
/// @brief Is this statement the final read statement?
|
||||
///
|
||||
/// A final read statement is scheduled after all statements to model
|
||||
/// that all data used in the Scop is read after the Scop.
|
||||
bool isFinalRead() { return getBasicBlock() == NULL; }
|
||||
|
||||
bool isReduction() { return IsReduction; }
|
||||
|
||||
/// @brief Print the ScopStmt.
|
||||
///
|
||||
/// @param OS The output stream the ScopStmt is printed to.
|
||||
void print(raw_ostream &OS) const;
|
||||
|
||||
/// @brief Print the ScopStmt to stderr.
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
/// @brief Print ScopStmt S to raw_ostream O.
|
||||
static inline raw_ostream& operator<<(raw_ostream &O, const ScopStmt &S) {
|
||||
S.print(O);
|
||||
return O;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief Static Control Part
|
||||
///
|
||||
/// A Scop is the polyhedral representation of a control flow region detected
|
||||
/// by the Scop detection. It is generated by translating the LLVM-IR and
|
||||
/// abstracting its effects.
|
||||
///
|
||||
/// A Scop consists of a set of:
|
||||
///
|
||||
/// * A set of statements executed in the Scop.
|
||||
///
|
||||
/// * A set of global parameters
|
||||
/// Those parameters are scalar integer values, which are constant during
|
||||
/// execution.
|
||||
///
|
||||
/// * A context
|
||||
/// This context contains information about the values the parameters
|
||||
/// can take and relations between different parameters.
|
||||
class Scop {
|
||||
//===-------------------------------------------------------------------===//
|
||||
// DO NOT IMPLEMENT
|
||||
Scop(const Scop &);
|
||||
// DO NOT IMPLEMENT
|
||||
const Scop &operator=(const Scop &);
|
||||
|
||||
ScalarEvolution *SE;
|
||||
|
||||
/// The underlying Region.
|
||||
Region &R;
|
||||
|
||||
/// Max loop depth.
|
||||
unsigned MaxLoopDepth;
|
||||
|
||||
typedef std::vector<ScopStmt*> StmtSet;
|
||||
/// The Statments in this Scop.
|
||||
StmtSet Stmts;
|
||||
|
||||
/// Parameters of this Scop
|
||||
typedef SmallVector<const SCEV*, 8> ParamVecType;
|
||||
ParamVecType Parameters;
|
||||
|
||||
/// Constraints on parameters.
|
||||
isl_set *Context;
|
||||
|
||||
/// Create the static control part with a region, max loop depth of this
|
||||
/// region and parameters used in this region.
|
||||
Scop(TempScop &TempScop, LoopInfo &LI, ScalarEvolution &SE);
|
||||
|
||||
/// @brief Check if a basic block is trivial.
|
||||
///
|
||||
/// A trivial basic block does not contain any useful calculation. Therefore,
|
||||
/// it does not need to be represented as a polyhedral statement.
|
||||
///
|
||||
/// @param BB The basic block to check
|
||||
/// @param tempScop TempScop returning further information regarding the Scop.
|
||||
///
|
||||
/// @return True if the basic block is trivial, otherwise false.
|
||||
static bool isTrivialBB(BasicBlock *BB, TempScop &tempScop);
|
||||
|
||||
/// Build the Scop and Statement with precalculate scop information.
|
||||
void buildScop(TempScop &TempScop, const Region &CurRegion,
|
||||
// Loops in Scop containing CurRegion
|
||||
SmallVectorImpl<Loop*> &NestLoops,
|
||||
// The scattering numbers
|
||||
SmallVectorImpl<unsigned> &Scatter,
|
||||
LoopInfo &LI);
|
||||
|
||||
/// Helper function for printing the Scop.
|
||||
void printContext(raw_ostream &OS) const;
|
||||
void printStatements(raw_ostream &OS) const;
|
||||
|
||||
friend class ScopInfo;
|
||||
public:
|
||||
|
||||
~Scop();
|
||||
|
||||
ScalarEvolution *getSE() const;
|
||||
|
||||
/// @brief Get the count of parameters used in this Scop.
|
||||
///
|
||||
/// @return The count of parameters used in this Scop.
|
||||
inline ParamVecType::size_type getNumParams() const {
|
||||
return Parameters.size();
|
||||
}
|
||||
|
||||
/// @brief Get a set containing the parameters used in this Scop
|
||||
///
|
||||
/// @return The set containing the parameters used in this Scop.
|
||||
inline const ParamVecType &getParams() const { return Parameters; }
|
||||
|
||||
/// @name Parameter Iterators
|
||||
///
|
||||
/// These iterators iterate over all parameters of this Scop.
|
||||
//@{
|
||||
typedef ParamVecType::iterator param_iterator;
|
||||
typedef ParamVecType::const_iterator const_param_iterator;
|
||||
|
||||
param_iterator param_begin() { return Parameters.begin(); }
|
||||
param_iterator param_end() { return Parameters.end(); }
|
||||
const_param_iterator param_begin() const { return Parameters.begin(); }
|
||||
const_param_iterator param_end() const { return Parameters.end(); }
|
||||
//@}
|
||||
|
||||
/// @brief Get the maximum region of this static control part.
|
||||
///
|
||||
/// @return The maximum region of this static control part.
|
||||
inline const Region &getRegion() const { return R; }
|
||||
inline Region &getRegion() { return R; }
|
||||
|
||||
/// @brief Get the maximum depth of the loop.
|
||||
///
|
||||
/// @return The maximum depth of the loop.
|
||||
inline unsigned getMaxLoopDepth() const { return MaxLoopDepth; }
|
||||
|
||||
/// @brief Get the scattering dimension number of this Scop.
|
||||
///
|
||||
/// @return The scattering dimension number of this Scop.
|
||||
inline unsigned getScatterDim() const {
|
||||
unsigned maxScatterDim = 0;
|
||||
|
||||
for (const_iterator SI = begin(), SE = end(); SI != SE; ++SI)
|
||||
maxScatterDim = std::max(maxScatterDim, (*SI)->getNumScattering());
|
||||
|
||||
return maxScatterDim;
|
||||
}
|
||||
|
||||
/// @brief Get the name of this Scop.
|
||||
std::string getNameStr() const;
|
||||
|
||||
/// @brief Get the constraint on parameter of this Scop.
|
||||
///
|
||||
/// @return The constraint on parameter of this Scop.
|
||||
inline isl_set *getContext() const { return Context; }
|
||||
|
||||
/// @brief Get an isl string representing the context.
|
||||
std::string getContextStr() const;
|
||||
|
||||
/// @name Statements Iterators
|
||||
///
|
||||
/// These iterators iterate over all statements of this Scop.
|
||||
//@{
|
||||
typedef StmtSet::iterator iterator;
|
||||
typedef StmtSet::const_iterator const_iterator;
|
||||
|
||||
iterator begin() { return Stmts.begin(); }
|
||||
iterator end() { return Stmts.end(); }
|
||||
const_iterator begin() const { return Stmts.begin(); }
|
||||
const_iterator end() const { return Stmts.end(); }
|
||||
|
||||
typedef StmtSet::reverse_iterator reverse_iterator;
|
||||
typedef StmtSet::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
reverse_iterator rbegin() { return Stmts.rbegin(); }
|
||||
reverse_iterator rend() { return Stmts.rend(); }
|
||||
const_reverse_iterator rbegin() const { return Stmts.rbegin(); }
|
||||
const_reverse_iterator rend() const { return Stmts.rend(); }
|
||||
//@}
|
||||
|
||||
/// @brief Print the static control part.
|
||||
///
|
||||
/// @param OS The output stream the static control part is printed to.
|
||||
void print(raw_ostream &OS) const;
|
||||
|
||||
/// @brief Print the ScopStmt to stderr.
|
||||
void dump() const;
|
||||
|
||||
/// @brief Get the isl context of this static control part.
|
||||
///
|
||||
/// @return The isl context of this static control part.
|
||||
isl_ctx *getCtx() const;
|
||||
};
|
||||
|
||||
/// @brief Print Scop scop to raw_ostream O.
|
||||
static inline raw_ostream& operator<<(raw_ostream &O, const Scop &scop) {
|
||||
scop.print(O);
|
||||
return O;
|
||||
}
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
/// @brief Build the Polly IR (Scop and ScopStmt) on a Region.
|
||||
///
|
||||
class ScopInfo : public RegionPass {
|
||||
//===-------------------------------------------------------------------===//
|
||||
// DO NOT IMPLEMENT
|
||||
ScopInfo(const ScopInfo &);
|
||||
// DO NOT IMPLEMENT
|
||||
const ScopInfo &operator=(const ScopInfo &);
|
||||
|
||||
// The Scop
|
||||
Scop *scop;
|
||||
|
||||
void clear() {
|
||||
if (scop) {
|
||||
delete scop;
|
||||
scop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
explicit ScopInfo() : RegionPass(ID), scop(0) {}
|
||||
~ScopInfo() { clear(); }
|
||||
|
||||
/// @brief Try to build the Polly IR of static control part on the current
|
||||
/// SESE-Region.
|
||||
///
|
||||
/// @return If the current region is a valid for a static control part,
|
||||
/// return the Polly IR representing this static control part,
|
||||
/// return null otherwise.
|
||||
Scop *getScop() { return scop; }
|
||||
const Scop *getScop() const { return scop; }
|
||||
|
||||
/// @name RegionPass interface
|
||||
//@{
|
||||
virtual bool runOnRegion(Region *R, RGPassManager &RGM);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
virtual void releaseMemory() { clear(); }
|
||||
virtual void print(raw_ostream &OS, const Module *) const {
|
||||
if (scop)
|
||||
scop->print(OS);
|
||||
else
|
||||
OS << "Invalid Scop!\n";
|
||||
}
|
||||
//@}
|
||||
};
|
||||
|
||||
} //end namespace polly
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
//===- ScopLib.h - ScopLib interface ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Scoplib interface.
|
||||
//
|
||||
// The scoplib interface allows to import/export a scop using scoplib.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_SCOPLIB_H
|
||||
#define POLLY_SCOPLIB_H
|
||||
|
||||
#define SCOPLIB_INT_T_IS_MP
|
||||
#include "scoplib/scop.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace llvm {
|
||||
class Value;
|
||||
}
|
||||
|
||||
struct isl_constraint;
|
||||
struct isl_basic_map;
|
||||
struct isl_basic_set;
|
||||
struct isl_map;
|
||||
struct isl_set;
|
||||
|
||||
namespace polly {
|
||||
class Dependences;
|
||||
class ScopStmt;
|
||||
class Scop;
|
||||
class ScopLib {
|
||||
Scop *PollyScop;
|
||||
scoplib_scop_p scoplib;
|
||||
Dependences *D;
|
||||
|
||||
std::map<const llvm::Value*, int> ArrayMap;
|
||||
|
||||
void initializeArrays();
|
||||
void initializeParameters();
|
||||
void initializeScattering();
|
||||
void initializeStatements();
|
||||
scoplib_statement_p initializeStatement(ScopStmt *stmt);
|
||||
void freeStatement(scoplib_statement_p stmt);
|
||||
static int accessToMatrix_constraint(isl_constraint *c, void *user);
|
||||
static int accessToMatrix_basic_map(isl_basic_map *bmap, void *user);
|
||||
scoplib_matrix_p createAccessMatrix(ScopStmt *S, bool isRead);
|
||||
static int domainToMatrix_constraint(isl_constraint *c, void *user);
|
||||
static int domainToMatrix_basic_set(isl_basic_set *bset, void *user);
|
||||
scoplib_matrix_p domainToMatrix(isl_set *PS);
|
||||
static int scatteringToMatrix_constraint(isl_constraint *c, void *user);
|
||||
static int scatteringToMatrix_basic_map(isl_basic_map *bmap, void *user);
|
||||
scoplib_matrix_p scatteringToMatrix(isl_map *pmap);
|
||||
|
||||
public:
|
||||
ScopLib(Scop *S);
|
||||
ScopLib(Scop *S, FILE *F, Dependences *D);
|
||||
~ScopLib();
|
||||
void print(FILE *F);
|
||||
bool updateScattering();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* POLLY_SCOPLIB_H */
|
|
@ -0,0 +1,64 @@
|
|||
//===--------- ScopPass.h - Pass for Static Control Parts --------*-C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the ScopPass class. ScopPasses are just RegionPasses,
|
||||
// except they operate on Polly IR (Scop and ScopStmt) built by ScopInfo Pass.
|
||||
// Because they operate on Polly IR, not the LLVM IR, ScopPasses are not allowed
|
||||
// to modify the LLVM IR. Due to this limitation, the ScopPass class takes
|
||||
// care of declaring that no LLVM passes are invalidated.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_SCOP_PASS_H
|
||||
#define POLLY_SCOP_PASS_H
|
||||
|
||||
#include "llvm/Analysis/RegionPass.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
struct isl_ctx;
|
||||
|
||||
namespace polly {
|
||||
class Scop;
|
||||
|
||||
/// ScopPass - This class adapts the RegionPass interface to allow convenient
|
||||
/// creation of passes that operate on the Polly IR. Instead of overriding
|
||||
/// runOnRegion, subclasses override runOnScop.
|
||||
class ScopPass : public RegionPass {
|
||||
Scop *S;
|
||||
protected:
|
||||
explicit ScopPass(char &ID) : RegionPass(ID), S(0) {}
|
||||
|
||||
/// runOnScop - This method must be overloaded to perform the
|
||||
/// desired Polyhedral transformation or analysis.
|
||||
///
|
||||
virtual bool runOnScop(Scop &S) = 0;
|
||||
|
||||
/// getAnalysisUsage - Subclasses that override getAnalysisUsage
|
||||
/// must call this.
|
||||
///
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
|
||||
public:
|
||||
/// getIslContext - Get the isl_ctx of current SCoP.
|
||||
isl_ctx *getIslContext();
|
||||
|
||||
Scop &getCurScop() const {
|
||||
assert(S && "Not on a Scop!");
|
||||
return *S;
|
||||
}
|
||||
private:
|
||||
virtual bool runOnRegion(Region *R, RGPassManager &RGM);
|
||||
void print(raw_ostream &OS, const Module *) const;
|
||||
virtual void printScop(raw_ostream &OS) const {}
|
||||
};
|
||||
|
||||
} // End llvm namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,292 @@
|
|||
//===-- AffineSCEVIterator.h - Iterate the SCEV in an affine way -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The iterator can be used to iterate over the affine component of the SCEV
|
||||
// expression.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef AFFINE_SCEV_ITERATOR_H
|
||||
#define AFFINE_SCEV_ITERATOR_H
|
||||
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace polly {
|
||||
|
||||
/// @brief The itertor transform the scalar expressions to the form of sum of
|
||||
/// (constant * varialbe)s, and return the variable/constant pairs one by one
|
||||
/// on the fly.
|
||||
///
|
||||
/// For example, we can write SCEV:
|
||||
/// {{%x,+,sizeof(i32)}<%bb2.preheader>,+,(4 * sizeof(i32))}<%bb1>
|
||||
/// in affine form:
|
||||
/// (4 * sizeof(i32)) * %indvar + sizeof(i32) * %0 + 1 * %x + 0 * 1
|
||||
/// so we can get the follow pair from the iterator:
|
||||
/// {%indvar, (4 * sizeof(i32))}, {%0, sizeof(i32)}, {%x, 1} and {1, 0}
|
||||
/// where %indvar is the induction variable of loop %bb1 and %0 is the induction
|
||||
/// variable of loop %bb2.preheader.
|
||||
///
|
||||
/// In the returned pair,
|
||||
/// The "first" field is the variable part, the "second" field constant part.
|
||||
/// And the translation part of the expression will always return last.
|
||||
///
|
||||
class AffineSCEVIterator : public std::iterator<std::forward_iterator_tag,
|
||||
std::pair<const SCEV*, const SCEV*>, ptrdiff_t>,
|
||||
SCEVVisitor<AffineSCEVIterator,
|
||||
std::pair<const SCEV*, const SCEV*> >
|
||||
{
|
||||
typedef std::iterator<std::forward_iterator_tag,
|
||||
std::pair<const SCEV*, const SCEV*>, ptrdiff_t> super;
|
||||
|
||||
friend struct llvm::SCEVVisitor<AffineSCEVIterator,
|
||||
std::pair<const SCEV*, const SCEV*> >;
|
||||
|
||||
ScalarEvolution *SE;
|
||||
public:
|
||||
typedef super::value_type value_type;
|
||||
typedef super::pointer ptr_type;
|
||||
typedef AffineSCEVIterator Self;
|
||||
private:
|
||||
typedef SCEVNAryExpr::op_iterator scev_op_it;
|
||||
|
||||
// The stack help us remember the SCEVs that not visit yet.
|
||||
SmallVector<const SCEV*, 8> visitStack;
|
||||
|
||||
// The current value of this iterator.
|
||||
value_type val;
|
||||
|
||||
const SCEVConstant* getSCEVOne(const SCEV* S) const {
|
||||
return cast<SCEVConstant>(SE->getConstant(S->getType(), 1));
|
||||
}
|
||||
|
||||
//===-------------------------------------------------------------------===//
|
||||
/// Functions for SCEVVisitor.
|
||||
///
|
||||
/// These function compute the constant part and variable part of the SCEV,
|
||||
/// and return them in a std::pair, where the first field is the variable,
|
||||
/// and the second field is the constant.
|
||||
///
|
||||
value_type visitConstant(const SCEVConstant *S) {
|
||||
return std::make_pair(getSCEVOne(S), S);
|
||||
}
|
||||
|
||||
value_type visitUnknown(const SCEVUnknown* S) {
|
||||
const Type *AllocTy;
|
||||
Constant *FieldNo;
|
||||
// We treat these as constant.
|
||||
if (S->isSizeOf (AllocTy) ||
|
||||
S->isAlignOf (AllocTy) ||
|
||||
S->isOffsetOf(AllocTy, FieldNo))
|
||||
return std::make_pair(getSCEVOne(S), S);
|
||||
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
value_type visitMulExpr(const SCEVMulExpr* S) {
|
||||
SmallVector<const SCEV*, 4> Coeffs, Variables;
|
||||
|
||||
// Do not worry about the Constant * Variable * (Variable + Variable)
|
||||
// MulExpr, we will never get a affine expression from it, so we just
|
||||
// leave it there.
|
||||
for (scev_op_it I = S->op_begin(), E = S->op_end(); I != E; ++I) {
|
||||
// Get the constant part and the variable part of each operand.
|
||||
value_type res = visit(*I);
|
||||
|
||||
Coeffs.push_back(res.second);
|
||||
Variables.push_back(res.first);
|
||||
}
|
||||
|
||||
// Get the constant part and variable part of this MulExpr by
|
||||
// multiply them together.
|
||||
const SCEV *Coeff = SE->getMulExpr(Coeffs);
|
||||
// There maybe "sizeof" and others.
|
||||
// TODO: Assert the allowed coeff type.
|
||||
// assert(Coeff && "Expect Coeff to be a const!");
|
||||
|
||||
const SCEV *Var = SE->getMulExpr(Variables);
|
||||
|
||||
return std::make_pair(Var, Coeff);
|
||||
}
|
||||
|
||||
value_type visitCastExpr(const SCEVCastExpr *S) {
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
value_type visitTruncateExpr(const SCEVTruncateExpr *S) {
|
||||
return visitCastExpr(S);
|
||||
}
|
||||
|
||||
value_type visitZeroExtendExpr(const SCEVZeroExtendExpr *S) {
|
||||
return visitCastExpr(S);
|
||||
}
|
||||
|
||||
value_type visitSignExtendExpr(const SCEVSignExtendExpr *S) {
|
||||
return visitCastExpr(S);
|
||||
}
|
||||
|
||||
value_type visitAddExpr(const SCEVAddExpr *S) {
|
||||
// AddExpr will handled out in visit Next;
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
value_type visitAddRecExpr(const SCEVAddRecExpr *S) {
|
||||
// AddRecExpr will handled out in visit Next;
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
value_type visitUDivExpr(const SCEVUDivExpr *S) {
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
value_type visitSMaxExpr(const SCEVSMaxExpr *S) {
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
value_type visitUMaxExpr(const SCEVUMaxExpr *S) {
|
||||
return std::make_pair(S, getSCEVOne(S));
|
||||
}
|
||||
|
||||
/// Get the next {variable, constant} pair of the SCEV.
|
||||
value_type visitNext() {
|
||||
value_type ret(0, 0);
|
||||
|
||||
if (visitStack.empty())
|
||||
return ret;
|
||||
const SCEV* nextS = visitStack.back();
|
||||
|
||||
if (const SCEVAddRecExpr *ARec = dyn_cast<SCEVAddRecExpr>(nextS)){
|
||||
// Visiting the AddRec, check if its Affine;
|
||||
PHINode *IV = ARec->getLoop()->getCanonicalInductionVariable();
|
||||
// Only decompose the AddRec, if the loop has a canonical induction
|
||||
// variable.
|
||||
if (ARec->isAffine() && IV != 0) {
|
||||
ret = visit(ARec->getStepRecurrence(*SE));
|
||||
if (isa<SCEVConstant>(ret.first)) { // If the step is constant.
|
||||
const SCEV *Start = ARec->getStart();
|
||||
visitStack.back() = Start;
|
||||
|
||||
// The AddRec is expect to be decomposed to
|
||||
//
|
||||
// | start + step * {1, +, 1}<loop>
|
||||
//
|
||||
// Now we get the {1, +, 1}<loop> part.
|
||||
ret.first = SE->getSCEV(IV);
|
||||
|
||||
// Push CouldNotCompute to take the place.
|
||||
visitStack.push_back(SE->getCouldNotCompute());
|
||||
|
||||
return ret;
|
||||
}
|
||||
// The step is not a constant. Then this AddRec is not Affine or
|
||||
// no canonical induction variable found.
|
||||
// Fall through.
|
||||
}
|
||||
}
|
||||
|
||||
// Get the constant part and variable part of the SCEV.
|
||||
ret = visit(nextS);
|
||||
|
||||
// If the reach the last constant
|
||||
if (isa<SCEVConstant>(ret.first) && (visitStack.size() != 1)) {
|
||||
// Else, merge all constant component, we will output it at last.
|
||||
visitStack.front() = SE->getAddExpr(visitStack.front(), ret.second);
|
||||
//assert(isa<SCEVConstant>(visitStack.front().first));
|
||||
// Pop the top constant, because it already merged into the bottom of the Stack
|
||||
// and output it last.
|
||||
visitStack.pop_back();
|
||||
// Try again.
|
||||
return visitNext();
|
||||
}
|
||||
// Not a constant or Stack not empty
|
||||
// If ret is in (xxx) * AddExpr form, we will decompose the AddExpr
|
||||
else if (const SCEVAddExpr *AddExpr = dyn_cast<SCEVAddExpr>(ret.first)) {
|
||||
// Pop the current SCEV, we will decompose it.
|
||||
visitStack.pop_back();
|
||||
assert(AddExpr->getNumOperands() && "AddExpr without operand?");
|
||||
for (scev_op_it I = AddExpr->op_begin(), E = AddExpr->op_end(); I != E; ++I){
|
||||
visitStack.push_back(SE->getMulExpr(ret.second, *I));
|
||||
}
|
||||
// Try again with the new SCEV.
|
||||
return visitNext();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Create the iterator from a SCEV and the ScalarEvolution analysis.
|
||||
AffineSCEVIterator(const SCEV* S, ScalarEvolution *se ) : SE(se) {
|
||||
// Dont iterate CouldNotCompute.
|
||||
if (isa<SCEVCouldNotCompute>(S))
|
||||
return;
|
||||
|
||||
const Type *Ty = S->getType();
|
||||
|
||||
// Init the constant component.
|
||||
visitStack.push_back(SE->getConstant(Ty, 0));
|
||||
|
||||
// Get the first affine component.
|
||||
visitStack.push_back(S);
|
||||
val = visitNext();
|
||||
}
|
||||
|
||||
/// @brief Create an end iterator.
|
||||
inline AffineSCEVIterator() {}
|
||||
|
||||
inline bool operator==(const Self& x) const {
|
||||
return visitStack == x.visitStack;
|
||||
}
|
||||
inline bool operator!=(const Self& x) const { return !operator==(x); }
|
||||
|
||||
/// @brief Return the current (constant * variable) component of the SCEV.
|
||||
///
|
||||
/// @return The "first" field of the pair is the variable part,
|
||||
/// the "second" field of the pair is the constant part.
|
||||
inline value_type operator*() const {
|
||||
assert(val.first && val.second && "Cant dereference iterator!");
|
||||
return val;
|
||||
}
|
||||
|
||||
inline const value_type* operator->() const {
|
||||
assert(val.first && val.second && "Cant dereference iterator!");
|
||||
return &val;
|
||||
}
|
||||
|
||||
inline Self& operator++() { // Preincrement
|
||||
assert(!visitStack.empty() && "Cant ++ iterator!");
|
||||
// Pop the last SCEV.
|
||||
visitStack.pop_back();
|
||||
val = visitNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Self operator++(int) { // Postincrement
|
||||
Self tmp = *this; ++*this; return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
inline static AffineSCEVIterator affine_begin(const SCEV* S, ScalarEvolution *SE) {
|
||||
return AffineSCEVIterator(S, SE);
|
||||
}
|
||||
|
||||
inline static AffineSCEVIterator affine_end() {
|
||||
return AffineSCEVIterator();
|
||||
}
|
||||
|
||||
} // end namespace polly
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
//===- Support/GICHelper.h -- Helper functions for GMP, ISL, and Cloog -----===/
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Helper functions for gmp, isl and Cloog objects.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
#ifndef POLLY_SUPPORT_GIC_HELPER_H
|
||||
#define POLLY_SUPPORT_GIC_HELPER_H
|
||||
|
||||
#include "llvm/ADT/APInt.h"
|
||||
#include <gmp.h>
|
||||
|
||||
struct isl_map;
|
||||
struct isl_union_map;
|
||||
struct isl_set;
|
||||
struct isl_union_set;
|
||||
|
||||
namespace polly {
|
||||
|
||||
/// @brief Convert APInt to mpz.
|
||||
///
|
||||
/// @param v The mpz_t object your want to hold the result.
|
||||
/// @param apint The APInt you want to convert.
|
||||
void MPZ_from_APInt (mpz_t v, const llvm::APInt apint, bool is_signed = true);
|
||||
|
||||
/// @brief Convert mpz to APInt.
|
||||
///
|
||||
/// @param mpz The mpz_t you want to convert.
|
||||
llvm::APInt APInt_from_MPZ (const mpz_t mpz);
|
||||
|
||||
/// @brief Get c++ string from Isl objects.
|
||||
//@{
|
||||
std::string stringFromIslObj(/*__isl_keep*/ isl_map *map);
|
||||
std::string stringFromIslObj(/*__isl_keep*/ isl_union_map *umap);
|
||||
std::string stringFromIslObj(/*__isl_keep*/ isl_set *set);
|
||||
std::string stringFromIslObj(/*__isl_keep*/ isl_union_set *uset);
|
||||
//@}
|
||||
} //end namespace polly
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
//===------ Support/ScopHelper.h -- Some Helper Functions for Scop. --------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Small functions that help with LLVM-IR.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_SUPPORT_IRHELPER_H
|
||||
#define POLLY_SUPPORT_IRHELPER_H
|
||||
|
||||
namespace llvm {
|
||||
class Instruction;
|
||||
class LoopInfo;
|
||||
class Loop;
|
||||
class ScalarEvolution;
|
||||
class SCEV;
|
||||
class Value;
|
||||
class PHINode;
|
||||
class Region;
|
||||
class Pass;
|
||||
class BasicBlock;
|
||||
}
|
||||
|
||||
namespace polly {
|
||||
// Helper function for Scop.
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// Temporary Hack for extended regiontree.
|
||||
///
|
||||
/// @brief Cast the region to loop.
|
||||
///
|
||||
/// @param R The Region to be casted.
|
||||
/// @param LI The LoopInfo to help the casting.
|
||||
///
|
||||
/// @return If there is a a loop that has the same entry and exit as the region,
|
||||
/// return the loop, otherwise, return null.
|
||||
llvm::Loop *castToLoop(const llvm::Region &R, llvm::LoopInfo &LI);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Functions for checking affine functions.
|
||||
bool isInvariant(const llvm::SCEV *S, llvm::Region &R);
|
||||
|
||||
bool isParameter(const llvm::SCEV *Var, llvm::Region &RefRegion,
|
||||
llvm::LoopInfo &LI, llvm::ScalarEvolution &SE);
|
||||
|
||||
bool isIndVar(const llvm::SCEV *Var, llvm::Region &RefRegion,
|
||||
llvm::LoopInfo &LI, llvm::ScalarEvolution &SE);
|
||||
|
||||
/// @brief Check if the instruction I is the induction variable of a loop.
|
||||
///
|
||||
/// @param I The instruction to check.
|
||||
/// @param LI The LoopInfo analysis.
|
||||
///
|
||||
/// @return Return true if I is the induction variable of a loop, false
|
||||
/// otherwise.
|
||||
bool isIndVar(const llvm::Instruction *I, const llvm::LoopInfo *LI);
|
||||
|
||||
/// @brief Check if the PHINode has any incoming Invoke edge.
|
||||
///
|
||||
/// @param PN The PHINode to check.
|
||||
///
|
||||
/// @return If the PHINode has an incoming BB that jumps to the parent BB
|
||||
/// of the PHINode with an invoke instruction, return true,
|
||||
/// otherwise, return false.
|
||||
bool hasInvokeEdge(const llvm::PHINode *PN);
|
||||
|
||||
llvm::Value *getPointerOperand(llvm::Instruction &Inst);
|
||||
|
||||
// Helper function for LLVM-IR about Scop.
|
||||
llvm::BasicBlock *createSingleEntryEdge(llvm::Region *R, llvm::Pass *P);
|
||||
llvm::BasicBlock *createSingleExitEdge(llvm::Region *R, llvm::Pass *P);
|
||||
|
||||
/// @brief Split the entry block of a function to store the newly inserted
|
||||
/// allocations outside of all Scops.
|
||||
///
|
||||
/// @param EntryBlock The entry block of the current function.
|
||||
/// @param P The pass that currently running.
|
||||
///
|
||||
void splitEntryBlockForAlloca(llvm::BasicBlock *EntryBlock, llvm::Pass *P);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,388 @@
|
|||
//===-------- polly/TempScopInfo.h - Extract TempScops ----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Collect information about the control flow regions detected by the Scop
|
||||
// detection, such that this information can be translated info its polyhedral
|
||||
// representation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef POLLY_TEMP_SCOP_EXTRACTION_H
|
||||
#define POLLY_TEMP_SCOP_EXTRACTION_H
|
||||
|
||||
#include "polly/MayAliasSet.h"
|
||||
#include "polly/ScopDetection.h"
|
||||
|
||||
#include "llvm/Analysis/RegionPass.h"
|
||||
#include "llvm/Instructions.h"
|
||||
|
||||
namespace llvm {
|
||||
class TargetData;
|
||||
}
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace polly {
|
||||
class MayAliasSetInfo;
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
/// @brief Affine function represent in llvm SCEV expressions.
|
||||
///
|
||||
/// A helper class for collect affine function information
|
||||
class SCEVAffFunc {
|
||||
// Temporary hack
|
||||
friend class TempScopInfo;
|
||||
// The translation component
|
||||
const SCEV *TransComp;
|
||||
|
||||
// { Variable, Coefficient }
|
||||
typedef std::map<const SCEV*, const SCEV*> LnrTransSet;
|
||||
LnrTransSet LnrTrans;
|
||||
|
||||
public:
|
||||
// The type of the scev affine function
|
||||
enum SCEVAffFuncType {
|
||||
None = 0,
|
||||
ReadMem, // Or we could call it "Use"
|
||||
WriteMem, // Or define
|
||||
Eq, // == 0
|
||||
Ne, // != 0
|
||||
GE // >= 0
|
||||
};
|
||||
|
||||
private:
|
||||
// The base address of the address SCEV, if the Value is a pointer, this is
|
||||
// an array access, otherwise, this is a value access.
|
||||
// And the Write/Read modifier
|
||||
Value *BaseAddr;
|
||||
unsigned ElemBytes : 28;
|
||||
SCEVAffFuncType FuncType : 3;
|
||||
bool has_sign : 1;
|
||||
|
||||
public:
|
||||
/// @brief Create a new SCEV affine function.
|
||||
SCEVAffFunc() : TransComp(0), BaseAddr(0), ElemBytes(0), FuncType(None),
|
||||
has_sign(true) {}
|
||||
|
||||
/// @brief Create a new SCEV affine function with memory access type or
|
||||
/// condition type
|
||||
|
||||
explicit SCEVAffFunc(SCEVAffFuncType Type, unsigned elemBytes = 0,
|
||||
Value* baseAddr = 0)
|
||||
: TransComp(0), BaseAddr(baseAddr), ElemBytes(elemBytes),
|
||||
FuncType(Type), has_sign(true) {}
|
||||
|
||||
/// @brief Construct a new SCEVAffFunc from a SCEV
|
||||
///
|
||||
/// @param S The SCEV that should be translated.
|
||||
/// @param Type The type of this affine function.
|
||||
/// @param R The region in which the affine function is evaluated.
|
||||
/// @param Param A set of parameters, where new parameters found in this
|
||||
/// affine function will be added.
|
||||
/// @param LI A pointer to a current LoopInfo analysis.
|
||||
/// @param SE A pointer to a current ScalarEvolution analysis.
|
||||
SCEVAffFunc(const SCEV *S, SCEVAffFuncType Type, Region &R,
|
||||
ParamSetType &Param, LoopInfo *LI, ScalarEvolution *SE);
|
||||
|
||||
void setUnsigned() {has_sign = false;}
|
||||
|
||||
// getCoeff - Get the Coefficient of a given variable.
|
||||
const SCEV *getCoeff(const SCEV *Var) const {
|
||||
LnrTransSet::const_iterator At = LnrTrans.find(Var);
|
||||
return At == LnrTrans.end() ? 0 : At->second;
|
||||
}
|
||||
|
||||
const SCEV *getTransComp() const {
|
||||
return TransComp;
|
||||
}
|
||||
|
||||
bool isSigned() const { return has_sign; }
|
||||
|
||||
enum SCEVAffFuncType getType() const { return FuncType; }
|
||||
|
||||
bool isDataRef() const {
|
||||
return getType() == ReadMem || getType() == WriteMem;
|
||||
}
|
||||
|
||||
unsigned getElemSizeInBytes() const {
|
||||
assert(isDataRef() && "getElemSizeInBytes on the wrong type!");
|
||||
return ElemBytes;
|
||||
}
|
||||
|
||||
bool isRead() const { return FuncType == ReadMem; }
|
||||
|
||||
const Value *getBaseAddr() const { return BaseAddr; }
|
||||
|
||||
/// @brief Print the affine function.
|
||||
///
|
||||
/// @param OS The output stream the affine function is printed to.
|
||||
void print(raw_ostream &OS, bool PrintInequality = true) const;
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
static inline raw_ostream& operator<<(raw_ostream &OS, const SCEVAffFunc &SAF){
|
||||
SAF.print(OS);
|
||||
return OS;
|
||||
}
|
||||
|
||||
class Comparison {
|
||||
|
||||
SCEVAffFunc *LHS;
|
||||
SCEVAffFunc *RHS;
|
||||
|
||||
ICmpInst::Predicate Pred;
|
||||
|
||||
public:
|
||||
Comparison(SCEVAffFunc *lhs, SCEVAffFunc *rhs, ICmpInst::Predicate pred)
|
||||
: LHS(lhs), RHS(rhs), Pred(pred) {}
|
||||
|
||||
SCEVAffFunc *getLHS() const { return LHS; }
|
||||
SCEVAffFunc *getRHS() const { return RHS; }
|
||||
|
||||
ICmpInst::Predicate getPred() const { return Pred; }
|
||||
void print(raw_ostream &OS) const;
|
||||
};
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
/// Types
|
||||
// The condition of a Basicblock, combine brcond with "And" operator.
|
||||
typedef SmallVector<Comparison, 4> BBCond;
|
||||
|
||||
/// Maps from a loop to the affine function expressing its backedge taken count.
|
||||
/// The backedge taken count already enough to express iteration domain as we
|
||||
/// only allow loops with canonical induction variable.
|
||||
/// A canonical induction variable is:
|
||||
/// an integer recurrence that starts at 0 and increments by one each time
|
||||
/// through the loop.
|
||||
typedef std::map<const Loop*, SCEVAffFunc> LoopBoundMapType;
|
||||
|
||||
/// Mapping BBs to its condition constrains
|
||||
typedef std::map<const BasicBlock*, BBCond> BBCondMapType;
|
||||
|
||||
typedef std::vector<std::pair<SCEVAffFunc, Instruction*> > AccFuncSetType;
|
||||
typedef std::map<const BasicBlock*, AccFuncSetType> AccFuncMapType;
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
/// @brief Scop represent with llvm objects.
|
||||
///
|
||||
/// A helper class for remembering the parameter number and the max depth in
|
||||
/// this Scop, and others context.
|
||||
class TempScop {
|
||||
// The Region.
|
||||
Region &R;
|
||||
|
||||
// Parameters used in this Scop.
|
||||
ParamSetType Params;
|
||||
|
||||
// The max loop depth of this Scop
|
||||
unsigned MaxLoopDepth;
|
||||
|
||||
// Remember the bounds of loops, to help us build iteration domain of BBs.
|
||||
const LoopBoundMapType &LoopBounds;
|
||||
const BBCondMapType &BBConds;
|
||||
|
||||
// Access function of bbs.
|
||||
const AccFuncMapType &AccFuncMap;
|
||||
|
||||
// The alias information about this SCoP.
|
||||
MayAliasSetInfo *MayASInfo;
|
||||
|
||||
// Basic blocks detected as reductions
|
||||
std::set<BasicBlock*> Reductions;
|
||||
|
||||
friend class TempScopInfo;
|
||||
|
||||
explicit TempScop(Region &r, LoopBoundMapType &loopBounds,
|
||||
BBCondMapType &BBCmps, AccFuncMapType &accFuncMap)
|
||||
: R(r), MaxLoopDepth(0), LoopBounds(loopBounds), BBConds(BBCmps),
|
||||
AccFuncMap(accFuncMap), MayASInfo(new MayAliasSetInfo()) {}
|
||||
|
||||
public:
|
||||
~TempScop();
|
||||
|
||||
bool is_Reduction(BasicBlock &BB) { return Reductions.count(&BB) != 0; }
|
||||
|
||||
/// @name Information about this Temporary Scop.
|
||||
///
|
||||
//@{
|
||||
/// @brief Get the parameters used in this Scop.
|
||||
///
|
||||
/// @return The parameters use in region.
|
||||
ParamSetType &getParamSet() { return Params; }
|
||||
|
||||
/// @brief Get the maximum Region contained by this Scop.
|
||||
///
|
||||
/// @return The maximum Region contained by this Scop.
|
||||
Region &getMaxRegion() const { return R; }
|
||||
|
||||
/// @brief Get the maximum loop depth of Region R.
|
||||
///
|
||||
/// @return The maximum loop depth of Region R.
|
||||
unsigned getMaxLoopDepth() const { return MaxLoopDepth; }
|
||||
|
||||
/// @brief Get the loop bounds of the given loop.
|
||||
///
|
||||
/// @param L The loop to get the bounds.
|
||||
///
|
||||
/// @return The bounds of the loop L in { Lower bound, Upper bound } form.
|
||||
///
|
||||
const SCEVAffFunc &getLoopBound(const Loop *L) const {
|
||||
LoopBoundMapType::const_iterator at = LoopBounds.find(L);
|
||||
assert(at != LoopBounds.end() && "Only valid loop is allow!");
|
||||
return at->second;
|
||||
}
|
||||
|
||||
/// @brief Get the condition from entry block of the Scop to a BasicBlock
|
||||
///
|
||||
/// @param BB The BasicBlock
|
||||
///
|
||||
/// @return The condition from entry block of the Scop to a BB
|
||||
///
|
||||
const BBCond *getBBCond(const BasicBlock *BB) const {
|
||||
BBCondMapType::const_iterator at = BBConds.find(BB);
|
||||
return at != BBConds.end() ? &(at->second) : 0;
|
||||
}
|
||||
|
||||
/// @brief Get all access functions in a BasicBlock
|
||||
///
|
||||
/// @param BB The BasicBlock that containing the access functions.
|
||||
///
|
||||
/// @return All access functions in BB
|
||||
///
|
||||
const AccFuncSetType *getAccessFunctions(const BasicBlock* BB) const {
|
||||
AccFuncMapType::const_iterator at = AccFuncMap.find(BB);
|
||||
return at != AccFuncMap.end()? &(at->second) : 0;
|
||||
}
|
||||
//@}
|
||||
|
||||
/// @brief Print the Temporary Scop information.
|
||||
///
|
||||
/// @param OS The output stream the access functions is printed to.
|
||||
/// @param SE The ScalarEvolution that help printing Temporary Scop
|
||||
/// information.
|
||||
/// @param LI The LoopInfo that help printing the access functions.
|
||||
void print(raw_ostream &OS, ScalarEvolution *SE, LoopInfo *LI) const;
|
||||
|
||||
/// @brief Print the access functions and loop bounds in this Scop.
|
||||
///
|
||||
/// @param OS The output stream the access functions is printed to.
|
||||
/// @param SE The ScalarEvolution that help printing the access functions.
|
||||
/// @param LI The LoopInfo that help printing the access functions.
|
||||
void printDetail(raw_ostream &OS, ScalarEvolution *SE,
|
||||
LoopInfo *LI, const Region *Reg, unsigned ind) const;
|
||||
};
|
||||
|
||||
typedef std::map<const Region*, TempScop*> TempScopMapType;
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief The Function Pass to extract temporary information for Static control
|
||||
/// part in llvm function.
|
||||
///
|
||||
class TempScopInfo : public FunctionPass {
|
||||
//===-------------------------------------------------------------------===//
|
||||
// DO NOT IMPLEMENT
|
||||
TempScopInfo(const TempScopInfo &);
|
||||
// DO NOT IMPLEMENT
|
||||
const TempScopInfo &operator=(const TempScopInfo &);
|
||||
|
||||
// The ScalarEvolution to help building Scop.
|
||||
ScalarEvolution* SE;
|
||||
|
||||
// LoopInfo for information about loops
|
||||
LoopInfo *LI;
|
||||
|
||||
// The AliasAnalysis to build AliasSetTracker.
|
||||
AliasAnalysis *AA;
|
||||
|
||||
// Valid Regions for Scop
|
||||
ScopDetection *SD;
|
||||
|
||||
// For condition extraction support.
|
||||
DominatorTree *DT;
|
||||
PostDominatorTree *PDT;
|
||||
|
||||
// Target data for element size computing.
|
||||
TargetData *TD;
|
||||
|
||||
// Remember the bounds of loops, to help us build iteration domain of BBs.
|
||||
LoopBoundMapType LoopBounds;
|
||||
|
||||
// And also Remember the constrains for BBs
|
||||
BBCondMapType BBConds;
|
||||
|
||||
// Access function of bbs.
|
||||
AccFuncMapType AccFuncMap;
|
||||
|
||||
// Mapping regions to the corresponding Scop in current function.
|
||||
TempScopMapType TempScops;
|
||||
|
||||
// Clear the context.
|
||||
void clear();
|
||||
|
||||
/// @brief Build an affine function from a SCEV expression.
|
||||
///
|
||||
/// @param S The SCEV expression to be converted to affine
|
||||
/// function.
|
||||
/// @param Scop The Scope of this expression.
|
||||
/// @param FuncToBuild The SCEVAffFunc to hold the result.
|
||||
///
|
||||
void buildAffineFunction(const SCEV *S, SCEVAffFunc &FuncToBuild,
|
||||
Region &R, ParamSetType &Params) const;
|
||||
|
||||
|
||||
/// @brief Build condition constrains to BBs in a valid Scop.
|
||||
///
|
||||
/// @param BB The BasicBlock to build condition constrains
|
||||
/// @param RegionEntry The entry block of the Smallest Region that containing
|
||||
/// BB
|
||||
/// @param Cond The built condition
|
||||
void buildCondition(BasicBlock *BB, BasicBlock *Region, TempScop &Scop);
|
||||
|
||||
// Build the affine function of the given condition
|
||||
void buildAffineCondition(Value &V, bool inverted, Comparison **Comp,
|
||||
TempScop &Scop) const;
|
||||
|
||||
// Return the temporary Scop information of Region R, where R must be a valid
|
||||
// part of Scop
|
||||
TempScop *getTempScop(Region &R);
|
||||
|
||||
// Build the temprory information of Region R, where R must be a valid part
|
||||
// of Scop.
|
||||
TempScop *buildTempScop(Region &R);
|
||||
|
||||
bool isReduction(BasicBlock &BB);
|
||||
|
||||
void buildAccessFunctions(Region &RefRegion, ParamSetType &Params,
|
||||
BasicBlock &BB);
|
||||
|
||||
void buildLoopBounds(TempScop &Scop);
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
explicit TempScopInfo() : FunctionPass(ID) {}
|
||||
~TempScopInfo();
|
||||
|
||||
/// @brief Get the temporay Scop information in LLVM IR represent
|
||||
/// for Region R.
|
||||
///
|
||||
/// @return The Scop information in LLVM IR represent.
|
||||
TempScop *getTempScop(const Region *R) const;
|
||||
|
||||
/// @name FunctionPass interface
|
||||
//@{
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
virtual void releaseMemory() { clear(); }
|
||||
virtual bool runOnFunction(Function &F);
|
||||
virtual void print(raw_ostream &OS, const Module *) const;
|
||||
//@}
|
||||
};
|
||||
|
||||
} // end namespace polly
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
add_polly_library(PollyAnalysis
|
||||
Dependences.cpp
|
||||
ScopDetection.cpp
|
||||
ScopInfo.cpp
|
||||
ScopGraphPrinter.cpp
|
||||
ScopPass.cpp
|
||||
TempScopInfo.cpp
|
||||
)
|
|
@ -0,0 +1,414 @@
|
|||
//===- Dependency.cpp - Calculate dependency information for a Scop. -----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Calculate the data dependency relations for a Scop using ISL.
|
||||
//
|
||||
// The integer set library (ISL) from Sven, has a integrated dependency analysis
|
||||
// to calculate data dependences. This pass takes advantage of this and
|
||||
// calculate those dependences a Scop.
|
||||
//
|
||||
// The dependences in this pass are exact in terms that for a specific read
|
||||
// statement instance only the last write statement instance is returned. In
|
||||
// case of may writes a set of possible write instances is returned. This
|
||||
// analysis will never produce redundant dependences.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
#include "polly/Dependences.h"
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/Support/GICHelper.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-dependences"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#include <isl/flow.h>
|
||||
#include <isl/map.h>
|
||||
#include <isl/constraint.h>
|
||||
|
||||
using namespace polly;
|
||||
using namespace llvm;
|
||||
|
||||
static cl::opt<bool>
|
||||
LegalityCheckDisabled("disable-polly-legality",
|
||||
cl::desc("Disable polly legality check"), cl::Hidden,
|
||||
cl::init(false));
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
Dependences::Dependences() : ScopPass(ID) {
|
||||
must_dep = may_dep = NULL;
|
||||
must_no_source = may_no_source = NULL;
|
||||
sink = must_source = may_source = NULL;
|
||||
war_dep = waw_dep = NULL;
|
||||
}
|
||||
|
||||
bool Dependences::runOnScop(Scop &S) {
|
||||
isl_dim *dim = isl_dim_alloc(S.getCtx(), S.getNumParams(), 0, 0);
|
||||
|
||||
if (sink)
|
||||
isl_union_map_free(sink);
|
||||
|
||||
if (must_source)
|
||||
isl_union_map_free(must_source);
|
||||
|
||||
if (may_source)
|
||||
isl_union_map_free(may_source);
|
||||
|
||||
sink = isl_union_map_empty(isl_dim_copy(dim));
|
||||
must_source = isl_union_map_empty(isl_dim_copy(dim));
|
||||
may_source = isl_union_map_empty(isl_dim_copy(dim));
|
||||
isl_union_map *schedule = isl_union_map_empty(dim);
|
||||
|
||||
if (must_dep)
|
||||
isl_union_map_free(must_dep);
|
||||
|
||||
if (may_dep)
|
||||
isl_union_map_free(may_dep);
|
||||
|
||||
if (must_no_source)
|
||||
isl_union_map_free(must_no_source);
|
||||
|
||||
if (may_no_source)
|
||||
isl_union_map_free(may_no_source);
|
||||
|
||||
if (war_dep)
|
||||
isl_union_map_free(war_dep);
|
||||
|
||||
if (waw_dep)
|
||||
isl_union_map_free(waw_dep);
|
||||
|
||||
must_dep = may_dep = NULL;
|
||||
must_no_source = may_no_source = NULL;
|
||||
|
||||
war_dep = waw_dep = NULL;
|
||||
|
||||
for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
for (ScopStmt::memacc_iterator MI = Stmt->memacc_begin(),
|
||||
ME = Stmt->memacc_end(); MI != ME; ++MI) {
|
||||
isl_set *domcp = isl_set_copy(Stmt->getDomain());
|
||||
isl_map *accdom = isl_map_copy((*MI)->getAccessFunction());
|
||||
|
||||
accdom = isl_map_intersect_domain(accdom, domcp);
|
||||
|
||||
if ((*MI)->isRead())
|
||||
isl_union_map_add_map(sink, accdom);
|
||||
else
|
||||
isl_union_map_add_map(must_source, accdom);
|
||||
}
|
||||
isl_map *scattering = isl_map_copy(Stmt->getScattering());
|
||||
isl_union_map_add_map(schedule, scattering);
|
||||
}
|
||||
|
||||
DEBUG(
|
||||
dbgs().indent(4) << "Sink:\n";
|
||||
dbgs().indent(8) << stringFromIslObj(sink) << "\n";
|
||||
|
||||
dbgs().indent(4) << "MustSource:\n";
|
||||
dbgs().indent(8) << stringFromIslObj(must_source) << "\n";
|
||||
|
||||
dbgs().indent(4) << "MaySource:\n";
|
||||
dbgs().indent(8) << stringFromIslObj(may_source) << "\n";
|
||||
|
||||
dbgs().indent(4) << "Schedule:\n";
|
||||
dbgs().indent(8) << stringFromIslObj(schedule) << "\n";
|
||||
);
|
||||
|
||||
isl_union_map_compute_flow(isl_union_map_copy(sink),
|
||||
isl_union_map_copy(must_source),
|
||||
isl_union_map_copy(may_source),
|
||||
isl_union_map_copy(schedule),
|
||||
&must_dep, &may_dep, &must_no_source,
|
||||
&may_no_source);
|
||||
|
||||
isl_union_map_compute_flow(isl_union_map_copy(must_source),
|
||||
isl_union_map_copy(must_source),
|
||||
isl_union_map_copy(sink), schedule,
|
||||
&waw_dep, &war_dep, NULL, NULL);
|
||||
|
||||
// Remove redundant statements.
|
||||
must_dep = isl_union_map_coalesce(must_dep);
|
||||
may_dep = isl_union_map_coalesce(may_dep);
|
||||
must_no_source = isl_union_map_coalesce(must_no_source);
|
||||
may_no_source = isl_union_map_coalesce(may_no_source);
|
||||
waw_dep = isl_union_map_coalesce(waw_dep);
|
||||
war_dep = isl_union_map_coalesce(war_dep);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Dependences::isValidScattering(StatementToIslMapTy *NewScattering) {
|
||||
Scop &S = getCurScop();
|
||||
|
||||
if (LegalityCheckDisabled)
|
||||
return true;
|
||||
|
||||
isl_dim *dim = isl_dim_alloc(S.getCtx(), S.getNumParams(), 0, 0);
|
||||
|
||||
isl_union_map *schedule = isl_union_map_empty(dim);
|
||||
|
||||
for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
isl_map *scattering;
|
||||
|
||||
if (NewScattering->find(*SI) == NewScattering->end())
|
||||
scattering = isl_map_copy(Stmt->getScattering());
|
||||
else
|
||||
scattering = isl_map_copy((*NewScattering)[Stmt]);
|
||||
|
||||
isl_union_map_add_map(schedule, scattering);
|
||||
}
|
||||
|
||||
isl_union_map *temp_must_dep, *temp_may_dep;
|
||||
isl_union_map *temp_must_no_source, *temp_may_no_source;
|
||||
|
||||
DEBUG(
|
||||
dbgs().indent(4) << "Sink :=\n";
|
||||
dbgs().indent(8) << stringFromIslObj(sink) << ";\n";
|
||||
|
||||
dbgs().indent(4) << "MustSource :=\n";
|
||||
dbgs().indent(8) << stringFromIslObj(must_source) << ";\n";
|
||||
|
||||
dbgs().indent(4) << "MaySource :=\n";
|
||||
dbgs().indent(8) << stringFromIslObj(may_source) << ";\n";
|
||||
|
||||
dbgs().indent(4) << "Schedule :=\n";
|
||||
dbgs().indent(8) << stringFromIslObj(schedule) << ";\n";
|
||||
);
|
||||
|
||||
isl_union_map_compute_flow(isl_union_map_copy(sink),
|
||||
isl_union_map_copy(must_source),
|
||||
isl_union_map_copy(may_source), schedule,
|
||||
&temp_must_dep, &temp_may_dep,
|
||||
&temp_must_no_source, &temp_may_no_source);
|
||||
|
||||
DEBUG(dbgs().indent(4) << "\nDependences calculated\n");
|
||||
DEBUG(
|
||||
dbgs().indent(4) << "TempMustDep:=\n";
|
||||
dbgs().indent(8) << stringFromIslObj(temp_must_dep) << ";\n";
|
||||
|
||||
dbgs().indent(4) << "MustDep:=\n";
|
||||
dbgs().indent(8) << stringFromIslObj(must_dep) << ";\n";
|
||||
);
|
||||
|
||||
// Remove redundant statements.
|
||||
temp_must_dep = isl_union_map_coalesce(temp_must_dep);
|
||||
temp_may_dep = isl_union_map_coalesce(temp_may_dep);
|
||||
temp_must_no_source = isl_union_map_coalesce(temp_must_no_source);
|
||||
temp_may_no_source = isl_union_map_coalesce(temp_may_no_source);
|
||||
|
||||
if (!isl_union_map_is_equal(temp_must_dep, must_dep)) {
|
||||
DEBUG(dbgs().indent(4) << "\nEqual 1 calculated\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(dbgs().indent(4) << "\nEqual 1 calculated\n");
|
||||
|
||||
if (!isl_union_map_is_equal(temp_may_dep, may_dep))
|
||||
return false;
|
||||
|
||||
DEBUG(dbgs().indent(4) << "\nEqual 2 calculated\n");
|
||||
|
||||
if (!isl_union_map_is_equal(temp_must_no_source, must_no_source))
|
||||
return false;
|
||||
|
||||
if (!isl_union_map_is_equal(temp_may_no_source, may_no_source))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isl_union_map* getCombinedScheduleForDim(Scop *scop, unsigned dimLevel) {
|
||||
isl_dim *dim = isl_dim_alloc(scop->getCtx(), scop->getNumParams(), 0, 0);
|
||||
|
||||
isl_union_map *schedule = isl_union_map_empty(dim);
|
||||
|
||||
for (Scop::iterator SI = scop->begin(), SE = scop->end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
isl_map *scattering = isl_map_copy(Stmt->getScattering());
|
||||
unsigned remainingDimensions = isl_map_n_out(scattering) - dimLevel;
|
||||
scattering = isl_map_project_out(scattering, isl_dim_out, dimLevel,
|
||||
remainingDimensions);
|
||||
isl_union_map_add_map(schedule, scattering);
|
||||
}
|
||||
|
||||
return schedule;
|
||||
}
|
||||
|
||||
bool Dependences::isParallelDimension(isl_set *loopDomain,
|
||||
unsigned parallelDimension) {
|
||||
Scop *S = &getCurScop();
|
||||
isl_union_map *schedule = getCombinedScheduleForDim(S, parallelDimension);
|
||||
|
||||
// Calculate distance vector.
|
||||
isl_union_set *scheduleSubset;
|
||||
isl_union_map *scheduleDeps, *restrictedDeps;
|
||||
isl_union_map *scheduleDeps_war, *restrictedDeps_war;
|
||||
isl_union_map *scheduleDeps_waw, *restrictedDeps_waw;
|
||||
|
||||
scheduleSubset = isl_union_set_from_set(isl_set_copy(loopDomain));
|
||||
|
||||
scheduleDeps = isl_union_map_apply_range(isl_union_map_copy(must_dep),
|
||||
isl_union_map_copy(schedule));
|
||||
scheduleDeps = isl_union_map_apply_domain(scheduleDeps,
|
||||
isl_union_map_copy(schedule));
|
||||
|
||||
scheduleDeps_war = isl_union_map_apply_range(isl_union_map_copy(war_dep),
|
||||
isl_union_map_copy(schedule));
|
||||
scheduleDeps_war = isl_union_map_apply_domain(scheduleDeps_war,
|
||||
isl_union_map_copy(schedule));
|
||||
|
||||
scheduleDeps_waw = isl_union_map_apply_range(isl_union_map_copy(waw_dep),
|
||||
isl_union_map_copy(schedule));
|
||||
scheduleDeps_waw = isl_union_map_apply_domain(scheduleDeps_waw,
|
||||
isl_union_map_copy(schedule));
|
||||
|
||||
// Dependences need to originate and to terminate in the scheduling space
|
||||
// enumerated by this loop.
|
||||
restrictedDeps = isl_union_map_intersect_domain(scheduleDeps,
|
||||
isl_union_set_copy(scheduleSubset));
|
||||
restrictedDeps = isl_union_map_intersect_range(restrictedDeps,
|
||||
isl_union_set_copy(scheduleSubset));
|
||||
|
||||
isl_union_set *distance = isl_union_map_deltas(restrictedDeps);
|
||||
|
||||
restrictedDeps_war = isl_union_map_intersect_domain(scheduleDeps_war,
|
||||
isl_union_set_copy(scheduleSubset));
|
||||
restrictedDeps_war = isl_union_map_intersect_range(restrictedDeps_war,
|
||||
isl_union_set_copy(scheduleSubset));
|
||||
|
||||
isl_union_set *distance_war = isl_union_map_deltas(restrictedDeps_war);
|
||||
|
||||
restrictedDeps_waw = isl_union_map_intersect_domain(scheduleDeps_waw,
|
||||
isl_union_set_copy(scheduleSubset));
|
||||
restrictedDeps_waw = isl_union_map_intersect_range(restrictedDeps_waw,
|
||||
isl_union_set_copy(scheduleSubset));
|
||||
|
||||
isl_union_set *distance_waw = isl_union_map_deltas(restrictedDeps_waw);
|
||||
|
||||
isl_dim *dim = isl_dim_set_alloc(S->getCtx(), S->getNumParams(),
|
||||
parallelDimension);
|
||||
|
||||
// [0, 0, 0, 0] - All zero
|
||||
isl_basic_set *allZeroBS = isl_basic_set_universe(isl_dim_copy(dim));
|
||||
unsigned dimensions = isl_dim_size(dim, isl_dim_set);
|
||||
|
||||
for (unsigned i = 0; i < dimensions; i++) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_set, i, v);
|
||||
allZeroBS = isl_basic_set_add_constraint(allZeroBS, c);
|
||||
isl_int_clear(v);
|
||||
}
|
||||
|
||||
isl_set *allZero = isl_set_from_basic_set(allZeroBS);
|
||||
|
||||
// All zero, last unknown.
|
||||
// [0, 0, 0, ?]
|
||||
isl_basic_set *lastUnknownBS = isl_basic_set_universe(isl_dim_copy(dim));
|
||||
dimensions = isl_dim_size(dim, isl_dim_set);
|
||||
|
||||
for (unsigned i = 0; i < dimensions - 1; i++) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_set, i, v);
|
||||
lastUnknownBS = isl_basic_set_add_constraint(lastUnknownBS, c);
|
||||
isl_int_clear(v);
|
||||
}
|
||||
|
||||
isl_set *lastUnknown = isl_set_from_basic_set(lastUnknownBS);
|
||||
|
||||
// Valid distance vectors
|
||||
isl_set *validDistances = isl_set_subtract(lastUnknown, allZero);
|
||||
validDistances = isl_set_complement(validDistances);
|
||||
isl_union_set *validDistancesUS = isl_union_set_from_set(validDistances);
|
||||
|
||||
isl_union_set *nonValid = isl_union_set_subtract(distance,
|
||||
isl_union_set_copy(validDistancesUS));
|
||||
|
||||
isl_union_set *nonValid_war = isl_union_set_subtract(distance_war,
|
||||
isl_union_set_copy(validDistancesUS));
|
||||
|
||||
isl_union_set *nonValid_waw = isl_union_set_subtract(distance_waw,
|
||||
validDistancesUS);
|
||||
|
||||
return isl_union_set_is_empty(nonValid)
|
||||
&& isl_union_set_is_empty(nonValid_war)
|
||||
&& isl_union_set_is_empty(nonValid_waw);
|
||||
}
|
||||
|
||||
void Dependences::printScop(raw_ostream &OS) const {
|
||||
OS.indent(4) << "Must dependences:\n";
|
||||
OS.indent(8) << stringFromIslObj(must_dep) << "\n";
|
||||
|
||||
OS.indent(4) << "May dependences:\n";
|
||||
OS.indent(8) << stringFromIslObj(may_dep) << "\n";
|
||||
|
||||
OS.indent(4) << "Must no source:\n";
|
||||
OS.indent(8) << stringFromIslObj(must_no_source) << "\n";
|
||||
|
||||
OS.indent(4) << "May no source:\n";
|
||||
OS.indent(8) << stringFromIslObj(may_no_source) << "\n";
|
||||
}
|
||||
|
||||
void Dependences::releaseMemory() {
|
||||
if (must_dep)
|
||||
isl_union_map_free(must_dep);
|
||||
|
||||
if (may_dep)
|
||||
isl_union_map_free(may_dep);
|
||||
|
||||
if (must_no_source)
|
||||
isl_union_map_free(must_no_source);
|
||||
|
||||
if (may_no_source)
|
||||
isl_union_map_free(may_no_source);
|
||||
|
||||
if (war_dep)
|
||||
isl_union_map_free(war_dep);
|
||||
|
||||
if (waw_dep)
|
||||
isl_union_map_free(waw_dep);
|
||||
|
||||
must_dep = may_dep = NULL;
|
||||
must_no_source = may_no_source = NULL;
|
||||
war_dep = waw_dep = NULL;
|
||||
|
||||
if (sink)
|
||||
isl_union_map_free(sink);
|
||||
|
||||
if (must_source)
|
||||
isl_union_map_free(must_source);
|
||||
|
||||
if (may_source)
|
||||
isl_union_map_free(may_source);
|
||||
|
||||
sink = must_source = may_source = NULL;
|
||||
}
|
||||
|
||||
void Dependences::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
char Dependences::ID = 0;
|
||||
|
||||
static RegisterPass<Dependences>
|
||||
X("polly-dependences", "Polly - Calculate dependences for Scop");
|
||||
|
||||
Pass* polly::createDependencesPass() {
|
||||
return new Dependences();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
##===- polly/lib/Exchange/Makefile ----------------*- Makefile -*-===##
|
||||
|
||||
#
|
||||
# Indicate where we are relative to the top of the source tree.
|
||||
#
|
||||
LEVEL=../..
|
||||
|
||||
LIBRARYNAME=pollyanalysis
|
||||
BUILD_ARCHIVE = 1
|
||||
|
||||
CPP.Flags += $(POLLY_INC)
|
||||
|
||||
#
|
||||
# Include Makefile.common so we know what to do.
|
||||
#
|
||||
include $(LEVEL)/Makefile.common
|
|
@ -0,0 +1,659 @@
|
|||
//===----- ScopDetection.cpp - Detect Scops --------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Detect the maximal Scops of a function.
|
||||
//
|
||||
// A static control part (Scop) is a subgraph of the control flow graph (CFG)
|
||||
// that only has statically known control flow and can therefore be described
|
||||
// within the polyhedral model.
|
||||
//
|
||||
// Every Scop fullfills these restrictions:
|
||||
//
|
||||
// * It is a single entry single exit region
|
||||
//
|
||||
// * Only affine linear bounds in the loops
|
||||
//
|
||||
// Every natural loop in a Scop must have a number of loop iterations that can
|
||||
// be described as an affine linear function in surrounding loop iterators or
|
||||
// parameters. (A parameter is a scalar that does not change its value during
|
||||
// execution of the Scop).
|
||||
//
|
||||
// * Only comparisons of affine linear expressions in conditions
|
||||
//
|
||||
// * All loops and conditions perfectly nested
|
||||
//
|
||||
// The control flow needs to be structured such that it could be written using
|
||||
// just 'for' and 'if' statements, without the need for any 'goto', 'break' or
|
||||
// 'continue'.
|
||||
//
|
||||
// * Side effect free functions call
|
||||
//
|
||||
// Only function calls and intrinsics that do not have side effects are allowed
|
||||
// (readnone).
|
||||
//
|
||||
// The Scop detection finds the largest Scops by checking if the largest
|
||||
// region is a Scop. If this is not the case, its canonical subregions are
|
||||
// checked until a region is a Scop. It is now tried to extend this Scop by
|
||||
// creating a larger non canonical region.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/ScopDetection.h"
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/Support/ScopHelper.h"
|
||||
#include "polly/Support/AffineSCEVIterator.h"
|
||||
|
||||
#include "llvm/LLVMContext.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/RegionIterator.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-detect"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Statistics.
|
||||
|
||||
STATISTIC(ValidRegion, "Number of regions that a valid part of Scop");
|
||||
|
||||
#define BADSCOP_STAT(NAME, DESC) STATISTIC(Bad##NAME##ForScop, \
|
||||
"Number of bad regions for Scop: "\
|
||||
DESC)
|
||||
|
||||
#define STATSCOP(NAME); assert(!Context.Verifying && #NAME); \
|
||||
if (!Context.Verifying) ++Bad##NAME##ForScop;
|
||||
|
||||
BADSCOP_STAT(CFG, "CFG too complex");
|
||||
BADSCOP_STAT(IndVar, "Non canonical induction variable in loop");
|
||||
BADSCOP_STAT(LoopBound, "Loop bounds can not be computed");
|
||||
BADSCOP_STAT(FuncCall, "Function call with side effects appeared");
|
||||
BADSCOP_STAT(AffFunc, "Expression not affine");
|
||||
BADSCOP_STAT(Scalar, "Found scalar dependency");
|
||||
BADSCOP_STAT(Alias, "Found base address alias");
|
||||
BADSCOP_STAT(SimpleRegion, "Region not simple");
|
||||
BADSCOP_STAT(Other, "Others");
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ScopDetection.
|
||||
|
||||
bool ScopDetection::isMaxRegionInScop(const Region &R) const {
|
||||
// The Region is valid only if it could be found in the set.
|
||||
return ValidRegions.count(&R);
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidAffineFunction(const SCEV *S, Region &RefRegion,
|
||||
Value **BasePtr) const {
|
||||
assert(S && "S must not be null!");
|
||||
bool isMemoryAccess = (BasePtr != 0);
|
||||
if (isMemoryAccess) *BasePtr = 0;
|
||||
DEBUG(dbgs() << "Checking " << *S << " ... ");
|
||||
|
||||
if (isa<SCEVCouldNotCompute>(S)) {
|
||||
DEBUG(dbgs() << "Non Affine: SCEV could not be computed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AffineSCEVIterator I = affine_begin(S, SE), E = affine_end(); I != E;
|
||||
++I) {
|
||||
// The constant part must be a SCEVConstant.
|
||||
// TODO: support sizeof in coefficient.
|
||||
if (!isa<SCEVConstant>(I->second)) {
|
||||
DEBUG(dbgs() << "Non Affine: Right hand side is not constant\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const SCEV *Var = I->first;
|
||||
|
||||
// A constant offset is affine.
|
||||
if(isa<SCEVConstant>(Var))
|
||||
continue;
|
||||
|
||||
// Memory accesses are allowed to have a base pointer.
|
||||
if (Var->getType()->isPointerTy()) {
|
||||
if (!isMemoryAccess) {
|
||||
DEBUG(dbgs() << "Non Affine: Pointer in non memory access\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(I->second->isOne() && "Only one as pointer coefficient allowed.\n");
|
||||
const SCEVUnknown *BaseAddr = dyn_cast<SCEVUnknown>(Var);
|
||||
|
||||
if (!BaseAddr || isa<UndefValue>(BaseAddr->getValue())){
|
||||
DEBUG(dbgs() << "Cannot handle base: " << *Var << "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// BaseAddr must be invariant in Scop.
|
||||
if (!isParameter(BaseAddr, RefRegion, *LI, *SE)) {
|
||||
DEBUG(dbgs() << "Non Affine: Base address not invariant in SCoP\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(*BasePtr == 0 && "Found second base pointer.\n");
|
||||
*BasePtr = BaseAddr->getValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isParameter(Var, RefRegion, *LI, *SE)
|
||||
|| isIndVar(Var, RefRegion, *LI, *SE))
|
||||
continue;
|
||||
|
||||
DEBUG(dbgs() << "Non Affine: " ;
|
||||
Var->print(dbgs());
|
||||
dbgs() << " is neither parameter nor induction variable\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(dbgs() << " is affine.\n");
|
||||
return !isMemoryAccess || (*BasePtr != 0);
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidCFG(BasicBlock &BB, DetectionContext &Context) const
|
||||
{
|
||||
Region &RefRegion = Context.CurRegion;
|
||||
TerminatorInst *TI = BB.getTerminator();
|
||||
|
||||
// Return instructions are only valid if the region is the top level region.
|
||||
if (isa<ReturnInst>(TI) && !RefRegion.getExit() && TI->getNumOperands() == 0)
|
||||
return true;
|
||||
|
||||
BranchInst *Br = dyn_cast<BranchInst>(TI);
|
||||
|
||||
if (!Br) {
|
||||
DEBUG(dbgs() << "Non branch instruction as terminator of BB: ";
|
||||
WriteAsOperand(dbgs(), &BB, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(CFG);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Br->isUnconditional()) return true;
|
||||
|
||||
Value *Condition = Br->getCondition();
|
||||
|
||||
// UndefValue is not allowed as condition.
|
||||
if (isa<UndefValue>(Condition)) {
|
||||
DEBUG(dbgs() << "Undefined value in branch instruction of BB: ";
|
||||
WriteAsOperand(dbgs(), &BB, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(AffFunc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only Constant and ICmpInst are allowed as condition.
|
||||
if (!(isa<Constant>(Condition) || isa<ICmpInst>(Condition))) {
|
||||
DEBUG(dbgs() << "Non Constant and non ICmpInst instruction in BB: ";
|
||||
WriteAsOperand(dbgs(), &BB, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(AffFunc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow perfectly nested conditions.
|
||||
assert(Br->getNumSuccessors() == 2 && "Unexpected number of successors");
|
||||
|
||||
if (ICmpInst *ICmp = dyn_cast<ICmpInst>(Condition)) {
|
||||
// Unsigned comparisons are not allowed. They trigger overflow problems
|
||||
// in the code generation.
|
||||
//
|
||||
// TODO: This is not sufficient and just hides bugs. However it does pretty
|
||||
// well.
|
||||
if(ICmp->isUnsigned())
|
||||
return false;
|
||||
|
||||
// Are both operands of the ICmp affine?
|
||||
if (isa<UndefValue>(ICmp->getOperand(0))
|
||||
|| isa<UndefValue>(ICmp->getOperand(1))) {
|
||||
DEBUG(dbgs() << "Undefined operand in branch instruction of BB: ";
|
||||
WriteAsOperand(dbgs(), &BB, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(AffFunc);
|
||||
return false;
|
||||
}
|
||||
|
||||
const SCEV *ScevLHS = SE->getSCEV(ICmp->getOperand(0));
|
||||
const SCEV *ScevRHS = SE->getSCEV(ICmp->getOperand(1));
|
||||
|
||||
bool affineLHS = isValidAffineFunction(ScevLHS, RefRegion);
|
||||
bool affineRHS = isValidAffineFunction(ScevRHS, RefRegion);
|
||||
|
||||
if (!affineLHS || !affineRHS) {
|
||||
DEBUG(dbgs() << "Non affine branch instruction in BB: ";
|
||||
WriteAsOperand(dbgs(), &BB, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(AffFunc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow loop exit conditions.
|
||||
Loop *L = LI->getLoopFor(&BB);
|
||||
if (L && L->getExitingBlock() == &BB)
|
||||
return true;
|
||||
|
||||
// Allow perfectly nested conditions.
|
||||
Region *R = RI->getRegionFor(&BB);
|
||||
if (R->getEntry() != &BB) {
|
||||
DEBUG(dbgs() << "Non well structured condition starting at BB: ";
|
||||
WriteAsOperand(dbgs(), &BB, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(CFG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidCallInst(CallInst &CI) {
|
||||
if (CI.mayHaveSideEffects() || CI.doesNotReturn())
|
||||
return false;
|
||||
|
||||
if (CI.doesNotAccessMemory())
|
||||
return true;
|
||||
|
||||
Function *CalledFunction = CI.getCalledFunction();
|
||||
|
||||
// Indirect calls are not supported.
|
||||
if (CalledFunction == 0)
|
||||
return false;
|
||||
|
||||
// TODO: Intrinsics.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidMemoryAccess(Instruction &Inst,
|
||||
DetectionContext &Context) const {
|
||||
Value *Ptr = getPointerOperand(Inst), *BasePtr;
|
||||
const SCEV *AccessFunction = SE->getSCEV(Ptr);
|
||||
|
||||
if (!isValidAffineFunction(AccessFunction, Context.CurRegion, &BasePtr)) {
|
||||
DEBUG(dbgs() << "Bad memory addr " << *AccessFunction << "\n");
|
||||
STATSCOP(AffFunc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: Alias Analysis thinks IntToPtrInst aliases with alloca instructions
|
||||
// created by IndependentBlocks Pass.
|
||||
if (isa<IntToPtrInst>(BasePtr)) {
|
||||
DEBUG(dbgs() << "Find bad intoptr prt: " << *BasePtr << '\n');
|
||||
STATSCOP(Other);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the base pointer of the memory access does alias with
|
||||
// any other pointer. This cannot be handled at the moment.
|
||||
AliasSet &AS =
|
||||
Context.AST.getAliasSetForPointer(BasePtr, AliasAnalysis::UnknownSize,
|
||||
Inst.getMetadata(LLVMContext::MD_tbaa));
|
||||
if (!AS.isMustAlias()) {
|
||||
DEBUG(dbgs() << "Bad pointer alias found:" << *BasePtr << "\nAS:\n" << AS);
|
||||
|
||||
// STATSCOP triggers an assertion if we are in verifying mode.
|
||||
// This is generally good to check that we do not change the SCoP after we
|
||||
// run the SCoP detection and consequently to ensure that we can still
|
||||
// represent that SCoP. However, in case of aliasing this does not work.
|
||||
// The independent blocks pass may create memory references which seem to
|
||||
// alias, if -basicaa is not available. They actually do not. As we do not
|
||||
// not know this and we would fail here if we verify it.
|
||||
if (!Context.Verifying) {
|
||||
STATSCOP(Alias);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ScopDetection::hasScalarDependency(Instruction &Inst,
|
||||
Region &RefRegion) const {
|
||||
for (Instruction::use_iterator UI = Inst.use_begin(), UE = Inst.use_end();
|
||||
UI != UE; ++UI)
|
||||
if (Instruction *Use = dyn_cast<Instruction>(*UI))
|
||||
if (!RefRegion.contains(Use->getParent())) {
|
||||
// DirtyHack 1: PHINode user outside the Scop is not allow, if this
|
||||
// PHINode is induction variable, the scalar to array transform may
|
||||
// break it and introduce a non-indvar PHINode, which is not allow in
|
||||
// Scop.
|
||||
// This can be fix by:
|
||||
// Introduce a IndependentBlockPrepare pass, which translate all
|
||||
// PHINodes not in Scop to array.
|
||||
// The IndependentBlockPrepare pass can also split the entry block of
|
||||
// the function to hold the alloca instruction created by scalar to
|
||||
// array. and split the exit block of the Scop so the new create load
|
||||
// instruction for escape users will not break other Scops.
|
||||
if (isa<PHINode>(Use))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidInstruction(Instruction &Inst,
|
||||
DetectionContext &Context) const {
|
||||
// Only canonical IVs are allowed.
|
||||
if (PHINode *PN = dyn_cast<PHINode>(&Inst))
|
||||
if (!isIndVar(PN, LI)) {
|
||||
DEBUG(dbgs() << "Non canonical PHI node found: ";
|
||||
WriteAsOperand(dbgs(), &Inst, false);
|
||||
dbgs() << "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scalar dependencies are not allowed.
|
||||
if (hasScalarDependency(Inst, Context.CurRegion)) {
|
||||
DEBUG(dbgs() << "Scalar dependency found: ";
|
||||
WriteAsOperand(dbgs(), &Inst, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(Scalar);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only check the call instruction but not invoke instruction.
|
||||
if (CallInst *CI = dyn_cast<CallInst>(&Inst)) {
|
||||
if (isValidCallInst(*CI))
|
||||
return true;
|
||||
|
||||
DEBUG(dbgs() << "Bad call Inst: ";
|
||||
WriteAsOperand(dbgs(), &Inst, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(FuncCall);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Inst.mayWriteToMemory() && !Inst.mayReadFromMemory()) {
|
||||
// Handle cast instruction.
|
||||
if (isa<IntToPtrInst>(Inst) || isa<BitCastInst>(Inst)) {
|
||||
DEBUG(dbgs() << "Bad cast Inst!\n");
|
||||
STATSCOP(Other);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isa<AllocaInst>(Inst)) {
|
||||
DEBUG(dbgs() << "AllocaInst is not allowed!!\n");
|
||||
STATSCOP(Other);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the access function.
|
||||
if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst))
|
||||
return isValidMemoryAccess(Inst, Context);
|
||||
|
||||
// We do not know this instruction, therefore we assume it is invalid.
|
||||
DEBUG(dbgs() << "Bad instruction found: ";
|
||||
WriteAsOperand(dbgs(), &Inst, false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(Other);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidBasicBlock(BasicBlock &BB,
|
||||
DetectionContext &Context) const {
|
||||
if (!isValidCFG(BB, Context))
|
||||
return false;
|
||||
|
||||
// Check all instructions, except the terminator instruction.
|
||||
for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I)
|
||||
if (!isValidInstruction(*I, Context))
|
||||
return false;
|
||||
|
||||
Loop *L = LI->getLoopFor(&BB);
|
||||
if (L && L->getHeader() == &BB && !isValidLoop(L, Context))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidLoop(Loop *L, DetectionContext &Context) const {
|
||||
PHINode *IndVar = L->getCanonicalInductionVariable();
|
||||
// No canonical induction variable.
|
||||
if (!IndVar) {
|
||||
DEBUG(dbgs() << "No canonical iv for loop: ";
|
||||
WriteAsOperand(dbgs(), L->getHeader(), false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(IndVar);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the loop count affine?
|
||||
const SCEV *LoopCount = SE->getBackedgeTakenCount(L);
|
||||
if (!isValidAffineFunction(LoopCount, Context.CurRegion)) {
|
||||
DEBUG(dbgs() << "Non affine loop bound for loop: ";
|
||||
WriteAsOperand(dbgs(), L->getHeader(), false);
|
||||
dbgs() << "\n");
|
||||
STATSCOP(LoopBound);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Region *ScopDetection::expandRegion(Region &R) {
|
||||
Region *CurrentRegion = &R;
|
||||
Region *TmpRegion = R.getExpandedRegion();
|
||||
|
||||
DEBUG(dbgs() << "\tExpanding " << R.getNameStr() << "\n");
|
||||
|
||||
while (TmpRegion) {
|
||||
DetectionContext Context(*TmpRegion, *AA, false /*verifying*/);
|
||||
DEBUG(dbgs() << "\t\tTrying " << TmpRegion->getNameStr() << "\n");
|
||||
|
||||
if (!allBlocksValid(Context))
|
||||
break;
|
||||
|
||||
if (isValidExit(Context)) {
|
||||
if (CurrentRegion != &R)
|
||||
delete CurrentRegion;
|
||||
|
||||
CurrentRegion = TmpRegion;
|
||||
}
|
||||
|
||||
Region *TmpRegion2 = TmpRegion->getExpandedRegion();
|
||||
|
||||
if (TmpRegion != &R && TmpRegion != CurrentRegion)
|
||||
delete TmpRegion;
|
||||
|
||||
TmpRegion = TmpRegion2;
|
||||
}
|
||||
|
||||
if (&R == CurrentRegion)
|
||||
return NULL;
|
||||
|
||||
DEBUG(dbgs() << "\tto " << CurrentRegion->getNameStr() << "\n");
|
||||
|
||||
return CurrentRegion;
|
||||
}
|
||||
|
||||
|
||||
void ScopDetection::findScops(Region &R) {
|
||||
DetectionContext Context(R, *AA, false /*verifying*/);
|
||||
|
||||
if (isValidRegion(Context)) {
|
||||
++ValidRegion;
|
||||
ValidRegions.insert(&R);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Region::iterator I = R.begin(), E = R.end(); I != E; ++I)
|
||||
findScops(**I);
|
||||
|
||||
// Try to expand regions.
|
||||
//
|
||||
// As the region tree normally only contains canonical regions, non canonical
|
||||
// regions that form a Scop are not found. Therefore, those non canonical
|
||||
// regions are checked by expanding the canonical ones.
|
||||
|
||||
std::vector<Region*> ToExpand;
|
||||
|
||||
for (Region::iterator I = R.begin(), E = R.end(); I != E; ++I)
|
||||
ToExpand.push_back(*I);
|
||||
|
||||
for (std::vector<Region*>::iterator RI = ToExpand.begin(),
|
||||
RE = ToExpand.end(); RI != RE; ++RI) {
|
||||
Region *CurrentRegion = *RI;
|
||||
|
||||
// Skip invalid regions. Regions may become invalid, if they are element of
|
||||
// an already expanded region.
|
||||
if (ValidRegions.find(CurrentRegion) == ValidRegions.end())
|
||||
continue;
|
||||
|
||||
Region *ExpandedR = expandRegion(*CurrentRegion);
|
||||
|
||||
if (!ExpandedR)
|
||||
continue;
|
||||
|
||||
R.addSubRegion(ExpandedR, true);
|
||||
ValidRegions.insert(ExpandedR);
|
||||
ValidRegions.erase(CurrentRegion);
|
||||
|
||||
for (Region::iterator I = ExpandedR->begin(), E = ExpandedR->end(); I != E;
|
||||
++I)
|
||||
ValidRegions.erase(*I);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScopDetection::allBlocksValid(DetectionContext &Context) const {
|
||||
Region &R = Context.CurRegion;
|
||||
|
||||
for (Region::block_iterator I = R.block_begin(), E = R.block_end(); I != E;
|
||||
++I)
|
||||
if (!isValidBasicBlock(*(I->getNodeAs<BasicBlock>()), Context))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidExit(DetectionContext &Context) const {
|
||||
Region &R = Context.CurRegion;
|
||||
|
||||
// PHI nodes are not allowed in the exit basic block.
|
||||
if (BasicBlock *Exit = R.getExit()) {
|
||||
BasicBlock::iterator I = Exit->begin();
|
||||
if (I != Exit->end() && isa<PHINode> (*I)) {
|
||||
DEBUG(dbgs() << "PHI node in exit";
|
||||
dbgs() << "\n");
|
||||
STATSCOP(Other);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidRegion(DetectionContext &Context) const {
|
||||
Region &R = Context.CurRegion;
|
||||
|
||||
DEBUG(dbgs() << "Checking region: " << R.getNameStr() << "\n\t");
|
||||
|
||||
// The toplevel region is no valid region.
|
||||
if (!R.getParent()) {
|
||||
DEBUG(dbgs() << "Top level region is invalid";
|
||||
dbgs() << "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// SCoP can not contains the entry block of the function, because we need
|
||||
// to insert alloca instruction there when translate scalar to array.
|
||||
if (R.getEntry() == &(R.getEntry()->getParent()->getEntryBlock())) {
|
||||
DEBUG(dbgs() << "Region containing entry block of function is invalid!\n");
|
||||
STATSCOP(Other);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only a simple region is allowed.
|
||||
if (!R.isSimple()) {
|
||||
DEBUG(dbgs() << "Region not simple: " << R.getNameStr() << '\n');
|
||||
STATSCOP(SimpleRegion);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!allBlocksValid(Context))
|
||||
return false;
|
||||
|
||||
if (!isValidExit(Context))
|
||||
return false;
|
||||
|
||||
DEBUG(dbgs() << "OK\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopDetection::isValidFunction(llvm::Function &F) {
|
||||
const std::string &Name = F.getNameStr();
|
||||
size_t found = Name.find(".omp_subfn");
|
||||
if (found != std::string::npos)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopDetection::runOnFunction(llvm::Function &F) {
|
||||
AA = &getAnalysis<AliasAnalysis>();
|
||||
SE = &getAnalysis<ScalarEvolution>();
|
||||
LI = &getAnalysis<LoopInfo>();
|
||||
RI = &getAnalysis<RegionInfo>();
|
||||
Region *TopRegion = RI->getTopLevelRegion();
|
||||
|
||||
if(!isValidFunction(F))
|
||||
return false;
|
||||
|
||||
findScops(*TopRegion);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void polly::ScopDetection::verifyRegion(const Region &R) const {
|
||||
assert(isMaxRegionInScop(R) && "Expect R is a valid region.");
|
||||
DetectionContext Context(const_cast<Region&>(R), *AA, true /*verifying*/);
|
||||
isValidRegion(Context);
|
||||
}
|
||||
|
||||
void polly::ScopDetection::verifyAnalysis() const {
|
||||
for (RegionSet::const_iterator I = ValidRegions.begin(),
|
||||
E = ValidRegions.end(); I != E; ++I)
|
||||
verifyRegion(**I);
|
||||
}
|
||||
|
||||
void ScopDetection::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<DominatorTree>();
|
||||
AU.addRequired<PostDominatorTree>();
|
||||
AU.addRequired<LoopInfo>();
|
||||
AU.addRequired<ScalarEvolution>();
|
||||
// We also need AA and RegionInfo when we are verifying analysis.
|
||||
AU.addRequiredTransitive<AliasAnalysis>();
|
||||
AU.addRequiredTransitive<RegionInfo>();
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
|
||||
void ScopDetection::print(raw_ostream &OS, const Module *) const {
|
||||
for (RegionSet::const_iterator I = ValidRegions.begin(),
|
||||
E = ValidRegions.end(); I != E; ++I)
|
||||
OS << "Valid Region for Scop: " << (*I)->getNameStr() << '\n';
|
||||
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
void ScopDetection::releaseMemory() {
|
||||
ValidRegions.clear();
|
||||
}
|
||||
|
||||
char ScopDetection::ID = 0;
|
||||
|
||||
static RegisterPass<ScopDetection>
|
||||
X("polly-detect", "Polly - Detect Scops in functions");
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
//===- GraphPrinter.cpp - Create a DOT output describing the Scop. --------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Create a DOT output describing the Scop.
|
||||
//
|
||||
// For each function a dot file is created that shows the control flow graph of
|
||||
// the function and highlights the detected Scops.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/ScopDetection.h"
|
||||
|
||||
#include "llvm/Analysis/DOTGraphTraitsPass.h"
|
||||
#include "llvm/Analysis/RegionInfo.h"
|
||||
#include "llvm/Analysis/RegionIterator.h"
|
||||
|
||||
using namespace polly;
|
||||
using namespace llvm;
|
||||
|
||||
namespace llvm {
|
||||
template <> struct GraphTraits<ScopDetection*>
|
||||
: public GraphTraits<RegionInfo*> {
|
||||
|
||||
static NodeType *getEntryNode(ScopDetection *SD) {
|
||||
return GraphTraits<RegionInfo*>::getEntryNode(SD->getRI());
|
||||
}
|
||||
static nodes_iterator nodes_begin(ScopDetection* SD) {
|
||||
return nodes_iterator::begin(getEntryNode(SD));
|
||||
}
|
||||
static nodes_iterator nodes_end(ScopDetection *SD) {
|
||||
return nodes_iterator::end(getEntryNode(SD));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct DOTGraphTraits<RegionNode*> : public DefaultDOTGraphTraits {
|
||||
|
||||
DOTGraphTraits (bool isSimple=false)
|
||||
: DefaultDOTGraphTraits(isSimple) {}
|
||||
|
||||
std::string getNodeLabel(RegionNode *Node, RegionNode *Graph) {
|
||||
|
||||
if (!Node->isSubRegion()) {
|
||||
BasicBlock *BB = Node->getNodeAs<BasicBlock>();
|
||||
|
||||
if (isSimple())
|
||||
return DOTGraphTraits<const Function*>
|
||||
::getSimpleNodeLabel(BB, BB->getParent());
|
||||
else
|
||||
return DOTGraphTraits<const Function*>
|
||||
::getCompleteNodeLabel(BB, BB->getParent());
|
||||
}
|
||||
|
||||
return "Not implemented";
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct DOTGraphTraits<ScopDetection*> : public DOTGraphTraits<RegionNode*> {
|
||||
DOTGraphTraits (bool isSimple=false)
|
||||
: DOTGraphTraits<RegionNode*>(isSimple) {}
|
||||
static std::string getGraphName(ScopDetection *SD) {
|
||||
return "Scop Graph";
|
||||
}
|
||||
|
||||
std::string getEdgeAttributes(RegionNode *srcNode,
|
||||
GraphTraits<RegionInfo*>::ChildIteratorType CI, ScopDetection *SD) {
|
||||
|
||||
RegionNode *destNode = *CI;
|
||||
|
||||
if (srcNode->isSubRegion() || destNode->isSubRegion())
|
||||
return "";
|
||||
|
||||
// In case of a backedge, do not use it to define the layout of the nodes.
|
||||
BasicBlock *srcBB = srcNode->getNodeAs<BasicBlock>();
|
||||
BasicBlock *destBB = destNode->getNodeAs<BasicBlock>();
|
||||
|
||||
RegionInfo *RI = SD->getRI();
|
||||
Region *R = RI->getRegionFor(destBB);
|
||||
|
||||
while (R && R->getParent())
|
||||
if (R->getParent()->getEntry() == destBB)
|
||||
R = R->getParent();
|
||||
else
|
||||
break;
|
||||
|
||||
if (R->getEntry() == destBB && R->contains(srcBB))
|
||||
return "constraint=false";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string getNodeLabel(RegionNode *Node, ScopDetection *SD) {
|
||||
return DOTGraphTraits<RegionNode*>
|
||||
::getNodeLabel(Node, SD->getRI()->getTopLevelRegion());
|
||||
}
|
||||
// Print the cluster of the subregions. This groups the single basic blocks
|
||||
// and adds a different background color for each group.
|
||||
static void printRegionCluster(const ScopDetection *SD, const Region *R,
|
||||
raw_ostream &O, unsigned depth = 0) {
|
||||
O.indent(2 * depth) << "subgraph cluster_" << static_cast<const void*>(R)
|
||||
<< " {\n";
|
||||
O.indent(2 * (depth + 1)) << "label = \"\";\n";
|
||||
|
||||
if (SD->isMaxRegionInScop(*R)) {
|
||||
O.indent(2 * (depth + 1)) << "style = filled;\n";
|
||||
|
||||
// Set color to green.
|
||||
O.indent(2 * (depth + 1)) << "color = 3";
|
||||
} else {
|
||||
O.indent(2 * (depth + 1)) << "style = solid;\n";
|
||||
|
||||
int color = (R->getDepth() * 2 % 12) + 1;
|
||||
|
||||
// We do not want green again.
|
||||
if (color == 3)
|
||||
color = 6;
|
||||
|
||||
O.indent(2 * (depth + 1)) << "color = "
|
||||
<< color << "\n";
|
||||
}
|
||||
|
||||
for (Region::const_iterator RI = R->begin(), RE = R->end(); RI != RE; ++RI)
|
||||
printRegionCluster(SD, *RI, O, depth + 1);
|
||||
|
||||
RegionInfo *RI = R->getRegionInfo();
|
||||
|
||||
for (Region::const_block_iterator BI = R->block_begin(),
|
||||
BE = R->block_end(); BI != BE; ++BI) {
|
||||
BasicBlock *BB = (*BI)->getNodeAs<BasicBlock>();
|
||||
if (RI->getRegionFor(BB) == R)
|
||||
O.indent(2 * (depth + 1)) << "Node"
|
||||
<< static_cast<const void*>(RI->getTopLevelRegion()->getBBNode(BB))
|
||||
<< ";\n";
|
||||
}
|
||||
|
||||
O.indent(2 * depth) << "}\n";
|
||||
}
|
||||
static void addCustomGraphFeatures(const ScopDetection* SD,
|
||||
GraphWriter<ScopDetection*> &GW) {
|
||||
raw_ostream &O = GW.getOStream();
|
||||
O << "\tcolorscheme = \"paired12\"\n";
|
||||
printRegionCluster(SD, SD->getRI()->getTopLevelRegion(), O, 4);
|
||||
}
|
||||
};
|
||||
|
||||
} //end namespace llvm
|
||||
|
||||
struct ScopViewer
|
||||
: public DOTGraphTraitsViewer<ScopDetection, false> {
|
||||
static char ID;
|
||||
ScopViewer() : DOTGraphTraitsViewer<ScopDetection, false>("scops", ID){}
|
||||
};
|
||||
char ScopViewer::ID = 0;
|
||||
|
||||
struct ScopOnlyViewer
|
||||
: public DOTGraphTraitsViewer<ScopDetection, true> {
|
||||
static char ID;
|
||||
ScopOnlyViewer()
|
||||
: DOTGraphTraitsViewer<ScopDetection, true>("scopsonly", ID){}
|
||||
};
|
||||
char ScopOnlyViewer::ID = 0;
|
||||
|
||||
struct ScopPrinter
|
||||
: public DOTGraphTraitsPrinter<ScopDetection, false> {
|
||||
static char ID;
|
||||
ScopPrinter() :
|
||||
DOTGraphTraitsPrinter<ScopDetection, false>("scops", ID) {}
|
||||
};
|
||||
char ScopPrinter::ID = 0;
|
||||
|
||||
struct ScopOnlyPrinter
|
||||
: public DOTGraphTraitsPrinter<ScopDetection, true> {
|
||||
static char ID;
|
||||
ScopOnlyPrinter() :
|
||||
DOTGraphTraitsPrinter<ScopDetection, true>("scopsonly", ID) {}
|
||||
};
|
||||
char ScopOnlyPrinter::ID = 0;
|
||||
|
||||
static RegisterPass<ScopViewer>
|
||||
X("view-scops","Polly - View Scops of function");
|
||||
|
||||
static RegisterPass<ScopOnlyViewer>
|
||||
Y("view-scops-only",
|
||||
"Polly - View Scops of function (with no function bodies)");
|
||||
|
||||
static RegisterPass<ScopPrinter>
|
||||
M("dot-scops", "Polly - Print Scops of function");
|
||||
|
||||
static RegisterPass<ScopOnlyPrinter>
|
||||
N("dot-scops-only",
|
||||
"Polly - Print Scops of function (with no function bodies)");
|
||||
|
||||
Pass* polly::createDOTViewerPass() {
|
||||
return new ScopViewer();
|
||||
}
|
||||
|
||||
Pass* polly::createDOTOnlyViewerPass() {
|
||||
return new ScopOnlyViewer();
|
||||
}
|
||||
|
||||
Pass* polly::createDOTPrinterPass() {
|
||||
return new ScopPrinter();
|
||||
}
|
||||
|
||||
Pass* polly::createDOTOnlyPrinterPass() {
|
||||
return new ScopOnlyPrinter();
|
||||
}
|
|
@ -0,0 +1,906 @@
|
|||
//===--------- ScopInfo.cpp - Create Scops from LLVM IR ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Create a polyhedral description for a static control flow region.
|
||||
//
|
||||
// The pass creates a polyhedral description of the Scops detected by the Scop
|
||||
// detection derived from their LLVM-IR code.
|
||||
//
|
||||
// This represantation is shared among several tools in the polyhedral
|
||||
// community, which are e.g. Cloog, Pluto, Loopo, Graphite.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/ScopInfo.h"
|
||||
|
||||
#include "polly/TempScopInfo.h"
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/Support/GICHelper.h"
|
||||
#include "polly/Support/ScopHelper.h"
|
||||
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
||||
#include "llvm/Analysis/RegionIterator.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-scops"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
#include "isl/constraint.h"
|
||||
#include "isl/set.h"
|
||||
#include "isl/map.h"
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
STATISTIC(ScopFound, "Number of valid Scops");
|
||||
STATISTIC(RichScopFound, "Number of Scops containing a loop");
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
static void setCoefficient(const SCEV *Coeff, mpz_t v, bool negative,
|
||||
bool isSigned = true) {
|
||||
if (Coeff) {
|
||||
const SCEVConstant *C = dyn_cast<SCEVConstant>(Coeff);
|
||||
const APInt &CI = C->getValue()->getValue();
|
||||
MPZ_from_APInt(v, negative ? (-CI) : CI, isSigned);
|
||||
} else
|
||||
isl_int_set_si(v, 0);
|
||||
}
|
||||
|
||||
static isl_map *getValueOf(const SCEVAffFunc &AffFunc,
|
||||
const ScopStmt *Statement, isl_dim *dim) {
|
||||
|
||||
const SmallVectorImpl<const SCEV*> &Params =
|
||||
Statement->getParent()->getParams();
|
||||
unsigned num_in = Statement->getNumIterators(), num_param = Params.size();
|
||||
|
||||
const char *dimname = isl_dim_get_tuple_name(dim, isl_dim_set);
|
||||
dim = isl_dim_alloc(isl_dim_get_ctx(dim), num_param,
|
||||
isl_dim_size(dim, isl_dim_set), 1);
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_in, dimname);
|
||||
|
||||
assert((AffFunc.getType() == SCEVAffFunc::Eq
|
||||
|| AffFunc.getType() == SCEVAffFunc::ReadMem
|
||||
|| AffFunc.getType() == SCEVAffFunc::WriteMem)
|
||||
&& "AffFunc is not an equality");
|
||||
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// Set single output dimension.
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, 0, v);
|
||||
|
||||
// Set the coefficient for induction variables.
|
||||
for (unsigned i = 0, e = num_in; i != e; ++i) {
|
||||
setCoefficient(AffFunc.getCoeff(Statement->getSCEVForDimension(i)), v,
|
||||
false, AffFunc.isSigned());
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, i, v);
|
||||
}
|
||||
|
||||
// Set the coefficient of parameters
|
||||
for (unsigned i = 0, e = num_param; i != e; ++i) {
|
||||
setCoefficient(AffFunc.getCoeff(Params[i]), v, false, AffFunc.isSigned());
|
||||
isl_constraint_set_coefficient(c, isl_dim_param, i, v);
|
||||
}
|
||||
|
||||
// Set the constant.
|
||||
setCoefficient(AffFunc.getTransComp(), v, false, AffFunc.isSigned());
|
||||
isl_constraint_set_constant(c, v);
|
||||
isl_int_clear(v);
|
||||
|
||||
isl_basic_map *BasicMap = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
BasicMap = isl_basic_map_add_constraint(BasicMap, c);
|
||||
return isl_map_from_basic_map(BasicMap);
|
||||
}
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
MemoryAccess::~MemoryAccess() {
|
||||
isl_map_free(getAccessFunction());
|
||||
}
|
||||
|
||||
static void replace(std::string& str, const std::string& find,
|
||||
const std::string& replace) {
|
||||
size_t pos = 0;
|
||||
while((pos = str.find(find, pos)) != std::string::npos)
|
||||
{
|
||||
str.replace(pos, find.length(), replace);
|
||||
pos += replace.length();
|
||||
}
|
||||
}
|
||||
|
||||
static void makeIslCompatible(std::string& str) {
|
||||
replace(str, ".", "_");
|
||||
}
|
||||
|
||||
void MemoryAccess::setBaseName() {
|
||||
raw_string_ostream OS(BaseName);
|
||||
WriteAsOperand(OS, getBaseAddr(), false);
|
||||
BaseName = OS.str();
|
||||
|
||||
// Remove the % in the name. This is not supported by isl.
|
||||
BaseName.erase(0,1);
|
||||
makeIslCompatible(BaseName);
|
||||
BaseName = "MemRef_" + BaseName;
|
||||
}
|
||||
|
||||
std::string MemoryAccess::getAccessFunctionStr() const {
|
||||
return stringFromIslObj(getAccessFunction());
|
||||
}
|
||||
|
||||
isl_basic_map *MemoryAccess::createBasicAccessMap(ScopStmt *Statement) {
|
||||
isl_dim *dim = isl_dim_alloc(Statement->getIslContext(),
|
||||
Statement->getNumParams(),
|
||||
Statement->getNumIterators(), 1);
|
||||
setBaseName();
|
||||
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_out, getBaseName().c_str());
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_in, Statement->getBaseName());
|
||||
|
||||
return isl_basic_map_universe(dim);
|
||||
}
|
||||
|
||||
MemoryAccess::MemoryAccess(const SCEVAffFunc &AffFunc, ScopStmt *Statement) {
|
||||
BaseAddr = AffFunc.getBaseAddr();
|
||||
Type = AffFunc.isRead() ? Read : Write;
|
||||
statement = Statement;
|
||||
|
||||
setBaseName();
|
||||
|
||||
isl_dim *dim = isl_dim_set_alloc(Statement->getIslContext(),
|
||||
Statement->getNumParams(),
|
||||
Statement->getNumIterators());
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_set, Statement->getBaseName());
|
||||
|
||||
AccessRelation = getValueOf(AffFunc, Statement, dim);
|
||||
|
||||
// Devide the access function by the size of the elements in the function.
|
||||
isl_dim *dim2 = isl_dim_alloc(Statement->getIslContext(),
|
||||
Statement->getNumParams(), 1, 1);
|
||||
isl_basic_map *bmap = isl_basic_map_universe(isl_dim_copy(dim2));
|
||||
isl_constraint *c = isl_equality_alloc(dim2);
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, 0, v);
|
||||
isl_int_set_si(v, AffFunc.getElemSizeInBytes());
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, 0, v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
isl_map* dataSizeMap = isl_map_from_basic_map(bmap);
|
||||
|
||||
AccessRelation = isl_map_apply_range(AccessRelation, dataSizeMap);
|
||||
|
||||
AccessRelation = isl_map_set_tuple_name(AccessRelation, isl_dim_out,
|
||||
getBaseName().c_str());
|
||||
}
|
||||
|
||||
MemoryAccess::MemoryAccess(const Value *BaseAddress, ScopStmt *Statement) {
|
||||
BaseAddr = BaseAddress;
|
||||
Type = Read;
|
||||
statement = Statement;
|
||||
|
||||
isl_basic_map *BasicAccessMap = createBasicAccessMap(Statement);
|
||||
AccessRelation = isl_map_from_basic_map(BasicAccessMap);
|
||||
}
|
||||
|
||||
void MemoryAccess::print(raw_ostream &OS) const {
|
||||
OS.indent(12) << (isRead() ? "Read" : "Write") << "Access := \n";
|
||||
OS.indent(16) << getAccessFunctionStr() << ";\n";
|
||||
}
|
||||
|
||||
void MemoryAccess::dump() const {
|
||||
print(errs());
|
||||
}
|
||||
|
||||
// Create a map in the size of the provided set domain, that maps from the
|
||||
// one element of the provided set domain to another element of the provided
|
||||
// set domain.
|
||||
// The mapping is limited to all points that are equal in all but the last
|
||||
// dimension and for which the last dimension of the input is strict smaller
|
||||
// than the last dimension of the output.
|
||||
//
|
||||
// getEqualAndLarger(set[i0, i1, ..., iX]):
|
||||
//
|
||||
// set[i0, i1, ..., iX] -> set[o0, o1, ..., oX]
|
||||
// : i0 = o0, i1 = o1, ..., i(X-1) = o(X-1), iX < oX
|
||||
//
|
||||
static isl_map *getEqualAndLarger(isl_dim *setDomain) {
|
||||
isl_dim *mapDomain = isl_dim_map_from_set(setDomain);
|
||||
isl_basic_map *bmap = isl_basic_map_universe(mapDomain);
|
||||
|
||||
// Set all but the last dimension to be equal for the input and output
|
||||
//
|
||||
// input[i0, i1, ..., iX] -> output[o0, o1, ..., oX]
|
||||
// : i0 = o0, i1 = o1, ..., i(X-1) = o(X-1)
|
||||
for (unsigned i = 0; i < isl_basic_map_n_in(bmap) - 1; ++i) {
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_constraint *c = isl_equality_alloc(isl_basic_map_get_dim(bmap));
|
||||
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, i, v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, i, v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
|
||||
isl_int_clear(v);
|
||||
}
|
||||
|
||||
// Set the last dimension of the input to be strict smaller than the
|
||||
// last dimension of the output.
|
||||
//
|
||||
// input[?,?,?,...,iX] -> output[?,?,?,...,oX] : iX < oX
|
||||
//
|
||||
unsigned lastDimension = isl_basic_map_n_in(bmap) - 1;
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_constraint *c = isl_inequality_alloc(isl_basic_map_get_dim(bmap));
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, lastDimension, v);
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, lastDimension, v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_constant(c, v);
|
||||
isl_int_clear(v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
|
||||
return isl_map_from_basic_map(bmap);
|
||||
}
|
||||
|
||||
isl_set *MemoryAccess::getStride(const isl_set *domainSubset) const {
|
||||
isl_map *accessRelation = isl_map_copy(getAccessFunction());
|
||||
isl_set *scatteringDomain = isl_set_copy(const_cast<isl_set*>(domainSubset));
|
||||
isl_map *scattering = isl_map_copy(getStatement()->getScattering());
|
||||
|
||||
scattering = isl_map_reverse(scattering);
|
||||
int difference = isl_map_n_in(scattering) - isl_set_n_dim(scatteringDomain);
|
||||
scattering = isl_map_project_out(scattering, isl_dim_in,
|
||||
isl_set_n_dim(scatteringDomain),
|
||||
difference);
|
||||
|
||||
// Remove all names of the scattering dimensions, as the names may be lost
|
||||
// anyways during the project. This leads to consistent results.
|
||||
scattering = isl_map_set_tuple_name(scattering, isl_dim_in, "");
|
||||
scatteringDomain = isl_set_set_tuple_name(scatteringDomain, "");
|
||||
|
||||
isl_map *nextScatt = getEqualAndLarger(isl_set_get_dim(scatteringDomain));
|
||||
nextScatt = isl_map_lexmin(nextScatt);
|
||||
|
||||
scattering = isl_map_intersect_domain(scattering, scatteringDomain);
|
||||
|
||||
nextScatt = isl_map_apply_range(nextScatt, isl_map_copy(scattering));
|
||||
nextScatt = isl_map_apply_range(nextScatt, isl_map_copy(accessRelation));
|
||||
nextScatt = isl_map_apply_domain(nextScatt, scattering);
|
||||
nextScatt = isl_map_apply_domain(nextScatt, accessRelation);
|
||||
|
||||
return isl_map_deltas(nextScatt);
|
||||
}
|
||||
|
||||
bool MemoryAccess::isStrideZero(const isl_set *domainSubset) const {
|
||||
isl_set *stride = getStride(domainSubset);
|
||||
isl_constraint *c = isl_equality_alloc(isl_set_get_dim(stride));
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_set, 0, v);
|
||||
isl_int_set_si(v, 0);
|
||||
isl_constraint_set_constant(c, v);
|
||||
isl_int_clear(v);
|
||||
|
||||
isl_basic_set *bset = isl_basic_set_universe(isl_set_get_dim(stride));
|
||||
|
||||
bset = isl_basic_set_add_constraint(bset, c);
|
||||
isl_set *strideZero = isl_set_from_basic_set(bset);
|
||||
|
||||
return isl_set_is_equal(stride, strideZero);
|
||||
}
|
||||
|
||||
bool MemoryAccess::isStrideOne(const isl_set *domainSubset) const {
|
||||
isl_set *stride = getStride(domainSubset);
|
||||
isl_constraint *c = isl_equality_alloc(isl_set_get_dim(stride));
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_set, 0, v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_constant(c, v);
|
||||
isl_int_clear(v);
|
||||
|
||||
isl_basic_set *bset = isl_basic_set_universe(isl_set_get_dim(stride));
|
||||
|
||||
bset = isl_basic_set_add_constraint(bset, c);
|
||||
isl_set *strideZero = isl_set_from_basic_set(bset);
|
||||
|
||||
return isl_set_is_equal(stride, strideZero);
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
void ScopStmt::buildScattering(SmallVectorImpl<unsigned> &Scatter) {
|
||||
unsigned NumberOfIterators = getNumIterators();
|
||||
unsigned ScatDim = Parent.getMaxLoopDepth() * 2 + 1;
|
||||
isl_dim *dim = isl_dim_alloc(Parent.getCtx(), Parent.getNumParams(),
|
||||
NumberOfIterators, ScatDim);
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_out, "scattering");
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_in, getBaseName());
|
||||
isl_basic_map *bmap = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// Loop dimensions.
|
||||
for (unsigned i = 0; i < NumberOfIterators; ++i) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, 2 * i + 1, v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, i, v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
// Constant dimensions
|
||||
for (unsigned i = 0; i < NumberOfIterators + 1; ++i) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, 2 * i, v);
|
||||
isl_int_set_si(v, Scatter[i]);
|
||||
isl_constraint_set_constant(c, v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
// Fill scattering dimensions.
|
||||
for (unsigned i = 2 * NumberOfIterators + 1; i < ScatDim ; ++i) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, i, v);
|
||||
isl_int_set_si(v, 0);
|
||||
isl_constraint_set_constant(c, v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
isl_int_clear(v);
|
||||
isl_dim_free(dim);
|
||||
Scattering = isl_map_from_basic_map(bmap);
|
||||
}
|
||||
|
||||
void ScopStmt::buildAccesses(TempScop &tempScop, const Region &CurRegion) {
|
||||
const AccFuncSetType *AccFuncs = tempScop.getAccessFunctions(BB);
|
||||
|
||||
for (AccFuncSetType::const_iterator I = AccFuncs->begin(),
|
||||
E = AccFuncs->end(); I != E; ++I) {
|
||||
MemAccs.push_back(new MemoryAccess(I->first, this));
|
||||
InstructionToAccess[I->second] = MemAccs.back();
|
||||
}
|
||||
}
|
||||
|
||||
static isl_map *MapValueToLHS(isl_ctx *Context, unsigned ParameterNumber) {
|
||||
std::string MapString;
|
||||
isl_map *Map;
|
||||
|
||||
MapString = "{[i0] -> [i0, o1]}";
|
||||
Map = isl_map_read_from_str(Context, MapString.c_str(), -1);
|
||||
return isl_map_add_dims(Map, isl_dim_param, ParameterNumber);
|
||||
}
|
||||
|
||||
static isl_map *MapValueToRHS(isl_ctx *Context, unsigned ParameterNumber) {
|
||||
std::string MapString;
|
||||
isl_map *Map;
|
||||
|
||||
MapString = "{[i0] -> [o0, i0]}";
|
||||
Map = isl_map_read_from_str(Context, MapString.c_str(), -1);
|
||||
return isl_map_add_dims(Map, isl_dim_param, ParameterNumber);
|
||||
}
|
||||
|
||||
static isl_set *getComparison(isl_ctx *Context, const ICmpInst::Predicate Pred,
|
||||
unsigned ParameterNumber) {
|
||||
std::string SetString;
|
||||
|
||||
switch (Pred) {
|
||||
case ICmpInst::ICMP_EQ:
|
||||
SetString = "{[i0, i1] : i0 = i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_NE:
|
||||
SetString = "{[i0, i1] : i0 + 1 <= i1; [i0, i1] : i0 - 1 >= i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_SLT:
|
||||
SetString = "{[i0, i1] : i0 + 1 <= i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_ULT:
|
||||
SetString = "{[i0, i1] : i0 + 1 <= i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_SGT:
|
||||
SetString = "{[i0, i1] : i0 >= i1 + 1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_UGT:
|
||||
SetString = "{[i0, i1] : i0 >= i1 + 1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_SLE:
|
||||
SetString = "{[i0, i1] : i0 <= i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_ULE:
|
||||
SetString = "{[i0, i1] : i0 <= i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_SGE:
|
||||
SetString = "{[i0, i1] : i0 >= i1}";
|
||||
break;
|
||||
case ICmpInst::ICMP_UGE:
|
||||
SetString = "{[i0, i1] : i0 >= i1}";
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Non integer predicate not supported");
|
||||
}
|
||||
|
||||
isl_set *Set = isl_set_read_from_str(Context, SetString.c_str(), -1);
|
||||
return isl_set_add_dims(Set, isl_dim_param, ParameterNumber);
|
||||
}
|
||||
|
||||
static isl_set *compareValues(isl_map *LeftValue, isl_map *RightValue,
|
||||
const ICmpInst::Predicate Predicate) {
|
||||
isl_ctx *Context = isl_map_get_ctx(LeftValue);
|
||||
unsigned NumberOfParameters = isl_map_n_param(LeftValue);
|
||||
|
||||
isl_map *MapToLHS = MapValueToLHS(Context, NumberOfParameters);
|
||||
isl_map *MapToRHS = MapValueToRHS(Context, NumberOfParameters);
|
||||
|
||||
isl_map *LeftValueAtLHS = isl_map_apply_range(LeftValue, MapToLHS);
|
||||
isl_map *RightValueAtRHS = isl_map_apply_range(RightValue, MapToRHS);
|
||||
|
||||
isl_map *BothValues = isl_map_intersect(LeftValueAtLHS, RightValueAtRHS);
|
||||
isl_set *Comparison = getComparison(Context, Predicate, NumberOfParameters);
|
||||
|
||||
isl_map *ComparedValues = isl_map_intersect_range(BothValues, Comparison);
|
||||
return isl_map_domain(ComparedValues);
|
||||
}
|
||||
|
||||
isl_set *ScopStmt::toConditionSet(const Comparison &Comp, isl_dim *dim) const {
|
||||
isl_map *LHSValue = getValueOf(*Comp.getLHS(), this, dim);
|
||||
isl_map *RHSValue = getValueOf(*Comp.getRHS(), this, dim);
|
||||
|
||||
return compareValues(LHSValue, RHSValue, Comp.getPred());
|
||||
}
|
||||
|
||||
isl_set *ScopStmt::toUpperLoopBound(const SCEVAffFunc &UpperBound, isl_dim *dim,
|
||||
unsigned BoundedDimension) const {
|
||||
// Set output dimension to bounded dimension.
|
||||
isl_dim *RHSDim = isl_dim_alloc(Parent.getCtx(), getNumParams(),
|
||||
getNumIterators(), 1);
|
||||
RHSDim = isl_dim_set_tuple_name(RHSDim, isl_dim_in, getBaseName());
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(RHSDim));
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, BoundedDimension, v);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, 0, v);
|
||||
isl_int_clear(v);
|
||||
isl_basic_map *bmap = isl_basic_map_universe(RHSDim);
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
|
||||
isl_map *LHSValue = isl_map_from_basic_map(bmap);
|
||||
|
||||
isl_map *RHSValue = getValueOf(UpperBound, this, dim);
|
||||
|
||||
return compareValues(LHSValue, RHSValue, ICmpInst::ICMP_SLE);
|
||||
}
|
||||
|
||||
void ScopStmt::buildIterationDomainFromLoops(TempScop &tempScop) {
|
||||
isl_dim *dim = isl_dim_set_alloc(Parent.getCtx(), getNumParams(),
|
||||
getNumIterators());
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_set, getBaseName());
|
||||
|
||||
Domain = isl_set_universe(isl_dim_copy(dim));
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
for (int i = 0, e = getNumIterators(); i != e; ++i) {
|
||||
// Lower bound: IV >= 0.
|
||||
isl_basic_set *bset = isl_basic_set_universe(isl_dim_copy(dim));
|
||||
isl_constraint *c = isl_inequality_alloc(isl_dim_copy(dim));
|
||||
isl_int_set_si(v, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_set, i, v);
|
||||
bset = isl_basic_set_add_constraint(bset, c);
|
||||
Domain = isl_set_intersect(Domain, isl_set_from_basic_set(bset));
|
||||
|
||||
// Upper bound: IV <= NumberOfIterations.
|
||||
const Loop *L = getSCEVForDimension(i)->getLoop();
|
||||
const SCEVAffFunc &UpperBound = tempScop.getLoopBound(L);
|
||||
isl_set *UpperBoundSet = toUpperLoopBound(UpperBound, isl_dim_copy(dim), i);
|
||||
Domain = isl_set_intersect(Domain, UpperBoundSet);
|
||||
}
|
||||
|
||||
isl_int_clear(v);
|
||||
}
|
||||
|
||||
void ScopStmt::addConditionsToDomain(TempScop &tempScop,
|
||||
const Region &CurRegion) {
|
||||
isl_dim *dim = isl_set_get_dim(Domain);
|
||||
const Region *TopR = tempScop.getMaxRegion().getParent(),
|
||||
*CurR = &CurRegion;
|
||||
const BasicBlock *CurEntry = BB;
|
||||
|
||||
// Build BB condition constrains, by traveling up the region tree.
|
||||
do {
|
||||
assert(CurR && "We exceed the top region?");
|
||||
// Skip when multiple regions share the same entry.
|
||||
if (CurEntry != CurR->getEntry()) {
|
||||
if (const BBCond *Cnd = tempScop.getBBCond(CurEntry))
|
||||
for (BBCond::const_iterator I = Cnd->begin(), E = Cnd->end();
|
||||
I != E; ++I) {
|
||||
isl_set *c = toConditionSet(*I, dim);
|
||||
Domain = isl_set_intersect(Domain, c);
|
||||
}
|
||||
}
|
||||
CurEntry = CurR->getEntry();
|
||||
CurR = CurR->getParent();
|
||||
} while (TopR != CurR);
|
||||
|
||||
isl_dim_free(dim);
|
||||
}
|
||||
|
||||
void ScopStmt::buildIterationDomain(TempScop &tempScop, const Region &CurRegion)
|
||||
{
|
||||
buildIterationDomainFromLoops(tempScop);
|
||||
addConditionsToDomain(tempScop, CurRegion);
|
||||
}
|
||||
|
||||
ScopStmt::ScopStmt(Scop &parent, TempScop &tempScop,
|
||||
const Region &CurRegion, BasicBlock &bb,
|
||||
SmallVectorImpl<Loop*> &NestLoops,
|
||||
SmallVectorImpl<unsigned> &Scatter)
|
||||
: Parent(parent), BB(&bb), IVS(NestLoops.size()) {
|
||||
// Setup the induction variables.
|
||||
for (unsigned i = 0, e = NestLoops.size(); i < e; ++i) {
|
||||
PHINode *PN = NestLoops[i]->getCanonicalInductionVariable();
|
||||
assert(PN && "Non canonical IV in Scop!");
|
||||
IVS[i] = PN;
|
||||
}
|
||||
|
||||
raw_string_ostream OS(BaseName);
|
||||
WriteAsOperand(OS, &bb, false);
|
||||
BaseName = OS.str();
|
||||
|
||||
// Remove the % in the name. This is not supported by isl.
|
||||
BaseName.erase(0, 1);
|
||||
makeIslCompatible(BaseName);
|
||||
BaseName = "Stmt_" + BaseName;
|
||||
|
||||
buildIterationDomain(tempScop, CurRegion);
|
||||
buildScattering(Scatter);
|
||||
buildAccesses(tempScop, CurRegion);
|
||||
|
||||
IsReduction = tempScop.is_Reduction(*BB);
|
||||
}
|
||||
|
||||
ScopStmt::ScopStmt(Scop &parent, SmallVectorImpl<unsigned> &Scatter)
|
||||
: Parent(parent), BB(NULL), IVS(0) {
|
||||
|
||||
BaseName = "FinalRead";
|
||||
|
||||
// Build iteration domain.
|
||||
std::string IterationDomainString = "{[i0] : i0 = 0}";
|
||||
Domain = isl_set_read_from_str(Parent.getCtx(), IterationDomainString.c_str(),
|
||||
-1);
|
||||
Domain = isl_set_add_dims(Domain, isl_dim_param, Parent.getNumParams());
|
||||
Domain = isl_set_set_tuple_name(Domain, getBaseName());
|
||||
|
||||
// Build scattering.
|
||||
unsigned ScatDim = Parent.getMaxLoopDepth() * 2 + 1;
|
||||
isl_dim *dim = isl_dim_alloc(Parent.getCtx(), Parent.getNumParams(), 1,
|
||||
ScatDim);
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_out, "scattering");
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_in, getBaseName());
|
||||
isl_basic_map *bmap = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
isl_constraint *c = isl_equality_alloc(dim);
|
||||
isl_int_set_si(v, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, 0, v);
|
||||
|
||||
// TODO: This is incorrect. We should not use a very large number to ensure
|
||||
// that this statement is executed last.
|
||||
isl_int_set_si(v, 200000000);
|
||||
isl_constraint_set_constant(c, v);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
isl_int_clear(v);
|
||||
Scattering = isl_map_from_basic_map(bmap);
|
||||
|
||||
// Build memory accesses, use SetVector to keep the order of memory accesses
|
||||
// and prevent the same memory access inserted more than once.
|
||||
SetVector<const Value*> BaseAddressSet;
|
||||
|
||||
for (Scop::const_iterator SI = Parent.begin(), SE = Parent.end(); SI != SE;
|
||||
++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
for (MemoryAccessVec::const_iterator I = Stmt->memacc_begin(),
|
||||
E = Stmt->memacc_end(); I != E; ++I)
|
||||
BaseAddressSet.insert((*I)->getBaseAddr());
|
||||
}
|
||||
|
||||
for (SetVector<const Value*>::iterator BI = BaseAddressSet.begin(),
|
||||
BE = BaseAddressSet.end(); BI != BE; ++BI)
|
||||
MemAccs.push_back(new MemoryAccess(*BI, this));
|
||||
|
||||
IsReduction = false;
|
||||
}
|
||||
|
||||
std::string ScopStmt::getDomainStr() const {
|
||||
return stringFromIslObj(getDomain());
|
||||
}
|
||||
|
||||
std::string ScopStmt::getScatteringStr() const {
|
||||
return stringFromIslObj(getScattering());
|
||||
}
|
||||
|
||||
unsigned ScopStmt::getNumParams() const {
|
||||
return Parent.getNumParams();
|
||||
}
|
||||
|
||||
unsigned ScopStmt::getNumIterators() const {
|
||||
// The final read has one dimension with one element.
|
||||
if (!BB)
|
||||
return 1;
|
||||
|
||||
return IVS.size();
|
||||
}
|
||||
|
||||
unsigned ScopStmt::getNumScattering() const {
|
||||
return isl_map_dim(Scattering, isl_dim_out);
|
||||
}
|
||||
|
||||
const char *ScopStmt::getBaseName() const { return BaseName.c_str(); }
|
||||
|
||||
const PHINode *ScopStmt::getInductionVariableForDimension(unsigned Dimension)
|
||||
const {
|
||||
return IVS[Dimension];
|
||||
}
|
||||
|
||||
const SCEVAddRecExpr *ScopStmt::getSCEVForDimension(unsigned Dimension)
|
||||
const {
|
||||
PHINode *PN = IVS[Dimension];
|
||||
return cast<SCEVAddRecExpr>(getParent()->getSE()->getSCEV(PN));
|
||||
}
|
||||
|
||||
isl_ctx *ScopStmt::getIslContext() {
|
||||
return Parent.getCtx();
|
||||
}
|
||||
|
||||
ScopStmt::~ScopStmt() {
|
||||
while (!MemAccs.empty()) {
|
||||
delete MemAccs.back();
|
||||
MemAccs.pop_back();
|
||||
}
|
||||
|
||||
isl_set_free(Domain);
|
||||
isl_map_free(Scattering);
|
||||
}
|
||||
|
||||
void ScopStmt::print(raw_ostream &OS) const {
|
||||
OS << "\t" << getBaseName() << "\n";
|
||||
|
||||
OS.indent(12) << "Domain :=\n";
|
||||
|
||||
if (Domain) {
|
||||
OS.indent(16) << getDomainStr() << ";\n";
|
||||
} else
|
||||
OS.indent(16) << "n/a\n";
|
||||
|
||||
OS.indent(12) << "Scattering :=\n";
|
||||
|
||||
if (Domain) {
|
||||
OS.indent(16) << getScatteringStr() << ";\n";
|
||||
} else
|
||||
OS.indent(16) << "n/a\n";
|
||||
|
||||
for (MemoryAccessVec::const_iterator I = MemAccs.begin(), E = MemAccs.end();
|
||||
I != E; ++I)
|
||||
(*I)->print(OS);
|
||||
}
|
||||
|
||||
void ScopStmt::dump() const { print(dbgs()); }
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// Scop class implement
|
||||
Scop::Scop(TempScop &tempScop, LoopInfo &LI, ScalarEvolution &ScalarEvolution)
|
||||
: SE(&ScalarEvolution), R(tempScop.getMaxRegion()),
|
||||
MaxLoopDepth(tempScop.getMaxLoopDepth()) {
|
||||
isl_ctx *ctx = isl_ctx_alloc();
|
||||
|
||||
ParamSetType &Params = tempScop.getParamSet();
|
||||
Parameters.insert(Parameters.begin(), Params.begin(), Params.end());
|
||||
|
||||
isl_dim *dim = isl_dim_set_alloc(ctx, getNumParams(), 0);
|
||||
|
||||
// TODO: Insert relations between parameters.
|
||||
// TODO: Insert constraints on parameters.
|
||||
Context = isl_set_universe (dim);
|
||||
|
||||
SmallVector<Loop*, 8> NestLoops;
|
||||
SmallVector<unsigned, 8> Scatter;
|
||||
|
||||
Scatter.assign(MaxLoopDepth + 1, 0);
|
||||
|
||||
// Build the iteration domain, access functions and scattering functions
|
||||
// traversing the region tree.
|
||||
buildScop(tempScop, getRegion(), NestLoops, Scatter, LI);
|
||||
Stmts.push_back(new ScopStmt(*this, Scatter));
|
||||
|
||||
assert(NestLoops.empty() && "NestLoops not empty at top level!");
|
||||
}
|
||||
|
||||
Scop::~Scop() {
|
||||
isl_set_free(Context);
|
||||
|
||||
// Free the statements;
|
||||
for (iterator I = begin(), E = end(); I != E; ++I)
|
||||
delete *I;
|
||||
|
||||
// Do we need a singleton to manage this?
|
||||
//isl_ctx_free(ctx);
|
||||
}
|
||||
|
||||
std::string Scop::getContextStr() const {
|
||||
return stringFromIslObj(getContext());
|
||||
}
|
||||
|
||||
std::string Scop::getNameStr() const {
|
||||
std::string ExitName, EntryName;
|
||||
raw_string_ostream ExitStr(ExitName);
|
||||
raw_string_ostream EntryStr(EntryName);
|
||||
|
||||
WriteAsOperand(EntryStr, R.getEntry(), false);
|
||||
EntryStr.str();
|
||||
|
||||
if (R.getExit()) {
|
||||
WriteAsOperand(ExitStr, R.getExit(), false);
|
||||
ExitStr.str();
|
||||
} else
|
||||
ExitName = "FunctionExit";
|
||||
|
||||
return EntryName + "---" + ExitName;
|
||||
}
|
||||
|
||||
void Scop::printContext(raw_ostream &OS) const {
|
||||
OS << "Context:\n";
|
||||
|
||||
if (!Context) {
|
||||
OS.indent(4) << "n/a\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
OS.indent(4) << getContextStr() << "\n";
|
||||
}
|
||||
|
||||
void Scop::printStatements(raw_ostream &OS) const {
|
||||
OS << "Statements {\n";
|
||||
|
||||
for (const_iterator SI = begin(), SE = end();SI != SE; ++SI)
|
||||
OS.indent(4) << (**SI);
|
||||
|
||||
OS.indent(4) << "}\n";
|
||||
}
|
||||
|
||||
|
||||
void Scop::print(raw_ostream &OS) const {
|
||||
printContext(OS.indent(4));
|
||||
printStatements(OS.indent(4));
|
||||
}
|
||||
|
||||
void Scop::dump() const { print(dbgs()); }
|
||||
|
||||
isl_ctx *Scop::getCtx() const { return isl_set_get_ctx(Context); }
|
||||
|
||||
ScalarEvolution *Scop::getSE() const { return SE; }
|
||||
|
||||
bool Scop::isTrivialBB(BasicBlock *BB, TempScop &tempScop) {
|
||||
if (tempScop.getAccessFunctions(BB))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scop::buildScop(TempScop &tempScop,
|
||||
const Region &CurRegion,
|
||||
SmallVectorImpl<Loop*> &NestLoops,
|
||||
SmallVectorImpl<unsigned> &Scatter,
|
||||
LoopInfo &LI) {
|
||||
Loop *L = castToLoop(CurRegion, LI);
|
||||
|
||||
if (L)
|
||||
NestLoops.push_back(L);
|
||||
|
||||
unsigned loopDepth = NestLoops.size();
|
||||
assert(Scatter.size() > loopDepth && "Scatter not big enough!");
|
||||
|
||||
for (Region::const_element_iterator I = CurRegion.element_begin(),
|
||||
E = CurRegion.element_end(); I != E; ++I)
|
||||
if (I->isSubRegion())
|
||||
buildScop(tempScop, *(I->getNodeAs<Region>()), NestLoops, Scatter, LI);
|
||||
else {
|
||||
BasicBlock *BB = I->getNodeAs<BasicBlock>();
|
||||
|
||||
if (isTrivialBB(BB, tempScop))
|
||||
continue;
|
||||
|
||||
Stmts.push_back(new ScopStmt(*this, tempScop, CurRegion, *BB, NestLoops,
|
||||
Scatter));
|
||||
|
||||
// Increasing the Scattering function is OK for the moment, because
|
||||
// we are using a depth first iterator and the program is well structured.
|
||||
++Scatter[loopDepth];
|
||||
}
|
||||
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
// Exiting a loop region.
|
||||
Scatter[loopDepth] = 0;
|
||||
NestLoops.pop_back();
|
||||
++Scatter[loopDepth-1];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void ScopInfo::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<LoopInfo>();
|
||||
AU.addRequired<RegionInfo>();
|
||||
AU.addRequired<ScalarEvolution>();
|
||||
AU.addRequired<TempScopInfo>();
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
|
||||
bool ScopInfo::runOnRegion(Region *R, RGPassManager &RGM) {
|
||||
LoopInfo &LI = getAnalysis<LoopInfo>();
|
||||
ScalarEvolution &SE = getAnalysis<ScalarEvolution>();
|
||||
|
||||
TempScop *tempScop = getAnalysis<TempScopInfo>().getTempScop(R);
|
||||
|
||||
// This region is no Scop.
|
||||
if (!tempScop) {
|
||||
scop = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Statistics.
|
||||
++ScopFound;
|
||||
if (tempScop->getMaxLoopDepth() > 0) ++RichScopFound;
|
||||
|
||||
scop = new Scop(*tempScop, LI, SE);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char ScopInfo::ID = 0;
|
||||
|
||||
|
||||
static RegisterPass<ScopInfo>
|
||||
X("polly-scops", "Polly - Create polyhedral description of Scops");
|
||||
|
||||
Pass *polly::createScopInfoPass() {
|
||||
return new ScopInfo();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//===- ScopPass.cpp - The base class of Passes that operate on Polly IR ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the definitions of the ScopPass members.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/ScopPass.h"
|
||||
#include "polly/ScopInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
bool ScopPass::runOnRegion(Region *R, RGPassManager &RGM) {
|
||||
S = 0;
|
||||
|
||||
if ((S = getAnalysis<ScopInfo>().getScop()))
|
||||
return runOnScop(*S);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isl_ctx *ScopPass::getIslContext() {
|
||||
assert(S && "Not in on a Scop!");
|
||||
return S->getCtx();
|
||||
}
|
||||
|
||||
void ScopPass::print(raw_ostream &OS, const Module *M) const {
|
||||
if (S)
|
||||
printScop(OS);
|
||||
}
|
||||
|
||||
void ScopPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<ScopInfo>();
|
||||
AU.setPreservesAll();
|
||||
}
|
|
@ -0,0 +1,523 @@
|
|||
//===---------- TempScopInfo.cpp - Extract TempScops ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Collect information about the control flow regions detected by the Scop
|
||||
// detection, such that this information can be translated info its polyhedral
|
||||
// representation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/TempScopInfo.h"
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/Support/AffineSCEVIterator.h"
|
||||
#include "polly/Support/GICHelper.h"
|
||||
#include "polly/Support/ScopHelper.h"
|
||||
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/RegionIterator.h"
|
||||
#include "llvm/Target/TargetData.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-analyze-ir"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// Helper Class
|
||||
|
||||
SCEVAffFunc::SCEVAffFunc(const SCEV *S, SCEVAffFuncType Type, Region &R,
|
||||
ParamSetType &Params, LoopInfo *LI,
|
||||
ScalarEvolution *SE)
|
||||
: ElemBytes(0), FuncType(Type), has_sign(true) {
|
||||
assert(S && "S can not be null!");
|
||||
assert(!isa<SCEVCouldNotCompute>(S) && "Non affine function in Scop");
|
||||
|
||||
for (AffineSCEVIterator I = affine_begin(S, SE), E = affine_end();
|
||||
I != E; ++I) {
|
||||
// The constant part must be a SCEVConstant.
|
||||
// TODO: support sizeof in coefficient.
|
||||
assert(isa<SCEVConstant>(I->second)
|
||||
&& "Expected SCEVConst in coefficient!");
|
||||
|
||||
const SCEV *Var = I->first;
|
||||
|
||||
if (isa<SCEVConstant>(Var)) // Extract the constant part.
|
||||
// Add the translation component.
|
||||
TransComp = I->second;
|
||||
else if (Var->getType()->isPointerTy()) { // Extract the base address.
|
||||
const SCEVUnknown *Addr = dyn_cast<SCEVUnknown>(Var);
|
||||
assert(Addr && "Broken SCEV detected!");
|
||||
BaseAddr = Addr->getValue();
|
||||
} else { // Extract other affine components.
|
||||
LnrTrans.insert(*I);
|
||||
|
||||
if (isIndVar(Var, R, *LI, *SE))
|
||||
continue;
|
||||
|
||||
assert(isParameter(Var, R, *LI, *SE)
|
||||
&& "Found non affine function in Scop!");
|
||||
Params.insert(Var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SCEVAffFunc::print(raw_ostream &OS, bool PrintInequality) const {
|
||||
// Print BaseAddr.
|
||||
if (isDataRef()) {
|
||||
OS << (isRead() ? "Reads" : "Writes") << " ";
|
||||
WriteAsOperand(OS, getBaseAddr(), false);
|
||||
OS << "[";
|
||||
}
|
||||
|
||||
for (LnrTransSet::const_iterator I = LnrTrans.begin(), E = LnrTrans.end();
|
||||
I != E; ++I)
|
||||
OS << *I->second << " * " << *I->first << " + ";
|
||||
|
||||
if (TransComp)
|
||||
OS << *TransComp;
|
||||
|
||||
if (isDataRef())
|
||||
OS << "]";
|
||||
|
||||
if (!PrintInequality)
|
||||
return;
|
||||
|
||||
if (getType() == GE)
|
||||
OS << " >= 0";
|
||||
else if (getType() == Eq)
|
||||
OS << " == 0";
|
||||
else if (getType() == Ne)
|
||||
OS << " != 0";
|
||||
}
|
||||
|
||||
void SCEVAffFunc::dump() const {
|
||||
print(errs());
|
||||
}
|
||||
|
||||
inline raw_ostream &operator<<(raw_ostream &OS, const SCEVAffFunc &AffFunc) {
|
||||
AffFunc.print(OS);
|
||||
return OS;
|
||||
}
|
||||
|
||||
void Comparison::print(raw_ostream &OS) const {
|
||||
// Not yet implemented.
|
||||
}
|
||||
|
||||
/// Helper function to print the condition
|
||||
static void printBBCond(raw_ostream &OS, const BBCond &Cond) {
|
||||
assert(!Cond.empty() && "Unexpected empty condition!");
|
||||
Cond[0].print(OS);
|
||||
for (unsigned i = 1, e = Cond.size(); i != e; ++i) {
|
||||
OS << " && ";
|
||||
Cond[i].print(OS);
|
||||
}
|
||||
}
|
||||
|
||||
inline raw_ostream &operator<<(raw_ostream &OS, const BBCond &Cond) {
|
||||
printBBCond(OS, Cond);
|
||||
return OS;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TempScop implementation
|
||||
TempScop::~TempScop() {
|
||||
if (MayASInfo) delete MayASInfo;
|
||||
}
|
||||
|
||||
void TempScop::print(raw_ostream &OS, ScalarEvolution *SE, LoopInfo *LI) const {
|
||||
OS << "Scop: " << R.getNameStr() << "\tParameters: (";
|
||||
// Print Parameters.
|
||||
for (ParamSetType::const_iterator PI = Params.begin(), PE = Params.end();
|
||||
PI != PE; ++PI)
|
||||
OS << **PI << ", ";
|
||||
|
||||
OS << "), Max Loop Depth: "<< MaxLoopDepth <<"\n";
|
||||
|
||||
printDetail(OS, SE, LI, &R, 0);
|
||||
}
|
||||
|
||||
void TempScop::printDetail(llvm::raw_ostream &OS, ScalarEvolution *SE,
|
||||
LoopInfo *LI, const Region *CurR,
|
||||
unsigned ind) const {
|
||||
// Print the loop bounds, if the current region is a loop.
|
||||
LoopBoundMapType::const_iterator at = LoopBounds.find(castToLoop(*CurR, *LI));
|
||||
if (at != LoopBounds.end()) {
|
||||
OS.indent(ind) << "Bounds of Loop: " << at->first->getHeader()->getName()
|
||||
<< ":\t{ ";
|
||||
at->second.print(OS, false);
|
||||
OS << " }\n";
|
||||
ind += 2;
|
||||
}
|
||||
|
||||
// Iterate over the region nodes of this Scop to print the access functions
|
||||
// and loop bounds.
|
||||
for (Region::const_element_iterator I = CurR->element_begin(),
|
||||
E = CurR->element_end(); I != E; ++I) {
|
||||
if (I->isSubRegion()) {
|
||||
Region *subR = I->getNodeAs<Region>();
|
||||
printDetail(OS, SE, LI, subR, ind + 2);
|
||||
} else {
|
||||
BasicBlock *BB = I->getNodeAs<BasicBlock>();
|
||||
|
||||
if (const AccFuncSetType *AccFunc = getAccessFunctions(BB)) {
|
||||
OS.indent(ind) << "BB: " << BB->getName() << "{\n";
|
||||
|
||||
for (AccFuncSetType::const_iterator FI = AccFunc->begin(),
|
||||
FE = AccFunc->end(); FI != FE; ++FI) {
|
||||
const SCEVAffFunc &AF = FI->first;
|
||||
const Value *Ptr = AF.getBaseAddr();
|
||||
|
||||
OS.indent(ind + 2) << AF << " Refs: ";
|
||||
for (MayAliasSetInfo::const_alias_iterator
|
||||
MI = MayASInfo->alias_begin(Ptr), ME = MayASInfo->alias_end(Ptr);
|
||||
MI != ME; ++MI) {
|
||||
MI->second->print(OS);
|
||||
OS << ", ";
|
||||
}
|
||||
|
||||
OS << '\n';
|
||||
}
|
||||
|
||||
if (Reductions.count(BB))
|
||||
OS.indent(ind + 2) << "Reduction\n";
|
||||
|
||||
OS.indent(ind) << "}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TempScopInfo::buildAffineFunction(const SCEV *S, SCEVAffFunc &FuncToBuild,
|
||||
Region &R, ParamSetType &Params) const {
|
||||
assert(S && "S can not be null!");
|
||||
|
||||
assert(!isa<SCEVCouldNotCompute>(S)
|
||||
&& "Un Expect broken affine function in Scop!");
|
||||
|
||||
for (AffineSCEVIterator I = affine_begin(S, SE), E = affine_end();
|
||||
I != E; ++I) {
|
||||
// The constant part must be a SCEVConstant.
|
||||
// TODO: support sizeof in coefficient.
|
||||
assert(isa<SCEVConstant>(I->second) && "Expect SCEVConst in coefficient!");
|
||||
|
||||
const SCEV *Var = I->first;
|
||||
// Extract the constant part
|
||||
if (isa<SCEVConstant>(Var))
|
||||
// Add the translation component
|
||||
FuncToBuild.TransComp = I->second;
|
||||
else if (Var->getType()->isPointerTy()) { // Extract the base address
|
||||
const SCEVUnknown *BaseAddr = dyn_cast<SCEVUnknown>(Var);
|
||||
assert(BaseAddr && "Why we got a broken scev?");
|
||||
FuncToBuild.BaseAddr = BaseAddr->getValue();
|
||||
} else { // Extract other affine components.
|
||||
FuncToBuild.LnrTrans.insert(*I);
|
||||
// Do not add the indvar to the parameter list.
|
||||
if (!isIndVar(Var, R, *LI, *SE)) {
|
||||
DEBUG(dbgs() << "Non indvar: "<< *Var << '\n');
|
||||
assert(isParameter(Var, R, *LI, *SE)
|
||||
&& "Find non affine function in scop!");
|
||||
Params.insert(Var);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TempScopInfo::isReduction(BasicBlock &BB) {
|
||||
int loadAccess = 0, storeAccess = 0;
|
||||
const StoreInst *storeInst;
|
||||
const Value *storePointer;
|
||||
const LoadInst *loadInst[2];
|
||||
const Value *loadPointer[2];
|
||||
|
||||
for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I) {
|
||||
Instruction &Inst = *I;
|
||||
if (isa<LoadInst>(&Inst)) {
|
||||
if (loadAccess >= 2)
|
||||
return false;
|
||||
loadInst[loadAccess] = dyn_cast<LoadInst>(&Inst);
|
||||
loadPointer[loadAccess] = loadInst[loadAccess]->getPointerOperand();
|
||||
loadAccess++;
|
||||
} else if (isa<StoreInst>(&Inst)) {
|
||||
if (storeAccess >= 1)
|
||||
return false;
|
||||
storeInst = dyn_cast<StoreInst>(&Inst);
|
||||
storePointer = storeInst->getPointerOperand();
|
||||
storeAccess++;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadAccess < 2)
|
||||
return false;
|
||||
|
||||
if (loadPointer[0] == loadPointer[1])
|
||||
return false;
|
||||
|
||||
const Value *reductionLoadInst;
|
||||
if (storePointer == loadPointer[0])
|
||||
reductionLoadInst = loadInst[0];
|
||||
else if (storePointer == loadPointer[1])
|
||||
reductionLoadInst = loadInst[1];
|
||||
else
|
||||
return false;
|
||||
|
||||
const Instruction *reductionInst =
|
||||
dyn_cast<Instruction>(storeInst->getValueOperand());
|
||||
|
||||
// Check if the value stored is an instruction
|
||||
if (!reductionInst)
|
||||
return false;
|
||||
|
||||
// Reduction operations must be associative and commutative
|
||||
if (!reductionInst->isAssociative() || !reductionInst->isCommutative())
|
||||
return false;
|
||||
|
||||
// Check if this instruction is using the loaded value
|
||||
for (User::const_op_iterator I = reductionInst->op_begin(),
|
||||
E = reductionInst->op_end(); I != E; I++) {
|
||||
const Value *operand = I->get();
|
||||
if (operand == reductionLoadInst) {
|
||||
// The loaded value's one and only use must be this one.
|
||||
return operand->hasOneUse();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TempScopInfo::buildAccessFunctions(Region &R, ParamSetType &Params,
|
||||
BasicBlock &BB) {
|
||||
AccFuncSetType Functions;
|
||||
for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I) {
|
||||
Instruction &Inst = *I;
|
||||
if (isa<LoadInst>(&Inst) || isa<StoreInst>(&Inst)) {
|
||||
// Create the SCEVAffFunc.
|
||||
if (LoadInst *ld = dyn_cast<LoadInst>(&Inst)) {
|
||||
unsigned size = TD->getTypeStoreSize(ld->getType());
|
||||
Functions.push_back(
|
||||
std::make_pair(SCEVAffFunc(SCEVAffFunc::ReadMem, size), &Inst));
|
||||
} else {//Else it must be a StoreInst.
|
||||
StoreInst *st = cast<StoreInst>(&Inst);
|
||||
unsigned size = TD->getTypeStoreSize(st->getValueOperand()->getType());
|
||||
Functions.push_back(
|
||||
std::make_pair(SCEVAffFunc(SCEVAffFunc::WriteMem, size), &Inst));
|
||||
}
|
||||
|
||||
Value *Ptr = getPointerOperand(Inst);
|
||||
buildAffineFunction(SE->getSCEV(Ptr), Functions.back().first, R, Params);
|
||||
}
|
||||
}
|
||||
|
||||
if (Functions.empty())
|
||||
return;
|
||||
|
||||
AccFuncSetType &Accs = AccFuncMap[&BB];
|
||||
Accs.insert(Accs.end(), Functions.begin(), Functions.end());
|
||||
}
|
||||
|
||||
void TempScopInfo::buildLoopBounds(TempScop &Scop) {
|
||||
Region &R = Scop.getMaxRegion();
|
||||
unsigned MaxLoopDepth = 0;
|
||||
|
||||
for (Region::block_iterator I = R.block_begin(), E = R.block_end();
|
||||
I != E; ++I) {
|
||||
Loop *L = LI->getLoopFor(I->getNodeAs<BasicBlock>());
|
||||
|
||||
if (!L || !R.contains(L))
|
||||
continue;
|
||||
|
||||
if (LoopBounds.find(L) != LoopBounds.end())
|
||||
continue;
|
||||
|
||||
LoopBounds[L] = SCEVAffFunc(SCEVAffFunc::Eq);
|
||||
const SCEV *LoopCount = SE->getBackedgeTakenCount(L);
|
||||
buildAffineFunction(LoopCount, LoopBounds[L], Scop.getMaxRegion(),
|
||||
Scop.getParamSet());
|
||||
|
||||
Loop *OL = R.outermostLoopInRegion(L);
|
||||
unsigned LoopDepth = L->getLoopDepth() - OL->getLoopDepth() + 1;
|
||||
|
||||
if (LoopDepth > MaxLoopDepth)
|
||||
MaxLoopDepth = LoopDepth;
|
||||
}
|
||||
|
||||
Scop.MaxLoopDepth = MaxLoopDepth;
|
||||
}
|
||||
|
||||
void TempScopInfo::buildAffineCondition(Value &V, bool inverted,
|
||||
Comparison **Comp,
|
||||
TempScop &Scop) const {
|
||||
Region &R = Scop.getMaxRegion();
|
||||
ParamSetType &Params = Scop.getParamSet();
|
||||
if (ConstantInt *C = dyn_cast<ConstantInt>(&V)) {
|
||||
// If this is always true condition, we will create 1 >= 0,
|
||||
// otherwise we will create 1 == 0.
|
||||
SCEVAffFunc *AffLHS = new SCEVAffFunc(SE->getConstant(C->getType(), 0),
|
||||
SCEVAffFunc::Eq, R, Params, LI, SE);
|
||||
SCEVAffFunc *AffRHS = new SCEVAffFunc(SE->getConstant(C->getType(), 1),
|
||||
SCEVAffFunc::Eq, R, Params, LI, SE);
|
||||
if (C->isOne() == inverted)
|
||||
*Comp = new Comparison(AffRHS, AffLHS, ICmpInst::ICMP_NE);
|
||||
else
|
||||
*Comp = new Comparison(AffLHS, AffLHS, ICmpInst::ICMP_EQ);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ICmpInst *ICmp = dyn_cast<ICmpInst>(&V);
|
||||
assert(ICmp && "Only ICmpInst of constant as condition supported!");
|
||||
|
||||
const SCEV *LHS = SE->getSCEV(ICmp->getOperand(0)),
|
||||
*RHS = SE->getSCEV(ICmp->getOperand(1));
|
||||
|
||||
ICmpInst::Predicate Pred = ICmp->getPredicate();
|
||||
|
||||
// Invert the predicate if needed.
|
||||
if (inverted)
|
||||
Pred = ICmpInst::getInversePredicate(Pred);
|
||||
|
||||
SCEVAffFunc *AffLHS = new SCEVAffFunc(LHS, SCEVAffFunc::Eq, R, Params, LI,
|
||||
SE);
|
||||
SCEVAffFunc *AffRHS = new SCEVAffFunc(RHS, SCEVAffFunc::Eq, R, Params, LI,
|
||||
SE);
|
||||
|
||||
switch (Pred) {
|
||||
case ICmpInst::ICMP_UGT:
|
||||
case ICmpInst::ICMP_UGE:
|
||||
case ICmpInst::ICMP_ULT:
|
||||
case ICmpInst::ICMP_ULE:
|
||||
// TODO: At the moment we need to see everything as signed. This is an
|
||||
// correctness issue that needs to be solved.
|
||||
//AffLHS->setUnsigned();
|
||||
//AffRHS->setUnsigned();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*Comp = new Comparison(AffLHS, AffRHS, Pred);
|
||||
}
|
||||
|
||||
void TempScopInfo::buildCondition(BasicBlock *BB, BasicBlock *RegionEntry,
|
||||
TempScop &Scop) {
|
||||
BBCond Cond;
|
||||
|
||||
DomTreeNode *BBNode = DT->getNode(BB), *EntryNode = DT->getNode(RegionEntry);
|
||||
assert(BBNode && EntryNode && "Get null node while building condition!");
|
||||
|
||||
// Walk up the dominance tree until reaching the entry node. Add all
|
||||
// conditions on the path to BB except if BB postdominates the block
|
||||
// containing the condition.
|
||||
while (BBNode != EntryNode) {
|
||||
BasicBlock *CurBB = BBNode->getBlock();
|
||||
BBNode = BBNode->getIDom();
|
||||
assert(BBNode && "BBNode should not reach the root node!");
|
||||
|
||||
if (PDT->dominates(CurBB, BBNode->getBlock()))
|
||||
continue;
|
||||
|
||||
BranchInst *Br = dyn_cast<BranchInst>(BBNode->getBlock()->getTerminator());
|
||||
assert(Br && "A Valid Scop should only contain branch instruction");
|
||||
|
||||
if (Br->isUnconditional())
|
||||
continue;
|
||||
|
||||
// Is BB on the ELSE side of the branch?
|
||||
bool inverted = DT->dominates(Br->getSuccessor(1), BB);
|
||||
|
||||
Comparison *Cmp;
|
||||
buildAffineCondition(*(Br->getCondition()), inverted, &Cmp, Scop);
|
||||
Cond.push_back(*Cmp);
|
||||
}
|
||||
|
||||
if (!Cond.empty())
|
||||
BBConds[BB] = Cond;
|
||||
}
|
||||
|
||||
TempScop *TempScopInfo::buildTempScop(Region &R) {
|
||||
TempScop *TScop = new TempScop(R, LoopBounds, BBConds, AccFuncMap);
|
||||
|
||||
for (Region::block_iterator I = R.block_begin(), E = R.block_end();
|
||||
I != E; ++I) {
|
||||
BasicBlock *BB = I->getNodeAs<BasicBlock>();
|
||||
buildAccessFunctions(R, TScop->getParamSet(), *BB);
|
||||
buildCondition(BB, R.getEntry(), *TScop);
|
||||
if (isReduction(*BB))
|
||||
TScop->Reductions.insert(BB);
|
||||
}
|
||||
|
||||
buildLoopBounds(*TScop);
|
||||
|
||||
// Build the MayAliasSets.
|
||||
TScop->MayASInfo->buildMayAliasSets(*TScop, *AA);
|
||||
return TScop;
|
||||
}
|
||||
|
||||
TempScop *TempScopInfo::getTempScop(const Region *R) const {
|
||||
TempScopMapType::const_iterator at = TempScops.find(R);
|
||||
return at == TempScops.end() ? 0 : at->second;
|
||||
}
|
||||
|
||||
void TempScopInfo::print(raw_ostream &OS, const Module *) const {
|
||||
for (TempScopMapType::const_iterator I = TempScops.begin(),
|
||||
E = TempScops.end(); I != E; ++I)
|
||||
I->second->print(OS, SE, LI);
|
||||
}
|
||||
|
||||
bool TempScopInfo::runOnFunction(Function &F) {
|
||||
DT = &getAnalysis<DominatorTree>();
|
||||
PDT = &getAnalysis<PostDominatorTree>();
|
||||
SE = &getAnalysis<ScalarEvolution>();
|
||||
LI = &getAnalysis<LoopInfo>();
|
||||
SD = &getAnalysis<ScopDetection>();
|
||||
AA = &getAnalysis<AliasAnalysis>();
|
||||
TD = &getAnalysis<TargetData>();
|
||||
|
||||
for (ScopDetection::iterator I = SD->begin(), E = SD->end(); I != E; ++I) {
|
||||
Region *R = const_cast<Region*>(*I);
|
||||
TempScops.insert(std::make_pair(R, buildTempScop(*R)));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TempScopInfo::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<TargetData>();
|
||||
AU.addRequiredTransitive<DominatorTree>();
|
||||
AU.addRequiredTransitive<PostDominatorTree>();
|
||||
AU.addRequiredTransitive<LoopInfo>();
|
||||
AU.addRequiredTransitive<ScalarEvolution>();
|
||||
AU.addRequiredTransitive<ScopDetection>();
|
||||
AU.addRequiredID(IndependentBlocksID);
|
||||
AU.addRequired<AliasAnalysis>();
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
|
||||
TempScopInfo::~TempScopInfo() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void TempScopInfo::clear() {
|
||||
BBConds.clear();
|
||||
LoopBounds.clear();
|
||||
AccFuncMap.clear();
|
||||
DeleteContainerSeconds(TempScops);
|
||||
TempScops.clear();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TempScop information extraction pass implement
|
||||
char TempScopInfo::ID = 0;
|
||||
|
||||
static RegisterPass<TempScopInfo>
|
||||
X("polly-analyze-ir", "Polly - Analyse the LLVM-IR in the detected regions");
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
add_subdirectory(Analysis)
|
||||
add_subdirectory(Support)
|
||||
add_subdirectory(JSON)
|
||||
|
||||
set(MODULE TRUE)
|
||||
set(LLVM_NO_RTTI 1)
|
||||
|
||||
if (OPENSCOP_FOUND)
|
||||
set(POLLY_EXCHANGE_FILES
|
||||
Exchange/OpenScopImporter.cpp Exchange/OpenScopExporter.cpp)
|
||||
endif (OPENSCOP_FOUND)
|
||||
if (SCOPLIB_FOUND)
|
||||
set(POLLY_SCOPLIB_FILES
|
||||
Pocc.cpp
|
||||
Exchange/ScopLib.cpp
|
||||
Exchange/ScopLibExporter.cpp
|
||||
Exchange/ScopLibImporter.cpp)
|
||||
endif (SCOPLIB_FOUND)
|
||||
|
||||
set(LLVM_USED_LIBS
|
||||
PollyAnalysis
|
||||
PollySupport
|
||||
PollyJSON
|
||||
)
|
||||
|
||||
add_polly_library(LLVMPolly
|
||||
Cloog.cpp
|
||||
CodePreparation.cpp
|
||||
CodeGeneration.cpp
|
||||
IndependentBlocks.cpp
|
||||
Interchange.cpp
|
||||
MayAliasSet.cpp
|
||||
Pocc.cpp
|
||||
RegionSimplify.cpp
|
||||
Exchange/JSONExporter.cpp
|
||||
${POLLY_EXCHANGE_FILES}
|
||||
${POLLY_SCOPLIB_FILES}
|
||||
)
|
||||
|
||||
add_dependencies(LLVMPolly
|
||||
PollyAnalysis
|
||||
PollySupport
|
||||
PollyJSON
|
||||
)
|
||||
|
||||
set_target_properties(LLVMPolly
|
||||
PROPERTIES
|
||||
LINKER_LANGUAGE CXX
|
||||
PREFIX "")
|
|
@ -0,0 +1,299 @@
|
|||
//===- Cloog.cpp - Cloog interface ----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Cloog[1] interface.
|
||||
//
|
||||
// The Cloog interface takes a Scop and generates a Cloog AST (clast). This
|
||||
// clast can either be returned directly or it can be pretty printed to stdout.
|
||||
//
|
||||
// A typical clast output looks like this:
|
||||
//
|
||||
// for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) {
|
||||
// bb2(c2);
|
||||
// }
|
||||
//
|
||||
// [1] http://www.cloog.org/ - The Chunky Loop Generator
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/Cloog.h"
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/ScopInfo.h"
|
||||
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/Module.h"
|
||||
|
||||
#include "cloog/isl/domain.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace polly {
|
||||
class Cloog {
|
||||
Scop *S;
|
||||
CloogOptions *Options;
|
||||
CloogState *State;
|
||||
clast_stmt *ClastRoot;
|
||||
|
||||
void buildCloogOptions();
|
||||
CloogUnionDomain *buildCloogUnionDomain();
|
||||
CloogInput *buildCloogInput();
|
||||
|
||||
public:
|
||||
Cloog(Scop *Scop);
|
||||
|
||||
~Cloog();
|
||||
|
||||
/// Write a .cloog input file
|
||||
void dump(FILE *F);
|
||||
|
||||
/// Print a source code representation of the program.
|
||||
void pprint(llvm::raw_ostream &OS);
|
||||
|
||||
/// Create the Cloog AST from this program.
|
||||
struct clast_stmt *getClast();
|
||||
};
|
||||
|
||||
Cloog::Cloog(Scop *Scop) : S(Scop) {
|
||||
State = cloog_state_malloc();
|
||||
buildCloogOptions();
|
||||
ClastRoot = cloog_clast_create_from_input(buildCloogInput(), Options);
|
||||
}
|
||||
|
||||
Cloog::~Cloog() {
|
||||
cloog_options_free(Options);
|
||||
cloog_clast_free(ClastRoot);
|
||||
cloog_state_free(State);
|
||||
}
|
||||
|
||||
// Create a FILE* write stream and get the output to it written
|
||||
// to a std::string.
|
||||
class FileToString {
|
||||
int FD[2];
|
||||
FILE *input;
|
||||
static const int BUFFERSIZE = 20;
|
||||
|
||||
char buf[BUFFERSIZE + 1];
|
||||
|
||||
|
||||
public:
|
||||
FileToString() {
|
||||
pipe(FD);
|
||||
input = fdopen(FD[1], "w");
|
||||
}
|
||||
~FileToString() {
|
||||
close(FD[0]);
|
||||
//close(FD[1]);
|
||||
}
|
||||
|
||||
FILE *getInputFile() {
|
||||
return input;
|
||||
}
|
||||
|
||||
void closeInput() {
|
||||
fclose(input);
|
||||
close(FD[1]);
|
||||
}
|
||||
|
||||
std::string getOutput() {
|
||||
std::string output;
|
||||
int readSize;
|
||||
|
||||
while (true) {
|
||||
readSize = read(FD[0], &buf, BUFFERSIZE);
|
||||
|
||||
if (readSize <= 0)
|
||||
break;
|
||||
|
||||
output += std::string(buf, readSize);
|
||||
}
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// Write .cloog input file.
|
||||
void Cloog::dump(FILE *F) {
|
||||
CloogInput *Input = buildCloogInput();
|
||||
cloog_input_dump_cloog(F, Input, Options);
|
||||
cloog_input_free(Input);
|
||||
}
|
||||
|
||||
/// Print a source code representation of the program.
|
||||
void Cloog::pprint(raw_ostream &OS) {
|
||||
FileToString *Output = new FileToString();
|
||||
clast_pprint(Output->getInputFile(), ClastRoot, 0, Options);
|
||||
Output->closeInput();
|
||||
OS << Output->getOutput();
|
||||
delete (Output);
|
||||
}
|
||||
|
||||
/// Create the Cloog AST from this program.
|
||||
struct clast_stmt *Cloog::getClast() {
|
||||
return ClastRoot;
|
||||
}
|
||||
|
||||
void Cloog::buildCloogOptions() {
|
||||
Options = cloog_options_malloc(State);
|
||||
Options->quiet = 1;
|
||||
Options->strides = 1;
|
||||
Options->save_domains = 1;
|
||||
Options->noscalars = 1;
|
||||
}
|
||||
|
||||
CloogUnionDomain *Cloog::buildCloogUnionDomain() {
|
||||
CloogUnionDomain *DU = cloog_union_domain_alloc(S->getNumParams());
|
||||
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
if (Stmt->isFinalRead())
|
||||
continue;
|
||||
|
||||
CloogScattering *Scattering=
|
||||
cloog_scattering_from_isl_map(isl_map_copy(Stmt->getScattering()));
|
||||
CloogDomain *Domain =
|
||||
cloog_domain_from_isl_set(isl_set_copy(Stmt->getDomain()));
|
||||
|
||||
std::string entryName = Stmt->getBaseName();
|
||||
char *Name = (char*)malloc(sizeof(char) * (entryName.size() + 1));
|
||||
strcpy(Name, entryName.c_str());
|
||||
|
||||
DU = cloog_union_domain_add_domain(DU, Name, Domain, Scattering, Stmt);
|
||||
}
|
||||
|
||||
return DU;
|
||||
}
|
||||
|
||||
CloogInput *Cloog::buildCloogInput() {
|
||||
CloogDomain *Context =
|
||||
cloog_domain_from_isl_set(isl_set_copy(S->getContext()));
|
||||
CloogUnionDomain *Statements = buildCloogUnionDomain();
|
||||
CloogInput *Input = cloog_input_alloc (Context, Statements);
|
||||
return Input;
|
||||
}
|
||||
} // End namespace polly.
|
||||
|
||||
namespace {
|
||||
|
||||
struct CloogExporter : public ScopPass {
|
||||
static char ID;
|
||||
Scop *S;
|
||||
explicit CloogExporter() : ScopPass(ID) {}
|
||||
|
||||
std::string getFileName(Region *R) const;
|
||||
virtual bool runOnScop(Scop &S);
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
|
||||
}
|
||||
std::string CloogExporter::getFileName(Region *R) const {
|
||||
std::string FunctionName = R->getEntry()->getParent()->getNameStr();
|
||||
std::string ExitName, EntryName;
|
||||
|
||||
raw_string_ostream ExitStr(ExitName);
|
||||
raw_string_ostream EntryStr(EntryName);
|
||||
|
||||
WriteAsOperand(EntryStr, R->getEntry(), false);
|
||||
EntryStr.str();
|
||||
|
||||
if (R->getExit()) {
|
||||
WriteAsOperand(ExitStr, R->getExit(), false);
|
||||
ExitStr.str();
|
||||
} else
|
||||
ExitName = "FunctionExit";
|
||||
|
||||
std::string RegionName = EntryName + "---" + ExitName;
|
||||
std::string FileName = FunctionName + "___" + RegionName + ".cloog";
|
||||
|
||||
return FileName;
|
||||
}
|
||||
|
||||
char CloogExporter::ID = 0;
|
||||
bool CloogExporter::runOnScop(Scop &S) {
|
||||
Region &R = S.getRegion();
|
||||
CloogInfo &C = getAnalysis<CloogInfo>();
|
||||
|
||||
std::string FunctionName = R.getEntry()->getParent()->getNameStr();
|
||||
std::string Filename = getFileName(&R);
|
||||
|
||||
errs() << "Writing Scop '" << R.getNameStr() << "' in function '"
|
||||
<< FunctionName << "' to '" << Filename << "'...\n";
|
||||
|
||||
FILE *F = fopen(Filename.c_str(), "w");
|
||||
C.dump(F);
|
||||
fclose(F);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CloogExporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
// Get the Common analysis usage of ScopPasses.
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
AU.addRequired<CloogInfo>();
|
||||
}
|
||||
|
||||
static RegisterPass<CloogExporter> A("polly-export-cloog",
|
||||
"Polly - Export the Cloog input file"
|
||||
" (Writes a .cloog file for each Scop)"
|
||||
);
|
||||
|
||||
llvm::Pass* polly::createCloogExporterPass() {
|
||||
return new CloogExporter();
|
||||
}
|
||||
|
||||
/// Write a .cloog input file
|
||||
void CloogInfo::dump(FILE *F) {
|
||||
C->dump(F);
|
||||
}
|
||||
|
||||
/// Print a source code representation of the program.
|
||||
void CloogInfo::pprint(llvm::raw_ostream &OS) {
|
||||
C->pprint(OS);
|
||||
}
|
||||
|
||||
/// Create the Cloog AST from this program.
|
||||
const struct clast_stmt *CloogInfo::getClast() {
|
||||
return C->getClast();
|
||||
}
|
||||
|
||||
bool CloogInfo::runOnScop(Scop &S) {
|
||||
if (C)
|
||||
delete C;
|
||||
|
||||
scop = &S;
|
||||
|
||||
C = new Cloog(&S);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CloogInfo::printScop(raw_ostream &OS) const {
|
||||
Function *function = scop->getRegion().getEntry()->getParent();
|
||||
|
||||
OS << function->getNameStr() << "():\n";
|
||||
|
||||
C->pprint(OS);
|
||||
}
|
||||
|
||||
void CloogInfo::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
// Get the Common analysis usage of ScopPasses.
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
}
|
||||
char CloogInfo::ID = 0;
|
||||
|
||||
|
||||
static RegisterPass<CloogInfo> B("polly-cloog",
|
||||
"Execute Cloog code generation");
|
||||
|
||||
Pass* polly::createCloogInfoPass() {
|
||||
return new CloogInfo();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,173 @@
|
|||
//===---- CodePreparation.cpp - Code preparation for Scop Detection -------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implement the code preparation for Scop detect, which will:
|
||||
// 1. Translate all PHINodes that not induction variable to memory access,
|
||||
// this will easier parameter and scalar dependencies checking.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/Support/ScopHelper.h"
|
||||
|
||||
#include "llvm/Instruction.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/RegionInfo.h"
|
||||
#include "llvm/Analysis/Dominators.h"
|
||||
#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/Support/InstIterator.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-code-prep"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// @brief Scop Code Preparation - Perform some transforms to make scop detect
|
||||
/// easier.
|
||||
///
|
||||
class CodePreperation : public FunctionPass {
|
||||
// DO NOT IMPLEMENT.
|
||||
CodePreperation(const CodePreperation &);
|
||||
// DO NOT IMPLEMENT.
|
||||
const CodePreperation &operator=(const CodePreperation &);
|
||||
|
||||
// LoopInfo to compute canonical induction variable.
|
||||
LoopInfo *LI;
|
||||
|
||||
// Clear the context.
|
||||
void clear();
|
||||
|
||||
bool eliminatePHINodes(Function &F);
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
explicit CodePreperation() : FunctionPass(ID) {}
|
||||
~CodePreperation();
|
||||
|
||||
/// @name FunctionPass interface.
|
||||
//@{
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
virtual void releaseMemory();
|
||||
virtual bool runOnFunction(Function &F);
|
||||
virtual void print(raw_ostream &OS, const Module *) const;
|
||||
//@}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// CodePreperation implement.
|
||||
|
||||
void CodePreperation::clear() {
|
||||
}
|
||||
|
||||
CodePreperation::~CodePreperation() {
|
||||
clear();
|
||||
}
|
||||
|
||||
bool CodePreperation::eliminatePHINodes(Function &F) {
|
||||
// The PHINodes that will be deleted.
|
||||
std::vector<PHINode*> PNtoDel;
|
||||
// The PHINodes that will be preserved.
|
||||
std::vector<PHINode*> PreservedPNs;
|
||||
|
||||
// Scan the PHINodes in this function.
|
||||
for (Function::iterator ibb = F.begin(), ibe = F.end();
|
||||
ibb != ibe; ++ibb)
|
||||
for (BasicBlock::iterator iib = ibb->begin(), iie = ibb->getFirstNonPHI();
|
||||
iib != iie; ++iib)
|
||||
if (PHINode *PN = cast<PHINode>(iib)) {
|
||||
if (Loop *L = LI->getLoopFor(ibb)) {
|
||||
// Induction variable will be preserved.
|
||||
if (L->getCanonicalInductionVariable() == PN) {
|
||||
PreservedPNs.push_back(PN);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// As DemotePHIToStack does not support invoke edges, we preserve
|
||||
// PHINodes that have invoke edges.
|
||||
if (hasInvokeEdge(PN))
|
||||
PreservedPNs.push_back(PN);
|
||||
else
|
||||
PNtoDel.push_back(PN);
|
||||
}
|
||||
|
||||
if (PNtoDel.empty())
|
||||
return false;
|
||||
|
||||
// Eliminate the PHINodes that not an Induction variable.
|
||||
while (!PNtoDel.empty()) {
|
||||
PHINode *PN = PNtoDel.back();
|
||||
PNtoDel.pop_back();
|
||||
|
||||
DemotePHIToStack(PN);
|
||||
}
|
||||
|
||||
// Move all preserved PHINodes to the beginning of the BasicBlock.
|
||||
while (!PreservedPNs.empty()) {
|
||||
PHINode *PN = PreservedPNs.back();
|
||||
PreservedPNs.pop_back();
|
||||
|
||||
BasicBlock *BB = PN->getParent();
|
||||
if (PN == BB->begin())
|
||||
continue;
|
||||
|
||||
PN->moveBefore(BB->begin());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CodePreperation::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<LoopInfo>();
|
||||
|
||||
AU.addPreserved<LoopInfo>();
|
||||
AU.addPreserved<RegionInfo>();
|
||||
AU.addPreserved<DominatorTree>();
|
||||
AU.addPreserved<DominanceFrontier>();
|
||||
}
|
||||
|
||||
bool CodePreperation::runOnFunction(Function &F) {
|
||||
LI = &getAnalysis<LoopInfo>();
|
||||
|
||||
splitEntryBlockForAlloca(&F.getEntryBlock(), this);
|
||||
|
||||
eliminatePHINodes(F);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CodePreperation::releaseMemory() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void CodePreperation::print(raw_ostream &OS, const Module *) const {
|
||||
}
|
||||
|
||||
char CodePreperation::ID = 0;
|
||||
|
||||
RegisterPass<CodePreperation> X("polly-prepare",
|
||||
"Polly - Prepare code for polly.",
|
||||
false, true);
|
||||
|
||||
char &polly::CodePreperationID = CodePreperation::ID;
|
||||
|
||||
Pass *polly::createCodePreperationPass() {
|
||||
return new CodePreperation();
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
//===-- JSONExporter.cpp - Export Scops as JSON -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Export the Scops build by ScopInfo pass as a JSON file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/Dependences.h"
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/ScopPass.h"
|
||||
|
||||
#include "json/reader.h"
|
||||
#include "json/writer.h"
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#include "stdio.h"
|
||||
#include "isl/set.h"
|
||||
#include "isl/map.h"
|
||||
#include "isl/constraint.h"
|
||||
#include "isl/printer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
static cl::opt<std::string>
|
||||
ImportDir("polly-import-jscop-dir",
|
||||
cl::desc("The directory to import the .jscop files from."), cl::Hidden,
|
||||
cl::value_desc("Directory path"), cl::ValueRequired, cl::init("."));
|
||||
static cl::opt<std::string>
|
||||
ImportPostfix("polly-import-jscop-postfix",
|
||||
cl::desc("Postfix to append to the import .jsop files."),
|
||||
cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
|
||||
cl::init(""));
|
||||
|
||||
struct JSONExporter : public ScopPass {
|
||||
static char ID;
|
||||
Scop *S;
|
||||
explicit JSONExporter() : ScopPass(ID) {}
|
||||
|
||||
std::string getFileName(Scop *S) const;
|
||||
Json::Value getJSON(Scop &scop) const;
|
||||
virtual bool runOnScop(Scop &S);
|
||||
void printScop(raw_ostream &OS) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
struct JSONImporter : public ScopPass {
|
||||
static char ID;
|
||||
Scop *S;
|
||||
explicit JSONImporter() : ScopPass(ID) {}
|
||||
|
||||
std::string getFileName(Scop *S) const;
|
||||
virtual bool runOnScop(Scop &S);
|
||||
void printScop(raw_ostream &OS) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
char JSONExporter::ID = 0;
|
||||
std::string JSONExporter::getFileName(Scop *S) const {
|
||||
std::string FunctionName =
|
||||
S->getRegion().getEntry()->getParent()->getNameStr();
|
||||
std::string FileName = FunctionName + "___" + S->getNameStr() + ".jscop";
|
||||
return FileName;
|
||||
}
|
||||
|
||||
void JSONExporter::printScop(raw_ostream &OS) const {
|
||||
S->print(OS);
|
||||
}
|
||||
|
||||
Json::Value JSONExporter::getJSON(Scop &scop) const {
|
||||
Json::Value root;
|
||||
|
||||
root["name"] = S->getRegion().getNameStr();
|
||||
root["context"] = S->getContextStr();
|
||||
root["statements"];
|
||||
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
if (Stmt->isFinalRead())
|
||||
continue;
|
||||
|
||||
Json::Value statement;
|
||||
|
||||
statement["name"] = Stmt->getBaseName();
|
||||
statement["domain"] = Stmt->getDomainStr();
|
||||
statement["schedule"] = Stmt->getScatteringStr();
|
||||
statement["accesses"];
|
||||
|
||||
for (ScopStmt::memacc_iterator MI = Stmt->memacc_begin(),
|
||||
ME = Stmt->memacc_end(); MI != ME; ++MI) {
|
||||
Json::Value access;
|
||||
|
||||
access["kind"] = (*MI)->isRead() ? "read" : "write";
|
||||
access["relation"] = (*MI)->getAccessFunctionStr();
|
||||
|
||||
statement["accesses"].append(access);
|
||||
}
|
||||
|
||||
root["statements"].append(statement);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
bool JSONExporter::runOnScop(Scop &scop) {
|
||||
S = &scop;
|
||||
Region &R = S->getRegion();
|
||||
|
||||
std::string FileName = ImportDir + "/" + getFileName(S);
|
||||
char *text;
|
||||
|
||||
Json::Value jscop = getJSON(scop);
|
||||
Json::StyledWriter writer;
|
||||
std::string fileContent = writer.write(jscop);
|
||||
|
||||
// Write to file.
|
||||
std::string ErrInfo;
|
||||
tool_output_file F(FileName.c_str(), ErrInfo);
|
||||
|
||||
std::string FunctionName = R.getEntry()->getParent()->getNameStr();
|
||||
errs() << "Writing JScop '" << R.getNameStr() << "' in function '"
|
||||
<< FunctionName << "' to '" << FileName << "'.\n";
|
||||
|
||||
if (ErrInfo.empty()) {
|
||||
F.os() << fileContent;
|
||||
F.os().close();
|
||||
if (!F.os().has_error()) {
|
||||
errs() << "\n";
|
||||
F.keep();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
errs() << " error opening file for writing!\n";
|
||||
F.os().clear_error();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void JSONExporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.setPreservesAll();
|
||||
AU.addRequired<ScopInfo>();
|
||||
}
|
||||
|
||||
static RegisterPass<JSONExporter> A("polly-export-jscop",
|
||||
"Polly - Export Scops as JSON"
|
||||
" (Writes a .jscop file for each Scop)"
|
||||
);
|
||||
|
||||
Pass *polly::createJSONExporterPass() {
|
||||
return new JSONExporter();
|
||||
}
|
||||
|
||||
char JSONImporter::ID = 0;
|
||||
std::string JSONImporter::getFileName(Scop *S) const {
|
||||
std::string FunctionName =
|
||||
S->getRegion().getEntry()->getParent()->getNameStr();
|
||||
std::string FileName = FunctionName + "___" + S->getNameStr() + ".jscop";
|
||||
|
||||
if (ImportPostfix != "")
|
||||
FileName += "." + ImportPostfix;
|
||||
|
||||
return FileName;
|
||||
}
|
||||
void JSONImporter::printScop(raw_ostream &OS) const {
|
||||
S->print(OS);
|
||||
}
|
||||
|
||||
typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
|
||||
|
||||
bool JSONImporter::runOnScop(Scop &scop) {
|
||||
S = &scop;
|
||||
Region &R = S->getRegion();
|
||||
Dependences *D = &getAnalysis<Dependences>();
|
||||
|
||||
|
||||
std::string FileName = ImportDir + "/" + getFileName(S);
|
||||
|
||||
std::string FunctionName = R.getEntry()->getParent()->getNameStr();
|
||||
errs() << "Reading JScop '" << R.getNameStr() << "' in function '"
|
||||
<< FunctionName << "' from '" << FileName << "'.\n";
|
||||
OwningPtr<MemoryBuffer> result;
|
||||
error_code ec = MemoryBuffer::getFile(FileName, result);
|
||||
|
||||
if (ec) {
|
||||
errs() << "File could not be read: " << ec.message() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
Json::Reader reader;
|
||||
Json::Value jscop;
|
||||
|
||||
bool parsingSuccessful = reader.parse(result->getBufferStart(), jscop);
|
||||
|
||||
if (!parsingSuccessful) {
|
||||
errs() << "JSCoP file could not be parsed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
StatementToIslMapTy &NewScattering = *(new StatementToIslMapTy());
|
||||
|
||||
int index = 0;
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
if (Stmt->isFinalRead())
|
||||
continue;
|
||||
Json::Value schedule = jscop["statements"][index]["schedule"];
|
||||
|
||||
isl_map *m = isl_map_read_from_str(S->getCtx(), schedule.asCString(), -1);
|
||||
NewScattering[*SI] = m;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!D->isValidScattering(&NewScattering)) {
|
||||
errs() << "JScop file contains a scattering that changes the "
|
||||
<< "dependences. Use -disable-polly-legality to continue anyways\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
if (NewScattering.find(Stmt) != NewScattering.end())
|
||||
Stmt->setScattering((NewScattering)[Stmt]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void JSONImporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
AU.addRequired<Dependences>();
|
||||
}
|
||||
|
||||
static RegisterPass<JSONImporter> B("polly-import-jscop",
|
||||
"Polly - Import Scops from JSON"
|
||||
" (Reads a .jscop file for each Scop)"
|
||||
);
|
||||
|
||||
Pass *polly::createJSONImporterPass() {
|
||||
return new JSONImporter();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
##===- polly/lib/Exchange/Makefile ----------------*- Makefile -*-===##
|
||||
|
||||
#
|
||||
# Indicate where we are relative to the top of the source tree.
|
||||
#
|
||||
LEVEL=../..
|
||||
|
||||
LIBRARYNAME=pollyexchange
|
||||
BUILD_ARCHIVE = 1
|
||||
|
||||
CPP.Flags += $(POLLY_INC)
|
||||
|
||||
#
|
||||
# Include Makefile.common so we know what to do.
|
||||
#
|
||||
include $(LEVEL)/Makefile.common
|
|
@ -0,0 +1,583 @@
|
|||
//===-- OpenScopExporter.cpp - Export Scops with openscop library --------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Export the Scops build by ScopInfo pass to text file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#ifdef OPENSCOP_FOUND
|
||||
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/ScopPass.h"
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#define OPENSCOP_INT_T_IS_MP
|
||||
#include "openscop/openscop.h"
|
||||
|
||||
#include "stdio.h"
|
||||
#include "isl/set.h"
|
||||
#include "isl/constraint.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
static cl::opt<std::string>
|
||||
ExportDir("polly-export-dir",
|
||||
cl::desc("The directory to export the .scop files to."), cl::Hidden,
|
||||
cl::value_desc("Directory path"), cl::ValueRequired, cl::init("."));
|
||||
|
||||
struct ScopExporter : public ScopPass {
|
||||
static char ID;
|
||||
Scop *S;
|
||||
explicit ScopExporter() : ScopPass(ID) {}
|
||||
|
||||
std::string getFileName(Scop *S) const;
|
||||
|
||||
virtual bool runOnScop(Scop &S);
|
||||
void printScop(raw_ostream &OS) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
char ScopExporter::ID = 0;
|
||||
|
||||
class OpenScop {
|
||||
Scop *PollyScop;
|
||||
openscop_scop_p openscop;
|
||||
|
||||
std::map<const Value*, int> ArrayMap;
|
||||
|
||||
void initializeArrays();
|
||||
void initializeParameters();
|
||||
void initializeScattering();
|
||||
void initializeStatements();
|
||||
openscop_statement_p initializeStatement(ScopStmt *stmt);
|
||||
void freeStatement(openscop_statement_p stmt);
|
||||
static int accessToMatrix_constraint(isl_constraint *c, void *user);
|
||||
static int accessToMatrix_basic_map(isl_basic_map *bmap, void *user);
|
||||
openscop_matrix_p createAccessMatrix(ScopStmt *S, bool isRead);
|
||||
static int domainToMatrix_constraint(isl_constraint *c, void *user);
|
||||
static int domainToMatrix_basic_set(isl_basic_set *bset, void *user);
|
||||
openscop_matrix_p domainToMatrix(isl_set *PS);
|
||||
static int scatteringToMatrix_constraint(isl_constraint *c, void *user);
|
||||
static int scatteringToMatrix_basic_map(isl_basic_map *bmap, void *user);
|
||||
openscop_matrix_p scatteringToMatrix(isl_map *pmap);
|
||||
|
||||
public:
|
||||
OpenScop(Scop *S);
|
||||
~OpenScop();
|
||||
void print(FILE *F);
|
||||
|
||||
};
|
||||
|
||||
OpenScop::OpenScop(Scop *S) : PollyScop(S) {
|
||||
openscop = openscop_scop_malloc();
|
||||
|
||||
initializeArrays();
|
||||
initializeParameters();
|
||||
initializeScattering();
|
||||
initializeStatements();
|
||||
}
|
||||
|
||||
void OpenScop::initializeParameters() {
|
||||
openscop->nb_parameters = PollyScop->getNumParams();
|
||||
openscop->parameters = new char*[openscop->nb_parameters];
|
||||
|
||||
for (int i = 0; i < openscop->nb_parameters; ++i) {
|
||||
openscop->parameters[i] = new char[20];
|
||||
sprintf(openscop->parameters[i], "p_%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenScop::initializeArrays() {
|
||||
int nb_arrays = 0;
|
||||
|
||||
for (Scop::iterator SI = PollyScop->begin(), SE = PollyScop->end(); SI != SE;
|
||||
++SI)
|
||||
for (ScopStmt::memacc_iterator MI = (*SI)->memacc_begin(),
|
||||
ME = (*SI)->memacc_end(); MI != ME; ++MI) {
|
||||
const Value *BaseAddr = (*MI)->getBaseAddr();
|
||||
if (ArrayMap.find(BaseAddr) == ArrayMap.end()) {
|
||||
ArrayMap.insert(std::make_pair(BaseAddr, nb_arrays));
|
||||
++nb_arrays;
|
||||
}
|
||||
}
|
||||
|
||||
openscop->nb_arrays = nb_arrays;
|
||||
openscop->arrays = new char*[nb_arrays];
|
||||
|
||||
for (int i = 0; i < nb_arrays; ++i)
|
||||
for (std::map<const Value*, int>::iterator VI = ArrayMap.begin(),
|
||||
VE = ArrayMap.end(); VI != VE; ++VI)
|
||||
if ((*VI).second == i) {
|
||||
const Value *V = (*VI).first;
|
||||
std::string name = V->getNameStr();
|
||||
openscop->arrays[i] = new char[name.size() + 1];
|
||||
strcpy(openscop->arrays[i], name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void OpenScop::initializeScattering() {
|
||||
openscop->nb_scattdims = PollyScop->getScatterDim();
|
||||
openscop->scattdims = new char*[openscop->nb_scattdims];
|
||||
|
||||
for (int i = 0; i < openscop->nb_scattdims; ++i) {
|
||||
openscop->scattdims[i] = new char[20];
|
||||
sprintf(openscop->scattdims[i], "s_%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
openscop_statement_p OpenScop::initializeStatement(ScopStmt *stmt) {
|
||||
openscop_statement_p Stmt = openscop_statement_malloc();
|
||||
|
||||
// Domain & Schedule
|
||||
Stmt->domain = domainToMatrix(stmt->getDomain());
|
||||
Stmt->schedule = scatteringToMatrix(stmt->getScattering());
|
||||
|
||||
// Statement name
|
||||
const char* entryName = stmt->getBaseName();
|
||||
Stmt->body = (char*)malloc(sizeof(char) * (strlen(entryName) + 1));
|
||||
strcpy(Stmt->body, entryName);
|
||||
|
||||
// Iterator names
|
||||
Stmt->nb_iterators = isl_set_n_dim(stmt->getDomain());
|
||||
Stmt->iterators = new char*[Stmt->nb_iterators];
|
||||
|
||||
for (int i = 0; i < Stmt->nb_iterators; ++i) {
|
||||
Stmt->iterators[i] = new char[20];
|
||||
sprintf(Stmt->iterators[i], "i_%d", i);
|
||||
}
|
||||
|
||||
// Memory Accesses
|
||||
Stmt->read = createAccessMatrix(stmt, true);
|
||||
Stmt->write = createAccessMatrix(stmt, false);
|
||||
|
||||
return Stmt;
|
||||
}
|
||||
|
||||
void OpenScop::initializeStatements() {
|
||||
for (Scop::reverse_iterator SI = PollyScop->rbegin(), SE = PollyScop->rend();
|
||||
SI != SE; ++SI) {
|
||||
if ((*SI)->isFinalRead())
|
||||
continue;
|
||||
openscop_statement_p stmt = initializeStatement(*SI);
|
||||
stmt->next = openscop->statement;
|
||||
openscop->statement = stmt;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenScop::freeStatement(openscop_statement_p stmt) {
|
||||
|
||||
if (stmt->read)
|
||||
openscop_matrix_free(stmt->read);
|
||||
stmt->read = NULL;
|
||||
|
||||
if (stmt->write)
|
||||
openscop_matrix_free(stmt->write);
|
||||
stmt->write = NULL;
|
||||
|
||||
if (stmt->domain)
|
||||
openscop_matrix_free(stmt->domain);
|
||||
stmt->domain = NULL;
|
||||
|
||||
if (stmt->schedule)
|
||||
openscop_matrix_free(stmt->schedule);
|
||||
stmt->schedule = NULL;
|
||||
|
||||
for (int i = 0; i < stmt->nb_iterators; ++i)
|
||||
delete[](stmt->iterators[i]);
|
||||
|
||||
delete[](stmt->iterators);
|
||||
stmt->iterators = NULL;
|
||||
stmt->nb_iterators = 0;
|
||||
|
||||
delete[](stmt->body);
|
||||
stmt->body = NULL;
|
||||
|
||||
openscop_statement_free(stmt);
|
||||
}
|
||||
|
||||
void OpenScop::print(FILE *F) {
|
||||
openscop_scop_print_dot_scop(F, openscop);
|
||||
}
|
||||
|
||||
/// Add an isl constraint to an OpenScop matrix.
|
||||
///
|
||||
/// @param user The matrix
|
||||
/// @param c The constraint
|
||||
int OpenScop::domainToMatrix_constraint(isl_constraint *c, void *user) {
|
||||
openscop_matrix_p m = (openscop_matrix_p) user;
|
||||
|
||||
int nb_params = isl_constraint_dim(c, isl_dim_param);
|
||||
int nb_vars = isl_constraint_dim(c, isl_dim_set);
|
||||
int nb_div = isl_constraint_dim(c, isl_dim_div);
|
||||
|
||||
assert(!nb_div && "Existentially quantified variables not yet supported");
|
||||
|
||||
openscop_vector_p vec = openscop_vector_malloc(nb_params + nb_vars + 2);
|
||||
|
||||
// Assign type
|
||||
if (isl_constraint_is_equality(c))
|
||||
openscop_vector_tag_equality(vec);
|
||||
else
|
||||
openscop_vector_tag_inequality(vec);
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// Assign variables
|
||||
for (int i = 0; i < nb_vars; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_set, i, &v);
|
||||
isl_int_set(vec->p[i + 1], v);
|
||||
}
|
||||
|
||||
// Assign parameters
|
||||
for (int i = 0; i < nb_params; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
|
||||
isl_int_set(vec->p[nb_vars + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign constant
|
||||
isl_constraint_get_constant(c, &v);
|
||||
isl_int_set(vec->p[nb_params + nb_vars + 1], v);
|
||||
|
||||
openscop_matrix_insert_vector(m, vec, m->NbRows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Add an isl basic set to a OpenScop matrix_list
|
||||
///
|
||||
/// @param bset The basic set to add
|
||||
/// @param user The matrix list we should add the basic set to
|
||||
///
|
||||
/// XXX: At the moment this function expects just a matrix, as support
|
||||
/// for matrix lists is currently not available in OpenScop. So union of
|
||||
/// polyhedron are not yet supported
|
||||
int OpenScop::domainToMatrix_basic_set(isl_basic_set *bset, void *user) {
|
||||
openscop_matrix_p m = (openscop_matrix_p) user;
|
||||
assert(!m->NbRows && "Union of polyhedron not yet supported");
|
||||
|
||||
isl_basic_set_foreach_constraint(bset, &domainToMatrix_constraint, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Translate a isl_set to a OpenScop matrix.
|
||||
///
|
||||
/// @param PS The set to be translated
|
||||
/// @return A OpenScop Matrix
|
||||
openscop_matrix_p OpenScop::domainToMatrix(isl_set *PS) {
|
||||
|
||||
// Create a canonical copy of this set.
|
||||
isl_set *set = isl_set_copy(PS);
|
||||
set = isl_set_compute_divs (set);
|
||||
set = isl_set_align_divs (set);
|
||||
|
||||
// Initialize the matrix.
|
||||
unsigned NbRows, NbColumns;
|
||||
NbRows = 0;
|
||||
NbColumns = isl_set_n_dim(PS) + isl_set_n_param(PS) + 2;
|
||||
openscop_matrix_p matrix = openscop_matrix_malloc(NbRows, NbColumns);
|
||||
|
||||
// Copy the content into the matrix.
|
||||
isl_set_foreach_basic_set(set, &domainToMatrix_basic_set, matrix);
|
||||
|
||||
isl_set_free(set);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/// Add an isl constraint to an OpenScop matrix.
|
||||
///
|
||||
/// @param user The matrix
|
||||
/// @param c The constraint
|
||||
int OpenScop::scatteringToMatrix_constraint(isl_constraint *c, void *user) {
|
||||
openscop_matrix_p m = (openscop_matrix_p) user;
|
||||
|
||||
int nb_params = isl_constraint_dim(c, isl_dim_param);
|
||||
int nb_in = isl_constraint_dim(c, isl_dim_in);
|
||||
int nb_out = isl_constraint_dim(c, isl_dim_out);
|
||||
int nb_div = isl_constraint_dim(c, isl_dim_div);
|
||||
|
||||
assert(!nb_div && "Existentially quantified variables not yet supported");
|
||||
|
||||
openscop_vector_p vec =
|
||||
openscop_vector_malloc(nb_params + nb_in + nb_out + 2);
|
||||
|
||||
// Assign type
|
||||
if (isl_constraint_is_equality(c))
|
||||
openscop_vector_tag_equality(vec);
|
||||
else
|
||||
openscop_vector_tag_inequality(vec);
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// Assign scattering
|
||||
for (int i = 0; i < nb_out; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_out, i, &v);
|
||||
isl_int_set(vec->p[i + 1], v);
|
||||
}
|
||||
|
||||
// Assign variables
|
||||
for (int i = 0; i < nb_in; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_in, i, &v);
|
||||
isl_int_set(vec->p[nb_out + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign parameters
|
||||
for (int i = 0; i < nb_params; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
|
||||
isl_int_set(vec->p[nb_out + nb_in + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign constant
|
||||
isl_constraint_get_constant(c, &v);
|
||||
isl_int_set(vec->p[nb_out + nb_in + nb_params + 1], v);
|
||||
|
||||
openscop_matrix_insert_vector(m, vec, m->NbRows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Add an isl basic map to a OpenScop matrix_list
|
||||
///
|
||||
/// @param bmap The basic map to add
|
||||
/// @param user The matrix list we should add the basic map to
|
||||
///
|
||||
/// XXX: At the moment this function expects just a matrix, as support
|
||||
/// for matrix lists is currently not available in OpenScop. So union of
|
||||
/// polyhedron are not yet supported
|
||||
int OpenScop::scatteringToMatrix_basic_map(isl_basic_map *bmap, void *user) {
|
||||
openscop_matrix_p m = (openscop_matrix_p) user;
|
||||
assert(!m->NbRows && "Union of polyhedron not yet supported");
|
||||
|
||||
isl_basic_map_foreach_constraint(bmap, &scatteringToMatrix_constraint, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Translate a isl_map to a OpenScop matrix.
|
||||
///
|
||||
/// @param map The map to be translated
|
||||
/// @return A OpenScop Matrix
|
||||
openscop_matrix_p OpenScop::scatteringToMatrix(isl_map *pmap) {
|
||||
|
||||
// Create a canonical copy of this set.
|
||||
isl_map *map = isl_map_copy(pmap);
|
||||
map = isl_map_compute_divs (map);
|
||||
map = isl_map_align_divs (map);
|
||||
|
||||
// Initialize the matrix.
|
||||
unsigned NbRows, NbColumns;
|
||||
NbRows = 0;
|
||||
NbColumns = isl_map_n_in(pmap) + isl_map_n_out(pmap) + isl_map_n_param(pmap)
|
||||
+ 2;
|
||||
openscop_matrix_p matrix = openscop_matrix_malloc(NbRows, NbColumns);
|
||||
|
||||
// Copy the content into the matrix.
|
||||
isl_map_foreach_basic_map(map, &scatteringToMatrix_basic_map, matrix);
|
||||
|
||||
isl_map_free(map);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/// Add an isl constraint to an OpenScop matrix.
|
||||
///
|
||||
/// @param user The matrix
|
||||
/// @param c The constraint
|
||||
int OpenScop::accessToMatrix_constraint(isl_constraint *c, void *user) {
|
||||
openscop_matrix_p m = (openscop_matrix_p) user;
|
||||
|
||||
int nb_params = isl_constraint_dim(c, isl_dim_param);
|
||||
int nb_in = isl_constraint_dim(c, isl_dim_in);
|
||||
int nb_div = isl_constraint_dim(c, isl_dim_div);
|
||||
|
||||
assert(!nb_div && "Existentially quantified variables not yet supported");
|
||||
|
||||
openscop_vector_p vec =
|
||||
openscop_vector_malloc(nb_params + nb_in + 2);
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// The access dimension has to be one.
|
||||
isl_constraint_get_coefficient(c, isl_dim_out, 0, &v);
|
||||
assert(isl_int_is_one(v));
|
||||
bool inverse = true ;
|
||||
|
||||
// Assign variables
|
||||
for (int i = 0; i < nb_in; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_in, i, &v);
|
||||
|
||||
if (inverse) isl_int_neg(v,v);
|
||||
|
||||
isl_int_set(vec->p[i + 1], v);
|
||||
}
|
||||
|
||||
// Assign parameters
|
||||
for (int i = 0; i < nb_params; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
|
||||
|
||||
if (inverse) isl_int_neg(v,v);
|
||||
|
||||
isl_int_set(vec->p[nb_in + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign constant
|
||||
isl_constraint_get_constant(c, &v);
|
||||
|
||||
if (inverse) isl_int_neg(v,v);
|
||||
|
||||
isl_int_set(vec->p[nb_in + nb_params + 1], v);
|
||||
|
||||
openscop_matrix_insert_vector(m, vec, m->NbRows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Add an isl basic map to a OpenScop matrix_list
|
||||
///
|
||||
/// @param bmap The basic map to add
|
||||
/// @param user The matrix list we should add the basic map to
|
||||
///
|
||||
/// XXX: At the moment this function expects just a matrix, as support
|
||||
/// for matrix lists is currently not available in OpenScop. So union of
|
||||
/// polyhedron are not yet supported
|
||||
int OpenScop::accessToMatrix_basic_map(isl_basic_map *bmap, void *user) {
|
||||
isl_basic_map_foreach_constraint(bmap, &accessToMatrix_constraint, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Create the memory access matrix for openscop
|
||||
///
|
||||
/// @param S The polly statement the access matrix is created for.
|
||||
/// @param isRead Are we looking for read or write accesses?
|
||||
/// @param ArrayMap A map translating from the memory references to the openscop
|
||||
/// indeces
|
||||
///
|
||||
/// @return The memory access matrix, as it is required by openscop.
|
||||
openscop_matrix_p OpenScop::createAccessMatrix(ScopStmt *S, bool isRead) {
|
||||
|
||||
unsigned NbColumns = S->getNumIterators() + S->getNumParams() + 2;
|
||||
openscop_matrix_p m = openscop_matrix_malloc(0, NbColumns);
|
||||
|
||||
for (ScopStmt::memacc_iterator MI = S->memacc_begin(), ME = S->memacc_end();
|
||||
MI != ME; ++MI)
|
||||
if ((*MI)->isRead() == isRead) {
|
||||
// Extract the access function.
|
||||
isl_map_foreach_basic_map((*MI)->getAccessFunction(),
|
||||
&accessToMatrix_basic_map, m);
|
||||
|
||||
// Set the index of the memory access base element.
|
||||
std::map<const Value*, int>::iterator BA =
|
||||
ArrayMap.find((*MI)->getBaseAddr());
|
||||
isl_int_set_si(m->p[m->NbRows - 1][0], (*BA).second + 1);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
OpenScop::~OpenScop() {
|
||||
// Free array names.
|
||||
for (int i = 0; i < openscop->nb_arrays; ++i)
|
||||
delete[](openscop->arrays[i]);
|
||||
|
||||
delete[](openscop->arrays);
|
||||
openscop->arrays = NULL;
|
||||
openscop->nb_arrays = 0;
|
||||
|
||||
// Free scattering names.
|
||||
for (int i = 0; i < openscop->nb_scattdims; ++i)
|
||||
delete[](openscop->scattdims[i]);
|
||||
|
||||
delete[](openscop->scattdims);
|
||||
openscop->scattdims = NULL;
|
||||
openscop->nb_scattdims = 0;
|
||||
|
||||
// Free parameters
|
||||
for (int i = 0; i < openscop->nb_parameters; ++i)
|
||||
delete[](openscop->parameters[i]);
|
||||
|
||||
delete[](openscop->parameters);
|
||||
openscop->parameters = NULL;
|
||||
openscop->nb_parameters = 0;
|
||||
|
||||
openscop_statement_p stmt = openscop->statement;
|
||||
|
||||
// Free Statements
|
||||
while (stmt) {
|
||||
openscop_statement_p TempStmt = stmt->next;
|
||||
stmt->next = NULL;
|
||||
freeStatement(stmt);
|
||||
stmt = TempStmt;
|
||||
}
|
||||
|
||||
openscop->statement = NULL;
|
||||
|
||||
openscop_scop_free(openscop);
|
||||
}
|
||||
|
||||
std::string ScopExporter::getFileName(Scop *S) const {
|
||||
std::string FunctionName =
|
||||
S->getRegion().getEntry()->getParent()->getNameStr();
|
||||
std::string FileName = FunctionName + "___" + S->getNameStr() + ".scop";
|
||||
return FileName;
|
||||
}
|
||||
|
||||
void ScopExporter::printScop(raw_ostream &OS) const {
|
||||
S->print(OS);
|
||||
}
|
||||
|
||||
bool ScopExporter::runOnScop(Scop &scop) {
|
||||
S = &scop;
|
||||
Region &R = S->getRegion();
|
||||
|
||||
std::string FileName = ExportDir + "/" + getFileName(S);
|
||||
FILE *F = fopen(FileName.c_str(), "w");
|
||||
|
||||
if (!F) {
|
||||
errs() << "Cannot open file: " << FileName << "\n";
|
||||
errs() << "Skipping export.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
OpenScop openscop(S);
|
||||
openscop.print(F);
|
||||
fclose(F);
|
||||
|
||||
std::string FunctionName = R.getEntry()->getParent()->getNameStr();
|
||||
errs() << "Writing Scop '" << R.getNameStr() << "' in function '"
|
||||
<< FunctionName << "' to '" << FileName << "'.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScopExporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
static RegisterPass<ScopExporter> A("polly-export",
|
||||
"Polly - Export Scops with OpenScop library"
|
||||
" (Writes a .scop file for each Scop)"
|
||||
);
|
||||
|
||||
Pass *polly::createScopExporterPass() {
|
||||
return new ScopExporter();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
//===-- OpenScopImporter.cpp - Import Scops with openscop library --------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Import modified .scop files into Polly. This allows to change the schedule of
|
||||
// statements.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#include "polly/Dependences.h"
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/ScopPass.h"
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#ifdef OPENSCOP_FOUND
|
||||
|
||||
#define OPENSCOP_INT_T_IS_MP
|
||||
#include "openscop/openscop.h"
|
||||
|
||||
#include "isl/set.h"
|
||||
#include "isl/constraint.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
static cl::opt<std::string>
|
||||
ImportDir("polly-import-dir",
|
||||
cl::desc("The directory to import the .scop files from."),
|
||||
cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
|
||||
cl::init("."));
|
||||
static cl::opt<std::string>
|
||||
ImportPostfix("polly-import-postfix",
|
||||
cl::desc("Postfix to append to the import .scop files."),
|
||||
cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
|
||||
cl::init(""));
|
||||
|
||||
struct ScopImporter : public ScopPass {
|
||||
static char ID;
|
||||
Scop *S;
|
||||
Dependences *D;
|
||||
explicit ScopImporter() : ScopPass(ID) {}
|
||||
bool updateScattering(Scop *S, openscop_scop_p OScop);
|
||||
|
||||
std::string getFileName(Scop *S) const;
|
||||
virtual bool runOnScop(Scop &S);
|
||||
virtual void printScop(raw_ostream &OS) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
}
|
||||
|
||||
char ScopImporter::ID = 0;
|
||||
|
||||
/// @brief Create an isl constraint from a row of OpenScop integers.
|
||||
///
|
||||
/// @param row An array of isl/OpenScop integers.
|
||||
/// @param dim An isl dim object, describing how to spilt the dimensions.
|
||||
///
|
||||
/// @return An isl constraint representing this integer array.
|
||||
isl_constraint *constraintFromMatrixRow(isl_int *row, isl_dim *dim) {
|
||||
isl_constraint *c;
|
||||
|
||||
unsigned NbOut = isl_dim_size(dim, isl_dim_out);
|
||||
unsigned NbIn = isl_dim_size(dim, isl_dim_in);
|
||||
unsigned NbParam = isl_dim_size(dim, isl_dim_param);
|
||||
|
||||
if (isl_int_is_zero(row[0]))
|
||||
c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
else
|
||||
c = isl_inequality_alloc(isl_dim_copy(dim));
|
||||
|
||||
unsigned current_column = 1;
|
||||
|
||||
for (unsigned j = 0; j < NbOut; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, j, row[current_column++]);
|
||||
|
||||
for (unsigned j = 0; j < NbIn; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, j, row[current_column++]);
|
||||
|
||||
for (unsigned j = 0; j < NbParam; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_param, j, row[current_column++]);
|
||||
|
||||
isl_constraint_set_constant(c, row[current_column]);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @brief Create an isl map from a OpenScop matrix.
|
||||
///
|
||||
/// @param m The OpenScop matrix to translate.
|
||||
/// @param dim The dimensions that are contained in the OpenScop matrix.
|
||||
///
|
||||
/// @return An isl map representing m.
|
||||
isl_map *mapFromMatrix(openscop_matrix_p m, isl_dim *dim) {
|
||||
isl_basic_map *bmap = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
|
||||
for (unsigned i = 0; i < m->NbRows; ++i) {
|
||||
isl_constraint *c;
|
||||
|
||||
c = constraintFromMatrixRow(m->p[i], dim);
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
return isl_map_from_basic_map(bmap);
|
||||
}
|
||||
|
||||
/// @brief Create a new scattering for PollyStmt.
|
||||
///
|
||||
/// @param m The matrix describing the new scattering.
|
||||
/// @param PollyStmt The statement to create the scattering for.
|
||||
///
|
||||
/// @return An isl_map describing the scattering.
|
||||
isl_map *scatteringForStmt(openscop_matrix_p m, ScopStmt *PollyStmt) {
|
||||
|
||||
unsigned NbParam = PollyStmt->getNumParams();
|
||||
unsigned NbIterators = PollyStmt->getNumIterators();
|
||||
unsigned NbScattering = m->NbColumns - 2 - NbParam - NbIterators;
|
||||
|
||||
isl_ctx *ctx = PollyStmt->getParent()->getCtx();
|
||||
isl_dim *dim = isl_dim_alloc(ctx, NbParam, NbIterators, NbScattering);
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_out, "scattering");
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_in, PollyStmt->getBaseName());
|
||||
isl_map *map = mapFromMatrix(m, dim);
|
||||
isl_dim_free(dim);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
|
||||
|
||||
/// @brief Read the new scattering from the OpenScop description.
|
||||
///
|
||||
/// @S The Scop to update
|
||||
/// @OScop The OpenScop data structure describing the new scattering.
|
||||
/// @return A map that contains for each Statement the new scattering.
|
||||
StatementToIslMapTy *readScattering(Scop *S, openscop_scop_p OScop) {
|
||||
StatementToIslMapTy &NewScattering = *(new StatementToIslMapTy());
|
||||
openscop_statement_p stmt = OScop->statement;
|
||||
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
|
||||
if ((*SI)->isFinalRead())
|
||||
continue;
|
||||
|
||||
if (!stmt) {
|
||||
errs() << "Not enough statements available in OpenScop file\n";
|
||||
delete &NewScattering;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NewScattering[*SI] = scatteringForStmt(stmt->schedule, *SI);
|
||||
stmt = stmt->next;
|
||||
}
|
||||
|
||||
if (stmt) {
|
||||
errs() << "Too many statements in OpenScop file\n";
|
||||
delete &NewScattering;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &NewScattering;
|
||||
}
|
||||
|
||||
/// @brief Update the scattering in a Scop using the OpenScop description of
|
||||
/// the scattering.
|
||||
///
|
||||
/// @S The Scop to update
|
||||
/// @OScop The OpenScop data structure describing the new scattering.
|
||||
/// @return Returns false, if the update failed.
|
||||
bool ScopImporter::updateScattering(Scop *S, openscop_scop_p OScop) {
|
||||
StatementToIslMapTy *NewScattering = readScattering(S, OScop);
|
||||
|
||||
if (!NewScattering)
|
||||
return false;
|
||||
|
||||
if (!D->isValidScattering(NewScattering)) {
|
||||
errs() << "OpenScop file contains a scattering that changes the "
|
||||
<< "dependences. Use -disable-polly-legality to continue anyways\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
if (NewScattering->find(Stmt) != NewScattering->end())
|
||||
Stmt->setScattering((*NewScattering)[Stmt]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
std::string ScopImporter::getFileName(Scop *S) const {
|
||||
std::string FunctionName =
|
||||
S->getRegion().getEntry()->getParent()->getNameStr();
|
||||
std::string FileName = FunctionName + "___" + S->getNameStr() + ".scop";
|
||||
return FileName;
|
||||
}
|
||||
|
||||
void ScopImporter::printScop(raw_ostream &OS) const {
|
||||
S->print(OS);
|
||||
}
|
||||
|
||||
bool ScopImporter::runOnScop(Scop &scop) {
|
||||
S = &scop;
|
||||
Region &R = scop.getRegion();
|
||||
D = &getAnalysis<Dependences>();
|
||||
|
||||
std::string FileName = ImportDir + "/" + getFileName(S) + ImportPostfix;
|
||||
FILE *F = fopen(FileName.c_str(), "r");
|
||||
|
||||
if (!F) {
|
||||
errs() << "Cannot open file: " << FileName << "\n";
|
||||
errs() << "Skipping import.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
openscop_scop_p openscop = openscop_scop_read(F);
|
||||
fclose(F);
|
||||
|
||||
std::string FunctionName = R.getEntry()->getParent()->getNameStr();
|
||||
errs() << "Reading Scop '" << R.getNameStr() << "' in function '"
|
||||
<< FunctionName << "' from '" << FileName << "'.\n";
|
||||
|
||||
bool UpdateSuccessfull = updateScattering(S, openscop);
|
||||
|
||||
if (!UpdateSuccessfull) {
|
||||
errs() << "Update failed" << "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScopImporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
AU.addRequired<Dependences>();
|
||||
}
|
||||
|
||||
static RegisterPass<ScopImporter> A("polly-import",
|
||||
"Polly - Import Scops with OpenScop library"
|
||||
" (Reads a .scop file for each Scop)"
|
||||
);
|
||||
|
||||
Pass *polly::createScopImporterPass() {
|
||||
return new ScopImporter();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,722 @@
|
|||
//===- ScopLib.cpp - ScopLib interface ------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// ScopLib Interface
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#ifdef SCOPLIB_FOUND
|
||||
|
||||
#include "polly/Dependences.h"
|
||||
#include "polly/ScopLib.h"
|
||||
#include "polly/ScopInfo.h"
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#include "stdio.h"
|
||||
#include "isl/set.h"
|
||||
#include "isl/constraint.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace polly {
|
||||
|
||||
ScopLib::ScopLib(Scop *S) : PollyScop(S) {
|
||||
scoplib = scoplib_scop_malloc();
|
||||
|
||||
initializeArrays();
|
||||
initializeParameters();
|
||||
initializeScattering();
|
||||
initializeStatements();
|
||||
}
|
||||
|
||||
ScopLib::ScopLib(Scop *S, FILE *F, Dependences *dep) : PollyScop(S), D(dep) {
|
||||
scoplib = scoplib_scop_read(F);
|
||||
}
|
||||
|
||||
void ScopLib::initializeParameters() {
|
||||
scoplib->nb_parameters = PollyScop->getNumParams();
|
||||
scoplib->parameters = (char**) malloc(sizeof(char*) * scoplib->nb_parameters);
|
||||
|
||||
for (int i = 0; i < scoplib->nb_parameters; ++i) {
|
||||
scoplib->parameters[i] = (char *) malloc(sizeof(char*) * 20);
|
||||
sprintf(scoplib->parameters[i], "p_%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
void ScopLib::initializeArrays() {
|
||||
int nb_arrays = 0;
|
||||
|
||||
for (Scop::iterator SI = PollyScop->begin(), SE = PollyScop->end(); SI != SE;
|
||||
++SI)
|
||||
for (ScopStmt::memacc_iterator MI = (*SI)->memacc_begin(),
|
||||
ME = (*SI)->memacc_end(); MI != ME; ++MI) {
|
||||
const Value *BaseAddr = (*MI)->getBaseAddr();
|
||||
if (ArrayMap.find(BaseAddr) == ArrayMap.end()) {
|
||||
ArrayMap.insert(std::make_pair(BaseAddr, nb_arrays));
|
||||
++nb_arrays;
|
||||
}
|
||||
}
|
||||
|
||||
scoplib->nb_arrays = nb_arrays;
|
||||
scoplib->arrays = (char**)malloc(sizeof(char*) * nb_arrays);
|
||||
|
||||
for (int i = 0; i < nb_arrays; ++i)
|
||||
for (std::map<const Value*, int>::iterator VI = ArrayMap.begin(),
|
||||
VE = ArrayMap.end(); VI != VE; ++VI)
|
||||
if ((*VI).second == i) {
|
||||
const Value *V = (*VI).first;
|
||||
std::string name = V->getNameStr();
|
||||
scoplib->arrays[i] = (char*) malloc(sizeof(char*) * (name.size() + 1));
|
||||
strcpy(scoplib->arrays[i], name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ScopLib::initializeScattering() {
|
||||
}
|
||||
|
||||
scoplib_statement_p ScopLib::initializeStatement(ScopStmt *stmt) {
|
||||
scoplib_statement_p Stmt = scoplib_statement_malloc();
|
||||
|
||||
// Domain & Schedule
|
||||
Stmt->domain = scoplib_matrix_list_malloc();
|
||||
Stmt->domain->elt = domainToMatrix(stmt->getDomain());
|
||||
Stmt->domain->next = NULL;
|
||||
Stmt->schedule = scatteringToMatrix(stmt->getScattering());
|
||||
|
||||
// Statement name
|
||||
std::string entryName;
|
||||
raw_string_ostream OS(entryName);
|
||||
WriteAsOperand(OS, stmt->getBasicBlock(), false);
|
||||
entryName = OS.str();
|
||||
Stmt->body = (char*)malloc(sizeof(char) * (entryName.size() + 1));
|
||||
strcpy(Stmt->body, entryName.c_str());
|
||||
|
||||
// Iterator names
|
||||
Stmt->nb_iterators = isl_set_n_dim(stmt->getDomain());
|
||||
Stmt->iterators = (char**) malloc(sizeof(char*) * Stmt->nb_iterators);
|
||||
|
||||
for (int i = 0; i < Stmt->nb_iterators; ++i) {
|
||||
Stmt->iterators[i] = (char*) malloc(sizeof(char*) * 20);
|
||||
sprintf(Stmt->iterators[i], "i_%d", i);
|
||||
}
|
||||
|
||||
// Memory Accesses
|
||||
Stmt->read = createAccessMatrix(stmt, true);
|
||||
Stmt->write = createAccessMatrix(stmt, false);
|
||||
|
||||
return Stmt;
|
||||
}
|
||||
|
||||
void ScopLib::initializeStatements() {
|
||||
for (Scop::reverse_iterator SI = PollyScop->rbegin(), SE = PollyScop->rend();
|
||||
SI != SE; ++SI) {
|
||||
|
||||
if ((*SI)->isFinalRead())
|
||||
continue;
|
||||
|
||||
scoplib_statement_p stmt = initializeStatement(*SI);
|
||||
stmt->next = scoplib->statement;
|
||||
scoplib->statement = stmt;
|
||||
}
|
||||
}
|
||||
|
||||
void ScopLib::freeStatement(scoplib_statement_p stmt) {
|
||||
|
||||
if (stmt->read)
|
||||
scoplib_matrix_free(stmt->read);
|
||||
stmt->read = NULL;
|
||||
|
||||
if (stmt->write)
|
||||
scoplib_matrix_free(stmt->write);
|
||||
stmt->write = NULL;
|
||||
|
||||
while (stmt->domain) {
|
||||
scoplib_matrix_free(stmt->domain->elt);
|
||||
stmt->domain = stmt->domain->next;
|
||||
}
|
||||
|
||||
if (stmt->schedule)
|
||||
scoplib_matrix_free(stmt->schedule);
|
||||
stmt->schedule = NULL;
|
||||
|
||||
for (int i = 0; i < stmt->nb_iterators; ++i)
|
||||
free(stmt->iterators[i]);
|
||||
|
||||
free(stmt->iterators);
|
||||
stmt->iterators = NULL;
|
||||
stmt->nb_iterators = 0;
|
||||
|
||||
scoplib_statement_free(stmt);
|
||||
}
|
||||
|
||||
void ScopLib::print(FILE *F) {
|
||||
scoplib_scop_print_dot_scop(F, scoplib);
|
||||
}
|
||||
|
||||
/// Add an isl constraint to an ScopLib matrix.
|
||||
///
|
||||
/// @param user The matrix
|
||||
/// @param c The constraint
|
||||
int ScopLib::domainToMatrix_constraint(isl_constraint *c, void *user) {
|
||||
scoplib_matrix_p m = (scoplib_matrix_p) user;
|
||||
|
||||
int nb_params = isl_constraint_dim(c, isl_dim_param);
|
||||
int nb_vars = isl_constraint_dim(c, isl_dim_set);
|
||||
int nb_div = isl_constraint_dim(c, isl_dim_div);
|
||||
|
||||
assert(!nb_div && "Existentially quantified variables not yet supported");
|
||||
|
||||
scoplib_vector_p vec = scoplib_vector_malloc(nb_params + nb_vars + 2);
|
||||
|
||||
// Assign type
|
||||
if (isl_constraint_is_equality(c))
|
||||
scoplib_vector_tag_equality(vec);
|
||||
else
|
||||
scoplib_vector_tag_inequality(vec);
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// Assign variables
|
||||
for (int i = 0; i < nb_vars; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_set, i, &v);
|
||||
isl_int_set(vec->p[i + 1], v);
|
||||
}
|
||||
|
||||
// Assign parameters
|
||||
for (int i = 0; i < nb_params; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
|
||||
isl_int_set(vec->p[nb_vars + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign constant
|
||||
isl_constraint_get_constant(c, &v);
|
||||
isl_int_set(vec->p[nb_params + nb_vars + 1], v);
|
||||
|
||||
scoplib_matrix_insert_vector(m, vec, m->NbRows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Add an isl basic set to a ScopLib matrix_list
|
||||
///
|
||||
/// @param bset The basic set to add
|
||||
/// @param user The matrix list we should add the basic set to
|
||||
///
|
||||
/// XXX: At the moment this function expects just a matrix, as support
|
||||
/// for matrix lists is currently not available in ScopLib. So union of
|
||||
/// polyhedron are not yet supported
|
||||
int ScopLib::domainToMatrix_basic_set(isl_basic_set *bset, void *user) {
|
||||
scoplib_matrix_p m = (scoplib_matrix_p) user;
|
||||
assert(!m->NbRows && "Union of polyhedron not yet supported");
|
||||
|
||||
isl_basic_set_foreach_constraint(bset, &domainToMatrix_constraint, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Translate a isl_set to a ScopLib matrix.
|
||||
///
|
||||
/// @param PS The set to be translated
|
||||
/// @return A ScopLib Matrix
|
||||
scoplib_matrix_p ScopLib::domainToMatrix(isl_set *PS) {
|
||||
|
||||
// Create a canonical copy of this set.
|
||||
isl_set *set = isl_set_copy(PS);
|
||||
set = isl_set_compute_divs (set);
|
||||
set = isl_set_align_divs (set);
|
||||
|
||||
// Initialize the matrix.
|
||||
unsigned NbRows, NbColumns;
|
||||
NbRows = 0;
|
||||
NbColumns = isl_set_n_dim(PS) + isl_set_n_param(PS) + 2;
|
||||
scoplib_matrix_p matrix = scoplib_matrix_malloc(NbRows, NbColumns);
|
||||
|
||||
// Copy the content into the matrix.
|
||||
isl_set_foreach_basic_set(set, &domainToMatrix_basic_set, matrix);
|
||||
|
||||
isl_set_free(set);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/// Add an isl constraint to an ScopLib matrix.
|
||||
///
|
||||
/// @param user The matrix
|
||||
/// @param c The constraint
|
||||
int ScopLib::scatteringToMatrix_constraint(isl_constraint *c, void *user) {
|
||||
scoplib_matrix_p m = (scoplib_matrix_p) user;
|
||||
|
||||
int nb_params = isl_constraint_dim(c, isl_dim_param);
|
||||
int nb_in = isl_constraint_dim(c, isl_dim_in);
|
||||
int nb_div = isl_constraint_dim(c, isl_dim_div);
|
||||
|
||||
assert(!nb_div && "Existentially quantified variables not yet supported");
|
||||
|
||||
scoplib_vector_p vec =
|
||||
scoplib_vector_malloc(nb_params + nb_in + 2);
|
||||
|
||||
// Assign type
|
||||
if (isl_constraint_is_equality(c))
|
||||
scoplib_vector_tag_equality(vec);
|
||||
else
|
||||
scoplib_vector_tag_inequality(vec);
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// Assign variables
|
||||
for (int i = 0; i < nb_in; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_in, i, &v);
|
||||
isl_int_set(vec->p[i + 1], v);
|
||||
}
|
||||
|
||||
// Assign parameters
|
||||
for (int i = 0; i < nb_params; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
|
||||
isl_int_set(vec->p[nb_in + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign constant
|
||||
isl_constraint_get_constant(c, &v);
|
||||
isl_int_set(vec->p[nb_in + nb_params + 1], v);
|
||||
|
||||
scoplib_vector_p null =
|
||||
scoplib_vector_malloc(nb_params + nb_in + 2);
|
||||
|
||||
vec = scoplib_vector_sub(null, vec);
|
||||
scoplib_matrix_insert_vector(m, vec, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Add an isl basic map to a ScopLib matrix_list
|
||||
///
|
||||
/// @param bmap The basic map to add
|
||||
/// @param user The matrix list we should add the basic map to
|
||||
///
|
||||
/// XXX: At the moment this function expects just a matrix, as support
|
||||
/// for matrix lists is currently not available in ScopLib. So union of
|
||||
/// polyhedron are not yet supported
|
||||
int ScopLib::scatteringToMatrix_basic_map(isl_basic_map *bmap, void *user) {
|
||||
scoplib_matrix_p m = (scoplib_matrix_p) user;
|
||||
assert(!m->NbRows && "Union of polyhedron not yet supported");
|
||||
|
||||
isl_basic_map_foreach_constraint(bmap, &scatteringToMatrix_constraint, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Translate a isl_map to a ScopLib matrix.
|
||||
///
|
||||
/// @param map The map to be translated
|
||||
/// @return A ScopLib Matrix
|
||||
scoplib_matrix_p ScopLib::scatteringToMatrix(isl_map *pmap) {
|
||||
|
||||
// Create a canonical copy of this set.
|
||||
isl_map *map = isl_map_copy(pmap);
|
||||
map = isl_map_compute_divs (map);
|
||||
map = isl_map_align_divs (map);
|
||||
|
||||
// Initialize the matrix.
|
||||
unsigned NbRows, NbColumns;
|
||||
NbRows = 0;
|
||||
NbColumns = isl_map_n_in(pmap) + isl_map_n_param(pmap) + 2;
|
||||
scoplib_matrix_p matrix = scoplib_matrix_malloc(NbRows, NbColumns);
|
||||
|
||||
// Copy the content into the matrix.
|
||||
isl_map_foreach_basic_map(map, &scatteringToMatrix_basic_map, matrix);
|
||||
|
||||
// Only keep the relevant rows.
|
||||
scoplib_matrix_p reduced = scoplib_matrix_ncopy(matrix,
|
||||
isl_map_n_in(pmap) * 2 + 1);
|
||||
|
||||
scoplib_matrix_free (matrix);
|
||||
isl_map_free(map);
|
||||
|
||||
return reduced;
|
||||
}
|
||||
|
||||
/// Add an isl constraint to an ScopLib matrix.
|
||||
///
|
||||
/// @param user The matrix
|
||||
/// @param c The constraint
|
||||
int ScopLib::accessToMatrix_constraint(isl_constraint *c, void *user) {
|
||||
scoplib_matrix_p m = (scoplib_matrix_p) user;
|
||||
|
||||
int nb_params = isl_constraint_dim(c, isl_dim_param);
|
||||
int nb_in = isl_constraint_dim(c, isl_dim_in);
|
||||
int nb_div = isl_constraint_dim(c, isl_dim_div);
|
||||
|
||||
assert(!nb_div && "Existentially quantified variables not yet supported");
|
||||
|
||||
scoplib_vector_p vec =
|
||||
scoplib_vector_malloc(nb_params + nb_in + 2);
|
||||
|
||||
isl_int v;
|
||||
isl_int_init(v);
|
||||
|
||||
// The access dimension has to be one.
|
||||
isl_constraint_get_coefficient(c, isl_dim_out, 0, &v);
|
||||
assert(isl_int_is_one(v));
|
||||
bool inverse = true ;
|
||||
|
||||
// Assign variables
|
||||
for (int i = 0; i < nb_in; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_in, i, &v);
|
||||
|
||||
if (inverse) isl_int_neg(v,v);
|
||||
|
||||
isl_int_set(vec->p[i + 1], v);
|
||||
}
|
||||
|
||||
// Assign parameters
|
||||
for (int i = 0; i < nb_params; ++i) {
|
||||
isl_constraint_get_coefficient(c, isl_dim_param, i, &v);
|
||||
|
||||
if (inverse) isl_int_neg(v,v);
|
||||
|
||||
isl_int_set(vec->p[nb_in + i + 1], v);
|
||||
}
|
||||
|
||||
// Assign constant
|
||||
isl_constraint_get_constant(c, &v);
|
||||
|
||||
if (inverse) isl_int_neg(v,v);
|
||||
|
||||
isl_int_set(vec->p[nb_in + nb_params + 1], v);
|
||||
|
||||
scoplib_matrix_insert_vector(m, vec, m->NbRows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Add an isl basic map to a ScopLib matrix_list
|
||||
///
|
||||
/// @param bmap The basic map to add
|
||||
/// @param user The matrix list we should add the basic map to
|
||||
///
|
||||
/// XXX: At the moment this function expects just a matrix, as support
|
||||
/// for matrix lists is currently not available in ScopLib. So union of
|
||||
/// polyhedron are not yet supported
|
||||
int ScopLib::accessToMatrix_basic_map(isl_basic_map *bmap, void *user) {
|
||||
isl_basic_map_foreach_constraint(bmap, &accessToMatrix_constraint, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Create the memory access matrix for scoplib
|
||||
///
|
||||
/// @param S The polly statement the access matrix is created for.
|
||||
/// @param isRead Are we looking for read or write accesses?
|
||||
/// @param ArrayMap A map translating from the memory references to the scoplib
|
||||
/// indeces
|
||||
///
|
||||
/// @return The memory access matrix, as it is required by scoplib.
|
||||
scoplib_matrix_p ScopLib::createAccessMatrix(ScopStmt *S, bool isRead) {
|
||||
|
||||
unsigned NbColumns = S->getNumIterators() + S->getNumParams() + 2;
|
||||
scoplib_matrix_p m = scoplib_matrix_malloc(0, NbColumns);
|
||||
|
||||
for (ScopStmt::memacc_iterator MI = S->memacc_begin(), ME = S->memacc_end();
|
||||
MI != ME; ++MI)
|
||||
if ((*MI)->isRead() == isRead) {
|
||||
// Extract the access function.
|
||||
isl_map_foreach_basic_map((*MI)->getAccessFunction(),
|
||||
&accessToMatrix_basic_map, m);
|
||||
|
||||
// Set the index of the memory access base element.
|
||||
std::map<const Value*, int>::iterator BA =
|
||||
ArrayMap.find((*MI)->getBaseAddr());
|
||||
isl_int_set_si(m->p[m->NbRows - 1][0], (*BA).second + 1);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
ScopLib::~ScopLib() {
|
||||
if (!scoplib)
|
||||
return;
|
||||
|
||||
// Free array names.
|
||||
for (int i = 0; i < scoplib->nb_arrays; ++i)
|
||||
free(scoplib->arrays[i]);
|
||||
|
||||
free(scoplib->arrays);
|
||||
scoplib->arrays = NULL;
|
||||
scoplib->nb_arrays = 0;
|
||||
|
||||
// Free parameters
|
||||
for (int i = 0; i < scoplib->nb_parameters; ++i)
|
||||
free(scoplib->parameters[i]);
|
||||
|
||||
free(scoplib->parameters);
|
||||
scoplib->parameters = NULL;
|
||||
scoplib->nb_parameters = 0;
|
||||
|
||||
scoplib_statement_p stmt = scoplib->statement;
|
||||
|
||||
// Free Statements
|
||||
while (stmt) {
|
||||
scoplib_statement_p TempStmt = stmt->next;
|
||||
stmt->next = NULL;
|
||||
freeStatement(stmt);
|
||||
stmt = TempStmt;
|
||||
}
|
||||
|
||||
scoplib->statement = NULL;
|
||||
|
||||
scoplib_scop_free(scoplib);
|
||||
}
|
||||
/// @brief Create an isl constraint from a row of OpenScop integers.
|
||||
///
|
||||
/// @param row An array of isl/OpenScop integers.
|
||||
/// @param dim An isl dim object, describing how to spilt the dimensions.
|
||||
///
|
||||
/// @return An isl constraint representing this integer array.
|
||||
isl_constraint *constraintFromMatrixRow(isl_int *row, isl_dim *dim) {
|
||||
isl_constraint *c;
|
||||
|
||||
unsigned NbIn = isl_dim_size(dim, isl_dim_in);
|
||||
unsigned NbParam = isl_dim_size(dim, isl_dim_param);
|
||||
|
||||
if (isl_int_is_zero(row[0]))
|
||||
c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
else
|
||||
c = isl_inequality_alloc(isl_dim_copy(dim));
|
||||
|
||||
unsigned current_column = 1;
|
||||
|
||||
for (unsigned j = 0; j < NbIn; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, j, row[current_column++]);
|
||||
|
||||
for (unsigned j = 0; j < NbParam; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_param, j, row[current_column++]);
|
||||
|
||||
isl_constraint_set_constant(c, row[current_column]);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @brief Create an isl map from a OpenScop matrix.
|
||||
///
|
||||
/// @param m The OpenScop matrix to translate.
|
||||
/// @param dim The dimensions that are contained in the OpenScop matrix.
|
||||
///
|
||||
/// @return An isl map representing m.
|
||||
isl_map *mapFromMatrix(scoplib_matrix_p m, isl_dim *dim,
|
||||
unsigned scatteringDims) {
|
||||
isl_basic_map *bmap = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
|
||||
for (unsigned i = 0; i < m->NbRows; ++i) {
|
||||
isl_constraint *c;
|
||||
|
||||
c = constraintFromMatrixRow(m->p[i], dim);
|
||||
|
||||
mpz_t minusOne;
|
||||
mpz_init(minusOne);
|
||||
mpz_set_si(minusOne, -1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, i, minusOne);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
for (unsigned i = m->NbRows; i < scatteringDims; i++) {
|
||||
isl_constraint *c;
|
||||
|
||||
c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
|
||||
mpz_t One;
|
||||
mpz_init(One);
|
||||
mpz_set_si(One, 1);
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, i, One);
|
||||
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
return isl_map_from_basic_map(bmap);
|
||||
}
|
||||
/// @brief Create an isl constraint from a row of OpenScop integers.
|
||||
///
|
||||
/// @param row An array of isl/OpenScop integers.
|
||||
/// @param dim An isl dim object, describing how to spilt the dimensions.
|
||||
///
|
||||
/// @return An isl constraint representing this integer array.
|
||||
isl_constraint *constraintFromMatrixRowFull(isl_int *row, isl_dim *dim) {
|
||||
isl_constraint *c;
|
||||
|
||||
unsigned NbOut = isl_dim_size(dim, isl_dim_out);
|
||||
unsigned NbIn = isl_dim_size(dim, isl_dim_in);
|
||||
unsigned NbParam = isl_dim_size(dim, isl_dim_param);
|
||||
|
||||
if (isl_int_is_zero(row[0]))
|
||||
c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
else
|
||||
c = isl_inequality_alloc(isl_dim_copy(dim));
|
||||
|
||||
unsigned current_column = 1;
|
||||
|
||||
for (unsigned j = 0; j < NbOut; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_out, j, row[current_column++]);
|
||||
|
||||
for (unsigned j = 0; j < NbIn; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_in, j, row[current_column++]);
|
||||
|
||||
for (unsigned j = 0; j < NbParam; ++j)
|
||||
isl_constraint_set_coefficient(c, isl_dim_param, j, row[current_column++]);
|
||||
|
||||
isl_constraint_set_constant(c, row[current_column]);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @brief Create an isl map from a OpenScop matrix.
|
||||
///
|
||||
/// @param m The OpenScop matrix to translate.
|
||||
/// @param dim The dimensions that are contained in the OpenScop matrix.
|
||||
///
|
||||
/// @return An isl map representing m.
|
||||
isl_map *mapFromMatrix(scoplib_matrix_p m, isl_dim *dim) {
|
||||
isl_basic_map *bmap = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
|
||||
for (unsigned i = 0; i < m->NbRows; ++i) {
|
||||
isl_constraint *c;
|
||||
|
||||
c = constraintFromMatrixRowFull(m->p[i], dim);
|
||||
bmap = isl_basic_map_add_constraint(bmap, c);
|
||||
}
|
||||
|
||||
return isl_map_from_basic_map(bmap);
|
||||
}
|
||||
|
||||
/// @brief Create a new scattering for PollyStmt.
|
||||
///
|
||||
/// @param m The matrix describing the new scattering.
|
||||
/// @param PollyStmt The statement to create the scattering for.
|
||||
///
|
||||
/// @return An isl_map describing the scattering.
|
||||
isl_map *scatteringForStmt(scoplib_matrix_p m, ScopStmt *PollyStmt,
|
||||
int scatteringDims) {
|
||||
|
||||
unsigned NbParam = PollyStmt->getNumParams();
|
||||
unsigned NbIterators = PollyStmt->getNumIterators();
|
||||
unsigned NbScattering;
|
||||
|
||||
if (scatteringDims == -1)
|
||||
NbScattering = m->NbColumns - 2 - NbParam - NbIterators;
|
||||
else
|
||||
NbScattering = scatteringDims;
|
||||
|
||||
isl_ctx *ctx = PollyStmt->getParent()->getCtx();
|
||||
isl_dim *dim = isl_dim_alloc(ctx, NbParam, NbIterators, NbScattering);
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_out, "scattering");
|
||||
dim = isl_dim_set_tuple_name(dim, isl_dim_in, PollyStmt->getBaseName());
|
||||
|
||||
isl_map *map;
|
||||
|
||||
if (scatteringDims == -1)
|
||||
map = mapFromMatrix(m, dim);
|
||||
else
|
||||
map = mapFromMatrix(m, dim, scatteringDims);
|
||||
|
||||
isl_dim_free(dim);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
unsigned maxScattering(scoplib_statement_p stmt) {
|
||||
unsigned max = 0;
|
||||
|
||||
while (stmt) {
|
||||
max = std::max(max, stmt->schedule->NbRows);
|
||||
stmt = stmt->next;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
|
||||
|
||||
/// @brief Read the new scattering from the scoplib description.
|
||||
///
|
||||
/// @S The Scop to update
|
||||
/// @OScop The ScopLib data structure describing the new scattering.
|
||||
/// @return A map that contains for each Statement the new scattering.
|
||||
StatementToIslMapTy *readScattering(Scop *S, scoplib_scop_p OScop) {
|
||||
StatementToIslMapTy &NewScattering = *(new StatementToIslMapTy());
|
||||
|
||||
scoplib_statement_p stmt = OScop->statement;
|
||||
|
||||
// Check if we have dimensions for each scattering or if each row
|
||||
// represents a scattering dimension.
|
||||
int numScatteringDims = -1;
|
||||
ScopStmt *pollyStmt = *S->begin();
|
||||
|
||||
if (stmt->schedule->NbColumns
|
||||
== 2 + pollyStmt->getNumParams() + pollyStmt->getNumIterators()) {
|
||||
numScatteringDims = maxScattering(stmt);
|
||||
}
|
||||
|
||||
for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) {
|
||||
|
||||
if ((*SI)->isFinalRead())
|
||||
continue;
|
||||
|
||||
if (!stmt) {
|
||||
errs() << "Not enough statements available in OpenScop file\n";
|
||||
delete &NewScattering;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NewScattering[*SI] = scatteringForStmt(stmt->schedule, *SI,
|
||||
numScatteringDims);
|
||||
stmt = stmt->next;
|
||||
}
|
||||
|
||||
if (stmt) {
|
||||
errs() << "Too many statements in OpenScop file\n";
|
||||
delete &NewScattering;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &NewScattering;
|
||||
}
|
||||
|
||||
/// @brief Update the scattering in a Scop using the scoplib description of
|
||||
/// the scattering.
|
||||
bool ScopLib::updateScattering() {
|
||||
if (!scoplib)
|
||||
return false;
|
||||
|
||||
StatementToIslMapTy *NewScattering = readScattering(PollyScop, scoplib);
|
||||
|
||||
if (!NewScattering)
|
||||
return false;
|
||||
|
||||
if (!D->isValidScattering(NewScattering)) {
|
||||
errs() << "OpenScop file contains a scattering that changes the "
|
||||
<< "dependences. Use -disable-polly-legality to continue anyways\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Scop::iterator SI = PollyScop->begin(), SE = PollyScop->end(); SI != SE;
|
||||
++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
|
||||
if (NewScattering->find(Stmt) != NewScattering->end())
|
||||
Stmt->setScattering((*NewScattering)[Stmt]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,99 @@
|
|||
//===-- ScopLibExporter.cpp - Export Scops with scoplib ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Export the Scops build by ScopInfo pass to text file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#ifdef SCOPLIB_FOUND
|
||||
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/ScopPass.h"
|
||||
#include "polly/ScopLib.h"
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#include "stdio.h"
|
||||
#include "isl/set.h"
|
||||
#include "isl/constraint.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
static cl::opt<std::string>
|
||||
ExportDir("polly-export-scoplib-dir",
|
||||
cl::desc("The directory to export the .scoplib files to."),
|
||||
cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
|
||||
cl::init("."));
|
||||
|
||||
class ScopLibExporter : public ScopPass {
|
||||
Scop *S;
|
||||
|
||||
std::string getFileName(Scop *S) const;
|
||||
public:
|
||||
static char ID;
|
||||
explicit ScopLibExporter() : ScopPass(ID) {}
|
||||
|
||||
virtual bool runOnScop(Scop &scop);
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
char ScopLibExporter::ID = 0;
|
||||
|
||||
std::string ScopLibExporter::getFileName(Scop *S) const {
|
||||
std::string FunctionName =
|
||||
S->getRegion().getEntry()->getParent()->getNameStr();
|
||||
std::string FileName = FunctionName + "___" + S->getNameStr() + ".scoplib";
|
||||
return FileName;
|
||||
}
|
||||
|
||||
bool ScopLibExporter::runOnScop(Scop &scop) {
|
||||
S = &scop;
|
||||
Region *R = &S->getRegion();
|
||||
|
||||
std::string FileName = ExportDir + "/" + getFileName(S);
|
||||
FILE *F = fopen(FileName.c_str(), "w");
|
||||
|
||||
if (!F) {
|
||||
errs() << "Cannot open file: " << FileName << "\n";
|
||||
errs() << "Skipping export.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopLib scoplib(S);
|
||||
scoplib.print(F);
|
||||
fclose(F);
|
||||
|
||||
std::string FunctionName = R->getEntry()->getParent()->getNameStr();
|
||||
errs() << "Writing Scop '" << R->getNameStr() << "' in function '"
|
||||
<< FunctionName << "' to '" << FileName << "'.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScopLibExporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
static RegisterPass<ScopLibExporter> A("polly-export-scoplib",
|
||||
"Polly - Export Scops with ScopLib library"
|
||||
" (Writes a .scoplib file for each Scop)"
|
||||
);
|
||||
|
||||
Pass *polly::createScopLibExporterPass() {
|
||||
return new ScopLibExporter();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,120 @@
|
|||
//===-- ScopLibImporter.cpp - Import Scops with scoplib. -----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Import modified .scop files into Polly. This allows to change the schedule of
|
||||
// statements.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#ifdef SCOPLIB_FOUND
|
||||
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/ScopLib.h"
|
||||
#include "polly/Dependences.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#define SCOPLIB_INT_T_IS_MP
|
||||
#include "scoplib/scop.h"
|
||||
|
||||
#include "isl/set.h"
|
||||
#include "isl/constraint.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
static cl::opt<std::string>
|
||||
ImportDir("polly-import-scoplib-dir",
|
||||
cl::desc("The directory to import the .scoplib files from."),
|
||||
cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
|
||||
cl::init("."));
|
||||
static cl::opt<std::string>
|
||||
ImportPostfix("polly-import-scoplib-postfix",
|
||||
cl::desc("Postfix to append to the import .scoplib files."),
|
||||
cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
|
||||
cl::init(""));
|
||||
|
||||
struct ScopLibImporter : public RegionPass {
|
||||
static char ID;
|
||||
Scop *S;
|
||||
Dependences *D;
|
||||
explicit ScopLibImporter() : RegionPass(ID) {}
|
||||
|
||||
bool updateScattering(Scop *S, scoplib_scop_p OScop);
|
||||
std::string getFileName(Scop *S) const;
|
||||
virtual bool runOnRegion(Region *R, RGPassManager &RGM);
|
||||
virtual void print(raw_ostream &OS, const Module *) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
}
|
||||
|
||||
char ScopLibImporter::ID = 0;
|
||||
|
||||
namespace {
|
||||
std::string ScopLibImporter::getFileName(Scop *S) const {
|
||||
std::string FunctionName =
|
||||
S->getRegion().getEntry()->getParent()->getNameStr();
|
||||
std::string FileName = FunctionName + "___" + S->getNameStr() + ".scoplib";
|
||||
return FileName;
|
||||
}
|
||||
|
||||
void ScopLibImporter::print(raw_ostream &OS, const Module *) const {}
|
||||
|
||||
bool ScopLibImporter::runOnRegion(Region *R, RGPassManager &RGM) {
|
||||
S = getAnalysis<ScopInfo>().getScop();
|
||||
D = &getAnalysis<Dependences>();
|
||||
|
||||
if (!S)
|
||||
return false;
|
||||
|
||||
std::string FileName = ImportDir + "/" + getFileName(S) + ImportPostfix;
|
||||
FILE *F = fopen(FileName.c_str(), "r");
|
||||
|
||||
if (!F) {
|
||||
errs() << "Cannot open file: " << FileName << "\n";
|
||||
errs() << "Skipping import.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string FunctionName = R->getEntry()->getParent()->getNameStr();
|
||||
errs() << "Reading Scop '" << R->getNameStr() << "' in function '"
|
||||
<< FunctionName << "' from '" << FileName << "'.\n";
|
||||
|
||||
ScopLib scoplib(S, F, D);
|
||||
bool UpdateSuccessfull = scoplib.updateScattering();
|
||||
fclose(F);
|
||||
|
||||
if (!UpdateSuccessfull) {
|
||||
errs() << "Update failed" << "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScopLibImporter::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.setPreservesAll();
|
||||
AU.addRequired<ScopInfo>();
|
||||
AU.addRequired<Dependences>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static RegisterPass<ScopLibImporter> A("polly-import-scoplib",
|
||||
"Polly - Import Scops with ScopLib library"
|
||||
" (Reads a .scoplib file for each Scop)"
|
||||
);
|
||||
|
||||
Pass *polly::createScopLibImporterPass() {
|
||||
return new ScopLibImporter();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,550 @@
|
|||
//===------ IndependentBlocks.cpp - Create Independent Blocks in Regions --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Create independent blocks in the regions detected by ScopDetection.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
#include "polly/LinkAllPasses.h"
|
||||
#include "polly/ScopDetection.h"
|
||||
#include "polly/Support/ScopHelper.h"
|
||||
#include "polly/Cloog.h"
|
||||
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/RegionInfo.h"
|
||||
#include "llvm/Analysis/RegionPass.h"
|
||||
#include "llvm/Analysis/RegionIterator.h"
|
||||
#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-independent"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace polly;
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
struct IndependentBlocks : public FunctionPass {
|
||||
RegionInfo *RI;
|
||||
ScalarEvolution *SE;
|
||||
ScopDetection *SD;
|
||||
LoopInfo *LI;
|
||||
|
||||
BasicBlock *AllocaBlock;
|
||||
|
||||
static char ID;
|
||||
|
||||
IndependentBlocks() : FunctionPass(ID) {}
|
||||
|
||||
// Create new code for every instruction operator that can be expressed by a
|
||||
// SCEV. Like this there are just two types of instructions left:
|
||||
//
|
||||
// 1. Instructions that only reference loop ivs or parameters outside the
|
||||
// region.
|
||||
//
|
||||
// 2. Instructions that are not used for any memory modification. (These
|
||||
// will be ignored later on.)
|
||||
//
|
||||
// Blocks containing only these kind of instructions are called independent
|
||||
// blocks as they can be scheduled arbitrarily.
|
||||
bool createIndependentBlocks(BasicBlock *BB, const Region *R);
|
||||
bool createIndependentBlocks(const Region *R);
|
||||
|
||||
// Elimination on the Scop to eliminate the scalar dependences come with
|
||||
// trivially dead instructions.
|
||||
bool eliminateDeadCode(const Region *R);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
/// Non trivial scalar dependences checking functions.
|
||||
/// Non trivial scalar dependences occur when the def and use are located in
|
||||
/// different BBs and we can not move them into the same one. This will
|
||||
/// prevent use from schedule BBs arbitrarily.
|
||||
///
|
||||
/// @brief This function checks if a scalar value that is part of the
|
||||
/// Scop is used outside of the Scop.
|
||||
///
|
||||
/// @param Use The use of the instruction.
|
||||
/// @param R The maximum region in the Scop.
|
||||
///
|
||||
/// @return Return true if the Use of an instruction and the instruction
|
||||
/// itself form a non trivial scalar dependence.
|
||||
static bool isEscapeUse(const Value *Use, const Region *R);
|
||||
|
||||
/// @brief This function just checks if a Value is either defined in the same
|
||||
/// basic block or outside the region, such that there are no scalar
|
||||
/// dependences between basic blocks that are both part of the same
|
||||
/// region.
|
||||
///
|
||||
/// @param Operand The operand of the instruction.
|
||||
/// @param CurBB The BasicBlock that contains the instruction.
|
||||
/// @param R The maximum region in the Scop.
|
||||
///
|
||||
/// @return Return true if the Operand of an instruction and the instruction
|
||||
/// itself form a non trivial scalar (true) dependence.
|
||||
bool isEscapeOperand(const Value *Operand, const BasicBlock *CurBB,
|
||||
const Region *R) const;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
/// Operand tree moving functions.
|
||||
/// Trivial scalar dependences can eliminate by move the def to the same BB
|
||||
/// that containing use.
|
||||
///
|
||||
/// @brief Check if the instruction can be moved to another place safely.
|
||||
///
|
||||
/// @param Inst The instruction.
|
||||
///
|
||||
/// @return Return true if the instruction can be moved safely, false
|
||||
/// otherwise.
|
||||
static bool isSafeToMove(Instruction *Inst);
|
||||
|
||||
typedef std::map<Instruction*, Instruction*> ReplacedMapType;
|
||||
|
||||
/// @brief Move all safe to move instructions in the Operand Tree (DAG) to
|
||||
/// eliminate trivial scalar dependences.
|
||||
///
|
||||
/// @param Inst The root of the operand Tree.
|
||||
/// @param R The maximum region in the Scop.
|
||||
/// @param ReplacedMap The map that mapping original instruction to the moved
|
||||
/// instruction.
|
||||
/// @param InsertPos The insert position of the moved instructions.
|
||||
void moveOperandTree(Instruction *Inst, const Region *R,
|
||||
ReplacedMapType &ReplacedMap,
|
||||
Instruction *InsertPos);
|
||||
|
||||
bool isIndependentBlock(const Region *R, BasicBlock *BB) const;
|
||||
bool areAllBlocksIndependent(const Region *R) const;
|
||||
|
||||
// Split the exit block to hold load instructions.
|
||||
bool splitExitBlock(Region *R);
|
||||
|
||||
bool translateScalarToArray(BasicBlock *BB, const Region *R);
|
||||
bool translateScalarToArray(Instruction *Inst, const Region *R);
|
||||
bool translateScalarToArray(const Region *R);
|
||||
|
||||
bool runOnFunction(Function &F);
|
||||
void verifyAnalysis() const;
|
||||
void verifyScop(const Region *R) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
}
|
||||
|
||||
bool IndependentBlocks::isSafeToMove(Instruction *Inst) {
|
||||
if (Inst->mayReadFromMemory() ||
|
||||
Inst->mayWriteToMemory())
|
||||
return false;
|
||||
|
||||
return Inst->isSafeToSpeculativelyExecute();
|
||||
}
|
||||
|
||||
void IndependentBlocks::moveOperandTree(Instruction *Inst, const Region *R,
|
||||
ReplacedMapType &ReplacedMap,
|
||||
Instruction *InsertPos) {
|
||||
BasicBlock *CurBB = Inst->getParent();
|
||||
|
||||
// Depth first traverse the operand tree (or operand dag, because we will
|
||||
// stop at PHINodes, so there are no cycle).
|
||||
typedef Instruction::op_iterator ChildIt;
|
||||
std::vector<std::pair<Instruction*, ChildIt> > WorkStack;
|
||||
|
||||
WorkStack.push_back(std::make_pair(Inst, Inst->op_begin()));
|
||||
|
||||
while (!WorkStack.empty()) {
|
||||
Instruction *CurInst = WorkStack.back().first;
|
||||
ChildIt It = WorkStack.back().second;
|
||||
DEBUG(dbgs() << "Checking Operand of Node:\n" << *CurInst << "\n------>\n");
|
||||
if (It == CurInst->op_end()) {
|
||||
// Insert the new instructions in topological order.
|
||||
if (!CurInst->getParent())
|
||||
CurInst->insertBefore(InsertPos);
|
||||
|
||||
WorkStack.pop_back();
|
||||
} else {
|
||||
// for each node N,
|
||||
Instruction *Operand = dyn_cast<Instruction>(*It);
|
||||
++WorkStack.back().second;
|
||||
|
||||
// Can not move no instruction value.
|
||||
if (Operand == 0) continue;
|
||||
|
||||
DEBUG(dbgs() << "For Operand:\n" << *Operand << "\n--->");
|
||||
|
||||
// If the Scop Region does not contain N, skip it and all its operand and
|
||||
// continue. because we reach a "parameter".
|
||||
// FIXME: we must keep the predicate instruction inside the Scop, otherwise
|
||||
// it will be translated to a load instruction, and we can not handle load
|
||||
// as affine predicate at this moment.
|
||||
if (!R->contains(Operand) && !isa<TerminatorInst>(CurInst)) {
|
||||
DEBUG(dbgs() << "Out of region.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// No need to move induction variable.
|
||||
if (isIndVar(Operand, LI)) {
|
||||
DEBUG(dbgs() << "is IV.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can not move the operand, a non trivial scalar dependence found!
|
||||
if (!isSafeToMove(Operand)) {
|
||||
DEBUG(dbgs() << "Can not move!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not need to move instruction if it contained in the same BB with
|
||||
// the root instruction.
|
||||
// FIXME: Remember this in visited Map.
|
||||
if (Operand->getParent() == CurBB) {
|
||||
DEBUG(dbgs() << "No need to move.\n");
|
||||
// Try to move its operand.
|
||||
WorkStack.push_back(std::make_pair(Operand, Operand->op_begin()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now we need to move Operand to CurBB.
|
||||
// Check if we already moved it.
|
||||
ReplacedMapType::iterator At = ReplacedMap.find(Operand);
|
||||
if (At != ReplacedMap.end()) {
|
||||
DEBUG(dbgs() << "Moved.\n");
|
||||
Instruction *MovedOp = At->second;
|
||||
It->set(MovedOp);
|
||||
// Skip all its children as we already processed them.
|
||||
continue;
|
||||
} else {
|
||||
// Note that NewOp is not inserted in any BB now, we will insert it when
|
||||
// it popped form the work stack, so it will be inserted in topological
|
||||
// order.
|
||||
Instruction *NewOp = Operand->clone();
|
||||
NewOp->setName(Operand->getName() + ".moved.to." + CurBB->getName());
|
||||
DEBUG(dbgs() << "Move to " << *NewOp << "\n");
|
||||
It->set(NewOp);
|
||||
ReplacedMap.insert(std::make_pair(Operand, NewOp));
|
||||
// Process its operands.
|
||||
WorkStack.push_back(std::make_pair(NewOp, NewOp->op_begin()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SE->forgetValue(Inst);
|
||||
}
|
||||
|
||||
bool IndependentBlocks::createIndependentBlocks(BasicBlock *BB,
|
||||
const Region *R) {
|
||||
std::vector<Instruction*> WorkList;
|
||||
for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE; ++II)
|
||||
if (!isSafeToMove(II) && !isIndVar(II, LI))
|
||||
WorkList.push_back(II);
|
||||
|
||||
ReplacedMapType ReplacedMap;
|
||||
Instruction *InsertPos = BB->getFirstNonPHIOrDbg();
|
||||
|
||||
for (std::vector<Instruction*>::iterator I = WorkList.begin(),
|
||||
E = WorkList.end(); I != E; ++I)
|
||||
moveOperandTree(*I, R, ReplacedMap, InsertPos);
|
||||
|
||||
// The BB was changed if we replaced any operand.
|
||||
return !ReplacedMap.empty();
|
||||
}
|
||||
|
||||
bool IndependentBlocks::createIndependentBlocks(const Region *R) {
|
||||
bool Changed = false;
|
||||
|
||||
for (Region::const_block_iterator SI = R->block_begin(), SE = R->block_end();
|
||||
SI != SE; ++SI)
|
||||
Changed |= createIndependentBlocks((*SI)->getNodeAs<BasicBlock>(), R);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::eliminateDeadCode(const Region *R) {
|
||||
std::vector<Instruction*> WorkList;
|
||||
|
||||
// Find all trivially dead instructions.
|
||||
for (Region::const_block_iterator SI = R->block_begin(), SE = R->block_end();
|
||||
SI != SE; ++SI) {
|
||||
BasicBlock *BB = (*SI)->getNodeAs<BasicBlock>();
|
||||
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I)
|
||||
if (isInstructionTriviallyDead(I))
|
||||
WorkList.push_back(I);
|
||||
}
|
||||
|
||||
if (WorkList.empty()) return false;
|
||||
|
||||
// Delete them so the cross BB scalar dependences come with them will
|
||||
// also be eliminated.
|
||||
while (!WorkList.empty()) {
|
||||
RecursivelyDeleteTriviallyDeadInstructions(WorkList.back());
|
||||
WorkList.pop_back();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::isEscapeUse(const Value *Use, const Region *R) {
|
||||
// Non-instruction user will never escape.
|
||||
if (!isa<Instruction>(Use)) return false;
|
||||
|
||||
return !R->contains(cast<Instruction>(Use));
|
||||
}
|
||||
|
||||
bool IndependentBlocks::isEscapeOperand(const Value *Operand,
|
||||
const BasicBlock *CurBB,
|
||||
const Region *R) const {
|
||||
const Instruction *OpInst = dyn_cast<Instruction>(Operand);
|
||||
|
||||
// Non-instruction operand will never escape.
|
||||
if (OpInst == 0) return false;
|
||||
|
||||
// Induction variables are valid operands.
|
||||
if (isIndVar(OpInst, LI)) return false;
|
||||
|
||||
// A value from a different BB is used in the same region.
|
||||
return R->contains(OpInst) && (OpInst->getParent() != CurBB);
|
||||
}
|
||||
|
||||
bool IndependentBlocks::splitExitBlock(Region *R) {
|
||||
// Split the exit BB to place the load instruction of escaped users.
|
||||
BasicBlock *ExitBB = R->getExit();
|
||||
Region *ExitRegion = RI->getRegionFor(ExitBB);
|
||||
|
||||
if (ExitBB != ExitRegion->getEntry())
|
||||
return false;
|
||||
|
||||
BasicBlock *NewExit = createSingleExitEdge(R, this);
|
||||
|
||||
std::vector<Region*> toUpdate;
|
||||
toUpdate.push_back(R);
|
||||
|
||||
while (!toUpdate.empty()) {
|
||||
Region *Reg = toUpdate.back();
|
||||
toUpdate.pop_back();
|
||||
|
||||
for (Region::iterator I = Reg->begin(), E = Reg->end(); I != E; ++I) {
|
||||
Region *SubR = *I;
|
||||
|
||||
if (SubR->getExit() == ExitBB)
|
||||
toUpdate.push_back(SubR);
|
||||
}
|
||||
|
||||
Reg->replaceExit(NewExit);
|
||||
}
|
||||
|
||||
RI->setRegionFor(NewExit, R->getParent());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::translateScalarToArray(const Region *R) {
|
||||
bool Changed = false;
|
||||
|
||||
for (Region::const_block_iterator SI = R->block_begin(), SE = R->block_end();
|
||||
SI != SE; ++SI)
|
||||
Changed |= translateScalarToArray((*SI)->getNodeAs<BasicBlock>(), R);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::translateScalarToArray(Instruction *Inst,
|
||||
const Region *R) {
|
||||
if (isIndVar(Inst, LI))
|
||||
return false;
|
||||
|
||||
SmallVector<Instruction*, 4> LoadInside, LoadOutside;
|
||||
for (Instruction::use_iterator UI = Inst->use_begin(),
|
||||
UE = Inst->use_end(); UI != UE; ++UI)
|
||||
// Inst is referenced outside or referenced as an escaped operand.
|
||||
if (Instruction *U = dyn_cast<Instruction>(*UI)) {
|
||||
BasicBlock *UParent = U->getParent();
|
||||
|
||||
if (isEscapeUse(U, R))
|
||||
LoadOutside.push_back(U);
|
||||
|
||||
if (isIndVar(U, LI))
|
||||
continue;
|
||||
|
||||
if (R->contains(UParent) && isEscapeOperand(Inst, UParent, R))
|
||||
LoadInside.push_back(U);
|
||||
}
|
||||
|
||||
if (LoadOutside.empty() && LoadInside.empty())
|
||||
return false;
|
||||
|
||||
// Create the alloca.
|
||||
AllocaInst *Slot = new AllocaInst(Inst->getType(), 0,
|
||||
Inst->getName() + ".s2a",
|
||||
AllocaBlock->begin());
|
||||
assert(!isa<InvokeInst>(Inst) && "Unexpect Invoke in Scop!");
|
||||
// Store right after Inst.
|
||||
BasicBlock::iterator StorePos = Inst;
|
||||
(void) new StoreInst(Inst, Slot, ++StorePos);
|
||||
|
||||
if (!LoadOutside.empty()) {
|
||||
LoadInst *ExitLoad = new LoadInst(Slot, Inst->getName()+".loadoutside",
|
||||
false, R->getExit()->getFirstNonPHI());
|
||||
|
||||
while (!LoadOutside.empty()) {
|
||||
Instruction *U = LoadOutside.pop_back_val();
|
||||
assert(!isa<PHINode>(U) && "Can not handle PHI node outside!");
|
||||
SE->forgetValue(U);
|
||||
U->replaceUsesOfWith(Inst, ExitLoad);
|
||||
}
|
||||
}
|
||||
|
||||
while (!LoadInside.empty()) {
|
||||
Instruction *U = LoadInside.pop_back_val();
|
||||
assert(!isa<PHINode>(U) && "Can not handle PHI node outside!");
|
||||
SE->forgetValue(U);
|
||||
LoadInst *L = new LoadInst(Slot, Inst->getName()+".loadarray",
|
||||
false, U);
|
||||
U->replaceUsesOfWith(Inst, L);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::translateScalarToArray(BasicBlock *BB,
|
||||
const Region *R) {
|
||||
bool changed = false;
|
||||
|
||||
SmallVector<Instruction*, 32> Insts;
|
||||
for (BasicBlock::iterator II = BB->begin(), IE = --BB->end();
|
||||
II != IE; ++II)
|
||||
Insts.push_back(II);
|
||||
|
||||
while (!Insts.empty()) {
|
||||
Instruction *Inst = Insts.pop_back_val();
|
||||
changed |= translateScalarToArray(Inst, R);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::isIndependentBlock(const Region *R,
|
||||
BasicBlock *BB) const {
|
||||
for (BasicBlock::iterator II = BB->begin(), IE = --BB->end();
|
||||
II != IE; ++II) {
|
||||
Instruction *Inst = &*II;
|
||||
|
||||
if (isIndVar(Inst, LI))
|
||||
continue;
|
||||
|
||||
// A value inside the Scop is referenced outside.
|
||||
for (Instruction::use_iterator UI = Inst->use_begin(),
|
||||
UE = Inst->use_end(); UI != UE; ++UI) {
|
||||
if (isEscapeUse(*UI, R)) {
|
||||
DEBUG(dbgs() << "Instruction not independent:\n");
|
||||
DEBUG(dbgs() << "Instruction used outside the Scop!\n");
|
||||
DEBUG(Inst->print(dbgs()));
|
||||
DEBUG(dbgs() << "\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (Instruction::op_iterator OI = Inst->op_begin(),
|
||||
OE = Inst->op_end(); OI != OE; ++OI) {
|
||||
if (isEscapeOperand(*OI, BB, R)) {
|
||||
DEBUG(dbgs() << "Instruction in function '";
|
||||
WriteAsOperand(dbgs(), BB->getParent(), false);
|
||||
dbgs() << "' not independent:\n");
|
||||
DEBUG(dbgs() << "Uses invalid operator\n");
|
||||
DEBUG(Inst->print(dbgs()));
|
||||
DEBUG(dbgs() << "\n");
|
||||
DEBUG(dbgs() << "Invalid operator is: ";
|
||||
WriteAsOperand(dbgs(), *OI, false);
|
||||
dbgs() << "\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IndependentBlocks::areAllBlocksIndependent(const Region *R) const {
|
||||
for (Region::const_block_iterator SI = R->block_begin(), SE = R->block_end();
|
||||
SI != SE; ++SI)
|
||||
if (!isIndependentBlock(R, (*SI)->getNodeAs<BasicBlock>()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IndependentBlocks::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
// FIXME: If we set preserves cfg, the cfg only passes do not need to
|
||||
// be "addPreserved"?
|
||||
AU.addPreserved<DominatorTree>();
|
||||
AU.addPreserved<DominanceFrontier>();
|
||||
AU.addPreserved<PostDominatorTree>();
|
||||
AU.addRequired<RegionInfo>();
|
||||
AU.addPreserved<RegionInfo>();
|
||||
AU.addRequired<LoopInfo>();
|
||||
AU.addPreserved<LoopInfo>();
|
||||
AU.addRequired<ScalarEvolution>();
|
||||
AU.addPreserved<ScalarEvolution>();
|
||||
AU.addRequired<ScopDetection>();
|
||||
AU.addPreserved<ScopDetection>();
|
||||
AU.addPreserved<CloogInfo>();
|
||||
}
|
||||
|
||||
bool IndependentBlocks::runOnFunction(llvm::Function &F) {
|
||||
bool Changed = false;
|
||||
|
||||
RI = &getAnalysis<RegionInfo>();
|
||||
LI = &getAnalysis<LoopInfo>();
|
||||
SD = &getAnalysis<ScopDetection>();
|
||||
SE = &getAnalysis<ScalarEvolution>();
|
||||
|
||||
AllocaBlock = &F.getEntryBlock();
|
||||
|
||||
DEBUG(dbgs() << "Run IndepBlock on " << F.getName() << '\n');
|
||||
|
||||
for (ScopDetection::iterator I = SD->begin(), E = SD->end(); I != E; ++I) {
|
||||
const Region *R = *I;
|
||||
Changed |= createIndependentBlocks(R);
|
||||
Changed |= eliminateDeadCode(R);
|
||||
// This may change the RegionTree.
|
||||
Changed |= splitExitBlock(const_cast<Region*>(R));
|
||||
}
|
||||
|
||||
DEBUG(dbgs() << "Before Scalar to Array------->\n");
|
||||
DEBUG(F.dump());
|
||||
|
||||
for (ScopDetection::iterator I = SD->begin(), E = SD->end(); I != E; ++I)
|
||||
Changed |= translateScalarToArray(*I);
|
||||
|
||||
DEBUG(dbgs() << "After Independent Blocks------------->\n");
|
||||
DEBUG(F.dump());
|
||||
|
||||
verifyAnalysis();
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
void IndependentBlocks::verifyAnalysis() const {
|
||||
for (ScopDetection::const_iterator I = SD->begin(), E = SD->end();I != E;++I)
|
||||
verifyScop(*I);
|
||||
}
|
||||
|
||||
void IndependentBlocks::verifyScop(const Region *R) const {
|
||||
assert (areAllBlocksIndependent(R) && "Cannot generate independent blocks");
|
||||
}
|
||||
|
||||
char IndependentBlocks::ID = 0;
|
||||
|
||||
static RegisterPass<IndependentBlocks>
|
||||
Z("polly-independent", "Polly - Create independent blocks");
|
||||
|
||||
char &polly::IndependentBlocksID = IndependentBlocks::ID;
|
||||
|
||||
Pass* polly::createIndependentBlocksPass() {
|
||||
return new IndependentBlocks();
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
//===- Interchange.cpp - Interchange interface ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/Cloog.h"
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/Dependences.h"
|
||||
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#include <isl/map.h>
|
||||
|
||||
#define DEBUG_TYPE "polly-interchange"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
namespace {
|
||||
|
||||
class Interchange : public ScopPass {
|
||||
public:
|
||||
static char ID;
|
||||
explicit Interchange() : ScopPass(ID) {}
|
||||
|
||||
virtual bool runOnScop(Scop &S);
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
char Interchange::ID = 0;
|
||||
bool Interchange::runOnScop(Scop &S) {
|
||||
if (std::distance(S.begin(), S.end()) != 2) // One statement besides the final statement
|
||||
return false;
|
||||
|
||||
for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
|
||||
ScopStmt *Stmt = *SI;
|
||||
if (!Stmt->isReduction())
|
||||
continue;
|
||||
|
||||
isl_map *Scattering = isl_map_copy(Stmt->getScattering());
|
||||
|
||||
const std::string MapString = "{scattering[i0, i1, i2, i3, i4] -> scattering[i0, i3, i2, i1, i4]}";
|
||||
isl_map *Map = isl_map_read_from_str(Stmt->getIslContext(), MapString.c_str(), -1);
|
||||
|
||||
isl_map_add_dims(Map, isl_dim_param, Stmt->getNumParams());
|
||||
Scattering = isl_map_apply_range(Scattering, Map);
|
||||
Stmt->setScattering(Scattering);
|
||||
|
||||
DEBUG(
|
||||
isl_printer *p = isl_printer_to_str(S.getCtx());
|
||||
isl_printer_print_map(p, Scattering);
|
||||
dbgs() << isl_printer_get_str(p) << '\n';
|
||||
isl_printer_flush(p);
|
||||
isl_printer_free(p);
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Interchange::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
AU.addRequired<Dependences>();
|
||||
}
|
||||
|
||||
static RegisterPass<Interchange> A("polly-interchange",
|
||||
"Polly - Perform loop interchange");
|
||||
|
||||
Pass* polly::createInterchangePass() {
|
||||
return new Interchange();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
add_polly_library(PollyJSON
|
||||
json_reader.cpp
|
||||
json_value.cpp
|
||||
json_writer.cpp
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
The json-cpp library and this documentation are in Public Domain.
|
|
@ -0,0 +1,16 @@
|
|||
##===- polly/lib/Support/Makefile ----------------*- Makefile -*-===##
|
||||
|
||||
#
|
||||
# Indicate where we are relative to the top of the source tree.
|
||||
#
|
||||
LEVEL=../..
|
||||
|
||||
LIBRARYNAME=pollyjson
|
||||
BUILD_ARCHIVE = 1
|
||||
|
||||
CPP.Flags += $(POLLY_INC)
|
||||
|
||||
#
|
||||
# Include Makefile.common so we know what to do.
|
||||
#
|
||||
include $(LEVEL)/Makefile.common
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef JSON_AUTOLINK_H_INCLUDED
|
||||
# define JSON_AUTOLINK_H_INCLUDED
|
||||
|
||||
# include "config.h"
|
||||
|
||||
# ifdef JSON_IN_CPPTL
|
||||
# include <cpptl/cpptl_autolink.h>
|
||||
# endif
|
||||
|
||||
# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL)
|
||||
# define CPPTL_AUTOLINK_NAME "json"
|
||||
# undef CPPTL_AUTOLINK_DLL
|
||||
# ifdef JSON_DLL
|
||||
# define CPPTL_AUTOLINK_DLL
|
||||
# endif
|
||||
# include "autolink.h"
|
||||
# endif
|
||||
|
||||
#endif // JSON_AUTOLINK_H_INCLUDED
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef JSON_CONFIG_H_INCLUDED
|
||||
# define JSON_CONFIG_H_INCLUDED
|
||||
|
||||
/// If defined, indicates that json library is embedded in CppTL library.
|
||||
//# define JSON_IN_CPPTL 1
|
||||
|
||||
/// If defined, indicates that json may leverage CppTL library
|
||||
//# define JSON_USE_CPPTL 1
|
||||
/// If defined, indicates that cpptl vector based map should be used instead of std::map
|
||||
/// as Value container.
|
||||
//# define JSON_USE_CPPTL_SMALLMAP 1
|
||||
/// If defined, indicates that Json specific container should be used
|
||||
/// (hash table & simple deque container with customizable allocator).
|
||||
/// THIS FEATURE IS STILL EXPERIMENTAL!
|
||||
//# define JSON_VALUE_USE_INTERNAL_MAP 1
|
||||
/// Force usage of standard new/malloc based allocator instead of memory pool based allocator.
|
||||
/// The memory pools allocator used optimization (initializing Value and ValueInternalLink
|
||||
/// as if it was a POD) that may cause some validation tool to report errors.
|
||||
/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
|
||||
//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
|
||||
|
||||
/// If defined, indicates that Json use exception to report invalid type manipulation
|
||||
/// instead of C assert macro.
|
||||
# define JSON_USE_EXCEPTION 1
|
||||
|
||||
# ifdef JSON_IN_CPPTL
|
||||
# include <cpptl/config.h>
|
||||
# ifndef JSON_USE_CPPTL
|
||||
# define JSON_USE_CPPTL 1
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifdef JSON_IN_CPPTL
|
||||
# define JSON_API CPPTL_API
|
||||
# elif defined(JSON_DLL_BUILD)
|
||||
# define JSON_API __declspec(dllexport)
|
||||
# elif defined(JSON_DLL)
|
||||
# define JSON_API __declspec(dllimport)
|
||||
# else
|
||||
# define JSON_API
|
||||
# endif
|
||||
|
||||
#endif // JSON_CONFIG_H_INCLUDED
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
|
||||
# define CPPTL_JSON_FEATURES_H_INCLUDED
|
||||
|
||||
# include "forwards.h"
|
||||
|
||||
namespace Json {
|
||||
|
||||
/** \brief Configuration passed to reader and writer.
|
||||
* This configuration object can be used to force the Reader or Writer
|
||||
* to behave in a standard conforming way.
|
||||
*/
|
||||
class JSON_API Features
|
||||
{
|
||||
public:
|
||||
/** \brief A configuration that allows all features and assumes all strings are UTF-8.
|
||||
* - C & C++ comments are allowed
|
||||
* - Root object can be any JSON value
|
||||
* - Assumes Value strings are encoded in UTF-8
|
||||
*/
|
||||
static Features all();
|
||||
|
||||
/** \brief A configuration that is strictly compatible with the JSON specification.
|
||||
* - Comments are forbidden.
|
||||
* - Root object must be either an array or an object value.
|
||||
* - Assumes Value strings are encoded in UTF-8
|
||||
*/
|
||||
static Features strictMode();
|
||||
|
||||
/** \brief Initialize the configuration like JsonConfig::allFeatures;
|
||||
*/
|
||||
Features();
|
||||
|
||||
/// \c true if comments are allowed. Default: \c true.
|
||||
bool allowComments_;
|
||||
|
||||
/// \c true if root must be either an array or an object value. Default: \c false.
|
||||
bool strictRoot_;
|
||||
};
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif // CPPTL_JSON_FEATURES_H_INCLUDED
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef JSON_FORWARDS_H_INCLUDED
|
||||
# define JSON_FORWARDS_H_INCLUDED
|
||||
|
||||
# include "config.h"
|
||||
|
||||
namespace Json {
|
||||
|
||||
// writer.h
|
||||
class FastWriter;
|
||||
class StyledWriter;
|
||||
|
||||
// reader.h
|
||||
class Reader;
|
||||
|
||||
// features.h
|
||||
class Features;
|
||||
|
||||
// value.h
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
class StaticString;
|
||||
class Path;
|
||||
class PathArgument;
|
||||
class Value;
|
||||
class ValueIteratorBase;
|
||||
class ValueIterator;
|
||||
class ValueConstIterator;
|
||||
#ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
class ValueAllocator;
|
||||
class ValueMapAllocator;
|
||||
class ValueInternalLink;
|
||||
class ValueInternalArray;
|
||||
class ValueInternalMap;
|
||||
#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
|
||||
|
||||
} // namespace Json
|
||||
|
||||
|
||||
#endif // JSON_FORWARDS_H_INCLUDED
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef JSON_JSON_H_INCLUDED
|
||||
# define JSON_JSON_H_INCLUDED
|
||||
|
||||
# include "autolink.h"
|
||||
# include "value.h"
|
||||
# include "reader.h"
|
||||
# include "writer.h"
|
||||
# include "features.h"
|
||||
|
||||
#endif // JSON_JSON_H_INCLUDED
|
|
@ -0,0 +1,196 @@
|
|||
#ifndef CPPTL_JSON_READER_H_INCLUDED
|
||||
# define CPPTL_JSON_READER_H_INCLUDED
|
||||
|
||||
# include "features.h"
|
||||
# include "value.h"
|
||||
# include <deque>
|
||||
# include <stack>
|
||||
# include <string>
|
||||
# include <iostream>
|
||||
|
||||
namespace Json {
|
||||
|
||||
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
|
||||
*
|
||||
*/
|
||||
class JSON_API Reader
|
||||
{
|
||||
public:
|
||||
typedef char Char;
|
||||
typedef const Char *Location;
|
||||
|
||||
/** \brief Constructs a Reader allowing all features
|
||||
* for parsing.
|
||||
*/
|
||||
Reader();
|
||||
|
||||
/** \brief Constructs a Reader allowing the specified feature set
|
||||
* for parsing.
|
||||
*/
|
||||
Reader( const Features &features );
|
||||
|
||||
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
|
||||
* \param document UTF-8 encoded string containing the document to read.
|
||||
* \param root [out] Contains the root value of the document if it was
|
||||
* successfully parsed.
|
||||
* \param collectComments \c true to collect comment and allow writing them back during
|
||||
* serialization, \c false to discard comments.
|
||||
* This parameter is ignored if Features::allowComments_
|
||||
* is \c false.
|
||||
* \return \c true if the document was successfully parsed, \c false if an error occurred.
|
||||
*/
|
||||
bool parse( const std::string &document,
|
||||
Value &root,
|
||||
bool collectComments = true );
|
||||
|
||||
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
|
||||
* \param document UTF-8 encoded string containing the document to read.
|
||||
* \param root [out] Contains the root value of the document if it was
|
||||
* successfully parsed.
|
||||
* \param collectComments \c true to collect comment and allow writing them back during
|
||||
* serialization, \c false to discard comments.
|
||||
* This parameter is ignored if Features::allowComments_
|
||||
* is \c false.
|
||||
* \return \c true if the document was successfully parsed, \c false if an error occurred.
|
||||
*/
|
||||
bool parse( const char *beginDoc, const char *endDoc,
|
||||
Value &root,
|
||||
bool collectComments = true );
|
||||
|
||||
/// \brief Parse from input stream.
|
||||
/// \see Json::operator>>(std::istream&, Json::Value&).
|
||||
bool parse( std::istream &is,
|
||||
Value &root,
|
||||
bool collectComments = true );
|
||||
|
||||
/** \brief Returns a user friendly string that list errors in the parsed document.
|
||||
* \return Formatted error message with the list of errors with their location in
|
||||
* the parsed document. An empty string is returned if no error occurred
|
||||
* during parsing.
|
||||
*/
|
||||
std::string getFormatedErrorMessages() const;
|
||||
|
||||
private:
|
||||
enum TokenType
|
||||
{
|
||||
tokenEndOfStream = 0,
|
||||
tokenObjectBegin,
|
||||
tokenObjectEnd,
|
||||
tokenArrayBegin,
|
||||
tokenArrayEnd,
|
||||
tokenString,
|
||||
tokenNumber,
|
||||
tokenTrue,
|
||||
tokenFalse,
|
||||
tokenNull,
|
||||
tokenArraySeparator,
|
||||
tokenMemberSeparator,
|
||||
tokenComment,
|
||||
tokenError
|
||||
};
|
||||
|
||||
class Token
|
||||
{
|
||||
public:
|
||||
TokenType type_;
|
||||
Location start_;
|
||||
Location end_;
|
||||
};
|
||||
|
||||
class ErrorInfo
|
||||
{
|
||||
public:
|
||||
Token token_;
|
||||
std::string message_;
|
||||
Location extra_;
|
||||
};
|
||||
|
||||
typedef std::deque<ErrorInfo> Errors;
|
||||
|
||||
bool expectToken( TokenType type, Token &token, const char *message );
|
||||
bool readToken( Token &token );
|
||||
void skipSpaces();
|
||||
bool match( Location pattern,
|
||||
int patternLength );
|
||||
bool readComment();
|
||||
bool readCStyleComment();
|
||||
bool readCppStyleComment();
|
||||
bool readString();
|
||||
void readNumber();
|
||||
bool readValue();
|
||||
bool readObject( Token &token );
|
||||
bool readArray( Token &token );
|
||||
bool decodeNumber( Token &token );
|
||||
bool decodeString( Token &token );
|
||||
bool decodeString( Token &token, std::string &decoded );
|
||||
bool decodeDouble( Token &token );
|
||||
bool decodeUnicodeCodePoint( Token &token,
|
||||
Location ¤t,
|
||||
Location end,
|
||||
unsigned int &unicode );
|
||||
bool decodeUnicodeEscapeSequence( Token &token,
|
||||
Location ¤t,
|
||||
Location end,
|
||||
unsigned int &unicode );
|
||||
bool addError( const std::string &message,
|
||||
Token &token,
|
||||
Location extra = 0 );
|
||||
bool recoverFromError( TokenType skipUntilToken );
|
||||
bool addErrorAndRecover( const std::string &message,
|
||||
Token &token,
|
||||
TokenType skipUntilToken );
|
||||
void skipUntilSpace();
|
||||
Value ¤tValue();
|
||||
Char getNextChar();
|
||||
void getLocationLineAndColumn( Location location,
|
||||
int &line,
|
||||
int &column ) const;
|
||||
std::string getLocationLineAndColumn( Location location ) const;
|
||||
void addComment( Location begin,
|
||||
Location end,
|
||||
CommentPlacement placement );
|
||||
void skipCommentTokens( Token &token );
|
||||
|
||||
typedef std::stack<Value *> Nodes;
|
||||
Nodes nodes_;
|
||||
Errors errors_;
|
||||
std::string document_;
|
||||
Location begin_;
|
||||
Location end_;
|
||||
Location current_;
|
||||
Location lastValueEnd_;
|
||||
Value *lastValue_;
|
||||
std::string commentsBefore_;
|
||||
Features features_;
|
||||
bool collectComments_;
|
||||
};
|
||||
|
||||
/** \brief Read from 'sin' into 'root'.
|
||||
|
||||
Always keep comments from the input JSON.
|
||||
|
||||
This can be used to read a file into a particular sub-object.
|
||||
For example:
|
||||
\code
|
||||
Json::Value root;
|
||||
cin >> root["dir"]["file"];
|
||||
cout << root;
|
||||
\endcode
|
||||
Result:
|
||||
\verbatim
|
||||
{
|
||||
"dir": {
|
||||
"file": {
|
||||
// The input stream JSON would be nested here.
|
||||
}
|
||||
}
|
||||
}
|
||||
\endverbatim
|
||||
\throw std::exception on parse error.
|
||||
\see Json::operator<<()
|
||||
*/
|
||||
std::istream& operator>>( std::istream&, Value& );
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif // CPPTL_JSON_READER_H_INCLUDED
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,174 @@
|
|||
#ifndef JSON_WRITER_H_INCLUDED
|
||||
# define JSON_WRITER_H_INCLUDED
|
||||
|
||||
# include "value.h"
|
||||
# include <vector>
|
||||
# include <string>
|
||||
# include <iostream>
|
||||
|
||||
namespace Json {
|
||||
|
||||
class Value;
|
||||
|
||||
/** \brief Abstract class for writers.
|
||||
*/
|
||||
class JSON_API Writer
|
||||
{
|
||||
public:
|
||||
virtual ~Writer();
|
||||
|
||||
virtual std::string write( const Value &root ) = 0;
|
||||
};
|
||||
|
||||
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
|
||||
*
|
||||
* The JSON document is written in a single line. It is not intended for 'human' consumption,
|
||||
* but may be usefull to support feature such as RPC where bandwith is limited.
|
||||
* \sa Reader, Value
|
||||
*/
|
||||
class JSON_API FastWriter : public Writer
|
||||
{
|
||||
public:
|
||||
FastWriter();
|
||||
virtual ~FastWriter(){}
|
||||
|
||||
void enableYAMLCompatibility();
|
||||
|
||||
public: // overridden from Writer
|
||||
virtual std::string write( const Value &root );
|
||||
|
||||
private:
|
||||
void writeValue( const Value &value );
|
||||
|
||||
std::string document_;
|
||||
bool yamlCompatiblityEnabled_;
|
||||
};
|
||||
|
||||
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
|
||||
*
|
||||
* The rules for line break and indent are as follow:
|
||||
* - Object value:
|
||||
* - if empty then print {} without indent and line break
|
||||
* - if not empty the print '{', line break & indent, print one value per line
|
||||
* and then unindent and line break and print '}'.
|
||||
* - Array value:
|
||||
* - if empty then print [] without indent and line break
|
||||
* - if the array contains no object value, empty array or some other value types,
|
||||
* and all the values fit on one lines, then print the array on a single line.
|
||||
* - otherwise, it the values do not fit on one line, or the array contains
|
||||
* object or non empty array, then print one value per line.
|
||||
*
|
||||
* If the Value have comments then they are outputed according to their #CommentPlacement.
|
||||
*
|
||||
* \sa Reader, Value, Value::setComment()
|
||||
*/
|
||||
class JSON_API StyledWriter: public Writer
|
||||
{
|
||||
public:
|
||||
StyledWriter();
|
||||
virtual ~StyledWriter(){}
|
||||
|
||||
public: // overridden from Writer
|
||||
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
|
||||
* \param root Value to serialize.
|
||||
* \return String containing the JSON document that represents the root value.
|
||||
*/
|
||||
virtual std::string write( const Value &root );
|
||||
|
||||
private:
|
||||
void writeValue( const Value &value );
|
||||
void writeArrayValue( const Value &value );
|
||||
bool isMultineArray( const Value &value );
|
||||
void pushValue( const std::string &value );
|
||||
void writeIndent();
|
||||
void writeWithIndent( const std::string &value );
|
||||
void indent();
|
||||
void unindent();
|
||||
void writeCommentBeforeValue( const Value &root );
|
||||
void writeCommentAfterValueOnSameLine( const Value &root );
|
||||
bool hasCommentForValue( const Value &value );
|
||||
static std::string normalizeEOL( const std::string &text );
|
||||
|
||||
typedef std::vector<std::string> ChildValues;
|
||||
|
||||
ChildValues childValues_;
|
||||
std::string document_;
|
||||
std::string indentString_;
|
||||
int rightMargin_;
|
||||
int indentSize_;
|
||||
bool addChildValues_;
|
||||
};
|
||||
|
||||
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
|
||||
to a stream rather than to a string.
|
||||
*
|
||||
* The rules for line break and indent are as follow:
|
||||
* - Object value:
|
||||
* - if empty then print {} without indent and line break
|
||||
* - if not empty the print '{', line break & indent, print one value per line
|
||||
* and then unindent and line break and print '}'.
|
||||
* - Array value:
|
||||
* - if empty then print [] without indent and line break
|
||||
* - if the array contains no object value, empty array or some other value types,
|
||||
* and all the values fit on one lines, then print the array on a single line.
|
||||
* - otherwise, it the values do not fit on one line, or the array contains
|
||||
* object or non empty array, then print one value per line.
|
||||
*
|
||||
* If the Value have comments then they are outputed according to their #CommentPlacement.
|
||||
*
|
||||
* \param indentation Each level will be indented by this amount extra.
|
||||
* \sa Reader, Value, Value::setComment()
|
||||
*/
|
||||
class JSON_API StyledStreamWriter
|
||||
{
|
||||
public:
|
||||
StyledStreamWriter( std::string indentation="\t" );
|
||||
~StyledStreamWriter(){}
|
||||
|
||||
public:
|
||||
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
|
||||
* \param out Stream to write to. (Can be ostringstream, e.g.)
|
||||
* \param root Value to serialize.
|
||||
* \note There is no point in deriving from Writer, since write() should not return a value.
|
||||
*/
|
||||
void write( std::ostream &out, const Value &root );
|
||||
|
||||
private:
|
||||
void writeValue( const Value &value );
|
||||
void writeArrayValue( const Value &value );
|
||||
bool isMultineArray( const Value &value );
|
||||
void pushValue( const std::string &value );
|
||||
void writeIndent();
|
||||
void writeWithIndent( const std::string &value );
|
||||
void indent();
|
||||
void unindent();
|
||||
void writeCommentBeforeValue( const Value &root );
|
||||
void writeCommentAfterValueOnSameLine( const Value &root );
|
||||
bool hasCommentForValue( const Value &value );
|
||||
static std::string normalizeEOL( const std::string &text );
|
||||
|
||||
typedef std::vector<std::string> ChildValues;
|
||||
|
||||
ChildValues childValues_;
|
||||
std::ostream* document_;
|
||||
std::string indentString_;
|
||||
int rightMargin_;
|
||||
std::string indentation_;
|
||||
bool addChildValues_;
|
||||
};
|
||||
|
||||
std::string JSON_API valueToString( Int value );
|
||||
std::string JSON_API valueToString( UInt value );
|
||||
std::string JSON_API valueToString( double value );
|
||||
std::string JSON_API valueToString( bool value );
|
||||
std::string JSON_API valueToQuotedString( const char *value );
|
||||
|
||||
/// \brief Output using the StyledStreamWriter.
|
||||
/// \see Json::operator>>()
|
||||
std::ostream& operator<<( std::ostream&, const Value &root );
|
||||
|
||||
} // namespace Json
|
||||
|
||||
|
||||
|
||||
#endif // JSON_WRITER_H_INCLUDED
|
|
@ -0,0 +1,125 @@
|
|||
#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED
|
||||
# define JSONCPP_BATCHALLOCATOR_H_INCLUDED
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <assert.h>
|
||||
|
||||
# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
|
||||
|
||||
namespace Json {
|
||||
|
||||
/* Fast memory allocator.
|
||||
*
|
||||
* This memory allocator allocates memory for a batch of object (specified by
|
||||
* the page size, the number of object in each page).
|
||||
*
|
||||
* It does not allow the destruction of a single object. All the allocated objects
|
||||
* can be destroyed at once. The memory can be either released or reused for future
|
||||
* allocation.
|
||||
*
|
||||
* The in-place new operator must be used to construct the object using the pointer
|
||||
* returned by allocate.
|
||||
*/
|
||||
template<typename AllocatedType
|
||||
,const unsigned int objectPerAllocation>
|
||||
class BatchAllocator
|
||||
{
|
||||
public:
|
||||
typedef AllocatedType Type;
|
||||
|
||||
BatchAllocator( unsigned int objectsPerPage = 255 )
|
||||
: freeHead_( 0 )
|
||||
, objectsPerPage_( objectsPerPage )
|
||||
{
|
||||
// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() );
|
||||
assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space.
|
||||
assert( objectsPerPage >= 16 );
|
||||
batches_ = allocateBatch( 0 ); // allocated a dummy page
|
||||
currentBatch_ = batches_;
|
||||
}
|
||||
|
||||
~BatchAllocator()
|
||||
{
|
||||
for ( BatchInfo *batch = batches_; batch; )
|
||||
{
|
||||
BatchInfo *nextBatch = batch->next_;
|
||||
free( batch );
|
||||
batch = nextBatch;
|
||||
}
|
||||
}
|
||||
|
||||
/// allocate space for an array of objectPerAllocation object.
|
||||
/// @warning it is the responsability of the caller to call objects constructors.
|
||||
AllocatedType *allocate()
|
||||
{
|
||||
if ( freeHead_ ) // returns node from free list.
|
||||
{
|
||||
AllocatedType *object = freeHead_;
|
||||
freeHead_ = *(AllocatedType **)object;
|
||||
return object;
|
||||
}
|
||||
if ( currentBatch_->used_ == currentBatch_->end_ )
|
||||
{
|
||||
currentBatch_ = currentBatch_->next_;
|
||||
while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ )
|
||||
currentBatch_ = currentBatch_->next_;
|
||||
|
||||
if ( !currentBatch_ ) // no free batch found, allocate a new one
|
||||
{
|
||||
currentBatch_ = allocateBatch( objectsPerPage_ );
|
||||
currentBatch_->next_ = batches_; // insert at the head of the list
|
||||
batches_ = currentBatch_;
|
||||
}
|
||||
}
|
||||
AllocatedType *allocated = currentBatch_->used_;
|
||||
currentBatch_->used_ += objectPerAllocation;
|
||||
return allocated;
|
||||
}
|
||||
|
||||
/// Release the object.
|
||||
/// @warning it is the responsability of the caller to actually destruct the object.
|
||||
void release( AllocatedType *object )
|
||||
{
|
||||
assert( object != 0 );
|
||||
*(AllocatedType **)object = freeHead_;
|
||||
freeHead_ = object;
|
||||
}
|
||||
|
||||
private:
|
||||
struct BatchInfo
|
||||
{
|
||||
BatchInfo *next_;
|
||||
AllocatedType *used_;
|
||||
AllocatedType *end_;
|
||||
AllocatedType buffer_[objectPerAllocation];
|
||||
};
|
||||
|
||||
// disabled copy constructor and assignement operator.
|
||||
BatchAllocator( const BatchAllocator & );
|
||||
void operator =( const BatchAllocator &);
|
||||
|
||||
static BatchInfo *allocateBatch( unsigned int objectsPerPage )
|
||||
{
|
||||
const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation
|
||||
+ sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
|
||||
BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) );
|
||||
batch->next_ = 0;
|
||||
batch->used_ = batch->buffer_;
|
||||
batch->end_ = batch->buffer_ + objectsPerPage;
|
||||
return batch;
|
||||
}
|
||||
|
||||
BatchInfo *batches_;
|
||||
BatchInfo *currentBatch_;
|
||||
/// Head of a single linked list within the allocated space of freeed object
|
||||
AllocatedType *freeHead_;
|
||||
unsigned int objectsPerPage_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Json
|
||||
|
||||
# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
|
||||
|
||||
#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
|
||||
|
|
@ -0,0 +1,448 @@
|
|||
// included by json_value.cpp
|
||||
// everything is within Json namespace
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class ValueInternalArray
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
ValueArrayAllocator::~ValueArrayAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class DefaultValueArrayAllocator
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
||||
class DefaultValueArrayAllocator : public ValueArrayAllocator
|
||||
{
|
||||
public: // overridden from ValueArrayAllocator
|
||||
virtual ~DefaultValueArrayAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ValueInternalArray *newArray()
|
||||
{
|
||||
return new ValueInternalArray();
|
||||
}
|
||||
|
||||
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
|
||||
{
|
||||
return new ValueInternalArray( other );
|
||||
}
|
||||
|
||||
virtual void destructArray( ValueInternalArray *array )
|
||||
{
|
||||
delete array;
|
||||
}
|
||||
|
||||
virtual void reallocateArrayPageIndex( Value **&indexes,
|
||||
ValueInternalArray::PageIndex &indexCount,
|
||||
ValueInternalArray::PageIndex minNewIndexCount )
|
||||
{
|
||||
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
|
||||
if ( minNewIndexCount > newIndexCount )
|
||||
newIndexCount = minNewIndexCount;
|
||||
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
|
||||
if ( !newIndexes )
|
||||
throw std::bad_alloc();
|
||||
indexCount = newIndexCount;
|
||||
indexes = static_cast<Value **>( newIndexes );
|
||||
}
|
||||
virtual void releaseArrayPageIndex( Value **indexes,
|
||||
ValueInternalArray::PageIndex indexCount )
|
||||
{
|
||||
if ( indexes )
|
||||
free( indexes );
|
||||
}
|
||||
|
||||
virtual Value *allocateArrayPage()
|
||||
{
|
||||
return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
|
||||
}
|
||||
|
||||
virtual void releaseArrayPage( Value *value )
|
||||
{
|
||||
if ( value )
|
||||
free( value );
|
||||
}
|
||||
};
|
||||
|
||||
#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
||||
/// @todo make this thread-safe (lock when accessign batch allocator)
|
||||
class DefaultValueArrayAllocator : public ValueArrayAllocator
|
||||
{
|
||||
public: // overridden from ValueArrayAllocator
|
||||
virtual ~DefaultValueArrayAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ValueInternalArray *newArray()
|
||||
{
|
||||
ValueInternalArray *array = arraysAllocator_.allocate();
|
||||
new (array) ValueInternalArray(); // placement new
|
||||
return array;
|
||||
}
|
||||
|
||||
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
|
||||
{
|
||||
ValueInternalArray *array = arraysAllocator_.allocate();
|
||||
new (array) ValueInternalArray( other ); // placement new
|
||||
return array;
|
||||
}
|
||||
|
||||
virtual void destructArray( ValueInternalArray *array )
|
||||
{
|
||||
if ( array )
|
||||
{
|
||||
array->~ValueInternalArray();
|
||||
arraysAllocator_.release( array );
|
||||
}
|
||||
}
|
||||
|
||||
virtual void reallocateArrayPageIndex( Value **&indexes,
|
||||
ValueInternalArray::PageIndex &indexCount,
|
||||
ValueInternalArray::PageIndex minNewIndexCount )
|
||||
{
|
||||
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
|
||||
if ( minNewIndexCount > newIndexCount )
|
||||
newIndexCount = minNewIndexCount;
|
||||
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
|
||||
if ( !newIndexes )
|
||||
throw std::bad_alloc();
|
||||
indexCount = newIndexCount;
|
||||
indexes = static_cast<Value **>( newIndexes );
|
||||
}
|
||||
virtual void releaseArrayPageIndex( Value **indexes,
|
||||
ValueInternalArray::PageIndex indexCount )
|
||||
{
|
||||
if ( indexes )
|
||||
free( indexes );
|
||||
}
|
||||
|
||||
virtual Value *allocateArrayPage()
|
||||
{
|
||||
return static_cast<Value *>( pagesAllocator_.allocate() );
|
||||
}
|
||||
|
||||
virtual void releaseArrayPage( Value *value )
|
||||
{
|
||||
if ( value )
|
||||
pagesAllocator_.release( value );
|
||||
}
|
||||
private:
|
||||
BatchAllocator<ValueInternalArray,1> arraysAllocator_;
|
||||
BatchAllocator<Value,ValueInternalArray::itemsPerPage> pagesAllocator_;
|
||||
};
|
||||
#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
||||
|
||||
static ValueArrayAllocator *&arrayAllocator()
|
||||
{
|
||||
static DefaultValueArrayAllocator defaultAllocator;
|
||||
static ValueArrayAllocator *arrayAllocator = &defaultAllocator;
|
||||
return arrayAllocator;
|
||||
}
|
||||
|
||||
static struct DummyArrayAllocatorInitializer {
|
||||
DummyArrayAllocatorInitializer()
|
||||
{
|
||||
arrayAllocator(); // ensure arrayAllocator() statics are initialized before main().
|
||||
}
|
||||
} dummyArrayAllocatorInitializer;
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class ValueInternalArray
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
bool
|
||||
ValueInternalArray::equals( const IteratorState &x,
|
||||
const IteratorState &other )
|
||||
{
|
||||
return x.array_ == other.array_
|
||||
&& x.currentItemIndex_ == other.currentItemIndex_
|
||||
&& x.currentPageIndex_ == other.currentPageIndex_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::increment( IteratorState &it )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( it.array_ &&
|
||||
(it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
|
||||
!= it.array_->size_,
|
||||
"ValueInternalArray::increment(): moving iterator beyond end" );
|
||||
++(it.currentItemIndex_);
|
||||
if ( it.currentItemIndex_ == itemsPerPage )
|
||||
{
|
||||
it.currentItemIndex_ = 0;
|
||||
++(it.currentPageIndex_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::decrement( IteratorState &it )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_
|
||||
&& it.currentItemIndex_ == 0,
|
||||
"ValueInternalArray::decrement(): moving iterator beyond end" );
|
||||
if ( it.currentItemIndex_ == 0 )
|
||||
{
|
||||
it.currentItemIndex_ = itemsPerPage-1;
|
||||
--(it.currentPageIndex_);
|
||||
}
|
||||
else
|
||||
{
|
||||
--(it.currentItemIndex_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
ValueInternalArray::unsafeDereference( const IteratorState &it )
|
||||
{
|
||||
return (*(it.currentPageIndex_))[it.currentItemIndex_];
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
ValueInternalArray::dereference( const IteratorState &it )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( it.array_ &&
|
||||
(it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
|
||||
< it.array_->size_,
|
||||
"ValueInternalArray::dereference(): dereferencing invalid iterator" );
|
||||
return unsafeDereference( it );
|
||||
}
|
||||
|
||||
void
|
||||
ValueInternalArray::makeBeginIterator( IteratorState &it ) const
|
||||
{
|
||||
it.array_ = const_cast<ValueInternalArray *>( this );
|
||||
it.currentItemIndex_ = 0;
|
||||
it.currentPageIndex_ = pages_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const
|
||||
{
|
||||
it.array_ = const_cast<ValueInternalArray *>( this );
|
||||
it.currentItemIndex_ = index % itemsPerPage;
|
||||
it.currentPageIndex_ = pages_ + index / itemsPerPage;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::makeEndIterator( IteratorState &it ) const
|
||||
{
|
||||
makeIterator( it, size_ );
|
||||
}
|
||||
|
||||
|
||||
ValueInternalArray::ValueInternalArray()
|
||||
: pages_( 0 )
|
||||
, size_( 0 )
|
||||
, pageCount_( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ValueInternalArray::ValueInternalArray( const ValueInternalArray &other )
|
||||
: pages_( 0 )
|
||||
, pageCount_( 0 )
|
||||
, size_( other.size_ )
|
||||
{
|
||||
PageIndex minNewPages = other.size_ / itemsPerPage;
|
||||
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
|
||||
JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages,
|
||||
"ValueInternalArray::reserve(): bad reallocation" );
|
||||
IteratorState itOther;
|
||||
other.makeBeginIterator( itOther );
|
||||
Value *value;
|
||||
for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) )
|
||||
{
|
||||
if ( index % itemsPerPage == 0 )
|
||||
{
|
||||
PageIndex pageIndex = index / itemsPerPage;
|
||||
value = arrayAllocator()->allocateArrayPage();
|
||||
pages_[pageIndex] = value;
|
||||
}
|
||||
new (value) Value( dereference( itOther ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ValueInternalArray &
|
||||
ValueInternalArray::operator =( const ValueInternalArray &other )
|
||||
{
|
||||
ValueInternalArray temp( other );
|
||||
swap( temp );
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
ValueInternalArray::~ValueInternalArray()
|
||||
{
|
||||
// destroy all constructed items
|
||||
IteratorState it;
|
||||
IteratorState itEnd;
|
||||
makeBeginIterator( it);
|
||||
makeEndIterator( itEnd );
|
||||
for ( ; !equals(it,itEnd); increment(it) )
|
||||
{
|
||||
Value *value = &dereference(it);
|
||||
value->~Value();
|
||||
}
|
||||
// release all pages
|
||||
PageIndex lastPageIndex = size_ / itemsPerPage;
|
||||
for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex )
|
||||
arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
|
||||
// release pages index
|
||||
arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::swap( ValueInternalArray &other )
|
||||
{
|
||||
Value **tempPages = pages_;
|
||||
pages_ = other.pages_;
|
||||
other.pages_ = tempPages;
|
||||
ArrayIndex tempSize = size_;
|
||||
size_ = other.size_;
|
||||
other.size_ = tempSize;
|
||||
PageIndex tempPageCount = pageCount_;
|
||||
pageCount_ = other.pageCount_;
|
||||
other.pageCount_ = tempPageCount;
|
||||
}
|
||||
|
||||
void
|
||||
ValueInternalArray::clear()
|
||||
{
|
||||
ValueInternalArray dummy;
|
||||
swap( dummy );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::resize( ArrayIndex newSize )
|
||||
{
|
||||
if ( newSize == 0 )
|
||||
clear();
|
||||
else if ( newSize < size_ )
|
||||
{
|
||||
IteratorState it;
|
||||
IteratorState itEnd;
|
||||
makeIterator( it, newSize );
|
||||
makeIterator( itEnd, size_ );
|
||||
for ( ; !equals(it,itEnd); increment(it) )
|
||||
{
|
||||
Value *value = &dereference(it);
|
||||
value->~Value();
|
||||
}
|
||||
PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage;
|
||||
PageIndex lastPageIndex = size_ / itemsPerPage;
|
||||
for ( ; pageIndex < lastPageIndex; ++pageIndex )
|
||||
arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
|
||||
size_ = newSize;
|
||||
}
|
||||
else if ( newSize > size_ )
|
||||
resolveReference( newSize );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalArray::makeIndexValid( ArrayIndex index )
|
||||
{
|
||||
// Need to enlarge page index ?
|
||||
if ( index >= pageCount_ * itemsPerPage )
|
||||
{
|
||||
PageIndex minNewPages = (index + 1) / itemsPerPage;
|
||||
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
|
||||
JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" );
|
||||
}
|
||||
|
||||
// Need to allocate new pages ?
|
||||
ArrayIndex nextPageIndex =
|
||||
(size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage
|
||||
: size_;
|
||||
if ( nextPageIndex <= index )
|
||||
{
|
||||
PageIndex pageIndex = nextPageIndex / itemsPerPage;
|
||||
PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1;
|
||||
for ( ; pageToAllocate-- > 0; ++pageIndex )
|
||||
pages_[pageIndex] = arrayAllocator()->allocateArrayPage();
|
||||
}
|
||||
|
||||
// Initialize all new entries
|
||||
IteratorState it;
|
||||
IteratorState itEnd;
|
||||
makeIterator( it, size_ );
|
||||
size_ = index + 1;
|
||||
makeIterator( itEnd, size_ );
|
||||
for ( ; !equals(it,itEnd); increment(it) )
|
||||
{
|
||||
Value *value = &dereference(it);
|
||||
new (value) Value(); // Construct a default value using placement new
|
||||
}
|
||||
}
|
||||
|
||||
Value &
|
||||
ValueInternalArray::resolveReference( ArrayIndex index )
|
||||
{
|
||||
if ( index >= size_ )
|
||||
makeIndexValid( index );
|
||||
return pages_[index/itemsPerPage][index%itemsPerPage];
|
||||
}
|
||||
|
||||
Value *
|
||||
ValueInternalArray::find( ArrayIndex index ) const
|
||||
{
|
||||
if ( index >= size_ )
|
||||
return 0;
|
||||
return &(pages_[index/itemsPerPage][index%itemsPerPage]);
|
||||
}
|
||||
|
||||
ValueInternalArray::ArrayIndex
|
||||
ValueInternalArray::size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
int
|
||||
ValueInternalArray::distance( const IteratorState &x, const IteratorState &y )
|
||||
{
|
||||
return indexOf(y) - indexOf(x);
|
||||
}
|
||||
|
||||
|
||||
ValueInternalArray::ArrayIndex
|
||||
ValueInternalArray::indexOf( const IteratorState &iterator )
|
||||
{
|
||||
if ( !iterator.array_ )
|
||||
return ArrayIndex(-1);
|
||||
return ArrayIndex(
|
||||
(iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage
|
||||
+ iterator.currentItemIndex_ );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ValueInternalArray::compare( const ValueInternalArray &other ) const
|
||||
{
|
||||
int sizeDiff( size_ - other.size_ );
|
||||
if ( sizeDiff != 0 )
|
||||
return sizeDiff;
|
||||
|
||||
for ( ArrayIndex index =0; index < size_; ++index )
|
||||
{
|
||||
int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare(
|
||||
other.pages_[index/itemsPerPage][index%itemsPerPage] );
|
||||
if ( diff != 0 )
|
||||
return diff;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,607 @@
|
|||
// included by json_value.cpp
|
||||
// everything is within Json namespace
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class ValueInternalMap
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) );
|
||||
* This optimization is used by the fast allocator.
|
||||
*/
|
||||
ValueInternalLink::ValueInternalLink()
|
||||
: previous_( 0 )
|
||||
, next_( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
ValueInternalLink::~ValueInternalLink()
|
||||
{
|
||||
for ( int index =0; index < itemPerLink; ++index )
|
||||
{
|
||||
if ( !items_[index].isItemAvailable() )
|
||||
{
|
||||
if ( !items_[index].isMemberNameStatic() )
|
||||
free( keys_[index] );
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ValueMapAllocator::~ValueMapAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
||||
class DefaultValueMapAllocator : public ValueMapAllocator
|
||||
{
|
||||
public: // overridden from ValueMapAllocator
|
||||
virtual ValueInternalMap *newMap()
|
||||
{
|
||||
return new ValueInternalMap();
|
||||
}
|
||||
|
||||
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
|
||||
{
|
||||
return new ValueInternalMap( other );
|
||||
}
|
||||
|
||||
virtual void destructMap( ValueInternalMap *map )
|
||||
{
|
||||
delete map;
|
||||
}
|
||||
|
||||
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
|
||||
{
|
||||
return new ValueInternalLink[size];
|
||||
}
|
||||
|
||||
virtual void releaseMapBuckets( ValueInternalLink *links )
|
||||
{
|
||||
delete [] links;
|
||||
}
|
||||
|
||||
virtual ValueInternalLink *allocateMapLink()
|
||||
{
|
||||
return new ValueInternalLink();
|
||||
}
|
||||
|
||||
virtual void releaseMapLink( ValueInternalLink *link )
|
||||
{
|
||||
delete link;
|
||||
}
|
||||
};
|
||||
#else
|
||||
/// @todo make this thread-safe (lock when accessign batch allocator)
|
||||
class DefaultValueMapAllocator : public ValueMapAllocator
|
||||
{
|
||||
public: // overridden from ValueMapAllocator
|
||||
virtual ValueInternalMap *newMap()
|
||||
{
|
||||
ValueInternalMap *map = mapsAllocator_.allocate();
|
||||
new (map) ValueInternalMap(); // placement new
|
||||
return map;
|
||||
}
|
||||
|
||||
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
|
||||
{
|
||||
ValueInternalMap *map = mapsAllocator_.allocate();
|
||||
new (map) ValueInternalMap( other ); // placement new
|
||||
return map;
|
||||
}
|
||||
|
||||
virtual void destructMap( ValueInternalMap *map )
|
||||
{
|
||||
if ( map )
|
||||
{
|
||||
map->~ValueInternalMap();
|
||||
mapsAllocator_.release( map );
|
||||
}
|
||||
}
|
||||
|
||||
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
|
||||
{
|
||||
return new ValueInternalLink[size];
|
||||
}
|
||||
|
||||
virtual void releaseMapBuckets( ValueInternalLink *links )
|
||||
{
|
||||
delete [] links;
|
||||
}
|
||||
|
||||
virtual ValueInternalLink *allocateMapLink()
|
||||
{
|
||||
ValueInternalLink *link = linksAllocator_.allocate();
|
||||
memset( link, 0, sizeof(ValueInternalLink) );
|
||||
return link;
|
||||
}
|
||||
|
||||
virtual void releaseMapLink( ValueInternalLink *link )
|
||||
{
|
||||
link->~ValueInternalLink();
|
||||
linksAllocator_.release( link );
|
||||
}
|
||||
private:
|
||||
BatchAllocator<ValueInternalMap,1> mapsAllocator_;
|
||||
BatchAllocator<ValueInternalLink,1> linksAllocator_;
|
||||
};
|
||||
#endif
|
||||
|
||||
static ValueMapAllocator *&mapAllocator()
|
||||
{
|
||||
static DefaultValueMapAllocator defaultAllocator;
|
||||
static ValueMapAllocator *mapAllocator = &defaultAllocator;
|
||||
return mapAllocator;
|
||||
}
|
||||
|
||||
static struct DummyMapAllocatorInitializer {
|
||||
DummyMapAllocatorInitializer()
|
||||
{
|
||||
mapAllocator(); // ensure mapAllocator() statics are initialized before main().
|
||||
}
|
||||
} dummyMapAllocatorInitializer;
|
||||
|
||||
|
||||
|
||||
// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32.
|
||||
|
||||
/*
|
||||
use linked list hash map.
|
||||
buckets array is a container.
|
||||
linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124)
|
||||
value have extra state: valid, available, deleted
|
||||
*/
|
||||
|
||||
|
||||
ValueInternalMap::ValueInternalMap()
|
||||
: buckets_( 0 )
|
||||
, tailLink_( 0 )
|
||||
, bucketsSize_( 0 )
|
||||
, itemCount_( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ValueInternalMap::ValueInternalMap( const ValueInternalMap &other )
|
||||
: buckets_( 0 )
|
||||
, tailLink_( 0 )
|
||||
, bucketsSize_( 0 )
|
||||
, itemCount_( 0 )
|
||||
{
|
||||
reserve( other.itemCount_ );
|
||||
IteratorState it;
|
||||
IteratorState itEnd;
|
||||
other.makeBeginIterator( it );
|
||||
other.makeEndIterator( itEnd );
|
||||
for ( ; !equals(it,itEnd); increment(it) )
|
||||
{
|
||||
bool isStatic;
|
||||
const char *memberName = key( it, isStatic );
|
||||
const Value &aValue = value( it );
|
||||
resolveReference(memberName, isStatic) = aValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ValueInternalMap &
|
||||
ValueInternalMap::operator =( const ValueInternalMap &other )
|
||||
{
|
||||
ValueInternalMap dummy( other );
|
||||
swap( dummy );
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
ValueInternalMap::~ValueInternalMap()
|
||||
{
|
||||
if ( buckets_ )
|
||||
{
|
||||
for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex )
|
||||
{
|
||||
ValueInternalLink *link = buckets_[bucketIndex].next_;
|
||||
while ( link )
|
||||
{
|
||||
ValueInternalLink *linkToRelease = link;
|
||||
link = link->next_;
|
||||
mapAllocator()->releaseMapLink( linkToRelease );
|
||||
}
|
||||
}
|
||||
mapAllocator()->releaseMapBuckets( buckets_ );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::swap( ValueInternalMap &other )
|
||||
{
|
||||
ValueInternalLink *tempBuckets = buckets_;
|
||||
buckets_ = other.buckets_;
|
||||
other.buckets_ = tempBuckets;
|
||||
ValueInternalLink *tempTailLink = tailLink_;
|
||||
tailLink_ = other.tailLink_;
|
||||
other.tailLink_ = tempTailLink;
|
||||
BucketIndex tempBucketsSize = bucketsSize_;
|
||||
bucketsSize_ = other.bucketsSize_;
|
||||
other.bucketsSize_ = tempBucketsSize;
|
||||
BucketIndex tempItemCount = itemCount_;
|
||||
itemCount_ = other.itemCount_;
|
||||
other.itemCount_ = tempItemCount;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::clear()
|
||||
{
|
||||
ValueInternalMap dummy;
|
||||
swap( dummy );
|
||||
}
|
||||
|
||||
|
||||
ValueInternalMap::BucketIndex
|
||||
ValueInternalMap::size() const
|
||||
{
|
||||
return itemCount_;
|
||||
}
|
||||
|
||||
bool
|
||||
ValueInternalMap::reserveDelta( BucketIndex growth )
|
||||
{
|
||||
return reserve( itemCount_ + growth );
|
||||
}
|
||||
|
||||
bool
|
||||
ValueInternalMap::reserve( BucketIndex newItemCount )
|
||||
{
|
||||
if ( !buckets_ && newItemCount > 0 )
|
||||
{
|
||||
buckets_ = mapAllocator()->allocateMapBuckets( 1 );
|
||||
bucketsSize_ = 1;
|
||||
tailLink_ = &buckets_[0];
|
||||
}
|
||||
// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const Value *
|
||||
ValueInternalMap::find( const char *key ) const
|
||||
{
|
||||
if ( !bucketsSize_ )
|
||||
return 0;
|
||||
HashKey hashedKey = hash( key );
|
||||
BucketIndex bucketIndex = hashedKey % bucketsSize_;
|
||||
for ( const ValueInternalLink *current = &buckets_[bucketIndex];
|
||||
current != 0;
|
||||
current = current->next_ )
|
||||
{
|
||||
for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index )
|
||||
{
|
||||
if ( current->items_[index].isItemAvailable() )
|
||||
return 0;
|
||||
if ( strcmp( key, current->keys_[index] ) == 0 )
|
||||
return ¤t->items_[index];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Value *
|
||||
ValueInternalMap::find( const char *key )
|
||||
{
|
||||
const ValueInternalMap *constThis = this;
|
||||
return const_cast<Value *>( constThis->find( key ) );
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
ValueInternalMap::resolveReference( const char *key,
|
||||
bool isStatic )
|
||||
{
|
||||
HashKey hashedKey = hash( key );
|
||||
if ( bucketsSize_ )
|
||||
{
|
||||
BucketIndex bucketIndex = hashedKey % bucketsSize_;
|
||||
ValueInternalLink **previous = 0;
|
||||
BucketIndex index;
|
||||
for ( ValueInternalLink *current = &buckets_[bucketIndex];
|
||||
current != 0;
|
||||
previous = ¤t->next_, current = current->next_ )
|
||||
{
|
||||
for ( index=0; index < ValueInternalLink::itemPerLink; ++index )
|
||||
{
|
||||
if ( current->items_[index].isItemAvailable() )
|
||||
return setNewItem( key, isStatic, current, index );
|
||||
if ( strcmp( key, current->keys_[index] ) == 0 )
|
||||
return current->items_[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reserveDelta( 1 );
|
||||
return unsafeAdd( key, isStatic, hashedKey );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::remove( const char *key )
|
||||
{
|
||||
HashKey hashedKey = hash( key );
|
||||
if ( !bucketsSize_ )
|
||||
return;
|
||||
BucketIndex bucketIndex = hashedKey % bucketsSize_;
|
||||
for ( ValueInternalLink *link = &buckets_[bucketIndex];
|
||||
link != 0;
|
||||
link = link->next_ )
|
||||
{
|
||||
BucketIndex index;
|
||||
for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
|
||||
{
|
||||
if ( link->items_[index].isItemAvailable() )
|
||||
return;
|
||||
if ( strcmp( key, link->keys_[index] ) == 0 )
|
||||
{
|
||||
doActualRemove( link, index, bucketIndex );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ValueInternalMap::doActualRemove( ValueInternalLink *link,
|
||||
BucketIndex index,
|
||||
BucketIndex bucketIndex )
|
||||
{
|
||||
// find last item of the bucket and swap it with the 'removed' one.
|
||||
// set removed items flags to 'available'.
|
||||
// if last page only contains 'available' items, then desallocate it (it's empty)
|
||||
ValueInternalLink *&lastLink = getLastLinkInBucket( index );
|
||||
BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1
|
||||
for ( ;
|
||||
lastItemIndex < ValueInternalLink::itemPerLink;
|
||||
++lastItemIndex ) // may be optimized with dicotomic search
|
||||
{
|
||||
if ( lastLink->items_[lastItemIndex].isItemAvailable() )
|
||||
break;
|
||||
}
|
||||
|
||||
BucketIndex lastUsedIndex = lastItemIndex - 1;
|
||||
Value *valueToDelete = &link->items_[index];
|
||||
Value *valueToPreserve = &lastLink->items_[lastUsedIndex];
|
||||
if ( valueToDelete != valueToPreserve )
|
||||
valueToDelete->swap( *valueToPreserve );
|
||||
if ( lastUsedIndex == 0 ) // page is now empty
|
||||
{ // remove it from bucket linked list and delete it.
|
||||
ValueInternalLink *linkPreviousToLast = lastLink->previous_;
|
||||
if ( linkPreviousToLast != 0 ) // can not deleted bucket link.
|
||||
{
|
||||
mapAllocator()->releaseMapLink( lastLink );
|
||||
linkPreviousToLast->next_ = 0;
|
||||
lastLink = linkPreviousToLast;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Value dummy;
|
||||
valueToPreserve->swap( dummy ); // restore deleted to default Value.
|
||||
valueToPreserve->setItemUsed( false );
|
||||
}
|
||||
--itemCount_;
|
||||
}
|
||||
|
||||
|
||||
ValueInternalLink *&
|
||||
ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex )
|
||||
{
|
||||
if ( bucketIndex == bucketsSize_ - 1 )
|
||||
return tailLink_;
|
||||
ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_;
|
||||
if ( !previous )
|
||||
previous = &buckets_[bucketIndex];
|
||||
return previous;
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
ValueInternalMap::setNewItem( const char *key,
|
||||
bool isStatic,
|
||||
ValueInternalLink *link,
|
||||
BucketIndex index )
|
||||
{
|
||||
char *duplicatedKey = valueAllocator()->makeMemberName( key );
|
||||
++itemCount_;
|
||||
link->keys_[index] = duplicatedKey;
|
||||
link->items_[index].setItemUsed();
|
||||
link->items_[index].setMemberNameIsStatic( isStatic );
|
||||
return link->items_[index]; // items already default constructed.
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
ValueInternalMap::unsafeAdd( const char *key,
|
||||
bool isStatic,
|
||||
HashKey hashedKey )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." );
|
||||
BucketIndex bucketIndex = hashedKey % bucketsSize_;
|
||||
ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex );
|
||||
ValueInternalLink *link = previousLink;
|
||||
BucketIndex index;
|
||||
for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
|
||||
{
|
||||
if ( link->items_[index].isItemAvailable() )
|
||||
break;
|
||||
}
|
||||
if ( index == ValueInternalLink::itemPerLink ) // need to add a new page
|
||||
{
|
||||
ValueInternalLink *newLink = mapAllocator()->allocateMapLink();
|
||||
index = 0;
|
||||
link->next_ = newLink;
|
||||
previousLink = newLink;
|
||||
link = newLink;
|
||||
}
|
||||
return setNewItem( key, isStatic, link, index );
|
||||
}
|
||||
|
||||
|
||||
ValueInternalMap::HashKey
|
||||
ValueInternalMap::hash( const char *key ) const
|
||||
{
|
||||
HashKey hash = 0;
|
||||
while ( *key )
|
||||
hash += *key++ * 37;
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ValueInternalMap::compare( const ValueInternalMap &other ) const
|
||||
{
|
||||
int sizeDiff( itemCount_ - other.itemCount_ );
|
||||
if ( sizeDiff != 0 )
|
||||
return sizeDiff;
|
||||
// Strict order guaranty is required. Compare all keys FIRST, then compare values.
|
||||
IteratorState it;
|
||||
IteratorState itEnd;
|
||||
makeBeginIterator( it );
|
||||
makeEndIterator( itEnd );
|
||||
for ( ; !equals(it,itEnd); increment(it) )
|
||||
{
|
||||
if ( !other.find( key( it ) ) )
|
||||
return 1;
|
||||
}
|
||||
|
||||
// All keys are equals, let's compare values
|
||||
makeBeginIterator( it );
|
||||
for ( ; !equals(it,itEnd); increment(it) )
|
||||
{
|
||||
const Value *otherValue = other.find( key( it ) );
|
||||
int valueDiff = value(it).compare( *otherValue );
|
||||
if ( valueDiff != 0 )
|
||||
return valueDiff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::makeBeginIterator( IteratorState &it ) const
|
||||
{
|
||||
it.map_ = const_cast<ValueInternalMap *>( this );
|
||||
it.bucketIndex_ = 0;
|
||||
it.itemIndex_ = 0;
|
||||
it.link_ = buckets_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::makeEndIterator( IteratorState &it ) const
|
||||
{
|
||||
it.map_ = const_cast<ValueInternalMap *>( this );
|
||||
it.bucketIndex_ = bucketsSize_;
|
||||
it.itemIndex_ = 0;
|
||||
it.link_ = 0;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValueInternalMap::equals( const IteratorState &x, const IteratorState &other )
|
||||
{
|
||||
return x.map_ == other.map_
|
||||
&& x.bucketIndex_ == other.bucketIndex_
|
||||
&& x.link_ == other.link_
|
||||
&& x.itemIndex_ == other.itemIndex_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::incrementBucket( IteratorState &iterator )
|
||||
{
|
||||
++iterator.bucketIndex_;
|
||||
JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_,
|
||||
"ValueInternalMap::increment(): attempting to iterate beyond end." );
|
||||
if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ )
|
||||
iterator.link_ = 0;
|
||||
else
|
||||
iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);
|
||||
iterator.itemIndex_ = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::increment( IteratorState &iterator )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." );
|
||||
++iterator.itemIndex_;
|
||||
if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.link_ != 0,
|
||||
"ValueInternalMap::increment(): attempting to iterate beyond end." );
|
||||
iterator.link_ = iterator.link_->next_;
|
||||
if ( iterator.link_ == 0 )
|
||||
incrementBucket( iterator );
|
||||
}
|
||||
else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() )
|
||||
{
|
||||
incrementBucket( iterator );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueInternalMap::decrement( IteratorState &iterator )
|
||||
{
|
||||
if ( iterator.itemIndex_ == 0 )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." );
|
||||
if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." );
|
||||
--(iterator.bucketIndex_);
|
||||
}
|
||||
iterator.link_ = iterator.link_->previous_;
|
||||
iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
ValueInternalMap::key( const IteratorState &iterator )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
|
||||
return iterator.link_->keys_[iterator.itemIndex_];
|
||||
}
|
||||
|
||||
const char *
|
||||
ValueInternalMap::key( const IteratorState &iterator, bool &isStatic )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
|
||||
isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic();
|
||||
return iterator.link_->keys_[iterator.itemIndex_];
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
ValueInternalMap::value( const IteratorState &iterator )
|
||||
{
|
||||
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
|
||||
return iterator.link_->items_[iterator.itemIndex_];
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ValueInternalMap::distance( const IteratorState &x, const IteratorState &y )
|
||||
{
|
||||
int offset = 0;
|
||||
IteratorState it = x;
|
||||
while ( !equals( it, y ) )
|
||||
increment( it );
|
||||
return offset;
|
||||
}
|
|
@ -0,0 +1,885 @@
|
|||
#include <json/reader.h>
|
||||
#include <json/value.h>
|
||||
#include <utility>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#if _MSC_VER >= 1400 // VC++ 8.0
|
||||
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
|
||||
#endif
|
||||
|
||||
namespace Json {
|
||||
|
||||
// Implementation of class Features
|
||||
// ////////////////////////////////
|
||||
|
||||
Features::Features()
|
||||
: allowComments_( true )
|
||||
, strictRoot_( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Features
|
||||
Features::all()
|
||||
{
|
||||
return Features();
|
||||
}
|
||||
|
||||
|
||||
Features
|
||||
Features::strictMode()
|
||||
{
|
||||
Features features;
|
||||
features.allowComments_ = false;
|
||||
features.strictRoot_ = true;
|
||||
return features;
|
||||
}
|
||||
|
||||
// Implementation of class Reader
|
||||
// ////////////////////////////////
|
||||
|
||||
|
||||
static inline bool
|
||||
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
|
||||
{
|
||||
return c == c1 || c == c2 || c == c3 || c == c4;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
|
||||
{
|
||||
return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
containsNewLine( Reader::Location begin,
|
||||
Reader::Location end )
|
||||
{
|
||||
for ( ;begin < end; ++begin )
|
||||
if ( *begin == '\n' || *begin == '\r' )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string codePointToUTF8(unsigned int cp)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
// based on description from http://en.wikipedia.org/wiki/UTF-8
|
||||
|
||||
if (cp <= 0x7f)
|
||||
{
|
||||
result.resize(1);
|
||||
result[0] = static_cast<char>(cp);
|
||||
}
|
||||
else if (cp <= 0x7FF)
|
||||
{
|
||||
result.resize(2);
|
||||
result[1] = static_cast<char>(0x80 | (0x3f & cp));
|
||||
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
|
||||
}
|
||||
else if (cp <= 0xFFFF)
|
||||
{
|
||||
result.resize(3);
|
||||
result[2] = static_cast<char>(0x80 | (0x3f & cp));
|
||||
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
|
||||
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
|
||||
}
|
||||
else if (cp <= 0x10FFFF)
|
||||
{
|
||||
result.resize(4);
|
||||
result[3] = static_cast<char>(0x80 | (0x3f & cp));
|
||||
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
|
||||
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
|
||||
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Class Reader
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
Reader::Reader()
|
||||
: features_( Features::all() )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reader::Reader( const Features &features )
|
||||
: features_( features )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::parse( const std::string &document,
|
||||
Value &root,
|
||||
bool collectComments )
|
||||
{
|
||||
document_ = document;
|
||||
const char *begin = document_.c_str();
|
||||
const char *end = begin + document_.length();
|
||||
return parse( begin, end, root, collectComments );
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::parse( std::istream& sin,
|
||||
Value &root,
|
||||
bool collectComments )
|
||||
{
|
||||
//std::istream_iterator<char> begin(sin);
|
||||
//std::istream_iterator<char> end;
|
||||
// Those would allow streamed input from a file, if parse() were a
|
||||
// template function.
|
||||
|
||||
// Since std::string is reference-counted, this at least does not
|
||||
// create an extra copy.
|
||||
std::string doc;
|
||||
std::getline(sin, doc, (char)EOF);
|
||||
return parse( doc, root, collectComments );
|
||||
}
|
||||
|
||||
bool
|
||||
Reader::parse( const char *beginDoc, const char *endDoc,
|
||||
Value &root,
|
||||
bool collectComments )
|
||||
{
|
||||
if ( !features_.allowComments_ )
|
||||
{
|
||||
collectComments = false;
|
||||
}
|
||||
|
||||
begin_ = beginDoc;
|
||||
end_ = endDoc;
|
||||
collectComments_ = collectComments;
|
||||
current_ = begin_;
|
||||
lastValueEnd_ = 0;
|
||||
lastValue_ = 0;
|
||||
commentsBefore_ = "";
|
||||
errors_.clear();
|
||||
while ( !nodes_.empty() )
|
||||
nodes_.pop();
|
||||
nodes_.push( &root );
|
||||
|
||||
bool successful = readValue();
|
||||
Token token;
|
||||
skipCommentTokens( token );
|
||||
if ( collectComments_ && !commentsBefore_.empty() )
|
||||
root.setComment( commentsBefore_, commentAfter );
|
||||
if ( features_.strictRoot_ )
|
||||
{
|
||||
if ( !root.isArray() && !root.isObject() )
|
||||
{
|
||||
// Set error location to start of doc, ideally should be first token found in doc
|
||||
token.type_ = tokenError;
|
||||
token.start_ = beginDoc;
|
||||
token.end_ = endDoc;
|
||||
addError( "A valid JSON document must be either an array or an object value.",
|
||||
token );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readValue()
|
||||
{
|
||||
Token token;
|
||||
skipCommentTokens( token );
|
||||
bool successful = true;
|
||||
|
||||
if ( collectComments_ && !commentsBefore_.empty() )
|
||||
{
|
||||
currentValue().setComment( commentsBefore_, commentBefore );
|
||||
commentsBefore_ = "";
|
||||
}
|
||||
|
||||
|
||||
switch ( token.type_ )
|
||||
{
|
||||
case tokenObjectBegin:
|
||||
successful = readObject( token );
|
||||
break;
|
||||
case tokenArrayBegin:
|
||||
successful = readArray( token );
|
||||
break;
|
||||
case tokenNumber:
|
||||
successful = decodeNumber( token );
|
||||
break;
|
||||
case tokenString:
|
||||
successful = decodeString( token );
|
||||
break;
|
||||
case tokenTrue:
|
||||
currentValue() = true;
|
||||
break;
|
||||
case tokenFalse:
|
||||
currentValue() = false;
|
||||
break;
|
||||
case tokenNull:
|
||||
currentValue() = Value();
|
||||
break;
|
||||
default:
|
||||
return addError( "Syntax error: value, object or array expected.", token );
|
||||
}
|
||||
|
||||
if ( collectComments_ )
|
||||
{
|
||||
lastValueEnd_ = current_;
|
||||
lastValue_ = ¤tValue();
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Reader::skipCommentTokens( Token &token )
|
||||
{
|
||||
if ( features_.allowComments_ )
|
||||
{
|
||||
do
|
||||
{
|
||||
readToken( token );
|
||||
}
|
||||
while ( token.type_ == tokenComment );
|
||||
}
|
||||
else
|
||||
{
|
||||
readToken( token );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::expectToken( TokenType type, Token &token, const char *message )
|
||||
{
|
||||
readToken( token );
|
||||
if ( token.type_ != type )
|
||||
return addError( message, token );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readToken( Token &token )
|
||||
{
|
||||
skipSpaces();
|
||||
token.start_ = current_;
|
||||
Char c = getNextChar();
|
||||
bool ok = true;
|
||||
switch ( c )
|
||||
{
|
||||
case '{':
|
||||
token.type_ = tokenObjectBegin;
|
||||
break;
|
||||
case '}':
|
||||
token.type_ = tokenObjectEnd;
|
||||
break;
|
||||
case '[':
|
||||
token.type_ = tokenArrayBegin;
|
||||
break;
|
||||
case ']':
|
||||
token.type_ = tokenArrayEnd;
|
||||
break;
|
||||
case '"':
|
||||
token.type_ = tokenString;
|
||||
ok = readString();
|
||||
break;
|
||||
case '/':
|
||||
token.type_ = tokenComment;
|
||||
ok = readComment();
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
token.type_ = tokenNumber;
|
||||
readNumber();
|
||||
break;
|
||||
case 't':
|
||||
token.type_ = tokenTrue;
|
||||
ok = match( "rue", 3 );
|
||||
break;
|
||||
case 'f':
|
||||
token.type_ = tokenFalse;
|
||||
ok = match( "alse", 4 );
|
||||
break;
|
||||
case 'n':
|
||||
token.type_ = tokenNull;
|
||||
ok = match( "ull", 3 );
|
||||
break;
|
||||
case ',':
|
||||
token.type_ = tokenArraySeparator;
|
||||
break;
|
||||
case ':':
|
||||
token.type_ = tokenMemberSeparator;
|
||||
break;
|
||||
case 0:
|
||||
token.type_ = tokenEndOfStream;
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if ( !ok )
|
||||
token.type_ = tokenError;
|
||||
token.end_ = current_;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Reader::skipSpaces()
|
||||
{
|
||||
while ( current_ != end_ )
|
||||
{
|
||||
Char c = *current_;
|
||||
if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
|
||||
++current_;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::match( Location pattern,
|
||||
int patternLength )
|
||||
{
|
||||
if ( end_ - current_ < patternLength )
|
||||
return false;
|
||||
int index = patternLength;
|
||||
while ( index-- )
|
||||
if ( current_[index] != pattern[index] )
|
||||
return false;
|
||||
current_ += patternLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readComment()
|
||||
{
|
||||
Location commentBegin = current_ - 1;
|
||||
Char c = getNextChar();
|
||||
bool successful = false;
|
||||
if ( c == '*' )
|
||||
successful = readCStyleComment();
|
||||
else if ( c == '/' )
|
||||
successful = readCppStyleComment();
|
||||
if ( !successful )
|
||||
return false;
|
||||
|
||||
if ( collectComments_ )
|
||||
{
|
||||
CommentPlacement placement = commentBefore;
|
||||
if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
|
||||
{
|
||||
if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
|
||||
placement = commentAfterOnSameLine;
|
||||
}
|
||||
|
||||
addComment( commentBegin, current_, placement );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Reader::addComment( Location begin,
|
||||
Location end,
|
||||
CommentPlacement placement )
|
||||
{
|
||||
assert( collectComments_ );
|
||||
if ( placement == commentAfterOnSameLine )
|
||||
{
|
||||
assert( lastValue_ != 0 );
|
||||
lastValue_->setComment( std::string( begin, end ), placement );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !commentsBefore_.empty() )
|
||||
commentsBefore_ += "\n";
|
||||
commentsBefore_ += std::string( begin, end );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readCStyleComment()
|
||||
{
|
||||
while ( current_ != end_ )
|
||||
{
|
||||
Char c = getNextChar();
|
||||
if ( c == '*' && *current_ == '/' )
|
||||
break;
|
||||
}
|
||||
return getNextChar() == '/';
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readCppStyleComment()
|
||||
{
|
||||
while ( current_ != end_ )
|
||||
{
|
||||
Char c = getNextChar();
|
||||
if ( c == '\r' || c == '\n' )
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Reader::readNumber()
|
||||
{
|
||||
while ( current_ != end_ )
|
||||
{
|
||||
if ( !(*current_ >= '0' && *current_ <= '9') &&
|
||||
!in( *current_, '.', 'e', 'E', '+', '-' ) )
|
||||
break;
|
||||
++current_;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Reader::readString()
|
||||
{
|
||||
Char c = 0;
|
||||
while ( current_ != end_ )
|
||||
{
|
||||
c = getNextChar();
|
||||
if ( c == '\\' )
|
||||
getNextChar();
|
||||
else if ( c == '"' )
|
||||
break;
|
||||
}
|
||||
return c == '"';
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readObject( Token &tokenStart )
|
||||
{
|
||||
Token tokenName;
|
||||
std::string name;
|
||||
currentValue() = Value( objectValue );
|
||||
while ( readToken( tokenName ) )
|
||||
{
|
||||
bool initialTokenOk = true;
|
||||
while ( tokenName.type_ == tokenComment && initialTokenOk )
|
||||
initialTokenOk = readToken( tokenName );
|
||||
if ( !initialTokenOk )
|
||||
break;
|
||||
if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
|
||||
return true;
|
||||
if ( tokenName.type_ != tokenString )
|
||||
break;
|
||||
|
||||
name = "";
|
||||
if ( !decodeString( tokenName, name ) )
|
||||
return recoverFromError( tokenObjectEnd );
|
||||
|
||||
Token colon;
|
||||
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
|
||||
{
|
||||
return addErrorAndRecover( "Missing ':' after object member name",
|
||||
colon,
|
||||
tokenObjectEnd );
|
||||
}
|
||||
Value &value = currentValue()[ name ];
|
||||
nodes_.push( &value );
|
||||
bool ok = readValue();
|
||||
nodes_.pop();
|
||||
if ( !ok ) // error already set
|
||||
return recoverFromError( tokenObjectEnd );
|
||||
|
||||
Token comma;
|
||||
if ( !readToken( comma )
|
||||
|| ( comma.type_ != tokenObjectEnd &&
|
||||
comma.type_ != tokenArraySeparator &&
|
||||
comma.type_ != tokenComment ) )
|
||||
{
|
||||
return addErrorAndRecover( "Missing ',' or '}' in object declaration",
|
||||
comma,
|
||||
tokenObjectEnd );
|
||||
}
|
||||
bool finalizeTokenOk = true;
|
||||
while ( comma.type_ == tokenComment &&
|
||||
finalizeTokenOk )
|
||||
finalizeTokenOk = readToken( comma );
|
||||
if ( comma.type_ == tokenObjectEnd )
|
||||
return true;
|
||||
}
|
||||
return addErrorAndRecover( "Missing '}' or object member name",
|
||||
tokenName,
|
||||
tokenObjectEnd );
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::readArray( Token &tokenStart )
|
||||
{
|
||||
currentValue() = Value( arrayValue );
|
||||
skipSpaces();
|
||||
if ( *current_ == ']' ) // empty array
|
||||
{
|
||||
Token endArray;
|
||||
readToken( endArray );
|
||||
return true;
|
||||
}
|
||||
int index = 0;
|
||||
while ( true )
|
||||
{
|
||||
Value &value = currentValue()[ index++ ];
|
||||
nodes_.push( &value );
|
||||
bool ok = readValue();
|
||||
nodes_.pop();
|
||||
if ( !ok ) // error already set
|
||||
return recoverFromError( tokenArrayEnd );
|
||||
|
||||
Token token;
|
||||
// Accept Comment after last item in the array.
|
||||
ok = readToken( token );
|
||||
while ( token.type_ == tokenComment && ok )
|
||||
{
|
||||
ok = readToken( token );
|
||||
}
|
||||
bool badTokenType = ( token.type_ == tokenArraySeparator &&
|
||||
token.type_ == tokenArrayEnd );
|
||||
if ( !ok || badTokenType )
|
||||
{
|
||||
return addErrorAndRecover( "Missing ',' or ']' in array declaration",
|
||||
token,
|
||||
tokenArrayEnd );
|
||||
}
|
||||
if ( token.type_ == tokenArrayEnd )
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::decodeNumber( Token &token )
|
||||
{
|
||||
bool isDouble = false;
|
||||
for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
|
||||
{
|
||||
isDouble = isDouble
|
||||
|| in( *inspect, '.', 'e', 'E', '+' )
|
||||
|| ( *inspect == '-' && inspect != token.start_ );
|
||||
}
|
||||
if ( isDouble )
|
||||
return decodeDouble( token );
|
||||
Location current = token.start_;
|
||||
bool isNegative = *current == '-';
|
||||
if ( isNegative )
|
||||
++current;
|
||||
Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt)
|
||||
: Value::maxUInt) / 10;
|
||||
Value::UInt value = 0;
|
||||
while ( current < token.end_ )
|
||||
{
|
||||
Char c = *current++;
|
||||
if ( c < '0' || c > '9' )
|
||||
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
|
||||
if ( value >= threshold )
|
||||
return decodeDouble( token );
|
||||
value = value * 10 + Value::UInt(c - '0');
|
||||
}
|
||||
if ( isNegative )
|
||||
currentValue() = -Value::Int( value );
|
||||
else if ( value <= Value::UInt(Value::maxInt) )
|
||||
currentValue() = Value::Int( value );
|
||||
else
|
||||
currentValue() = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::decodeDouble( Token &token )
|
||||
{
|
||||
double value = 0;
|
||||
const int bufferSize = 32;
|
||||
int count;
|
||||
int length = int(token.end_ - token.start_);
|
||||
if ( length <= bufferSize )
|
||||
{
|
||||
Char buffer[bufferSize];
|
||||
memcpy( buffer, token.start_, length );
|
||||
buffer[length] = 0;
|
||||
count = sscanf( buffer, "%lf", &value );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string buffer( token.start_, token.end_ );
|
||||
count = sscanf( buffer.c_str(), "%lf", &value );
|
||||
}
|
||||
|
||||
if ( count != 1 )
|
||||
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
|
||||
currentValue() = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::decodeString( Token &token )
|
||||
{
|
||||
std::string decoded;
|
||||
if ( !decodeString( token, decoded ) )
|
||||
return false;
|
||||
currentValue() = decoded;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::decodeString( Token &token, std::string &decoded )
|
||||
{
|
||||
decoded.reserve( token.end_ - token.start_ - 2 );
|
||||
Location current = token.start_ + 1; // skip '"'
|
||||
Location end = token.end_ - 1; // do not include '"'
|
||||
while ( current != end )
|
||||
{
|
||||
Char c = *current++;
|
||||
if ( c == '"' )
|
||||
break;
|
||||
else if ( c == '\\' )
|
||||
{
|
||||
if ( current == end )
|
||||
return addError( "Empty escape sequence in string", token, current );
|
||||
Char escape = *current++;
|
||||
switch ( escape )
|
||||
{
|
||||
case '"': decoded += '"'; break;
|
||||
case '/': decoded += '/'; break;
|
||||
case '\\': decoded += '\\'; break;
|
||||
case 'b': decoded += '\b'; break;
|
||||
case 'f': decoded += '\f'; break;
|
||||
case 'n': decoded += '\n'; break;
|
||||
case 'r': decoded += '\r'; break;
|
||||
case 't': decoded += '\t'; break;
|
||||
case 'u':
|
||||
{
|
||||
unsigned int unicode;
|
||||
if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
|
||||
return false;
|
||||
decoded += codePointToUTF8(unicode);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return addError( "Bad escape sequence in string", token, current );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
decoded += c;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Reader::decodeUnicodeCodePoint( Token &token,
|
||||
Location ¤t,
|
||||
Location end,
|
||||
unsigned int &unicode )
|
||||
{
|
||||
|
||||
if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
|
||||
return false;
|
||||
if (unicode >= 0xD800 && unicode <= 0xDBFF)
|
||||
{
|
||||
// surrogate pairs
|
||||
if (end - current < 6)
|
||||
return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
|
||||
unsigned int surrogatePair;
|
||||
if (*(current++) == '\\' && *(current++)== 'u')
|
||||
{
|
||||
if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
|
||||
{
|
||||
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Reader::decodeUnicodeEscapeSequence( Token &token,
|
||||
Location ¤t,
|
||||
Location end,
|
||||
unsigned int &unicode )
|
||||
{
|
||||
if ( end - current < 4 )
|
||||
return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
|
||||
unicode = 0;
|
||||
for ( int index =0; index < 4; ++index )
|
||||
{
|
||||
Char c = *current++;
|
||||
unicode *= 16;
|
||||
if ( c >= '0' && c <= '9' )
|
||||
unicode += c - '0';
|
||||
else if ( c >= 'a' && c <= 'f' )
|
||||
unicode += c - 'a' + 10;
|
||||
else if ( c >= 'A' && c <= 'F' )
|
||||
unicode += c - 'A' + 10;
|
||||
else
|
||||
return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::addError( const std::string &message,
|
||||
Token &token,
|
||||
Location extra )
|
||||
{
|
||||
ErrorInfo info;
|
||||
info.token_ = token;
|
||||
info.message_ = message;
|
||||
info.extra_ = extra;
|
||||
errors_.push_back( info );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::recoverFromError( TokenType skipUntilToken )
|
||||
{
|
||||
int errorCount = int(errors_.size());
|
||||
Token skip;
|
||||
while ( true )
|
||||
{
|
||||
if ( !readToken(skip) )
|
||||
errors_.resize( errorCount ); // discard errors caused by recovery
|
||||
if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
|
||||
break;
|
||||
}
|
||||
errors_.resize( errorCount );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Reader::addErrorAndRecover( const std::string &message,
|
||||
Token &token,
|
||||
TokenType skipUntilToken )
|
||||
{
|
||||
addError( message, token );
|
||||
return recoverFromError( skipUntilToken );
|
||||
}
|
||||
|
||||
|
||||
Value &
|
||||
Reader::currentValue()
|
||||
{
|
||||
return *(nodes_.top());
|
||||
}
|
||||
|
||||
|
||||
Reader::Char
|
||||
Reader::getNextChar()
|
||||
{
|
||||
if ( current_ == end_ )
|
||||
return 0;
|
||||
return *current_++;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Reader::getLocationLineAndColumn( Location location,
|
||||
int &line,
|
||||
int &column ) const
|
||||
{
|
||||
Location current = begin_;
|
||||
Location lastLineStart = current;
|
||||
line = 0;
|
||||
while ( current < location && current != end_ )
|
||||
{
|
||||
Char c = *current++;
|
||||
if ( c == '\r' )
|
||||
{
|
||||
if ( *current == '\n' )
|
||||
++current;
|
||||
lastLineStart = current;
|
||||
++line;
|
||||
}
|
||||
else if ( c == '\n' )
|
||||
{
|
||||
lastLineStart = current;
|
||||
++line;
|
||||
}
|
||||
}
|
||||
// column & line start at 1
|
||||
column = int(location - lastLineStart) + 1;
|
||||
++line;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Reader::getLocationLineAndColumn( Location location ) const
|
||||
{
|
||||
int line, column;
|
||||
getLocationLineAndColumn( location, line, column );
|
||||
char buffer[18+16+16+1];
|
||||
sprintf( buffer, "Line %d, Column %d", line, column );
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Reader::getFormatedErrorMessages() const
|
||||
{
|
||||
std::string formattedMessage;
|
||||
for ( Errors::const_iterator itError = errors_.begin();
|
||||
itError != errors_.end();
|
||||
++itError )
|
||||
{
|
||||
const ErrorInfo &error = *itError;
|
||||
formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
|
||||
formattedMessage += " " + error.message_ + "\n";
|
||||
if ( error.extra_ )
|
||||
formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
|
||||
}
|
||||
return formattedMessage;
|
||||
}
|
||||
|
||||
|
||||
std::istream& operator>>( std::istream &sin, Value &root )
|
||||
{
|
||||
Json::Reader reader;
|
||||
bool ok = reader.parse(sin, root, true);
|
||||
//JSON_ASSERT( ok );
|
||||
if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages());
|
||||
return sin;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Json
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,292 @@
|
|||
// included by json_value.cpp
|
||||
// everything is within Json namespace
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class ValueIteratorBase
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
ValueIteratorBase::ValueIteratorBase()
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
: current_()
|
||||
, isNull_( true )
|
||||
{
|
||||
}
|
||||
#else
|
||||
: isArray_( true )
|
||||
, isNull_( true )
|
||||
{
|
||||
iterator_.array_ = ValueInternalArray::IteratorState();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t )
|
||||
: current_( current )
|
||||
, isNull_( false )
|
||||
{
|
||||
}
|
||||
#else
|
||||
ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state )
|
||||
: isArray_( true )
|
||||
{
|
||||
iterator_.array_ = state;
|
||||
}
|
||||
|
||||
|
||||
ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state )
|
||||
: isArray_( false )
|
||||
{
|
||||
iterator_.map_ = state;
|
||||
}
|
||||
#endif
|
||||
|
||||
Value &
|
||||
ValueIteratorBase::deref() const
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
return current_->second;
|
||||
#else
|
||||
if ( isArray_ )
|
||||
return ValueInternalArray::dereference( iterator_.array_ );
|
||||
return ValueInternalMap::value( iterator_.map_ );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueIteratorBase::increment()
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
++current_;
|
||||
#else
|
||||
if ( isArray_ )
|
||||
ValueInternalArray::increment( iterator_.array_ );
|
||||
ValueInternalMap::increment( iterator_.map_ );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueIteratorBase::decrement()
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
--current_;
|
||||
#else
|
||||
if ( isArray_ )
|
||||
ValueInternalArray::decrement( iterator_.array_ );
|
||||
ValueInternalMap::decrement( iterator_.map_ );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ValueIteratorBase::difference_type
|
||||
ValueIteratorBase::computeDistance( const SelfType &other ) const
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
# ifdef JSON_USE_CPPTL_SMALLMAP
|
||||
return current_ - other.current_;
|
||||
# else
|
||||
// Iterator for null value are initialized using the default
|
||||
// constructor, which initialize current_ to the default
|
||||
// std::map::iterator. As begin() and end() are two instance
|
||||
// of the default std::map::iterator, they can not be compared.
|
||||
// To allow this, we handle this comparison specifically.
|
||||
if ( isNull_ && other.isNull_ )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL,
|
||||
// which is the one used by default).
|
||||
// Using a portable hand-made version for non random iterator instead:
|
||||
// return difference_type( std::distance( current_, other.current_ ) );
|
||||
difference_type myDistance = 0;
|
||||
for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it )
|
||||
{
|
||||
++myDistance;
|
||||
}
|
||||
return myDistance;
|
||||
# endif
|
||||
#else
|
||||
if ( isArray_ )
|
||||
return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ );
|
||||
return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ValueIteratorBase::isEqual( const SelfType &other ) const
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
if ( isNull_ )
|
||||
{
|
||||
return other.isNull_;
|
||||
}
|
||||
return current_ == other.current_;
|
||||
#else
|
||||
if ( isArray_ )
|
||||
return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ );
|
||||
return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ValueIteratorBase::copy( const SelfType &other )
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
current_ = other.current_;
|
||||
#else
|
||||
if ( isArray_ )
|
||||
iterator_.array_ = other.iterator_.array_;
|
||||
iterator_.map_ = other.iterator_.map_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Value
|
||||
ValueIteratorBase::key() const
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
const Value::CZString czstring = (*current_).first;
|
||||
if ( czstring.c_str() )
|
||||
{
|
||||
if ( czstring.isStaticString() )
|
||||
return Value( StaticString( czstring.c_str() ) );
|
||||
return Value( czstring.c_str() );
|
||||
}
|
||||
return Value( czstring.index() );
|
||||
#else
|
||||
if ( isArray_ )
|
||||
return Value( ValueInternalArray::indexOf( iterator_.array_ ) );
|
||||
bool isStatic;
|
||||
const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic );
|
||||
if ( isStatic )
|
||||
return Value( StaticString( memberName ) );
|
||||
return Value( memberName );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
UInt
|
||||
ValueIteratorBase::index() const
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
const Value::CZString czstring = (*current_).first;
|
||||
if ( !czstring.c_str() )
|
||||
return czstring.index();
|
||||
return Value::UInt( -1 );
|
||||
#else
|
||||
if ( isArray_ )
|
||||
return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) );
|
||||
return Value::UInt( -1 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
ValueIteratorBase::memberName() const
|
||||
{
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
const char *name = (*current_).first.c_str();
|
||||
return name ? name : "";
|
||||
#else
|
||||
if ( !isArray_ )
|
||||
return ValueInternalMap::key( iterator_.map_ );
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class ValueConstIterator
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
ValueConstIterator::ValueConstIterator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t )
|
||||
: ValueIteratorBase( current )
|
||||
{
|
||||
}
|
||||
#else
|
||||
ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state )
|
||||
: ValueIteratorBase( state )
|
||||
{
|
||||
}
|
||||
|
||||
ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state )
|
||||
: ValueIteratorBase( state )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
ValueConstIterator &
|
||||
ValueConstIterator::operator =( const ValueIteratorBase &other )
|
||||
{
|
||||
copy( other );
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// class ValueIterator
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
ValueIterator::ValueIterator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifndef JSON_VALUE_USE_INTERNAL_MAP
|
||||
ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t )
|
||||
: ValueIteratorBase( current )
|
||||
{
|
||||
}
|
||||
#else
|
||||
ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state )
|
||||
: ValueIteratorBase( state )
|
||||
{
|
||||
}
|
||||
|
||||
ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state )
|
||||
: ValueIteratorBase( state )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
ValueIterator::ValueIterator( const ValueConstIterator &other )
|
||||
: ValueIteratorBase( other )
|
||||
{
|
||||
}
|
||||
|
||||
ValueIterator::ValueIterator( const ValueIterator &other )
|
||||
: ValueIteratorBase( other )
|
||||
{
|
||||
}
|
||||
|
||||
ValueIterator &
|
||||
ValueIterator::operator =( const SelfType &other )
|
||||
{
|
||||
copy( other );
|
||||
return *this;
|
||||
}
|
|
@ -0,0 +1,829 @@
|
|||
#include <json/writer.h>
|
||||
#include <utility>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#if _MSC_VER >= 1400 // VC++ 8.0
|
||||
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
|
||||
#endif
|
||||
|
||||
namespace Json {
|
||||
|
||||
static bool isControlCharacter(char ch)
|
||||
{
|
||||
return ch > 0 && ch <= 0x1F;
|
||||
}
|
||||
|
||||
static bool containsControlCharacter( const char* str )
|
||||
{
|
||||
while ( *str )
|
||||
{
|
||||
if ( isControlCharacter( *(str++) ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void uintToString( unsigned int value,
|
||||
char *¤t )
|
||||
{
|
||||
*--current = 0;
|
||||
do
|
||||
{
|
||||
*--current = (value % 10) + '0';
|
||||
value /= 10;
|
||||
}
|
||||
while ( value != 0 );
|
||||
}
|
||||
|
||||
std::string valueToString( Int value )
|
||||
{
|
||||
char buffer[32];
|
||||
char *current = buffer + sizeof(buffer);
|
||||
bool isNegative = value < 0;
|
||||
if ( isNegative )
|
||||
value = -value;
|
||||
uintToString( UInt(value), current );
|
||||
if ( isNegative )
|
||||
*--current = '-';
|
||||
assert( current >= buffer );
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
std::string valueToString( UInt value )
|
||||
{
|
||||
char buffer[32];
|
||||
char *current = buffer + sizeof(buffer);
|
||||
uintToString( value, current );
|
||||
assert( current >= buffer );
|
||||
return current;
|
||||
}
|
||||
|
||||
std::string valueToString( double value )
|
||||
{
|
||||
char buffer[32];
|
||||
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
|
||||
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
|
||||
#else
|
||||
sprintf(buffer, "%#.16g", value);
|
||||
#endif
|
||||
char* ch = buffer + strlen(buffer) - 1;
|
||||
if (*ch != '0') return buffer; // nothing to truncate, so save time
|
||||
while(ch > buffer && *ch == '0'){
|
||||
--ch;
|
||||
}
|
||||
char* last_nonzero = ch;
|
||||
while(ch >= buffer){
|
||||
switch(*ch){
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
--ch;
|
||||
continue;
|
||||
case '.':
|
||||
// Truncate zeroes to save bytes in output, but keep one.
|
||||
*(last_nonzero+2) = '\0';
|
||||
return buffer;
|
||||
default:
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
std::string valueToString( bool value )
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
std::string valueToQuotedString( const char *value )
|
||||
{
|
||||
// Not sure how to handle unicode...
|
||||
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
|
||||
return std::string("\"") + value + "\"";
|
||||
// We have to walk value and escape any special characters.
|
||||
// Appending to std::string is not efficient, but this should be rare.
|
||||
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
||||
unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
|
||||
std::string result;
|
||||
result.reserve(maxsize); // to avoid lots of mallocs
|
||||
result += "\"";
|
||||
for (const char* c=value; *c != 0; ++c)
|
||||
{
|
||||
switch(*c)
|
||||
{
|
||||
case '\"':
|
||||
result += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
result += "\\\\";
|
||||
break;
|
||||
case '\b':
|
||||
result += "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
result += "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
result += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
result += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
result += "\\t";
|
||||
break;
|
||||
//case '/':
|
||||
// Even though \/ is considered a legal escape in JSON, a bare
|
||||
// slash is also legal, so I see no reason to escape it.
|
||||
// (I hope I am not misunderstanding something.
|
||||
// blep notes: actually escaping \/ may be useful in javascript to avoid </
|
||||
// sequence.
|
||||
// Should add a flag to allow this compatibility mode and prevent this
|
||||
// sequence from occurring.
|
||||
default:
|
||||
if ( isControlCharacter( *c ) )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
|
||||
result += oss.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
result += *c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
result += "\"";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Class Writer
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
Writer::~Writer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Class FastWriter
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
FastWriter::FastWriter()
|
||||
: yamlCompatiblityEnabled_( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastWriter::enableYAMLCompatibility()
|
||||
{
|
||||
yamlCompatiblityEnabled_ = true;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
FastWriter::write( const Value &root )
|
||||
{
|
||||
document_ = "";
|
||||
writeValue( root );
|
||||
document_ += "\n";
|
||||
return document_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastWriter::writeValue( const Value &value )
|
||||
{
|
||||
switch ( value.type() )
|
||||
{
|
||||
case nullValue:
|
||||
document_ += "null";
|
||||
break;
|
||||
case intValue:
|
||||
document_ += valueToString( value.asInt() );
|
||||
break;
|
||||
case uintValue:
|
||||
document_ += valueToString( value.asUInt() );
|
||||
break;
|
||||
case realValue:
|
||||
document_ += valueToString( value.asDouble() );
|
||||
break;
|
||||
case stringValue:
|
||||
document_ += valueToQuotedString( value.asCString() );
|
||||
break;
|
||||
case booleanValue:
|
||||
document_ += valueToString( value.asBool() );
|
||||
break;
|
||||
case arrayValue:
|
||||
{
|
||||
document_ += "[";
|
||||
int size = value.size();
|
||||
for ( int index =0; index < size; ++index )
|
||||
{
|
||||
if ( index > 0 )
|
||||
document_ += ",";
|
||||
writeValue( value[index] );
|
||||
}
|
||||
document_ += "]";
|
||||
}
|
||||
break;
|
||||
case objectValue:
|
||||
{
|
||||
Value::Members members( value.getMemberNames() );
|
||||
document_ += "{";
|
||||
for ( Value::Members::iterator it = members.begin();
|
||||
it != members.end();
|
||||
++it )
|
||||
{
|
||||
const std::string &name = *it;
|
||||
if ( it != members.begin() )
|
||||
document_ += ",";
|
||||
document_ += valueToQuotedString( name.c_str() );
|
||||
document_ += yamlCompatiblityEnabled_ ? ": "
|
||||
: ":";
|
||||
writeValue( value[name] );
|
||||
}
|
||||
document_ += "}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Class StyledWriter
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
StyledWriter::StyledWriter()
|
||||
: rightMargin_( 74 )
|
||||
, indentSize_( 3 )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
StyledWriter::write( const Value &root )
|
||||
{
|
||||
document_ = "";
|
||||
addChildValues_ = false;
|
||||
indentString_ = "";
|
||||
writeCommentBeforeValue( root );
|
||||
writeValue( root );
|
||||
writeCommentAfterValueOnSameLine( root );
|
||||
document_ += "\n";
|
||||
return document_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::writeValue( const Value &value )
|
||||
{
|
||||
switch ( value.type() )
|
||||
{
|
||||
case nullValue:
|
||||
pushValue( "null" );
|
||||
break;
|
||||
case intValue:
|
||||
pushValue( valueToString( value.asInt() ) );
|
||||
break;
|
||||
case uintValue:
|
||||
pushValue( valueToString( value.asUInt() ) );
|
||||
break;
|
||||
case realValue:
|
||||
pushValue( valueToString( value.asDouble() ) );
|
||||
break;
|
||||
case stringValue:
|
||||
pushValue( valueToQuotedString( value.asCString() ) );
|
||||
break;
|
||||
case booleanValue:
|
||||
pushValue( valueToString( value.asBool() ) );
|
||||
break;
|
||||
case arrayValue:
|
||||
writeArrayValue( value);
|
||||
break;
|
||||
case objectValue:
|
||||
{
|
||||
Value::Members members( value.getMemberNames() );
|
||||
if ( members.empty() )
|
||||
pushValue( "{}" );
|
||||
else
|
||||
{
|
||||
writeWithIndent( "{" );
|
||||
indent();
|
||||
Value::Members::iterator it = members.begin();
|
||||
while ( true )
|
||||
{
|
||||
const std::string &name = *it;
|
||||
const Value &childValue = value[name];
|
||||
writeCommentBeforeValue( childValue );
|
||||
writeWithIndent( valueToQuotedString( name.c_str() ) );
|
||||
document_ += " : ";
|
||||
writeValue( childValue );
|
||||
if ( ++it == members.end() )
|
||||
{
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
break;
|
||||
}
|
||||
document_ += ",";
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
}
|
||||
unindent();
|
||||
writeWithIndent( "}" );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::writeArrayValue( const Value &value )
|
||||
{
|
||||
unsigned size = value.size();
|
||||
if ( size == 0 )
|
||||
pushValue( "[]" );
|
||||
else
|
||||
{
|
||||
bool isArrayMultiLine = isMultineArray( value );
|
||||
if ( isArrayMultiLine )
|
||||
{
|
||||
writeWithIndent( "[" );
|
||||
indent();
|
||||
bool hasChildValue = !childValues_.empty();
|
||||
unsigned index =0;
|
||||
while ( true )
|
||||
{
|
||||
const Value &childValue = value[index];
|
||||
writeCommentBeforeValue( childValue );
|
||||
if ( hasChildValue )
|
||||
writeWithIndent( childValues_[index] );
|
||||
else
|
||||
{
|
||||
writeIndent();
|
||||
writeValue( childValue );
|
||||
}
|
||||
if ( ++index == size )
|
||||
{
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
break;
|
||||
}
|
||||
document_ += ",";
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
}
|
||||
unindent();
|
||||
writeWithIndent( "]" );
|
||||
}
|
||||
else // output on a single line
|
||||
{
|
||||
assert( childValues_.size() == size );
|
||||
document_ += "[ ";
|
||||
for ( unsigned index =0; index < size; ++index )
|
||||
{
|
||||
if ( index > 0 )
|
||||
document_ += ", ";
|
||||
document_ += childValues_[index];
|
||||
}
|
||||
document_ += " ]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StyledWriter::isMultineArray( const Value &value )
|
||||
{
|
||||
int size = value.size();
|
||||
bool isMultiLine = size*3 >= rightMargin_ ;
|
||||
childValues_.clear();
|
||||
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||
{
|
||||
const Value &childValue = value[index];
|
||||
isMultiLine = isMultiLine ||
|
||||
( (childValue.isArray() || childValue.isObject()) &&
|
||||
childValue.size() > 0 );
|
||||
}
|
||||
if ( !isMultiLine ) // check if line length > max line length
|
||||
{
|
||||
childValues_.reserve( size );
|
||||
addChildValues_ = true;
|
||||
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
|
||||
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||
{
|
||||
writeValue( value[index] );
|
||||
lineLength += int( childValues_[index].length() );
|
||||
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
|
||||
}
|
||||
addChildValues_ = false;
|
||||
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||
}
|
||||
return isMultiLine;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::pushValue( const std::string &value )
|
||||
{
|
||||
if ( addChildValues_ )
|
||||
childValues_.push_back( value );
|
||||
else
|
||||
document_ += value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::writeIndent()
|
||||
{
|
||||
if ( !document_.empty() )
|
||||
{
|
||||
char last = document_[document_.length()-1];
|
||||
if ( last == ' ' ) // already indented
|
||||
return;
|
||||
if ( last != '\n' ) // Comments may add new-line
|
||||
document_ += '\n';
|
||||
}
|
||||
document_ += indentString_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::writeWithIndent( const std::string &value )
|
||||
{
|
||||
writeIndent();
|
||||
document_ += value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::indent()
|
||||
{
|
||||
indentString_ += std::string( indentSize_, ' ' );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::unindent()
|
||||
{
|
||||
assert( int(indentString_.size()) >= indentSize_ );
|
||||
indentString_.resize( indentString_.size() - indentSize_ );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::writeCommentBeforeValue( const Value &root )
|
||||
{
|
||||
if ( !root.hasComment( commentBefore ) )
|
||||
return;
|
||||
document_ += normalizeEOL( root.getComment( commentBefore ) );
|
||||
document_ += "\n";
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
|
||||
{
|
||||
if ( root.hasComment( commentAfterOnSameLine ) )
|
||||
document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
|
||||
|
||||
if ( root.hasComment( commentAfter ) )
|
||||
{
|
||||
document_ += "\n";
|
||||
document_ += normalizeEOL( root.getComment( commentAfter ) );
|
||||
document_ += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StyledWriter::hasCommentForValue( const Value &value )
|
||||
{
|
||||
return value.hasComment( commentBefore )
|
||||
|| value.hasComment( commentAfterOnSameLine )
|
||||
|| value.hasComment( commentAfter );
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
StyledWriter::normalizeEOL( const std::string &text )
|
||||
{
|
||||
std::string normalized;
|
||||
normalized.reserve( text.length() );
|
||||
const char *begin = text.c_str();
|
||||
const char *end = begin + text.length();
|
||||
const char *current = begin;
|
||||
while ( current != end )
|
||||
{
|
||||
char c = *current++;
|
||||
if ( c == '\r' ) // mac or dos EOL
|
||||
{
|
||||
if ( *current == '\n' ) // convert dos EOL
|
||||
++current;
|
||||
normalized += '\n';
|
||||
}
|
||||
else // handle unix EOL & other char
|
||||
normalized += c;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
// Class StyledStreamWriter
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
|
||||
StyledStreamWriter::StyledStreamWriter( std::string indentation )
|
||||
: document_(NULL)
|
||||
, rightMargin_( 74 )
|
||||
, indentation_( indentation )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::write( std::ostream &out, const Value &root )
|
||||
{
|
||||
document_ = &out;
|
||||
addChildValues_ = false;
|
||||
indentString_ = "";
|
||||
writeCommentBeforeValue( root );
|
||||
writeValue( root );
|
||||
writeCommentAfterValueOnSameLine( root );
|
||||
*document_ << "\n";
|
||||
document_ = NULL; // Forget the stream, for safety.
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::writeValue( const Value &value )
|
||||
{
|
||||
switch ( value.type() )
|
||||
{
|
||||
case nullValue:
|
||||
pushValue( "null" );
|
||||
break;
|
||||
case intValue:
|
||||
pushValue( valueToString( value.asInt() ) );
|
||||
break;
|
||||
case uintValue:
|
||||
pushValue( valueToString( value.asUInt() ) );
|
||||
break;
|
||||
case realValue:
|
||||
pushValue( valueToString( value.asDouble() ) );
|
||||
break;
|
||||
case stringValue:
|
||||
pushValue( valueToQuotedString( value.asCString() ) );
|
||||
break;
|
||||
case booleanValue:
|
||||
pushValue( valueToString( value.asBool() ) );
|
||||
break;
|
||||
case arrayValue:
|
||||
writeArrayValue( value);
|
||||
break;
|
||||
case objectValue:
|
||||
{
|
||||
Value::Members members( value.getMemberNames() );
|
||||
if ( members.empty() )
|
||||
pushValue( "{}" );
|
||||
else
|
||||
{
|
||||
writeWithIndent( "{" );
|
||||
indent();
|
||||
Value::Members::iterator it = members.begin();
|
||||
while ( true )
|
||||
{
|
||||
const std::string &name = *it;
|
||||
const Value &childValue = value[name];
|
||||
writeCommentBeforeValue( childValue );
|
||||
writeWithIndent( valueToQuotedString( name.c_str() ) );
|
||||
*document_ << " : ";
|
||||
writeValue( childValue );
|
||||
if ( ++it == members.end() )
|
||||
{
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
break;
|
||||
}
|
||||
*document_ << ",";
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
}
|
||||
unindent();
|
||||
writeWithIndent( "}" );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::writeArrayValue( const Value &value )
|
||||
{
|
||||
unsigned size = value.size();
|
||||
if ( size == 0 )
|
||||
pushValue( "[]" );
|
||||
else
|
||||
{
|
||||
bool isArrayMultiLine = isMultineArray( value );
|
||||
if ( isArrayMultiLine )
|
||||
{
|
||||
writeWithIndent( "[" );
|
||||
indent();
|
||||
bool hasChildValue = !childValues_.empty();
|
||||
unsigned index =0;
|
||||
while ( true )
|
||||
{
|
||||
const Value &childValue = value[index];
|
||||
writeCommentBeforeValue( childValue );
|
||||
if ( hasChildValue )
|
||||
writeWithIndent( childValues_[index] );
|
||||
else
|
||||
{
|
||||
writeIndent();
|
||||
writeValue( childValue );
|
||||
}
|
||||
if ( ++index == size )
|
||||
{
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
break;
|
||||
}
|
||||
*document_ << ",";
|
||||
writeCommentAfterValueOnSameLine( childValue );
|
||||
}
|
||||
unindent();
|
||||
writeWithIndent( "]" );
|
||||
}
|
||||
else // output on a single line
|
||||
{
|
||||
assert( childValues_.size() == size );
|
||||
*document_ << "[ ";
|
||||
for ( unsigned index =0; index < size; ++index )
|
||||
{
|
||||
if ( index > 0 )
|
||||
*document_ << ", ";
|
||||
*document_ << childValues_[index];
|
||||
}
|
||||
*document_ << " ]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StyledStreamWriter::isMultineArray( const Value &value )
|
||||
{
|
||||
int size = value.size();
|
||||
bool isMultiLine = size*3 >= rightMargin_ ;
|
||||
childValues_.clear();
|
||||
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||
{
|
||||
const Value &childValue = value[index];
|
||||
isMultiLine = isMultiLine ||
|
||||
( (childValue.isArray() || childValue.isObject()) &&
|
||||
childValue.size() > 0 );
|
||||
}
|
||||
if ( !isMultiLine ) // check if line length > max line length
|
||||
{
|
||||
childValues_.reserve( size );
|
||||
addChildValues_ = true;
|
||||
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
|
||||
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||
{
|
||||
writeValue( value[index] );
|
||||
lineLength += int( childValues_[index].length() );
|
||||
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
|
||||
}
|
||||
addChildValues_ = false;
|
||||
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||
}
|
||||
return isMultiLine;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::pushValue( const std::string &value )
|
||||
{
|
||||
if ( addChildValues_ )
|
||||
childValues_.push_back( value );
|
||||
else
|
||||
*document_ << value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::writeIndent()
|
||||
{
|
||||
/*
|
||||
Some comments in this method would have been nice. ;-)
|
||||
|
||||
if ( !document_.empty() )
|
||||
{
|
||||
char last = document_[document_.length()-1];
|
||||
if ( last == ' ' ) // already indented
|
||||
return;
|
||||
if ( last != '\n' ) // Comments may add new-line
|
||||
*document_ << '\n';
|
||||
}
|
||||
*/
|
||||
*document_ << '\n' << indentString_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::writeWithIndent( const std::string &value )
|
||||
{
|
||||
writeIndent();
|
||||
*document_ << value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::indent()
|
||||
{
|
||||
indentString_ += indentation_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::unindent()
|
||||
{
|
||||
assert( indentString_.size() >= indentation_.size() );
|
||||
indentString_.resize( indentString_.size() - indentation_.size() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
|
||||
{
|
||||
if ( !root.hasComment( commentBefore ) )
|
||||
return;
|
||||
*document_ << normalizeEOL( root.getComment( commentBefore ) );
|
||||
*document_ << "\n";
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
|
||||
{
|
||||
if ( root.hasComment( commentAfterOnSameLine ) )
|
||||
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
|
||||
|
||||
if ( root.hasComment( commentAfter ) )
|
||||
{
|
||||
*document_ << "\n";
|
||||
*document_ << normalizeEOL( root.getComment( commentAfter ) );
|
||||
*document_ << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StyledStreamWriter::hasCommentForValue( const Value &value )
|
||||
{
|
||||
return value.hasComment( commentBefore )
|
||||
|| value.hasComment( commentAfterOnSameLine )
|
||||
|| value.hasComment( commentAfter );
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
StyledStreamWriter::normalizeEOL( const std::string &text )
|
||||
{
|
||||
std::string normalized;
|
||||
normalized.reserve( text.length() );
|
||||
const char *begin = text.c_str();
|
||||
const char *end = begin + text.length();
|
||||
const char *current = begin;
|
||||
while ( current != end )
|
||||
{
|
||||
char c = *current++;
|
||||
if ( c == '\r' ) // mac or dos EOL
|
||||
{
|
||||
if ( *current == '\n' ) // convert dos EOL
|
||||
++current;
|
||||
normalized += '\n';
|
||||
}
|
||||
else // handle unix EOL & other char
|
||||
normalized += c;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<( std::ostream &sout, const Value &root )
|
||||
{
|
||||
Json::StyledStreamWriter writer;
|
||||
writer.write(sout, root);
|
||||
return sout;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Json
|
|
@ -0,0 +1,8 @@
|
|||
Import( 'env buildLibrary' )
|
||||
|
||||
buildLibrary( env, Split( """
|
||||
json_reader.cpp
|
||||
json_value.cpp
|
||||
json_writer.cpp
|
||||
""" ),
|
||||
'json' )
|
|
@ -0,0 +1,78 @@
|
|||
##===- polly/lib/Makefile -----------------------*- Makefile -*-===##
|
||||
|
||||
#
|
||||
# Indicate where we are relative to the top of the source tree.
|
||||
#
|
||||
LEVEL :=..
|
||||
|
||||
DIRS = Support
|
||||
USEDLIBS = pollysupport.a
|
||||
|
||||
LIBRARYNAME=LLVMPolly
|
||||
LOADABLE_MODULE = 1
|
||||
|
||||
include $(LEVEL)/Makefile.config
|
||||
|
||||
CPP.Flags += $(POLLY_INC)
|
||||
|
||||
DIRS += Exchange
|
||||
USEDLIBS += pollyexchange.a
|
||||
|
||||
DIRS += Analysis
|
||||
USEDLIBS += pollyanalysis.a
|
||||
|
||||
DIRS += JSON
|
||||
USEDLIBS += pollyjson.a
|
||||
|
||||
# TODO: Export symbols for RTTI or EH?
|
||||
|
||||
#
|
||||
# Include Makefile.common so we know what to do.
|
||||
#
|
||||
include $(LEVEL)/Makefile.common
|
||||
|
||||
LIBS += $(POLLY_LD) $(POLLY_LIB)
|
||||
|
||||
$(LibDir)/libpollyanalysis.a : $(LibDir)/.dir $(PROJ_OBJ_DIR)/Analysis/$(BuildMode)/.dir \
|
||||
$(PROJ_SRC_DIR)/Analysis/*
|
||||
$(Verb) if [ -d $(PROJ_SRC_DIR)/Analysis ]; then\
|
||||
if ([ ! -f Analysis/Makefile ] || \
|
||||
command test Analysis/Makefile -ot $(PROJ_SRC_DIR)/Analysis/Makefile ); then \
|
||||
$(MKDIR) Analysis; \
|
||||
$(CP) $(PROJ_SRC_DIR)/Analysis/Makefile Analysis/Makefile; \
|
||||
fi; \
|
||||
($(MAKE) -C Analysis $@ ) || exit 1; \
|
||||
fi
|
||||
|
||||
$(LibDir)/libpollyexchange.a : $(LibDir)/.dir $(PROJ_OBJ_DIR)/Exchange/$(BuildMode)/.dir \
|
||||
$(PROJ_SRC_DIR)/Exchange/*
|
||||
$(Verb) if [ -d $(PROJ_SRC_DIR)/Exchange ]; then\
|
||||
if ([ ! -f Exchange/Makefile ] || \
|
||||
command test Exchange/Makefile -ot $(PROJ_SRC_DIR)/Exchange/Makefile ); then \
|
||||
$(MKDIR) Exchange; \
|
||||
$(CP) $(PROJ_SRC_DIR)/Exchange/Makefile Exchange/Makefile; \
|
||||
fi; \
|
||||
($(MAKE) -C Exchange $@ ) || exit 1; \
|
||||
fi
|
||||
|
||||
$(LibDir)/libpollysupport.a : $(LibDir)/.dir $(PROJ_OBJ_DIR)/Support/$(BuildMode)/.dir \
|
||||
$(PROJ_SRC_DIR)/Support/*
|
||||
$(Verb) if [ -d $(PROJ_SRC_DIR)/Support ]; then\
|
||||
if ([ ! -f Support/Makefile ] || \
|
||||
command test Support/Makefile -ot $(PROJ_SRC_DIR)/Support/Makefile ); then \
|
||||
$(MKDIR) Support; \
|
||||
$(CP) $(PROJ_SRC_DIR)/Support/Makefile Support/Makefile; \
|
||||
fi; \
|
||||
($(MAKE) -C Support $@ ) || exit 1; \
|
||||
fi
|
||||
|
||||
$(LibDir)/libpollyjson.a : $(LibDir)/.dir $(PROJ_OBJ_DIR)/JSON/$(BuildMode)/.dir \
|
||||
$(PROJ_SRC_DIR)/JSON/*
|
||||
$(Verb) if [ -d $(PROJ_SRC_DIR)/JSON ]; then\
|
||||
if ([ ! -f JSON/Makefile ] || \
|
||||
command test JSON/Makefile -ot $(PROJ_SRC_DIR)/JSON/Makefile ); then \
|
||||
$(MKDIR) JSON; \
|
||||
$(CP) $(PROJ_SRC_DIR)/JSON/Makefile JSON/Makefile; \
|
||||
fi; \
|
||||
($(MAKE) -C JSON $@ ) || exit 1; \
|
||||
fi
|
|
@ -0,0 +1,92 @@
|
|||
//===---------- MayAliasSet.cpp - May-Alais Set for base pointers --------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the MayAliasSet class
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/TempScopInfo.h"
|
||||
#include "polly/MayAliasSet.h"
|
||||
|
||||
#include "llvm/LLVMContext.h"
|
||||
#include "llvm/Analysis/RegionInfo.h"
|
||||
#include "llvm/Analysis/RegionIterator.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/AliasSetTracker.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
void MayAliasSet::print(raw_ostream &OS) const {
|
||||
OS << "Must alias {";
|
||||
|
||||
for (const_iterator I = mustalias_begin(), E = mustalias_end(); I != E; ++I) {
|
||||
WriteAsOperand(OS, *I, false);
|
||||
OS << ", ";
|
||||
}
|
||||
|
||||
OS << "} May alias {";
|
||||
OS << '}';
|
||||
}
|
||||
|
||||
void MayAliasSet::dump() const {
|
||||
print(dbgs());
|
||||
}
|
||||
|
||||
void MayAliasSetInfo::buildMayAliasSets(TempScop &Scop, AliasAnalysis &AA) {
|
||||
AliasSetTracker AST(AA);
|
||||
Region &MaxR = Scop.getMaxRegion();
|
||||
|
||||
// Find out all base pointers that appeared in Scop and build the Alias set.
|
||||
// Note: We may build the alias sets while we are building access functions
|
||||
// to obtain better performance.
|
||||
for (Region::block_iterator I = MaxR.block_begin(), E = MaxR.block_end();
|
||||
I != E; ++I) {
|
||||
BasicBlock *BB = I->getNodeAs<BasicBlock>();
|
||||
if (const AccFuncSetType *AFS = Scop.getAccessFunctions(BB)) {
|
||||
for (AccFuncSetType::const_iterator AI = AFS->begin(), AE = AFS->end();
|
||||
AI != AE; ++AI) {
|
||||
const SCEVAffFunc &AccFunc = AI->first;
|
||||
Instruction *Inst = AI->second;
|
||||
Value *BaseAddr = const_cast<Value*>(AccFunc.getBaseAddr());
|
||||
|
||||
AST.add(BaseAddr, AliasAnalysis::UnknownSize,
|
||||
Inst->getMetadata(LLVMContext::MD_tbaa));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the may-alias set with the AliasSetTracker.
|
||||
for (AliasSetTracker::iterator I = AST.begin(), E = AST.end(); I != E; ++I) {
|
||||
AliasSet &AS = *I;
|
||||
|
||||
// Ignore the dummy alias set.
|
||||
if (AS.isForwardingAliasSet()) continue;
|
||||
|
||||
// The most simple case: All pointers in the set must-alias each others.
|
||||
if (AS.isMustAlias()) {
|
||||
MayAliasSet *MayAS = new (MayASAllocator.Allocate()) MayAliasSet();
|
||||
|
||||
for (AliasSet::iterator PI = AS.begin(), PE = AS.end(); PI != PE; ++PI) {
|
||||
Value *Ptr = PI.getPointer();
|
||||
|
||||
MayAS->addMustAliasPtr(Ptr);
|
||||
BasePtrMap.insert(std::make_pair(Ptr, MayAS));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(0 && "SCoPDetection pass should not allow May-Alias set!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
//===- Pocc.cpp - Pocc interface ----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Pocc[1] interface.
|
||||
//
|
||||
// Pocc, the polyhedral compilation collection is a collection of polyhedral
|
||||
// tools. It is used as an optimizer in polly
|
||||
//
|
||||
// [1] http://www-roc.inria.fr/~pouchet/software/pocc/
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/Cloog.h"
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#ifdef SCOPLIB_FOUND
|
||||
#include "polly/ScopInfo.h"
|
||||
#include "polly/Dependences.h"
|
||||
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
|
||||
#include "polly/ScopLib.h"
|
||||
|
||||
#include "isl/dim.h"
|
||||
#include "isl/map.h"
|
||||
#include "isl/constraint.h"
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
static cl::opt<bool>
|
||||
PlutoTile("enable-pluto-tile",
|
||||
cl::desc("Enable pluto tiling for polly"), cl::Hidden,
|
||||
cl::value_desc("Pluto tiling enabled if true"),
|
||||
cl::init(false));
|
||||
static cl::opt<bool>
|
||||
PlutoPrevector("enable-pluto-prevector",
|
||||
cl::desc("Enable pluto prevectorization for polly"), cl::Hidden,
|
||||
cl::value_desc("Pluto prevectorization enabled if true"),
|
||||
cl::init(false));
|
||||
static cl::opt<std::string>
|
||||
PlutoFuse("pluto-fuse",
|
||||
cl::desc(""), cl::Hidden,
|
||||
cl::value_desc("Set fuse mode of Pluto"),
|
||||
cl::init("maxfuse"));
|
||||
|
||||
namespace {
|
||||
|
||||
class Pocc : public ScopPass {
|
||||
sys::Path plutoStderr;
|
||||
sys::Path plutoStdout;
|
||||
std::vector<const char*> arguments;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
explicit Pocc() : ScopPass(ID) {}
|
||||
|
||||
std::string getFileName(Region *R) const;
|
||||
virtual bool runOnScop(Scop &S);
|
||||
void printScop(llvm::raw_ostream &OS) const;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
char Pocc::ID = 0;
|
||||
bool Pocc::runOnScop(Scop &S) {
|
||||
Dependences *D = &getAnalysis<Dependences>();
|
||||
|
||||
// Only the final read statement in the SCoP. No need to optimize anything.
|
||||
// (In case we would try, Pocc complains that there is no statement in the
|
||||
// SCoP).
|
||||
if (S.begin() + 1 == S.end())
|
||||
return false;
|
||||
|
||||
// Create the scop file.
|
||||
sys::Path tempDir = sys::Path::GetTemporaryDirectory();
|
||||
sys::Path scopFile = tempDir;
|
||||
scopFile.appendComponent("polly.scop");
|
||||
scopFile.createFileOnDisk();
|
||||
|
||||
FILE *F = fopen(scopFile.c_str(), "w");
|
||||
|
||||
arguments.clear();
|
||||
|
||||
if (!F) {
|
||||
errs() << "Cannot open file: " << tempDir.c_str() << "\n";
|
||||
errs() << "Skipping export.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopLib scoplib(&S);
|
||||
scoplib.print(F);
|
||||
fclose(F);
|
||||
|
||||
// Execute pocc
|
||||
sys::Program program;
|
||||
|
||||
sys::Path pocc = sys::Program::FindProgramByName("pocc");
|
||||
|
||||
arguments.push_back("pocc");
|
||||
arguments.push_back("--read-scop");
|
||||
arguments.push_back(scopFile.c_str());
|
||||
arguments.push_back("--pluto-tile-scat");
|
||||
arguments.push_back("--candl-dep-isl-simp");
|
||||
arguments.push_back("--cloogify-scheds");
|
||||
arguments.push_back("--output-scop");
|
||||
arguments.push_back("--pluto");
|
||||
arguments.push_back("--pluto-bounds");
|
||||
arguments.push_back("10");
|
||||
arguments.push_back("--pluto-fuse");
|
||||
|
||||
arguments.push_back(PlutoFuse.c_str());
|
||||
|
||||
if (PlutoTile)
|
||||
arguments.push_back("--pluto-tile");
|
||||
|
||||
if (PlutoPrevector)
|
||||
arguments.push_back("--pluto-prevector");
|
||||
|
||||
arguments.push_back(0);
|
||||
|
||||
plutoStdout = tempDir;
|
||||
plutoStdout.appendComponent("pluto.stdout");
|
||||
plutoStderr = tempDir;
|
||||
plutoStderr.appendComponent("pluto.stderr");
|
||||
|
||||
std::vector<sys::Path*> redirect;
|
||||
redirect.push_back(0);
|
||||
redirect.push_back(&plutoStdout);
|
||||
redirect.push_back(&plutoStderr);
|
||||
|
||||
program.ExecuteAndWait(pocc, &arguments[0], 0,
|
||||
(sys::Path const **) &redirect[0]);
|
||||
|
||||
// Read the created scop file
|
||||
sys::Path newScopFile = tempDir;
|
||||
newScopFile.appendComponent("polly.pocc.c.scop");
|
||||
|
||||
FILE *poccFile = fopen(newScopFile.c_str(), "r");
|
||||
ScopLib newScoplib(&S, poccFile, D);
|
||||
|
||||
if (!newScoplib.updateScattering()) {
|
||||
errs() << "Failure when calculating the optimization with "
|
||||
"the following command: ";
|
||||
for (std::vector<const char*>::const_iterator AI = arguments.begin(),
|
||||
AE = arguments.end(); AI != AE; ++AI)
|
||||
if (*AI)
|
||||
errs() << " " << *AI;
|
||||
errs() << "\n";
|
||||
return false;
|
||||
} else
|
||||
fclose(poccFile);
|
||||
|
||||
if (!PlutoPrevector)
|
||||
return false;
|
||||
|
||||
// Find the innermost dimension that is not a constant dimension. This
|
||||
// dimension will be vectorized.
|
||||
unsigned scatterDims = S.getScatterDim();
|
||||
int lastLoop = scatterDims - 1;
|
||||
|
||||
while (lastLoop) {
|
||||
bool isSingleValued = true;
|
||||
|
||||
for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
|
||||
if ((*SI)->isFinalRead())
|
||||
continue;
|
||||
|
||||
isl_map *scat = isl_map_copy((*SI)->getScattering());
|
||||
isl_map *projected = isl_map_project_out(scat, isl_dim_out, lastLoop,
|
||||
scatterDims - lastLoop);
|
||||
|
||||
if (!isl_map_is_bijective(projected)) {
|
||||
isSingleValued = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSingleValued)
|
||||
break;
|
||||
|
||||
lastLoop--;
|
||||
}
|
||||
|
||||
// Strip mine the innermost loop.
|
||||
for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
|
||||
if ((*SI)->isFinalRead())
|
||||
continue;
|
||||
isl_map *scat = (*SI)->getScattering();
|
||||
|
||||
int scatDims = isl_map_n_out(scat);
|
||||
isl_dim *dim = isl_dim_alloc(S.getCtx(), S.getNumParams(), scatDims,
|
||||
scatDims + 1);
|
||||
isl_basic_map *map = isl_basic_map_universe(isl_dim_copy(dim));
|
||||
|
||||
for (int i = 0; i <= lastLoop - 1; i++) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_in, i, 1);
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_out, i, -1);
|
||||
|
||||
map = isl_basic_map_add_constraint(map, c);
|
||||
}
|
||||
|
||||
for (int i = lastLoop; i < scatDims; i++) {
|
||||
isl_constraint *c = isl_equality_alloc(isl_dim_copy(dim));
|
||||
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_in, i, 1);
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_out, i + 1, -1);
|
||||
|
||||
map = isl_basic_map_add_constraint(map, c);
|
||||
}
|
||||
|
||||
isl_constraint *c;
|
||||
|
||||
int vectorWidth = 4;
|
||||
c = isl_inequality_alloc(isl_dim_copy(dim));
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_out, lastLoop, -vectorWidth);
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_out, lastLoop + 1, 1);
|
||||
map = isl_basic_map_add_constraint(map, c);
|
||||
|
||||
c = isl_inequality_alloc(isl_dim_copy(dim));
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_out, lastLoop, vectorWidth);
|
||||
isl_constraint_set_coefficient_si(c, isl_dim_out, lastLoop + 1, -1);
|
||||
isl_constraint_set_constant_si(c, vectorWidth - 1);
|
||||
map = isl_basic_map_add_constraint(map, c);
|
||||
|
||||
isl_map *transform = isl_map_from_basic_map(map);
|
||||
transform = isl_map_set_tuple_name(transform, isl_dim_out, "scattering");
|
||||
transform = isl_map_set_tuple_name(transform, isl_dim_in, "scattering");
|
||||
|
||||
scat = isl_map_apply_range(scat, isl_map_copy(transform));
|
||||
(*SI)->setScattering(scat);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Pocc::printScop(raw_ostream &OS) const {
|
||||
OwningPtr<MemoryBuffer> stdoutBuffer;
|
||||
OwningPtr<MemoryBuffer> stderrBuffer;
|
||||
|
||||
OS << "Command line: ";
|
||||
|
||||
for (std::vector<const char*>::const_iterator AI = arguments.begin(),
|
||||
AE = arguments.end(); AI != AE; ++AI)
|
||||
if (*AI)
|
||||
OS << " " << *AI;
|
||||
|
||||
OS << "\n";
|
||||
|
||||
if (error_code ec = MemoryBuffer::getFile(plutoStdout.c_str(), stdoutBuffer))
|
||||
OS << "Could not open pocc stdout file: " + ec.message();
|
||||
else {
|
||||
OS << "pocc stdout: " << stdoutBuffer->getBufferIdentifier() << "\n";
|
||||
OS << stdoutBuffer->getBuffer() << "\n";
|
||||
}
|
||||
|
||||
if (error_code ec = MemoryBuffer::getFile(plutoStderr.c_str(), stderrBuffer))
|
||||
OS << "Could not open pocc stderr file: " + ec.message();
|
||||
else {
|
||||
OS << "pocc stderr: " << plutoStderr.c_str() << "\n";
|
||||
OS << stderrBuffer->getBuffer() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void Pocc::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
ScopPass::getAnalysisUsage(AU);
|
||||
AU.addRequired<Dependences>();
|
||||
}
|
||||
|
||||
static RegisterPass<Pocc> A("polly-optimize",
|
||||
"Polly - Optimize the scop using pocc");
|
||||
|
||||
Pass* polly::createPoccPass() {
|
||||
return new Pocc();
|
||||
}
|
||||
#endif /* SCOPLIB_FOUND */
|
|
@ -0,0 +1,219 @@
|
|||
//===- RegionSimplify.cpp -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file converts refined regions detected by the RegionInfo analysis
|
||||
// into simple regions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/LinkAllPasses.h"
|
||||
|
||||
#include "llvm/Instructions.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/Dominators.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/RegionPass.h"
|
||||
#include "llvm/Analysis/RegionInfo.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
|
||||
#define DEBUG_TYPE "region-simplify"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
STATISTIC(NumEntries, "The # of created entry edges");
|
||||
STATISTIC(NumExits, "The # of created exit edges");
|
||||
|
||||
namespace {
|
||||
class RegionSimplify: public RegionPass {
|
||||
// Remember the modified region.
|
||||
Region *r;
|
||||
void createSingleEntryEdge(Region *R);
|
||||
void createSingleExitEdge(Region *R);
|
||||
public:
|
||||
static char ID;
|
||||
explicit RegionSimplify() : RegionPass(ID), r(0) {}
|
||||
|
||||
virtual void print(raw_ostream &O, const Module *M) const;
|
||||
|
||||
virtual bool runOnRegion(Region *R, RGPassManager &RGM);
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||
};
|
||||
}
|
||||
|
||||
static RegisterPass<RegionSimplify>
|
||||
X("polly-region-simplify", "Transform refined regions into simple regions");
|
||||
|
||||
char RegionSimplify::ID = 0;
|
||||
namespace polly {
|
||||
Pass *createRegionSimplifyPass() {
|
||||
return new RegionSimplify();
|
||||
}
|
||||
}
|
||||
|
||||
void RegionSimplify::print(raw_ostream &O, const Module *M) const {
|
||||
if (r == 0) return;
|
||||
|
||||
BasicBlock *enteringBlock = r->getEnteringBlock();
|
||||
BasicBlock *exitingBlock = r->getExitingBlock();
|
||||
|
||||
O << "Region: " << r->getNameStr() << " Edges:\t";
|
||||
|
||||
if (enteringBlock)
|
||||
O << "Entering: [" << enteringBlock->getNameStr() << " -> "
|
||||
<< r->getEntry()->getName() << "], ";
|
||||
|
||||
if (exitingBlock) {
|
||||
O << "Exiting: [" << exitingBlock->getNameStr() << " -> ";
|
||||
if (r->getExit())
|
||||
O << r->getExit()->getName();
|
||||
else
|
||||
O << "<return>";
|
||||
O << "]";
|
||||
}
|
||||
|
||||
O << "\n";
|
||||
}
|
||||
|
||||
void RegionSimplify::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
// Function SplitBlockPredecessors currently updates/preserves AliasAnalysis,
|
||||
/// DominatorTree, LoopInfo, and LCCSA but no other analyses.
|
||||
//AU.addPreserved<AliasAnalysis>(); Break SCEV-AA
|
||||
AU.addPreserved<DominatorTree> ();
|
||||
AU.addPreserved<LoopInfo>();
|
||||
AU.addPreservedID(LCSSAID);
|
||||
|
||||
AU.addPreserved<RegionInfo> ();
|
||||
AU.addRequired<RegionInfo> ();
|
||||
}
|
||||
|
||||
// createSingleEntryEdge - Split the entry basic block of the given
|
||||
// region after the last PHINode to form a single entry edge.
|
||||
void RegionSimplify::createSingleEntryEdge(Region *R) {
|
||||
BasicBlock *oldEntry = R->getEntry();
|
||||
SmallVector<BasicBlock*, 4> Preds;
|
||||
for (pred_iterator PI = pred_begin(oldEntry), PE = pred_end(oldEntry);
|
||||
PI != PE; ++PI)
|
||||
if (!R->contains(*PI))
|
||||
Preds.push_back(*PI);
|
||||
|
||||
assert(Preds.size() && "This region has already a single entry edge");
|
||||
|
||||
BasicBlock *newEntry = SplitBlockPredecessors(oldEntry,
|
||||
Preds.data(), Preds.size(),
|
||||
".single_entry", this);
|
||||
|
||||
RegionInfo *RI = &getAnalysis<RegionInfo> ();
|
||||
// We do not update entry node for children of this region.
|
||||
// This make it easier to extract children regions because they do not share
|
||||
// the entry node with their parents.
|
||||
// all parent regions whose entry is oldEntry are updated with newEntry
|
||||
Region *r = R->getParent();
|
||||
|
||||
// Put the new entry to R's parent.
|
||||
RI->setRegionFor(newEntry,r);
|
||||
|
||||
while (r->getEntry() == oldEntry && !r->isTopLevelRegion()) {
|
||||
r->replaceEntry(newEntry);
|
||||
r = r->getParent();
|
||||
}
|
||||
|
||||
// We do not update exit node for children of this region for the same reason
|
||||
// of not updating entry node.
|
||||
// All other regions whose exit is oldEntry are updated with new exit node
|
||||
r = RI->getTopLevelRegion();
|
||||
std::vector<Region *> RQ;
|
||||
RQ.push_back(r);
|
||||
|
||||
while (!RQ.empty()){
|
||||
r = RQ.back();
|
||||
RQ.pop_back();
|
||||
|
||||
for (Region::const_iterator RI = r->begin(), RE = r->end(); RI!=RE; ++RI)
|
||||
RQ.push_back(*RI);
|
||||
|
||||
if (r->getExit() == oldEntry && !R->contains(r))
|
||||
r->replaceExit(newEntry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// createSingleExitEdge - Split the exit basic of the given region
|
||||
// to form a single exit edge.
|
||||
void RegionSimplify::createSingleExitEdge(Region *R) {
|
||||
BasicBlock *oldExit = R->getExit();
|
||||
|
||||
SmallVector<BasicBlock*, 4> Preds;
|
||||
for (pred_iterator PI = pred_begin(oldExit), PE = pred_end(oldExit);
|
||||
PI != PE; ++PI)
|
||||
if (R->contains(*PI))
|
||||
Preds.push_back(*PI);
|
||||
|
||||
DEBUG(dbgs() << "Going to create single exit for:\n");
|
||||
DEBUG(R->print(dbgs(), true, 0, Region::PrintRN));
|
||||
BasicBlock *newExit = SplitBlockPredecessors(oldExit,
|
||||
Preds.data(), Preds.size(),
|
||||
".single_exit", this);
|
||||
RegionInfo *RI = &getAnalysis<RegionInfo>();
|
||||
|
||||
// We do not need to update entry nodes because this split happens inside
|
||||
// this region and it affects only this region and all of its children.
|
||||
// The new split node belongs to this region
|
||||
RI->setRegionFor(newExit,R);
|
||||
DEBUG(dbgs() << "Adding new exiting block: " << newExit->getName() << '\n');
|
||||
|
||||
// all children of this region whose exit is oldExit is changed to newExit
|
||||
std::vector<Region *> RQ;
|
||||
for (Region::const_iterator RI = R->begin(), RE = R->end(); RI!=RE; ++RI)
|
||||
RQ.push_back(*RI);
|
||||
|
||||
while (!RQ.empty()){
|
||||
R = RQ.back();
|
||||
RQ.pop_back();
|
||||
|
||||
if (R->getExit() != oldExit)
|
||||
continue;
|
||||
|
||||
for (Region::const_iterator RI = R->begin(), RE = R->end(); RI!=RE; ++RI)
|
||||
RQ.push_back(*RI);
|
||||
|
||||
R->replaceExit(newExit);
|
||||
DEBUG(dbgs() << "Replacing exit for:\n");
|
||||
DEBUG(R->print(dbgs(), true, 0, Region::PrintRN));
|
||||
}
|
||||
|
||||
DEBUG(dbgs() << "After split exit:\n");
|
||||
DEBUG(R->print(dbgs(), true, 0, Region::PrintRN));
|
||||
}
|
||||
|
||||
bool RegionSimplify::runOnRegion(Region *R, RGPassManager &RGM) {
|
||||
r = 0;
|
||||
|
||||
if (!R->isTopLevelRegion()) {
|
||||
|
||||
// split entry node if the region has multiple entry edges
|
||||
if (!(R->getEnteringBlock())
|
||||
&& (pred_begin(R->getEntry()) != pred_end(R->getEntry()))) {
|
||||
createSingleEntryEdge(R);
|
||||
r = R;
|
||||
++NumEntries;
|
||||
}
|
||||
|
||||
// split exit node if the region has multiple exit edges
|
||||
if (!(R->getExitingBlock())) {
|
||||
createSingleExitEdge(R);
|
||||
r = R;
|
||||
++NumExits;
|
||||
}
|
||||
}
|
||||
|
||||
return r != 0;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//===- AffSCEVItTester.cpp - Test the affine scev itertor. ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Test the affine scev itertor.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#include "polly/Support/AffineSCEVIterator.h"
|
||||
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/Passes.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/Support/InstIterator.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace polly;
|
||||
|
||||
static void printSCEVAffine(raw_ostream &OS, const SCEV* S,
|
||||
ScalarEvolution *SE) {
|
||||
|
||||
for (AffineSCEVIterator I = affine_begin(S, SE), E = affine_end();
|
||||
I != E; ++I) {
|
||||
OS << *I->second << " * " << *I->first;
|
||||
|
||||
// The constant part of the SCEV will always be the last one.
|
||||
if (!isa<SCEVConstant>(S))
|
||||
OS << " + ";
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct AffSCEVItTester : public FunctionPass {
|
||||
static char ID;
|
||||
|
||||
ScalarEvolution *SE;
|
||||
LoopInfo *LI;
|
||||
Function *F;
|
||||
|
||||
explicit AffSCEVItTester() : FunctionPass(ID), SE(0), LI(0), F(0) {}
|
||||
|
||||
virtual bool runOnFunction(Function &F) {
|
||||
SE = &getAnalysis<ScalarEvolution>();
|
||||
LI = &getAnalysis<LoopInfo>();
|
||||
this->F = &F;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void print(raw_ostream &OS, const Module *M) const {
|
||||
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)
|
||||
if (SE->isSCEVable(I->getType())) {
|
||||
OS << *I << '\n';
|
||||
OS << " --> ";
|
||||
const SCEV *SV = SE->getSCEV(&*I);
|
||||
|
||||
if (Loop *L = LI->getLoopFor(I->getParent()))
|
||||
SV = SE->getSCEVAtScope(SV, L);
|
||||
SV->print(OS);
|
||||
OS << "\n";
|
||||
OS << "affine function --> ";
|
||||
printSCEVAffine(OS, SV, SE);
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
for (LoopInfo::iterator I = LI->begin(), E = LI->end(); I != E; ++I)
|
||||
PrintLoopInfo(OS, *I);
|
||||
}
|
||||
|
||||
void PrintLoopInfo(raw_ostream &OS, const Loop *L) const{
|
||||
// Print all inner loops first
|
||||
for (Loop::iterator I = L->begin(), E = L->end(); I != E; ++I)
|
||||
PrintLoopInfo(OS, *I);
|
||||
|
||||
OS << "Loop ";
|
||||
WriteAsOperand(OS, L->getHeader(), /*PrintType=*/false);
|
||||
OS << ": ";
|
||||
|
||||
if (SE->hasLoopInvariantBackedgeTakenCount(L)) {
|
||||
const SCEV *SV = SE->getBackedgeTakenCount(L);
|
||||
OS << "backedge-taken count is ";
|
||||
printSCEVAffine(OS, SV, SE);
|
||||
|
||||
OS << "\nloop count in scev ";
|
||||
SV->print(OS);
|
||||
OS << "\n";
|
||||
}
|
||||
else {
|
||||
OS << "Unpredictable\n";
|
||||
}
|
||||
}
|
||||
|
||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<ScalarEvolution>();
|
||||
AU.addRequired<LoopInfo>();
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
};
|
||||
} // end namespace
|
||||
|
||||
|
||||
char AffSCEVItTester::ID = 0;
|
||||
|
||||
RegisterPass<AffSCEVItTester> B("print-scev-affine",
|
||||
"Print the SCEV expressions in affine form.",
|
||||
true,
|
||||
true);
|
||||
|
||||
namespace polly {
|
||||
Pass *createAffSCEVItTesterPass() {
|
||||
return new AffSCEVItTester();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
add_polly_library(PollySupport
|
||||
AffSCEVItTester.cpp
|
||||
GICHelper.cpp
|
||||
ScopHelper.cpp
|
||||
)
|
|
@ -0,0 +1,91 @@
|
|||
//===- GmpConv.cpp - Recreate LLVM IR from the Scop. ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Functions for converting between gmp objects and apint.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "polly/Support/GICHelper.h"
|
||||
|
||||
#include "isl/set.h"
|
||||
#include "isl/union_set.h"
|
||||
#include "isl/map.h"
|
||||
#include "isl/union_map.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
void polly::MPZ_from_APInt (mpz_t v, const APInt apint, bool is_signed) {
|
||||
// There is no sign taken from the data, rop will simply be a positive
|
||||
// integer. An application can handle any sign itself, and apply it for
|
||||
// instance with mpz_neg.
|
||||
APInt abs;
|
||||
if (is_signed)
|
||||
abs = apint.abs();
|
||||
else
|
||||
abs = apint;
|
||||
|
||||
const uint64_t *rawdata = abs.getRawData();
|
||||
unsigned numWords = abs.getNumWords();
|
||||
|
||||
// TODO: Check if this is true for all platforms.
|
||||
mpz_import(v, numWords, 1, sizeof (uint64_t), 0, 0, rawdata);
|
||||
|
||||
if (is_signed && apint.isNegative()) mpz_neg(v, v);
|
||||
}
|
||||
|
||||
APInt polly::APInt_from_MPZ (const mpz_t mpz) {
|
||||
uint64_t *p = NULL;
|
||||
size_t sz;
|
||||
|
||||
p = (uint64_t*) mpz_export(p, &sz, 1, sizeof(uint64_t), 0, 0, mpz);
|
||||
|
||||
if (p) {
|
||||
APInt A((unsigned)mpz_sizeinbase(mpz, 2), (unsigned)sz , p);
|
||||
A = A.zext(A.getBitWidth() + 1);
|
||||
|
||||
if (mpz_sgn(mpz) == -1)
|
||||
return -A;
|
||||
else
|
||||
return A;
|
||||
} else {
|
||||
uint64_t val = 0;
|
||||
return APInt(1, 1, &val);
|
||||
}
|
||||
}
|
||||
|
||||
std::string polly::stringFromIslObj(/*__isl_keep*/ isl_map *map) {
|
||||
isl_printer *p = isl_printer_to_str(isl_map_get_ctx(map));
|
||||
isl_printer_print_map(p, map);
|
||||
std::string string(isl_printer_get_str(p));
|
||||
isl_printer_free(p);
|
||||
return string;
|
||||
}
|
||||
|
||||
std::string polly::stringFromIslObj(/*__isl_keep*/ isl_set *set) {
|
||||
isl_printer *p = isl_printer_to_str(isl_set_get_ctx(set));
|
||||
isl_printer_print_set(p, set);
|
||||
std::string string(isl_printer_get_str(p));
|
||||
isl_printer_free(p);
|
||||
return string;
|
||||
}
|
||||
|
||||
std::string polly::stringFromIslObj(/*__isl_keep*/ isl_union_map *umap) {
|
||||
isl_printer *p = isl_printer_to_str(isl_union_map_get_ctx(umap));
|
||||
isl_printer_print_union_map(p, umap);
|
||||
std::string string(isl_printer_get_str(p));
|
||||
isl_printer_free(p);
|
||||
return string;
|
||||
}
|
||||
|
||||
std::string polly::stringFromIslObj(/*__isl_keep*/ isl_union_set *uset) {
|
||||
isl_printer *p = isl_printer_to_str(isl_union_set_get_ctx(uset));
|
||||
isl_printer_print_union_set(p, uset);
|
||||
std::string string(isl_printer_get_str(p));
|
||||
isl_printer_free(p);
|
||||
return string;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
##===- polly/lib/Support/Makefile ----------------*- Makefile -*-===##
|
||||
|
||||
#
|
||||
# Indicate where we are relative to the top of the source tree.
|
||||
#
|
||||
LEVEL=../..
|
||||
|
||||
LIBRARYNAME=pollysupport
|
||||
BUILD_ARCHIVE = 1
|
||||
|
||||
CPP.Flags += $(POLLY_INC)
|
||||
|
||||
#
|
||||
# Include Makefile.common so we know what to do.
|
||||
#
|
||||
include $(LEVEL)/Makefile.common
|
|
@ -0,0 +1,280 @@
|
|||
//===- ScopHelper.cpp - Some Helper Functions for Scop. ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Small functions that help with Scop and LLVM-IR.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "polly/Support/ScopHelper.h"
|
||||
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/RegionInfo.h"
|
||||
#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
|
||||
#define DEBUG_TYPE "polly-scop-helper"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
||||
namespace {
|
||||
// Checks if a SCEV is invariant in a region. This is if all Values are
|
||||
// referenced in this SCEV are defined outside the region.
|
||||
class InvariantChecker: SCEVVisitor<InvariantChecker, bool> {
|
||||
Region &R;
|
||||
|
||||
public:
|
||||
bool visitConstant(const SCEVConstant *S) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitUnknown(const SCEVUnknown* S) {
|
||||
Value *V = S->getValue();
|
||||
|
||||
// An Instruction defined outside the region is invariant.
|
||||
if (Instruction *I = dyn_cast<Instruction>(V))
|
||||
return !R.contains(I);
|
||||
|
||||
// A constant is invariant.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitNAryExpr(const SCEVNAryExpr *S) {
|
||||
for (SCEVNAryExpr::op_iterator OI = S->op_begin(), OE = S->op_end();
|
||||
OI != OE; ++OI)
|
||||
if (!visit(*OI))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitMulExpr(const SCEVMulExpr* S) {
|
||||
return visitNAryExpr(S);
|
||||
}
|
||||
|
||||
bool visitCastExpr(const SCEVCastExpr *S) {
|
||||
return visit(S->getOperand());
|
||||
}
|
||||
|
||||
bool visitTruncateExpr(const SCEVTruncateExpr *S) {
|
||||
return visit(S->getOperand());
|
||||
}
|
||||
|
||||
bool visitZeroExtendExpr(const SCEVZeroExtendExpr *S) {
|
||||
return visit(S->getOperand());
|
||||
}
|
||||
|
||||
bool visitSignExtendExpr(const SCEVSignExtendExpr *S) {
|
||||
return visit(S->getOperand());
|
||||
}
|
||||
|
||||
bool visitAddExpr(const SCEVAddExpr *S) {
|
||||
return visitNAryExpr(S);
|
||||
}
|
||||
|
||||
bool visitAddRecExpr(const SCEVAddRecExpr *S) {
|
||||
// Check if the addrec is contained in the region.
|
||||
if (R.contains(S->getLoop()))
|
||||
return false;
|
||||
|
||||
return visitNAryExpr(S);
|
||||
}
|
||||
|
||||
bool visitUDivExpr(const SCEVUDivExpr *S) {
|
||||
return visit(S->getLHS()) && visit(S->getRHS());
|
||||
}
|
||||
|
||||
bool visitSMaxExpr(const SCEVSMaxExpr *S) {
|
||||
return visitNAryExpr(S);
|
||||
}
|
||||
|
||||
bool visitUMaxExpr(const SCEVUMaxExpr *S) {
|
||||
return visitNAryExpr(S);
|
||||
}
|
||||
|
||||
bool visitCouldNotCompute(const SCEVCouldNotCompute *S) {
|
||||
llvm_unreachable("SCEV cannot be checked");
|
||||
}
|
||||
|
||||
InvariantChecker(Region &RefRegion)
|
||||
: R(RefRegion) {}
|
||||
|
||||
static bool isInvariantInRegion(const SCEV *S, Region &R) {
|
||||
InvariantChecker Checker(R);
|
||||
return Checker.visit(S);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function for Scop
|
||||
// TODO: Add assertion to not allow parameter to be null
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Temporary Hack for extended region tree.
|
||||
// Cast the region to loop if there is a loop have the same header and exit.
|
||||
Loop *polly::castToLoop(const Region &R, LoopInfo &LI) {
|
||||
BasicBlock *entry = R.getEntry();
|
||||
|
||||
if (!LI.isLoopHeader(entry))
|
||||
return 0;
|
||||
|
||||
Loop *L = LI.getLoopFor(entry);
|
||||
|
||||
BasicBlock *exit = L->getExitBlock();
|
||||
|
||||
// Is the loop with multiple exits?
|
||||
if (!exit) return 0;
|
||||
|
||||
if (exit != R.getExit()) {
|
||||
// SubRegion/ParentRegion with the same entry.
|
||||
assert((R.getNode(R.getEntry())->isSubRegion()
|
||||
|| R.getParent()->getEntry() == entry)
|
||||
&& "Expect the loop is the smaller or bigger region");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
Value *polly::getPointerOperand(Instruction &Inst) {
|
||||
if (LoadInst *load = dyn_cast<LoadInst>(&Inst))
|
||||
return load->getPointerOperand();
|
||||
else if (StoreInst *store = dyn_cast<StoreInst>(&Inst))
|
||||
return store->getPointerOperand();
|
||||
else if (GetElementPtrInst *gep = dyn_cast<GetElementPtrInst>(&Inst))
|
||||
return gep->getPointerOperand();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Helper functions
|
||||
|
||||
bool polly::isInvariant(const SCEV *S, Region &R) {
|
||||
return InvariantChecker::isInvariantInRegion(S, R);
|
||||
}
|
||||
|
||||
// Helper function to check parameter
|
||||
bool polly::isParameter(const SCEV *Var, Region &RefRegion,
|
||||
LoopInfo &LI, ScalarEvolution &SE) {
|
||||
assert(Var && "Var can not be null!");
|
||||
|
||||
if (!isInvariant(Var, RefRegion))
|
||||
return false;
|
||||
|
||||
if (isa<SCEVAddRecExpr>(Var))
|
||||
return true;
|
||||
|
||||
if (const SCEVUnknown *U = dyn_cast<SCEVUnknown>(Var)) {
|
||||
if (isa<PHINode>(U->getValue()))
|
||||
return false;
|
||||
|
||||
if(isa<UndefValue>(U->getValue()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const SCEVCastExpr *Cast = dyn_cast<SCEVCastExpr>(Var))
|
||||
return isParameter(Cast->getOperand(), RefRegion, LI, SE);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool polly::isIndVar(const SCEV *Var, Region &RefRegion,
|
||||
LoopInfo &LI, ScalarEvolution &SE) {
|
||||
const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(Var);
|
||||
|
||||
// AddRecExprs are no induction variables.
|
||||
if (!AddRec) return false;
|
||||
|
||||
Loop *L = const_cast<Loop*>(AddRec->getLoop());
|
||||
|
||||
// Is the addrec an induction variable of a loop contained in the current
|
||||
// region.
|
||||
if (!RefRegion.contains(L))
|
||||
return false;
|
||||
|
||||
DEBUG(dbgs() << "Find AddRec: " << *AddRec
|
||||
<< " at region: " << RefRegion.getNameStr() << " as indvar\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool polly::isIndVar(const Instruction *I, const LoopInfo *LI) {
|
||||
Loop *L = LI->getLoopFor(I->getParent());
|
||||
|
||||
return L && I == L->getCanonicalInductionVariable();
|
||||
}
|
||||
|
||||
bool polly::hasInvokeEdge(const PHINode *PN) {
|
||||
for (unsigned i = 0, e = PN->getNumIncomingValues(); i < e; ++i)
|
||||
if (InvokeInst *II = dyn_cast<InvokeInst>(PN->getIncomingValue(i)))
|
||||
if (II->getParent() == PN->getIncomingBlock(i))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function for LLVM-IR about Scop
|
||||
BasicBlock *polly::createSingleEntryEdge(Region *R, Pass *P) {
|
||||
BasicBlock *BB = R->getEntry();
|
||||
|
||||
BasicBlock::iterator SplitIt = BB->begin();
|
||||
|
||||
while (isa<PHINode>(SplitIt))
|
||||
++SplitIt;
|
||||
|
||||
BasicBlock *newBB = SplitBlock(BB, SplitIt, P);
|
||||
|
||||
for (BasicBlock::iterator PI = BB->begin(); isa<PHINode>(PI); ++PI) {
|
||||
PHINode *PN = cast<PHINode>(PI);
|
||||
PHINode *NPN =
|
||||
PHINode::Create(PN->getType(), 2, PN->getName()+".ph", newBB->begin());
|
||||
|
||||
for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE; ++PI) {
|
||||
if (R->contains(*PI)) {
|
||||
Value *V = PN->removeIncomingValue(*PI, false);
|
||||
NPN->addIncoming(V, *PI);
|
||||
}
|
||||
}
|
||||
PN->replaceAllUsesWith(NPN);
|
||||
NPN->addIncoming(PN,BB);
|
||||
}
|
||||
|
||||
for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE; ++PI)
|
||||
if (R->contains(*PI))
|
||||
(*PI)->getTerminator()->replaceUsesOfWith(BB, newBB);
|
||||
|
||||
return newBB;
|
||||
}
|
||||
|
||||
BasicBlock *polly::createSingleExitEdge(Region *R, Pass *P) {
|
||||
BasicBlock *BB = R->getExit();
|
||||
|
||||
SmallVector<BasicBlock*, 4> Preds;
|
||||
for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE; ++PI)
|
||||
if (R->contains(*PI))
|
||||
Preds.push_back(*PI);
|
||||
|
||||
return SplitBlockPredecessors(BB, Preds.data(), Preds.size(), ".region", P);
|
||||
}
|
||||
|
||||
void polly::splitEntryBlockForAlloca(BasicBlock *EntryBlock, Pass *P) {
|
||||
// Find first non-alloca instruction. Every basic block has a non-alloc
|
||||
// instruction, as every well formed basic block has a terminator.
|
||||
BasicBlock::iterator I = EntryBlock->begin();
|
||||
while (isa<AllocaInst>(I)) ++I;
|
||||
|
||||
// SplitBlock updates DT, DF and LI.
|
||||
BasicBlock *NewEntry = SplitBlock(EntryBlock, I, P);
|
||||
if (RegionInfo *RI = P->getAnalysisIfAvailable<RegionInfo>())
|
||||
RI->splitBlock(NewEntry, EntryBlock);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
define void @f(i32* nocapture %a) nounwind {
|
||||
entry:
|
||||
%0 = tail call i32 (...)* @rnd() nounwind ; <i32> [#uses=2]
|
||||
; CHECK: 1 * %0 + 0 * 1
|
||||
%1 = icmp sgt i32 %0, 0 ; <i1> [#uses=1]
|
||||
br i1 %1, label %bb, label %return
|
||||
|
||||
bb: ; preds = %bb, %entry
|
||||
%i.03 = phi i32 [ 0, %entry ], [ %3, %bb ] ; <i32> [#uses=1]
|
||||
; CHECK: 1 * {0,+,1}<nuw><nsw><%bb> + 0 * 1
|
||||
%2 = tail call i32 (...)* @rnd() nounwind ; <i32> [#uses=0]
|
||||
; CHECK: 1 * %2 + 0 * 1
|
||||
%3 = add nsw i32 %i.03, 1 ; <i32> [#uses=2]
|
||||
; CHECK: 1 * {0,+,1}<nuw><nsw><%bb> + 1 * 1
|
||||
%exitcond = icmp eq i32 %3, %0 ; <i1> [#uses=1]
|
||||
br i1 %exitcond, label %return, label %bb
|
||||
|
||||
return: ; preds = %bb, %entry
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @rnd(...)
|
|
@ -0,0 +1,20 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
define i32 @f(i64 %a, i64 %b, i64 %c, [8 x i32]* nocapture %x) nounwind readonly {
|
||||
entry:
|
||||
%0 = shl i64 %a, 1 ; <i64> [#uses=1]
|
||||
%1 = add nsw i64 %0, %b ; <i64> [#uses=1]
|
||||
; CHECK: 1 * %b + 2 * %a + 0 * 1
|
||||
%2 = shl i64 %1, 1 ; <i64> [#uses=1]
|
||||
; CHECK: 2 * %b + 4 * %a + 0 * 1
|
||||
%3 = add i64 %2, 2 ; <i64> [#uses=1]
|
||||
%4 = mul i64 %a, 3 ; <i64> [#uses=1]
|
||||
%5 = shl i64 %b, 2 ; <i64> [#uses=1]
|
||||
%6 = add nsw i64 %4, 2 ; <i64> [#uses=1]
|
||||
%7 = add nsw i64 %6, %c ; <i64> [#uses=1]
|
||||
%8 = add nsw i64 %7, %5 ; <i64> [#uses=1]
|
||||
%9 = getelementptr inbounds [8 x i32]* %x, i64 %3, i64 %8 ; <i32*> [#uses=1]
|
||||
; CHECK: 1 * %x + sizeof(i32) * %c + (35 * sizeof(i32)) * %a + (20 * sizeof(i32)) * %b + (18 * sizeof(i32)) * 1
|
||||
%10 = load i32* %9, align 4 ; <i32> [#uses=1]
|
||||
ret i32 %10
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
define void @f([8 x i32]* nocapture %x) nounwind {
|
||||
entry:
|
||||
br label %bb5.preheader
|
||||
|
||||
bb2: ; preds = %bb3.preheader, %bb2
|
||||
%k.09 = phi i64 [ 0, %bb3.preheader ], [ %1, %bb2 ] ; <i64> [#uses=2]
|
||||
%tmp19 = add i64 %k.09, %tmp18 ; <i64> [#uses=1]
|
||||
%scevgep = getelementptr [8 x i32]* %x, i64 2, i64 %tmp19 ; <i32*> [#uses=1]
|
||||
; CHECK: sizeof(i32) * {0,+,1}<nuw><nsw><%bb2> + (20 * sizeof(i32)) * {0,+,1}<%bb3.preheader> + (35 * sizeof(i32)) * {0,+,1}<%bb5.preheader> + 1 * %x + (18 * sizeof(i32)) * 1
|
||||
%0 = tail call i32 (...)* @rnd() nounwind ; <i32> [#uses=1]
|
||||
store i32 %0, i32* %scevgep, align 4
|
||||
%1 = add nsw i64 %k.09, 1 ; <i64> [#uses=2]
|
||||
%exitcond = icmp eq i64 %1, 64 ; <i1> [#uses=1]
|
||||
br i1 %exitcond, label %bb4, label %bb2
|
||||
|
||||
bb4: ; preds = %bb2
|
||||
%2 = add i64 %j.010, 1 ; <i64> [#uses=2]
|
||||
%exitcond20 = icmp eq i64 %2, 64 ; <i1> [#uses=1]
|
||||
br i1 %exitcond20, label %bb6, label %bb3.preheader
|
||||
|
||||
bb3.preheader: ; preds = %bb5.preheader, %bb4
|
||||
%j.010 = phi i64 [ 0, %bb5.preheader ], [ %2, %bb4 ] ; <i64> [#uses=2]
|
||||
%tmp21 = mul i64 %j.010, 20 ; <i64> [#uses=1]
|
||||
%tmp18 = add i64 %tmp21, %tmp23 ; <i64> [#uses=1]
|
||||
br label %bb2
|
||||
|
||||
bb6: ; preds = %bb4
|
||||
%3 = add i64 %i.012, 1 ; <i64> [#uses=2]
|
||||
%exitcond25 = icmp eq i64 %3, 64 ; <i1> [#uses=1]
|
||||
br i1 %exitcond25, label %return, label %bb5.preheader
|
||||
|
||||
bb5.preheader: ; preds = %bb6, %entry
|
||||
%i.012 = phi i64 [ 0, %entry ], [ %3, %bb6 ] ; <i64> [#uses=2]
|
||||
%tmp = mul i64 %i.012, 35 ; <i64> [#uses=1]
|
||||
%tmp23 = add i64 %tmp, 2 ; <i64> [#uses=1]
|
||||
br label %bb3.preheader
|
||||
|
||||
return: ; preds = %bb6
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @rnd(...)
|
|
@ -0,0 +1,20 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d, i32* nocapture %x) nounwind readnone {
|
||||
entry:
|
||||
%0 = shl i32 %a, 1 ; <i32> [#uses=1]
|
||||
; CHECK: 2 * %a + 0 * 1
|
||||
%1 = mul i32 %b, 3 ; <i32> [#uses=1]
|
||||
; CHECK: 3 * %b + 0 * 1
|
||||
%2 = shl i32 %d, 2 ; <i32> [#uses=1]
|
||||
; CHECK: 4 * %d + 0 * 1
|
||||
%3 = add nsw i32 %0, 5 ; <i32> [#uses=1]
|
||||
; CHECK: 2 * %a + 5 * 1
|
||||
%4 = add nsw i32 %3, %c ; <i32> [#uses=1]
|
||||
; CHECK: 1 * %c + 2 * %a + 5 * 1
|
||||
%5 = add nsw i32 %4, %1 ; <i32> [#uses=1]
|
||||
; CHECK: 1 * %c + 3 * %b + 2 * %a + 5 * 1
|
||||
%6 = add nsw i32 %5, %2 ; <i32> [#uses=1]
|
||||
; CHECK: 1 * %c + 4 * %d + 3 * %b + 2 * %a + 5 * 1
|
||||
ret i32 %6
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define i32 @f(i32 %a, i32 %b, i32 %c, i64 %d, i8 signext %e, i32 %f, i32 %g, i32 %h) nounwind readnone {
|
||||
entry:
|
||||
%0 = mul i32 %a, 3 ; <i32> [#uses=1]
|
||||
%1 = mul i32 %b, 5 ; <i32> [#uses=1]
|
||||
%2 = mul i32 %1, %c ; <i32> [#uses=1]
|
||||
; CHECK: 5 * (%b * %c) + 0 * 1
|
||||
%3 = mul i32 %2, %f ; <i32> [#uses=1]
|
||||
; CHECK: 5 * (%b * %c * %f) + 0 * 1
|
||||
%4 = sext i8 %e to i32 ; <i32> [#uses=1]
|
||||
%5 = shl i32 %4, 2 ; <i32> [#uses=1]
|
||||
%6 = trunc i64 %d to i32 ; <i32> [#uses=1]
|
||||
%7 = mul i32 %6, %h ; <i32> [#uses=1]
|
||||
%8 = add nsw i32 %0, %g ; <i32> [#uses=1]
|
||||
%9 = add nsw i32 %8, %5 ; <i32> [#uses=1]
|
||||
%10 = add nsw i32 %9, %3 ; <i32> [#uses=1]
|
||||
%11 = add nsw i32 %10, %7 ; <i32> [#uses=1]
|
||||
; CHECK: 1 * %g + 1 * ((trunc i64 %d to i32) * %h) + 5 * (%b * %c * %f) + 4 * (sext i8 %e to i32) + 3 * %a + 0 * 1
|
||||
ret i32 %11
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d, i32* nocapture %x) nounwind {
|
||||
entry:
|
||||
br label %bb
|
||||
|
||||
bb: ; preds = %bb, %entry
|
||||
%indvar = phi i64 [ 0, %entry ], [ %indvar.next, %bb ] ; <i64> [#uses=3]
|
||||
; CHECK: 1 * {0,+,1}<%bb> + 0 * 1
|
||||
%scevgep = getelementptr i32* %x, i64 %indvar ; <i32*> [#uses=1]
|
||||
; CHECK: 4 * {0,+,1}<%bb> + 1 * %x + 0 * 1
|
||||
%i.04 = trunc i64 %indvar to i32 ; <i32> [#uses=1]
|
||||
; CHECK: 1 * {0,+,1}<%bb> + 0 * 1
|
||||
store i32 %i.04, i32* %scevgep, align 4
|
||||
%indvar.next = add i64 %indvar, 1 ; <i64> [#uses=2]
|
||||
; CHECK: 1 * {0,+,1}<%bb> + 1 * 1
|
||||
%exitcond = icmp eq i64 %indvar.next, 64 ; <i1> [#uses=1]
|
||||
br i1 %exitcond, label %bb2, label %bb
|
||||
|
||||
bb2: ; preds = %bb
|
||||
ret i32 %a
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -print-scev-affine -analyze < %s | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d, [4 x i32]* nocapture %x) nounwind {
|
||||
entry:
|
||||
br label %bb2.preheader
|
||||
|
||||
bb1: ; preds = %bb2.preheader, %bb1
|
||||
%indvar = phi i64 [ 0, %bb2.preheader ], [ %indvar.next, %bb1 ] ; <i64> [#uses=3]
|
||||
; CHECK: 1 * {0,+,1}<%bb1> + 0 * 1
|
||||
%scevgep = getelementptr [4 x i32]* %x, i64 %indvar, i64 %0 ; <i32*> [#uses=1]
|
||||
; CHECK: 16 * {0,+,1}<%bb1> + 4 * {0,+,1}<%bb2.preheader> + 1 * %x + 0 * 1
|
||||
%tmp = mul i64 %indvar, %0 ; <i64> [#uses=1]
|
||||
; CHECK: 1 * {0,+,{0,+,1}<%bb2.preheader>}<%bb1> + 0 * 1
|
||||
%tmp13 = trunc i64 %tmp to i32 ; <i32> [#uses=1]
|
||||
; CHECK: 1 * {0,+,{0,+,1}<%bb2.preheader>}<%bb1> + 0 * 1
|
||||
store i32 %tmp13, i32* %scevgep, align 4
|
||||
%indvar.next = add i64 %indvar, 1 ; <i64> [#uses=2]
|
||||
; CHECK: 1 * {0,+,1}<%bb1> + 1 * 1
|
||||
%exitcond = icmp eq i64 %indvar.next, 64 ; <i1> [#uses=1]
|
||||
br i1 %exitcond, label %bb3, label %bb1
|
||||
|
||||
bb3: ; preds = %bb1
|
||||
%indvar.next12 = add i64 %0, 1 ; <i64> [#uses=2]
|
||||
; CHECK: 1 * {0,+,1}<%bb2.preheader> + 1 * 1
|
||||
%exitcond14 = icmp eq i64 %indvar.next12, 64 ; <i1> [#uses=1]
|
||||
br i1 %exitcond14, label %bb5, label %bb2.preheader
|
||||
|
||||
bb2.preheader: ; preds = %bb3, %entry
|
||||
%0 = phi i64 [ 0, %entry ], [ %indvar.next12, %bb3 ] ; <i64> [#uses=3]
|
||||
; CHECK: 1 * {0,+,1}<%bb2.preheader> + 0 * 1
|
||||
br label %bb1
|
||||
|
||||
bb5: ; preds = %bb3
|
||||
ret i32 %a
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
set(POLLY_TEST_DIRECTORIES
|
||||
"ScopInfo"
|
||||
"AffineIterator"
|
||||
"CodeGen"
|
||||
"OpenMP"
|
||||
"polybench")
|
||||
|
||||
set(LLVM_SOURCE_DIR "${LLVM_MAIN_SRC_DIR}")
|
||||
set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}")
|
||||
set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/")
|
||||
set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib")
|
||||
set(POLLY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
set(POLLY_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
|
||||
|
||||
include(FindPythonInterp)
|
||||
if(PYTHONINTERP_FOUND)
|
||||
set(POLLY_TEST_EXTRA_ARGS)
|
||||
if (MSVC OR XCODE)
|
||||
set(POLLY_TEST_EXTRA_ARGS "--no-progress-bar")
|
||||
endif()
|
||||
|
||||
option(POLLY_TEST_USE_VG "Run Polly tests under Valgrind" OFF)
|
||||
if(POLLY_TEST_USE_VG)
|
||||
set(POLLY_TEST_EXTRA_ARGS ${POLLY_TEST_EXTRA_ARGS} "--vg")
|
||||
endif ()
|
||||
|
||||
foreach(testdir ${POLLY_TEST_DIRECTORIES})
|
||||
add_custom_target(polly-test-${testdir}
|
||||
COMMAND ${PYTHON_EXECUTABLE}
|
||||
${LLVM_SOURCE_DIR}/utils/lit/lit.py
|
||||
--param polly_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
--param build_config=${CMAKE_CFG_INTDIR}
|
||||
-sv ${POLLY_TEST_EXTRA_ARGS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${testdir}
|
||||
DEPENDS opt LLVMPolly
|
||||
COMMENT "Running Polly regression tests in ${testdir}")
|
||||
endforeach()
|
||||
|
||||
add_custom_target(polly-test
|
||||
COMMAND ${PYTHON_EXECUTABLE}
|
||||
${LLVM_SOURCE_DIR}/utils/lit/lit.py
|
||||
--param polly_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
--param build_config=${CMAKE_CFG_INTDIR}
|
||||
-sv ${POLLY_TEST_EXTRA_ARGS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS opt LLVMPolly
|
||||
COMMENT "Running Polly regression tests")
|
||||
endif()
|
|
@ -0,0 +1,20 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -polly-codegen < %s
|
||||
; ModuleID = 'a'
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define void @init_array() nounwind {
|
||||
entry:
|
||||
br label %for.cond
|
||||
|
||||
for.cond: ; preds = %for.cond1, %entry
|
||||
%indvar1 = phi i64 [ %indvar.next2, %for.cond1 ], [ 0, %entry ] ; <i64> [#uses=1]
|
||||
br i1 false, label %for.cond1, label %for.end32
|
||||
|
||||
for.cond1: ; preds = %for.cond
|
||||
%indvar.next2 = add i64 %indvar1, 1 ; <i64> [#uses=1]
|
||||
br label %for.cond
|
||||
|
||||
for.end32: ; preds = %for.cond
|
||||
ret void
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -polly-codegen < %s
|
||||
; RUN: opt %loadPolly %defaultOpts -polly-detect -analyze %s | not FileCheck %s
|
||||
|
||||
; ModuleID = 'a'
|
||||
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
|
||||
target triple = "i386-portbld-freebsd8.0"
|
||||
|
||||
define void @MAIN__() nounwind {
|
||||
entry:
|
||||
br i1 undef, label %bb6.preheader, label %bb3
|
||||
|
||||
bb3: ; preds = %bb3, %entry
|
||||
br i1 undef, label %bb6.preheader, label %bb3
|
||||
|
||||
bb6.preheader: ; preds = %bb3, %entry
|
||||
br i1 undef, label %bb11, label %bb9.preheader
|
||||
|
||||
bb9.preheader: ; preds = %bb6.preheader
|
||||
br label %bb11
|
||||
|
||||
bb11: ; preds = %bb9.preheader, %bb6.preheader
|
||||
br label %bb15
|
||||
|
||||
bb15: ; preds = %bb15, %bb11
|
||||
br i1 undef, label %bb26.loopexit, label %bb15
|
||||
|
||||
bb26.loopexit: ; preds = %bb15
|
||||
br i1 undef, label %bb31, label %bb29.preheader
|
||||
|
||||
bb29.preheader: ; preds = %bb26.loopexit
|
||||
br label %bb29
|
||||
|
||||
bb29: ; preds = %bb29, %bb29.preheader
|
||||
%indvar47 = phi i32 [ 0, %bb29.preheader ], [ %indvar.next48, %bb29 ] ; <i32> [#uses=1]
|
||||
%indvar.next48 = add i32 %indvar47, 1 ; <i32> [#uses=2]
|
||||
%exitcond50 = icmp eq i32 %indvar.next48, undef ; <i1> [#uses=1]
|
||||
br i1 %exitcond50, label %bb31, label %bb29
|
||||
|
||||
bb31: ; preds = %bb29, %bb26.loopexit
|
||||
%errtot.3 = phi float [ undef, %bb26.loopexit ], [ undef, %bb29 ] ; <float> [#uses=0]
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: SCOP:
|
|
@ -0,0 +1,28 @@
|
|||
; RUN: opt %loadPolly %defaultOpts -polly-codegen < %s
|
||||
; ModuleID = 'a'
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define void @clause_SetSplitField(i32 %Length) nounwind inlinehint {
|
||||
entry:
|
||||
br i1 undef, label %bb1, label %bb6
|
||||
|
||||
bb1: ; preds = %entry
|
||||
unreachable
|
||||
|
||||
bb6: ; preds = %entry
|
||||
%tmp = zext i32 %Length to i64 ; <i64> [#uses=1]
|
||||
br label %bb8
|
||||
|
||||
bb7: ; preds = %bb8
|
||||
%indvar.next = add i64 %indvar, 1 ; <i64> [#uses=1]
|
||||
br label %bb8
|
||||
|
||||
bb8: ; preds = %bb7, %bb6
|
||||
%indvar = phi i64 [ %indvar.next, %bb7 ], [ 0, %bb6 ] ; <i64> [#uses=2]
|
||||
%exitcond = icmp ne i64 %indvar, %tmp ; <i1> [#uses=1]
|
||||
br i1 %exitcond, label %bb7, label %return
|
||||
|
||||
return: ; preds = %bb8
|
||||
ret void
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue