New print implementation with support of genx.
This commit is contained in:
1 parent
1bfdd8b631
commit
2f6d077e0c
11 files changed
+1187
-361
No files matched your search
Executable
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Copyright (c) 2019, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @file builtins-c-common.cpp
|
||||
@brief Shared code base for __do_print and __do_print_cm.
|
||||
|
||||
This library provides instruments for parsing ISPC print format strings,
|
||||
some useful adapters, and other minor helper instruments.
|
||||
|
||||
FIXME: this file can be included only after
|
||||
StaticString, StaticStringRef, SizeT, RES_STR_SIZE, ARG_STR_SIZE
|
||||
are defined.
|
||||
Can be fixed with an additional template argument, but only when CMC includes are fixed.
|
||||
*/
|
||||
|
||||
#include "src/builtins-info.h"
|
||||
|
||||
namespace details {
|
||||
template <typename T> inline T ValueAdapterImpl(T val) { return val; }
|
||||
|
||||
static inline const char *ValueAdapterImpl(bool val) { return val ? "true" : "false"; }
|
||||
|
||||
// Copies everything from src (starting from srcIdx) to dst (starting from dstIdx), until it meets a separator.
|
||||
// Separators are defined by SEPS.
|
||||
// T is anything indexable, meaning you can write src[idx]
|
||||
template <char... SEPS, typename T, SizeT SIZE>
|
||||
inline int CopyTillSep(T src, int srcIdx, StaticStringRef<SIZE> dst, int dstIdx, int leftSpace) {
|
||||
const auto origDstIdx = dstIdx;
|
||||
while (((src[srcIdx] != SEPS) && ...) && leftSpace) {
|
||||
dst[dstIdx++] = src[srcIdx++];
|
||||
--leftSpace;
|
||||
}
|
||||
return dstIdx - origDstIdx;
|
||||
}
|
||||
|
||||
// FIXME: ugly copy-paste to work around cmc frontend issues. Remove it when CMC includes are fixed.
|
||||
template <char... SEPS, SizeT SRC_SIZE, SizeT SIZE>
|
||||
inline int CopyTillSep(StaticStringRef<SRC_SIZE> src, int srcIdx, StaticStringRef<SIZE> dst, int dstIdx,
|
||||
int leftSpace) {
|
||||
const auto origDstIdx = dstIdx;
|
||||
while (((src[srcIdx] != SEPS) && ...) && leftSpace) {
|
||||
dst[dstIdx++] = src[srcIdx++];
|
||||
--leftSpace;
|
||||
}
|
||||
return dstIdx - origDstIdx;
|
||||
}
|
||||
|
||||
// If type encodes uniform T, function puts result of uniform2Str<T>() in res and returns true,
|
||||
// otherwise it does nothing with res and returns false.
|
||||
template <typename T, typename ArgWriter>
|
||||
inline bool UniArg2StrIfSuitable(char type, ArgWriter &argWriter, StaticStringRef<ARG_STR_SIZE> res) {
|
||||
if (type == PrintInfo::getEncoding4Uniform<T>()) {
|
||||
res = argWriter.template uniform2Str<T>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If type encodes varying T, function puts result of varying2Str<T>() in res and returns true,
|
||||
// otherwise it does nothing with res and returns false.
|
||||
template <typename T, typename ArgWriter>
|
||||
inline bool VarArg2StrIfSuitable(char type, ArgWriter &argWriter, StaticStringRef<ARG_STR_SIZE> res) {
|
||||
if (type == PrintInfo::getEncoding4Varying<T>()) {
|
||||
res = argWriter.template varying2Str<T>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If type encodes uniform T or varying T, function calls corresponding *2Str method, writes its result into res,
|
||||
// returns true, otherwise function does nothing with res and returns false.
|
||||
template <typename T, typename ArgWriter>
|
||||
inline bool Arg2StrIfSuitable(char type, ArgWriter &argWriter, StaticStringRef<ARG_STR_SIZE> res) {
|
||||
return UniArg2StrIfSuitable<T>(type, argWriter, res) || VarArg2StrIfSuitable<T>(type, argWriter, res);
|
||||
}
|
||||
|
||||
// Requests the next argument from argWriter. Relies on uniform2Str<T> and varying2Str<T> methods of argWriter.
|
||||
// T is deduced from provided type argument. Returns the string representation of the argument returned from
|
||||
// uniform2Str<T> or varying2Str<T>.
|
||||
template <typename ArgWriter> inline StaticString<ARG_STR_SIZE> Arg2Str(char type, ArgWriter &argWriter) {
|
||||
StaticString<ARG_STR_SIZE> res;
|
||||
Arg2StrIfSuitable<bool>(type, argWriter, res) || Arg2StrIfSuitable<int>(type, argWriter, res) ||
|
||||
Arg2StrIfSuitable<unsigned>(type, argWriter, res) || Arg2StrIfSuitable<float>(type, argWriter, res) ||
|
||||
Arg2StrIfSuitable<long long>(type, argWriter, res) ||
|
||||
Arg2StrIfSuitable<unsigned long long>(type, argWriter, res) ||
|
||||
Arg2StrIfSuitable<double>(type, argWriter, res) || Arg2StrIfSuitable<void *>(type, argWriter, res);
|
||||
return res;
|
||||
}
|
||||
} // namespace details
|
||||
|
||||
const char UnsupportedTypeStr[] = "UNSUPPORTED TYPE";
|
||||
const char OffLaneBoolStr[] = "_________";
|
||||
|
||||
// For every T except bool it just returns the same value.
|
||||
// In this case value must have T type.
|
||||
// For T==bool it returns const char* to "true" or "false".
|
||||
//
|
||||
// Function is presumed to be used only for integral types.
|
||||
template <typename T> inline auto ValueAdapter(T val) { return details::ValueAdapterImpl(val); }
|
||||
|
||||
// Copies everything from src to dst (starting from dstIdx), until it meets '%' or '\0'
|
||||
// Doesn't copy '%' or '\0'. Doesn't null terminate dst string.
|
||||
template <SizeT SIZE>
|
||||
inline int CopyPlainText(const char *const src, StaticStringRef<SIZE> dst, const int dstIdx, const int leftSpace) {
|
||||
return details::CopyTillSep<'%', '\0'>(src, 0, dst, dstIdx, leftSpace);
|
||||
}
|
||||
|
||||
// Copies everything from src to dst (starting from dstIdx), until it meets '\0' (whole string).
|
||||
// Doesn't copy '\0', so dst string is not null terminated.
|
||||
template <SizeT SIZE>
|
||||
inline int CopyFullText(const char *const src, StaticStringRef<SIZE> dst, const int dstIdx, const int leftSpace) {
|
||||
return details::CopyTillSep<'\0'>(src, 0, dst, dstIdx, leftSpace);
|
||||
}
|
||||
|
||||
// Copies everything from src (starting from srcIdx) to dst (starting from dstIdx), until it meets '\0' (whole string).
|
||||
// Doesn't copy '\0', so dst string is not null terminated.
|
||||
template <SizeT SRC_SIZE, SizeT DST_SIZE>
|
||||
inline int CopyFullText(StaticStringRef<SRC_SIZE> src, const int srcIdx, StaticStringRef<DST_SIZE> dst,
|
||||
const int dstIdx, const int leftSpace) {
|
||||
return details::CopyTillSep<'\0'>(src, srcIdx, dst, dstIdx, leftSpace);
|
||||
}
|
||||
|
||||
// Converts type to corresponding printf type specifier
|
||||
template <typename T> inline const char *type2Specifier();
|
||||
|
||||
template <> inline const char *type2Specifier<bool>() {
|
||||
// %s is because we will eventually print "true" or "false"
|
||||
return "%s";
|
||||
}
|
||||
|
||||
template <> inline const char *type2Specifier<int>() { return "%d"; }
|
||||
|
||||
template <> inline const char *type2Specifier<unsigned>() { return "%u"; }
|
||||
|
||||
template <> inline const char *type2Specifier<float>() { return "%f"; }
|
||||
|
||||
template <> inline const char *type2Specifier<long long>() { return "%lld"; }
|
||||
|
||||
template <> inline const char *type2Specifier<long long unsigned>() { return "%llu"; }
|
||||
|
||||
template <> inline const char *type2Specifier<double>() { return "%f"; }
|
||||
|
||||
template <> inline const char *type2Specifier<void *>() { return "%p"; }
|
||||
|
||||
// This function parses format string and string of arg types.
|
||||
// User must implement ArgWriter class, which has such template methods as uniform2Str<type> and varying2Str<type>.
|
||||
// Those functions must return text representation (c-string) of the next argument, the type of argument is provided
|
||||
// with the template parameter. Returned c-string is placed instead of '%' in format string.
|
||||
// The function returns resulting string.
|
||||
template <typename ArgWriter>
|
||||
inline StaticString<RES_STR_SIZE> GetFormatedStr(const char *format, const char *types, ArgWriter &argWriter) {
|
||||
StaticString<RES_STR_SIZE> resultingStr;
|
||||
int haveBeenWritten = 0;
|
||||
int leftSpace = RES_STR_SIZE - 1; // space for null terminator
|
||||
|
||||
for (;; ++format, ++types) {
|
||||
auto wereCopied = CopyPlainText(format, resultingStr, haveBeenWritten, leftSpace);
|
||||
format += wereCopied;
|
||||
haveBeenWritten += wereCopied;
|
||||
leftSpace -= wereCopied;
|
||||
if (!leftSpace || *format == '\0') {
|
||||
// reached the end of format or limits of resultingStr
|
||||
break;
|
||||
}
|
||||
StaticString<ARG_STR_SIZE> argStr = details::Arg2Str(*types, argWriter);
|
||||
wereCopied = CopyFullText(argStr, 0, resultingStr, haveBeenWritten, leftSpace);
|
||||
haveBeenWritten += wereCopied;
|
||||
leftSpace -= wereCopied;
|
||||
}
|
||||
resultingStr[haveBeenWritten] = '\0';
|
||||
return resultingStr;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @file builtins-c-cpu.cpp
|
||||
@brief Standard library function implementations written in C/C++.
|
||||
|
||||
This file provides C/C++ implementations of various functions that can be
|
||||
called from ispc programs; in other words, this file is *not* linked
|
||||
into the ispc compiler executable, but rather provides functions that
|
||||
can be compiled into ispc programs.
|
||||
|
||||
When the ispc compiler is built, this file is compiled with clang to
|
||||
generate LLVM bitcode. This bitcode is later linked in to the program
|
||||
being compiled by the DefineStdlib() function. The first way to access
|
||||
definitions from this file is by asking for them name from the
|
||||
llvm::Module's' symbol table (e.g. as the PrintStmt implementation does
|
||||
with __do_print() below. Alternatively, if a function defined in this
|
||||
file has a signature that can be mapped back to ispc types by the
|
||||
lLLVMTypeToIspcType() function, then its declaration will be made
|
||||
available to ispc programs at compile time automatically.
|
||||
*/
|
||||
#ifndef WASM
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// We do want old school sprintf and don't want secure Microsoft extensions.
|
||||
// And we also don't want warnings about it, so the define.
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#else
|
||||
// Some versions of glibc has "fortification" feature, which expands sprintf
|
||||
// to __builtin___sprintf_chk(..., __builtin_object_size(...), ...).
|
||||
// We don't want this kind of expansion, as we don't support these intrinsics.
|
||||
#define _FORTIFY_SOURCE 0
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// In unistd.h we need the definition of sysconf and _SC_NPROCESSORS_ONLN used as its arguments.
|
||||
// We should include unistd.h, but it doesn't really work well for cross compilation, as
|
||||
// requires us to carry around unistd.h, which is not available on Windows out of the box.
|
||||
#include <unistd.h>
|
||||
|
||||
// Just for the reference: these lines are eventually included from unistd.h
|
||||
// #define _SC_NPROCESSORS_ONLN 58
|
||||
// long sysconf(int);
|
||||
#endif // !_MSC_VER
|
||||
|
||||
#endif // !WASM
|
||||
|
||||
#include <array>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
using SizeT = std::size_t;
|
||||
using MaskT = uint64_t;
|
||||
constexpr SizeT RES_STR_SIZE = 8196;
|
||||
constexpr SizeT ARG_STR_SIZE = 1024;
|
||||
template <typename T, SizeT Size> using StaticContainer = std::array<T, Size>;
|
||||
template <typename T, SizeT Size> using StaticContainerRef = StaticContainer<T, Size> &;
|
||||
|
||||
template <SizeT Size> using StaticString = StaticContainer<char, Size>;
|
||||
template <SizeT Size> using StaticStringRef = StaticContainerRef<char, Size>;
|
||||
using WidthT = int;
|
||||
|
||||
#include "builtins/builtins-c-common.hpp"
|
||||
|
||||
class ArgWriter {
|
||||
const void *const *const args_;
|
||||
int curArgIdx_;
|
||||
WidthT width_;
|
||||
MaskT mask_;
|
||||
|
||||
public:
|
||||
ArgWriter(const void *const *args, WidthT width, MaskT mask)
|
||||
: args_(args), curArgIdx_(0), width_(width), mask_(mask) {}
|
||||
|
||||
template <typename T> auto uniform2Str() {
|
||||
auto fmt = type2Specifier<T>();
|
||||
auto argPtr = getArg();
|
||||
StaticString<ARG_STR_SIZE> res;
|
||||
sprintf(&res[0], fmt, ValueAdapter<T>(*argCast<T>(argPtr)));
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T> auto varying2Str() {
|
||||
auto fmt = type2Specifier<T>();
|
||||
StaticString<ARG_STR_SIZE> res;
|
||||
res[0] = '[';
|
||||
int haveBeenWritten = 1;
|
||||
auto argPtr = getArg();
|
||||
for (int lane = 0; lane < width_; ++lane) {
|
||||
if (mask_ & (1ull << lane)) {
|
||||
haveBeenWritten += sprintf(&res[haveBeenWritten], fmt, ValueAdapter<T>(argCast<T>(argPtr)[lane]));
|
||||
} else {
|
||||
haveBeenWritten = writeOffLane<T>(res, haveBeenWritten, argPtr, lane);
|
||||
}
|
||||
res[haveBeenWritten] = lane == width_ - 1 ? ']' : ',';
|
||||
++haveBeenWritten;
|
||||
}
|
||||
res[haveBeenWritten] = '\0';
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
const void *getArg() { return args_[curArgIdx_++]; }
|
||||
|
||||
// casts void* to proper pointer
|
||||
// T is the type of argument (not pointer)
|
||||
template <typename T> auto argCast(const void *argPtr) { return reinterpret_cast<const T *>(argPtr); }
|
||||
|
||||
// bools are passed as ints
|
||||
template <> auto argCast<bool>(const void *argPtr) { return reinterpret_cast<const int *>(argPtr); }
|
||||
|
||||
template <typename T>
|
||||
int writeOffLane(StaticString<ARG_STR_SIZE> &res, int haveBeenWritten, const void *argPtr, int lane) {
|
||||
haveBeenWritten += sprintf(&res[haveBeenWritten], "((");
|
||||
auto fmt = type2Specifier<T>();
|
||||
haveBeenWritten += sprintf(&res[haveBeenWritten], fmt, ValueAdapter<T>(argCast<T>(argPtr)[lane]));
|
||||
haveBeenWritten += sprintf(&res[haveBeenWritten], "))");
|
||||
return haveBeenWritten;
|
||||
}
|
||||
|
||||
template <>
|
||||
int writeOffLane<bool>(StaticString<ARG_STR_SIZE> &res, int haveBeenWritten, const void *argPtr, int lane) {
|
||||
haveBeenWritten += sprintf(&res[haveBeenWritten], OffLaneBoolStr);
|
||||
return haveBeenWritten;
|
||||
}
|
||||
};
|
||||
|
||||
/** This function is called by PrintStmt to do the work of printing values
|
||||
from ispc programs. Note that the function signature here must match
|
||||
the parameters that PrintStmt::EmitCode() generates.
|
||||
|
||||
@param format Print format string
|
||||
@param types Encoded types of the values being printed.
|
||||
(See lEncodeType()).
|
||||
@param width Vector width of the compilation target
|
||||
@param mask Current lane mask when the print statement is called
|
||||
@param args Array of pointers to the values to be printed
|
||||
*/
|
||||
extern "C" void __do_print(const char *format, const char *types, WidthT width, MaskT mask, const void *const *args) {
|
||||
ArgWriter argWriter(args, width, mask);
|
||||
StaticString<RES_STR_SIZE> resultingStr = GetFormatedStr(format, types, argWriter);
|
||||
fputs(&resultingStr[0], stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#ifdef WASM
|
||||
int __num_cores() { return 1; }
|
||||
#else // WASM
|
||||
|
||||
extern "C" int __num_cores() {
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
// This is quite a hack. Including all of windows.h to get this definition
|
||||
// pulls in a bunch of stuff that leads to undefined symbols at link time.
|
||||
// So we don't #include <windows.h> but instead have the equivalent declarations
|
||||
// here. Presumably this struct declaration won't be changing in the future
|
||||
// anyway...
|
||||
struct SYSTEM_INFO {
|
||||
int pad0[2];
|
||||
void *pad1[2];
|
||||
int *pad2;
|
||||
int dwNumberOfProcessors;
|
||||
int pad3[3];
|
||||
};
|
||||
|
||||
struct SYSTEM_INFO sysInfo;
|
||||
extern void __stdcall GetSystemInfo(struct SYSTEM_INFO *);
|
||||
GetSystemInfo(&sysInfo);
|
||||
return sysInfo.dwNumberOfProcessors;
|
||||
#else
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif // !_MSC_VER
|
||||
}
|
||||
#endif // !WASM
|
||||
Executable
+289
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
Copyright (c) 2019, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @file builtins-c-genx.cpp
|
||||
@brief Standard library function implementations written in CM.
|
||||
|
||||
This file provides CM implementations of various functions that can be
|
||||
called from ispc programs; in other words, this file is *not* linked
|
||||
into the ispc compiler executable, but rather provides functions that
|
||||
can be compiled into ispc programs.
|
||||
|
||||
When the ispc compiler is built, this file is compiled with clang to
|
||||
generate LLVM bitcode. This bitcode is later linked in to the program
|
||||
being compiled by the DefineStdlib() function. The first way to access
|
||||
definitions from this file is by asking for them name from the
|
||||
llvm::Module's' symbol table (e.g. as the PrintStmt implementation does
|
||||
with __do_print_cm() below. Alternatively, if a function defined in this
|
||||
file has a signature that can be mapped back to ispc types by the
|
||||
lLLVMTypeToIspcType() function, then its declaration will be made
|
||||
available to ispc programs at compile time automatically.
|
||||
*/
|
||||
|
||||
using SizeT = unsigned;
|
||||
using MaskT = unsigned long long;
|
||||
using ArgT = unsigned;
|
||||
|
||||
constexpr SizeT RES_STR_SIZE = 128;
|
||||
constexpr SizeT ARG_STR_SIZE = 100;
|
||||
|
||||
template <typename T, SizeT Size> using StaticContainer = vector<T, Size>;
|
||||
template <typename T, SizeT Size> using StaticContainerRef = vector_ref<T, Size>;
|
||||
|
||||
template <SizeT Size> using StaticString = StaticContainer<char, Size>;
|
||||
template <SizeT Size> using StaticStringRef = StaticContainerRef<char, Size>;
|
||||
using WidthT = int;
|
||||
|
||||
#include "builtins/builtins-c-common.hpp"
|
||||
#include <cm/cm.h>
|
||||
|
||||
enum LaneState : bool { OFF = false, ON = true };
|
||||
|
||||
// returns length of the string
|
||||
// null character is not taken into account
|
||||
template <typename T> inline int strLen(T str) {
|
||||
int len = 0;
|
||||
for (; str[len] != '\0'; ++len)
|
||||
;
|
||||
return len;
|
||||
}
|
||||
|
||||
// is meant to be used only for integral types
|
||||
template <typename T> inline T max(T a, T b) {
|
||||
if (a > b)
|
||||
return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T> inline const char *cmType2Specifier() { return type2Specifier<T>(); }
|
||||
|
||||
// cm printf doesn't support %p, so it is emulated with %X
|
||||
template <> inline const char *cmType2Specifier<void *>() {
|
||||
if constexpr (sizeof(void *) > sizeof(unsigned)) {
|
||||
return "%016llX";
|
||||
} else {
|
||||
return "%08X";
|
||||
}
|
||||
}
|
||||
|
||||
// returns how many characters are required to write another vector element (element of varying argument)
|
||||
template <typename T, LaneState lane> inline int requiredSpace4VecElem() {
|
||||
auto specifierLen = strLen(cmType2Specifier<T>());
|
||||
if constexpr (lane) {
|
||||
// additional 1 for ',' or ']'
|
||||
return specifierLen + 1;
|
||||
} else {
|
||||
// additional 4 for "((" and "))", 1 for ',' or ']'
|
||||
return specifierLen + 5;
|
||||
}
|
||||
}
|
||||
|
||||
template <> inline int requiredSpace4VecElem<bool, LaneState::ON>() {
|
||||
return max(strLen(ValueAdapter<bool>(false)), strLen(ValueAdapter<bool>(true)));
|
||||
}
|
||||
|
||||
template <> inline int requiredSpace4VecElem<bool, LaneState::OFF>() { return strLen(OffLaneBoolStr); }
|
||||
|
||||
class ArgWriter {
|
||||
details::OffsetT offset_;
|
||||
const ArgT *const args_;
|
||||
int curArgIdx_;
|
||||
WidthT width_;
|
||||
MaskT mask_;
|
||||
|
||||
public:
|
||||
inline ArgWriter(details::OffsetT offset, const ArgT *args, WidthT width, MaskT mask)
|
||||
: offset_(offset), args_(args), curArgIdx_(0), width_(width), mask_(mask) {}
|
||||
|
||||
template <typename T> inline auto uniform2Str() {
|
||||
auto fmt = cmType2Specifier<T>();
|
||||
StaticString<ARG_STR_SIZE> res;
|
||||
auto wereCopied = CopyFullText(fmt, res, 0, ARG_STR_SIZE - 1);
|
||||
res[wereCopied] = '\0';
|
||||
|
||||
writeArg<T>();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <> inline auto uniform2Str<bool>() {
|
||||
StaticString<ARG_STR_SIZE> res;
|
||||
auto wereCopied = CopyFullText(ValueAdapter<bool>(getElementaryArg()), res, 0, ARG_STR_SIZE - 1);
|
||||
res[wereCopied] = '\0';
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T> inline auto varying2Str() {
|
||||
StaticString<ARG_STR_SIZE> res;
|
||||
res[0] = '[';
|
||||
int haveBeenWritten = 1;
|
||||
for (int lane = 0; lane < width_; ++lane) {
|
||||
if (mask_ & (1ull << lane)) {
|
||||
// require 1 additional character for '\0'
|
||||
if (haveBeenWritten >= ARG_STR_SIZE - requiredSpace4VecElem<T, LaneState::ON>() - 1) {
|
||||
// there's not enough space to write another value
|
||||
break;
|
||||
}
|
||||
haveBeenWritten = writeFormat4VecElem<T, LaneState::ON>(res, haveBeenWritten);
|
||||
} else {
|
||||
// require 1 additional character for '\0'
|
||||
if (haveBeenWritten >= ARG_STR_SIZE - requiredSpace4VecElem<T, LaneState::OFF>() - 1) {
|
||||
// there's not enough space to write another value
|
||||
break;
|
||||
}
|
||||
haveBeenWritten = writeFormat4VecElem<T, LaneState::OFF>(res, haveBeenWritten);
|
||||
}
|
||||
res[haveBeenWritten] = lane == width_ - 1 ? ']' : ',';
|
||||
++haveBeenWritten;
|
||||
|
||||
writeArg<T>();
|
||||
}
|
||||
res[haveBeenWritten] = '\0';
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
inline ArgT getElementaryArg() { return args_[curArgIdx_++]; }
|
||||
|
||||
// accesses next scalar argument or element of vector argument from args_ (reads 2 elementary args if 64 bit value)
|
||||
// and writes it to cm printf surface
|
||||
template <typename T> inline void writeArg() {
|
||||
ArgT low, high;
|
||||
if constexpr (sizeof(T) > 4) {
|
||||
low = getElementaryArg();
|
||||
high = getElementaryArg();
|
||||
} else {
|
||||
low = getElementaryArg();
|
||||
high = 0;
|
||||
}
|
||||
SurfaceIndex BTI = details::__cm_intrinsic_impl_predefined_surface(details::PRINT_SURF_IDX);
|
||||
details::_cm_print_args_raw<T>(BTI, offset_, low, high);
|
||||
offset_ += CMPHF_VEC_BSZ;
|
||||
}
|
||||
|
||||
// cm printf doesn't support %p specifier, so it is emulated with unsigned
|
||||
// look at cmType2Specifier for more details
|
||||
template <> inline void writeArg<void *>() {
|
||||
if constexpr (sizeof(void *) > sizeof(unsigned))
|
||||
writeArg<unsigned long long>();
|
||||
else
|
||||
writeArg<unsigned>();
|
||||
}
|
||||
|
||||
// in case of bool we do not push the argument to surface, we directly write true or false into format string
|
||||
template <> inline void writeArg<bool>() {}
|
||||
|
||||
// appends format string for vector element to res starting from position haveBeenWritten
|
||||
// behaves differently for bools, look at specializations for details
|
||||
// NOTE: this is a method because bool specialization require access to getElementaryArg
|
||||
template <typename T, LaneState lane>
|
||||
inline int writeFormat4VecElem(StaticStringRef<ARG_STR_SIZE> res, int haveBeenWritten) {
|
||||
auto fmt = cmType2Specifier<T>();
|
||||
if constexpr (!lane)
|
||||
haveBeenWritten += CopyFullText("((", res, haveBeenWritten, ARG_STR_SIZE - haveBeenWritten - 1);
|
||||
haveBeenWritten += CopyFullText(fmt, res, haveBeenWritten, ARG_STR_SIZE - haveBeenWritten - 1);
|
||||
if constexpr (!lane)
|
||||
haveBeenWritten += CopyFullText("))", res, haveBeenWritten, ARG_STR_SIZE - haveBeenWritten - 1);
|
||||
return haveBeenWritten;
|
||||
}
|
||||
|
||||
// in case of bools we directly write "true" or "false" to format string, as cm printf doesn't support %s
|
||||
template <>
|
||||
inline int writeFormat4VecElem<bool, LaneState::ON>(StaticStringRef<ARG_STR_SIZE> res, int haveBeenWritten) {
|
||||
haveBeenWritten += CopyFullText(ValueAdapter<bool>(getElementaryArg()), res, haveBeenWritten,
|
||||
ARG_STR_SIZE - haveBeenWritten - 1);
|
||||
return haveBeenWritten;
|
||||
}
|
||||
|
||||
// off lane bool unlike other types doesn't get additional parenthesis (not "((true))"), but becomes "_____"
|
||||
template <>
|
||||
inline int writeFormat4VecElem<bool, LaneState::OFF>(StaticStringRef<ARG_STR_SIZE> res, int haveBeenWritten) {
|
||||
haveBeenWritten += CopyFullText(OffLaneBoolStr, res, haveBeenWritten, ARG_STR_SIZE - haveBeenWritten - 1);
|
||||
return haveBeenWritten;
|
||||
}
|
||||
};
|
||||
|
||||
/** This function is called by PrintStmt to do the work of printing values
|
||||
from ispc programs. Note that the function signature here must match
|
||||
the parameters that PrintStmt::EmitCode() generates.
|
||||
|
||||
@param format Print format string
|
||||
@param types Encoded types of the values being printed.
|
||||
(See lEncodeType()).
|
||||
@param width Vector width of the compilation target
|
||||
@param mask Current lane mask when the print statement is called
|
||||
@param args Array of split into uints values
|
||||
(if a value bigger than uint it stored in 2 uints: low bits, then high bits,
|
||||
vector values stored by scalar values with the previous rule being applied)
|
||||
@param numNotBoolUniArgs number of uniform values, except bool ones (details are below)
|
||||
@param numNotBoolVarArgs number of varying values, except bool ones (details are below)
|
||||
|
||||
WARNING!!!
|
||||
CM supports maximum of 128 characters in format string. If your input doesn't fit, it will be cut.
|
||||
NOTE: 128 is the maximum size of printf format string, not ISPC print one. So consider space for brackets, commas,
|
||||
printf type specifiers, e.g. % with a vector of int being passed, will require 3*width+1 characters
|
||||
(for more details look for explanation below).
|
||||
|
||||
Root: cm printf works in the following way. It takes its arguments (format string and the arguments itself),
|
||||
adds some headers and sends it to host through predefined surface. Host gets all this arguments, and calls
|
||||
the real printf.
|
||||
|
||||
Idea: GetFormatedStr() transforms ISPC format string to normal printf format string:
|
||||
adds qualifiers (% -> %d,% -> %f,...),
|
||||
adds brackets for vector values (% -> [%d,((%d)),...,%d]).
|
||||
Real writing of an argument to surface happens in uniform2Str, varying2Str methods.
|
||||
|
||||
FIXME: __declspec(genx_SIMT(1)) is used to make __do_print_cm function be called unpredicated in simd cf.
|
||||
It is possibly an abuse of this attribute, in this case it's better to solve the issue differently.
|
||||
*/
|
||||
extern "C" __declspec(cm_builtin) __declspec(genx_SIMT(1)) void __do_print_cm(const char *format, const char *types,
|
||||
WidthT width, MaskT mask,
|
||||
const ArgT *args, int numNotBoolUniArgs,
|
||||
int numNotBoolVarArgs) {
|
||||
// boolean args are not included in total_len as we won't write them into the print surface
|
||||
// they will be written directly to format string
|
||||
// FIXME: if we won't write all arguments (which can happen if we don't fit in 128 characters), behavior
|
||||
// on runtime side can be undefined. We should either fix it here (will make this code even more heavier),
|
||||
// or tighten up contract of print (require from user to not overflow 128 characters).
|
||||
details::OffsetT total_len =
|
||||
CMPHF_VEC_BSZ + CMPHF_STR_SZ + (numNotBoolUniArgs + numNotBoolVarArgs * width) * CMPHF_VEC_BSZ;
|
||||
SurfaceIndex BTI = details::__cm_intrinsic_impl_predefined_surface(details::PRINT_SURF_IDX);
|
||||
auto init_offset = details::_cm_print_init_offset(BTI, total_len);
|
||||
auto arg_offset = init_offset + CMPHF_STR_SZ + CMPHF_VEC_BSZ;
|
||||
|
||||
ArgWriter argWriter(arg_offset, args, width, mask);
|
||||
StaticString<RES_STR_SIZE> resultingStr = GetFormatedStr(format, types, argWriter);
|
||||
details::_cm_print_format(BTI, init_offset, resultingStr);
|
||||
}
|
||||
|
||||
// TODO: implement correctly for genx if possible
|
||||
extern "C" int __declspec(cm_builtin) __num_cores() { return -1; }
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2010-2020, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @file builtins-c.c
|
||||
@brief Standard library function implementations written in C.
|
||||
|
||||
This file provides C implementations of various functions that can be
|
||||
called from ispc programs; in other words, this file is *not* linked
|
||||
into the ispc compiler executable, but rather provides functions that
|
||||
can be compiled into ispc programs.
|
||||
|
||||
When the ispc compiler is built, this file is compiled with clang to
|
||||
generate LLVM bitcode. This bitcode is later linked in to the program
|
||||
being compiled by the DefineStdlib() function. The first way to access
|
||||
definitions from this file is by asking for them name from the
|
||||
llvm::Module's' symbol table (e.g. as the PrintStmt implementation does
|
||||
with __do_print() below. Alternatively, if a function defined in this
|
||||
file has a signature that can be mapped back to ispc types by the
|
||||
lLLVMTypeToIspcType() function, then its declaration will be made
|
||||
available to ispc programs at compile time automatically.
|
||||
*/
|
||||
|
||||
#ifndef WASM
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// We do want old school sprintf and don't want secure Microsoft extensions.
|
||||
// And we also don't want warnings about it, so the define.
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#else
|
||||
// Some versions of glibc has "fortification" feature, which expands sprintf
|
||||
// to __builtin___sprintf_chk(..., __builtin_object_size(...), ...).
|
||||
// We don't want this kind of expansion, as we don't support these intrinsics.
|
||||
#define _FORTIFY_SOURCE 0
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// In unistd.h we need the definition of sysconf and _SC_NPROCESSORS_ONLN used as its arguments.
|
||||
// We should include unistd.h, but it doesn't really work well for cross compilation, as
|
||||
// requires us to carry around unistd.h, which is not available on Windows out of the box.
|
||||
#include <unistd.h>
|
||||
|
||||
// Just for the reference: these lines are eventually included from unistd.h
|
||||
// #define _SC_NPROCESSORS_ONLN 58
|
||||
// long sysconf(int);
|
||||
#endif // !_MSC_VER
|
||||
#endif // !WASM
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef int Bool;
|
||||
|
||||
#define PRINT_BUF_SIZE 4096
|
||||
|
||||
#define APPEND(str) \
|
||||
do { \
|
||||
int offset = bufp - &printString[0]; \
|
||||
*bufp = '\0'; \
|
||||
strncat(bufp, str, PRINT_BUF_SIZE - offset); \
|
||||
bufp += strlen(str); \
|
||||
if (bufp >= &printString[PRINT_BUF_SIZE]) \
|
||||
goto done; \
|
||||
} while (0) /* eat semicolon */
|
||||
|
||||
#define PRINT_SCALAR(fmt, type) \
|
||||
sprintf(tmpBuf, fmt, *((type *)ptr)); \
|
||||
APPEND(tmpBuf); \
|
||||
break
|
||||
|
||||
#define PRINT_VECTOR(fmt, type) \
|
||||
*bufp++ = '['; \
|
||||
if (bufp == &printString[PRINT_BUF_SIZE]) \
|
||||
break; \
|
||||
for (int i = 0; i < width; ++i) { \
|
||||
/* only print the value if the current lane is executing */ \
|
||||
if (mask & (1ull << i)) \
|
||||
sprintf(tmpBuf, fmt, ((type *)ptr)[i]); \
|
||||
else \
|
||||
sprintf(tmpBuf, "((" fmt "))", ((type *)ptr)[i]); \
|
||||
APPEND(tmpBuf); \
|
||||
*bufp++ = (i != width - 1 ? ',' : ']'); \
|
||||
} \
|
||||
break
|
||||
|
||||
/** This function is called by PrintStmt to do the work of printing values
|
||||
from ispc programs. Note that the function signature here must match
|
||||
the parameters that PrintStmt::EmitCode() generates.
|
||||
|
||||
@param format Print format string
|
||||
@param types Encoded types of the values being printed.
|
||||
(See lEncodeType()).
|
||||
@param width Vector width of the compilation target
|
||||
@param mask Current lane mask when the print statemnt is called
|
||||
@param args Array of pointers to the values to be printed
|
||||
*/
|
||||
void __do_print(const char *format, const char *types, int width, uint64_t mask, void **args) {
|
||||
char printString[PRINT_BUF_SIZE + 1]; // +1 for trailing NUL
|
||||
char *bufp = &printString[0];
|
||||
char tmpBuf[256];
|
||||
|
||||
int argCount = 0;
|
||||
while (*format && bufp < &printString[PRINT_BUF_SIZE]) {
|
||||
// Format strings are just single percent signs.
|
||||
if (*format != '%') {
|
||||
*bufp++ = *format;
|
||||
} else {
|
||||
if (*types) {
|
||||
void *ptr = args[argCount++];
|
||||
// Based on the encoding in the types string, cast the
|
||||
// value appropriately and print it with a reasonable
|
||||
// printf() formatting string.
|
||||
switch (*types) {
|
||||
case 'b': {
|
||||
sprintf(tmpBuf, "%s", *((Bool *)ptr) ? "true" : "false");
|
||||
APPEND(tmpBuf);
|
||||
break;
|
||||
}
|
||||
case 'B': {
|
||||
*bufp++ = '[';
|
||||
if (bufp == &printString[PRINT_BUF_SIZE])
|
||||
break;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (mask & (1ull << i)) {
|
||||
sprintf(tmpBuf, "%s", ((Bool *)ptr)[i] ? "true" : "false");
|
||||
APPEND(tmpBuf);
|
||||
} else
|
||||
APPEND("_________");
|
||||
*bufp++ = (i != width - 1) ? ',' : ']';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
PRINT_SCALAR("%d", int);
|
||||
case 'I':
|
||||
PRINT_VECTOR("%d", int);
|
||||
case 'u':
|
||||
PRINT_SCALAR("%u", unsigned int);
|
||||
case 'U':
|
||||
PRINT_VECTOR("%u", unsigned int);
|
||||
case 'f':
|
||||
PRINT_SCALAR("%f", float);
|
||||
case 'F':
|
||||
PRINT_VECTOR("%f", float);
|
||||
case 'l':
|
||||
PRINT_SCALAR("%lld", long long);
|
||||
case 'L':
|
||||
PRINT_VECTOR("%lld", long long);
|
||||
case 'v':
|
||||
PRINT_SCALAR("%llu", unsigned long long);
|
||||
case 'V':
|
||||
PRINT_VECTOR("%llu", unsigned long long);
|
||||
case 'd':
|
||||
PRINT_SCALAR("%f", double);
|
||||
case 'D':
|
||||
PRINT_VECTOR("%f", double);
|
||||
case 'p':
|
||||
PRINT_SCALAR("%p", void *);
|
||||
case 'P':
|
||||
PRINT_VECTOR("%p", void *);
|
||||
default:
|
||||
APPEND("UNKNOWN TYPE ");
|
||||
*bufp++ = *types;
|
||||
}
|
||||
++types;
|
||||
}
|
||||
}
|
||||
++format;
|
||||
}
|
||||
|
||||
done:
|
||||
*bufp = '\0';
|
||||
fputs(printString, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#ifdef WASM
|
||||
int __num_cores() { return 1; }
|
||||
#else // WASM
|
||||
int __num_cores() {
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
// This is quite a hack. Including all of windows.h to get this definition
|
||||
// pulls in a bunch of stuff that leads to undefined symbols at link time.
|
||||
// So we don't #include <windows.h> but instead have the equivalent declarations
|
||||
// here. Presumably this struct declaration won't be changing in the future
|
||||
// anyway...
|
||||
struct SYSTEM_INFO {
|
||||
int pad0[2];
|
||||
void *pad1[2];
|
||||
int *pad2;
|
||||
int dwNumberOfProcessors;
|
||||
int pad3[3];
|
||||
};
|
||||
|
||||
struct SYSTEM_INFO sysInfo;
|
||||
extern void __stdcall GetSystemInfo(struct SYSTEM_INFO *);
|
||||
GetSystemInfo(&sysInfo);
|
||||
return sysInfo.dwNumberOfProcessors;
|
||||
#else
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif // !_MSC_VER
|
||||
}
|
||||
#endif // !WASM
|
||||
@@ -4165,7 +4165,7 @@ define void @__send_eot() {
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @__do_print_cm(i8*) alwaysinline {
|
||||
define internal void @__do_print_cm_str(i8*) alwaysinline {
|
||||
;; predefined.surface return Value: surface index of the specified id
|
||||
;; id 2 is stdout
|
||||
%buf = tail call i32 @llvm.genx.predefined.surface(i32 2)
|
||||
@@ -4189,7 +4189,7 @@ define void @__do_assert_uniform(i8 *%str, i1 %test, <WIDTH x MASK> %mask) {
|
||||
br i1 %test, label %ok, label %fail
|
||||
|
||||
fail:
|
||||
call void @__do_print_cm(i8* %str)
|
||||
call void @__do_print_cm_str(i8* %str)
|
||||
call void @__send_eot()
|
||||
ret void
|
||||
|
||||
@@ -4208,7 +4208,7 @@ define void @__do_assert_varying(i8 *%str, <WIDTH x MASK> %test,
|
||||
br i1 %all_ok, label %ok, label %fail
|
||||
|
||||
fail:
|
||||
call void @__do_print_cm(i8* %str)
|
||||
call void @__do_print_cm_str(i8* %str)
|
||||
call void @__send_eot()
|
||||
ret void
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ function(ll_to_cpp llFileName bit os_name resultFileName)
|
||||
endfunction()
|
||||
|
||||
function(builtin_to_cpp bit os_name arch supported_archs supported_oses resultFileName)
|
||||
set(inputFilePath builtins/builtins.c)
|
||||
set(inputFilePath builtins/builtins-c-cpu.cpp)
|
||||
set(includePath "")
|
||||
set(SKIP OFF)
|
||||
if (NOT ${arch} IN_LIST supported_archs OR NOT ${os_name} IN_LIST supported_oses)
|
||||
@@ -245,12 +245,12 @@ function(builtin_to_cpp bit os_name arch supported_archs supported_oses resultFi
|
||||
# Compose target flags
|
||||
set(target_flags --target=${triple} ${fpic} ${includePath})
|
||||
|
||||
set(output ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/builtins-c-${bit}-${os_name}-${target_arch}.cpp)
|
||||
set(output ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/builtins-cpp-${bit}-${os_name}-${target_arch}.cpp)
|
||||
if (${os_name} STREQUAL "web")
|
||||
add_custom_command(
|
||||
OUTPUT ${output}
|
||||
COMMAND ${EMCC_EXECUTABLE} -DWASM -s WASM_OBJECT_FILES=0 -c ${inputFilePath} -emit-llvm -c -o -
|
||||
| (\"${LLVM_DIS_EXECUTABLE}\" - || echo "builtins.c compile error")
|
||||
COMMAND ${EMCC_EXECUTABLE} -DWASM -s WASM_OBJECT_FILES=0 -I${CMAKE_SOURCE_DIR} -c ${inputFilePath} --std=gnu++17 -emit-llvm -c -o -
|
||||
| (\"${LLVM_DIS_EXECUTABLE}\" - || echo "builtins-c-*.cpp compile error")
|
||||
| \"${Python3_EXECUTABLE}\" bitcode2cpp.py c --type=builtins-c --runtime=${bit} --os=${os_name} --arch=${target_arch} --llvm_as ${LLVM_AS_EXECUTABLE}
|
||||
> ${output}
|
||||
DEPENDS ${inputFilePath} bitcode2cpp.py
|
||||
@@ -259,7 +259,7 @@ function(builtin_to_cpp bit os_name arch supported_archs supported_oses resultFi
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${output}
|
||||
COMMAND ${CLANG_EXECUTABLE} ${target_flags} -w -m${bit} -emit-llvm -c ${inputFilePath} -o - | (\"${LLVM_DIS_EXECUTABLE}\" - || echo "builtins.c compile error")
|
||||
COMMAND ${CLANGPP_EXECUTABLE} ${target_flags} -I${CMAKE_SOURCE_DIR} -m${bit} -emit-llvm --std=gnu++17 -c ${inputFilePath} -o - | (\"${LLVM_DIS_EXECUTABLE}\" - || echo "builtins-c-*.cpp compile error")
|
||||
| \"${Python3_EXECUTABLE}\" bitcode2cpp.py c --type=builtins-c --runtime=${bit} --os=${os_name} --arch=${target_arch} --llvm_as ${LLVM_AS_EXECUTABLE}
|
||||
> ${output}
|
||||
DEPENDS ${inputFilePath} bitcode2cpp.py
|
||||
@@ -271,6 +271,40 @@ function(builtin_to_cpp bit os_name arch supported_archs supported_oses resultFi
|
||||
set_source_files_properties(${resultFileName} PROPERTIES GENERATED true)
|
||||
endfunction()
|
||||
|
||||
function(builtin_genx_to_cpp bit resultFileName)
|
||||
set(inputFilePath builtins/builtins-c-genx.cpp)
|
||||
set(SKIP OFF)
|
||||
if (WIN32)
|
||||
set(os_name "windows")
|
||||
elseif (APPLE)
|
||||
set(SKIP ON)
|
||||
else ()
|
||||
set(os_name "linux")
|
||||
endif()
|
||||
|
||||
if ("${bit}" STREQUAL "32")
|
||||
set(target_arch "genx32")
|
||||
elseif ("${bit}" STREQUAL "64")
|
||||
set(target_arch "genx64")
|
||||
else()
|
||||
set(SKIP ON)
|
||||
endif()
|
||||
|
||||
if (NOT SKIP)
|
||||
set(output ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/builtins-cm-${bit}.cpp)
|
||||
add_custom_command(
|
||||
OUTPUT ${output}
|
||||
COMMAND ${CMC_EXECUTABLE} -I${CMAKE_SOURCE_DIR} -m${bit} -isystem ${CM_INCLUDE_PATH} -emit-llvm -S ${inputFilePath} -o -
|
||||
| \"${Python3_EXECUTABLE}\" bitcode2cpp.py cm --type=builtins-c --runtime=${bit} --os=${os_name} --arch=${target_arch} --llvm_as ${LLVM_AS_EXECUTABLE}
|
||||
> ${output}
|
||||
DEPENDS ${inputFilePath}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
set(${resultFileName} ${output} PARENT_SCOPE)
|
||||
set_source_files_properties(${resultFileName} PROPERTIES GENERATED true)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function (generate_target_builtins resultList)
|
||||
# Dispatch module for Windows and/or Unix targets.
|
||||
foreach (os_name ${TARGET_OS_LIST_FOR_LL})
|
||||
@@ -358,5 +392,15 @@ function (generate_common_builtins resultList)
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
if (GENX_ENABLED)
|
||||
foreach (bit 32 64)
|
||||
builtin_genx_to_cpp(${bit} res_genx_${bit})
|
||||
list(APPEND tmpList ${res_genx_${bit}} )
|
||||
if(MSVC)
|
||||
# Group generated files inside Visual Studio
|
||||
source_group("Generated Builtins" FILES ${res_genx_${bit}})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
set(${resultList} ${tmpList} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -55,6 +55,31 @@ find_program(CLANG_EXECUTABLE NAMES clang
|
||||
endif()
|
||||
message(STATUS "CLANG_EXECUTABLE: ${CLANG_EXECUTABLE}")
|
||||
|
||||
find_program(CLANGPP_EXECUTABLE NAMES clang++
|
||||
PATHS ${LLVM_TOOLS_BINARY_DIR} PATH_SUFFIXES bin NO_DEFAULT_PATH)
|
||||
if (NOT CLANGPP_EXECUTABLE)
|
||||
message(FATAL_ERROR "Failed to find clang++" )
|
||||
endif()
|
||||
message(STATUS "CLANGPP_EXECUTABLE: ${CLANGPP_EXECUTABLE}")
|
||||
|
||||
if (GENX_ENABLED)
|
||||
find_program(CMC_EXECUTABLE NAMES cmc
|
||||
PATHS ${LLVM_TOOLS_BINARY_DIR} PATH_SUFFIXES bin NO_DEFAULT_PATH)
|
||||
if (NOT CMC_EXECUTABLE)
|
||||
message(FATAL_ERROR "Failed to find cmc" )
|
||||
endif()
|
||||
message(STATUS "CMC_EXECUTABLE: ${CMC_EXECUTABLE}")
|
||||
if (NOT CM_INCLUDE_PATH)
|
||||
get_filename_component(CM_INCLUDE_PATH ${CMC_EXECUTABLE} DIRECTORY)
|
||||
set(CM_INCLUDE_PATH ${CM_INCLUDE_PATH}/../include)
|
||||
get_filename_component(CM_INCLUDE_PATH ${CM_INCLUDE_PATH} ABSOLUTE)
|
||||
if (NOT EXISTS ${CM_INCLUDE_PATH} OR NOT EXISTS ${CM_INCLUDE_PATH}/cm)
|
||||
message(FATAL_ERROR "Cannot find path to CM library headers (CM_INCLUDE_PATH)")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "CM_INCLUDE_PATH: ${CM_INCLUDE_PATH}")
|
||||
endif()
|
||||
|
||||
find_program(LLVM_DIS_EXECUTABLE NAMES llvm-dis
|
||||
PATHS ${LLVM_TOOLS_BINARY_DIR} PATH_SUFFIXES bin NO_DEFAULT_PATH)
|
||||
if (NOT LLVM_DIS_EXECUTABLE)
|
||||
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright (c) 2019, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** @file builtins-info.h
|
||||
@brief This file contains information shared between code, that uses
|
||||
builtins and the code that implements them.
|
||||
*/
|
||||
|
||||
#ifndef ISPC_BUILTINS_INFO_H
|
||||
#define ISPC_BUILTINS_INFO_H 1
|
||||
|
||||
namespace PrintInfo {
|
||||
|
||||
// get encoding (as a char) for uniform T type, that is used in type argument of __do_print(_cm)
|
||||
template <typename T> char getEncoding4Uniform();
|
||||
|
||||
// get encoding (as a char) for uniform T type, that is used in type argument of __do_print(_cm)
|
||||
template <typename T> char getEncoding4Varying();
|
||||
|
||||
template <> inline constexpr char getEncoding4Uniform<bool>() { return 'b'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<int>() { return 'i'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<unsigned>() { return 'u'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<float>() { return 'f'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<long long>() { return 'l'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<unsigned long long>() { return 'v'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<double>() { return 'd'; }
|
||||
template <> inline constexpr char getEncoding4Uniform<void *>() { return 'p'; }
|
||||
|
||||
template <> inline constexpr char getEncoding4Varying<bool>() { return 'B'; }
|
||||
template <> inline constexpr char getEncoding4Varying<int>() { return 'I'; }
|
||||
template <> inline constexpr char getEncoding4Varying<unsigned>() { return 'U'; }
|
||||
template <> inline constexpr char getEncoding4Varying<float>() { return 'F'; }
|
||||
template <> inline constexpr char getEncoding4Varying<long long>() { return 'L'; }
|
||||
template <> inline constexpr char getEncoding4Varying<unsigned long long>() { return 'V'; }
|
||||
template <> inline constexpr char getEncoding4Varying<double>() { return 'D'; }
|
||||
template <> inline constexpr char getEncoding4Varying<void *>() { return 'P'; }
|
||||
} // namespace PrintInfo
|
||||
|
||||
#endif // ISPC_BUILTINS_INFO_H
|
||||
+22
-4
@@ -426,8 +426,9 @@ static void lSetInternalFunctions(llvm::Module *module) {
|
||||
"__do_print",
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
"__do_print_cm",
|
||||
"__do_print_cm_str",
|
||||
"__send_eot",
|
||||
#endif
|
||||
#endif //ISPC_GENX_ENABLED
|
||||
"__doublebits_uniform_int64",
|
||||
"__doublebits_varying_int64",
|
||||
"__exclusive_scan_add_double",
|
||||
@@ -811,9 +812,8 @@ static void lSetInternalFunctions(llvm::Module *module) {
|
||||
"__task_count",
|
||||
};
|
||||
// clang-format on
|
||||
int count = sizeof(names) / sizeof(names[0]);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
llvm::Function *f = module->getFunction(names[i]);
|
||||
for (auto name : names) {
|
||||
llvm::Function *f = module->getFunction(name);
|
||||
if (f != NULL && f->empty() == false) {
|
||||
f->setLinkage(llvm::GlobalValue::InternalLinkage);
|
||||
// TO-DO : Revisit adding this back for ARM support.
|
||||
@@ -822,6 +822,23 @@ static void lSetInternalFunctions(llvm::Module *module) {
|
||||
}
|
||||
}
|
||||
|
||||
static void lSetAlwaysInlineFunctions(llvm::Module *module) {
|
||||
std::vector<const char *> names = {
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
"__do_print_cm"
|
||||
#endif /* ISPC_GENX_ENABLED */
|
||||
};
|
||||
for (auto name : names) {
|
||||
llvm::Function *f = module->getFunction(name);
|
||||
if (f) {
|
||||
if (f->hasFnAttribute(llvm::Attribute::NoInline)) {
|
||||
f->removeFnAttr(llvm::Attribute::NoInline);
|
||||
}
|
||||
f->addFnAttr(llvm::Attribute::AlwaysInline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This utility function takes serialized binary LLVM bitcode and adds its
|
||||
definitions to the given module. Functions in the bitcode that can be
|
||||
mapped to ispc functions are also added to the symbol table.
|
||||
@@ -907,6 +924,7 @@ void AddBitcodeToModule(const BitcodeLib *lib, llvm::Module *module, SymbolTable
|
||||
}
|
||||
|
||||
lSetInternalFunctions(module);
|
||||
lSetAlwaysInlineFunctions(module);
|
||||
if (symbolTable != NULL)
|
||||
lAddModuleSymbols(module, symbolTable);
|
||||
lCheckModuleIntrinsics(module);
|
||||
|
||||
Regular → Executable
+299
-115
@@ -36,6 +36,7 @@
|
||||
*/
|
||||
|
||||
#include "stmt.h"
|
||||
#include "builtins-info.h"
|
||||
#include "ctx.h"
|
||||
#include "expr.h"
|
||||
#include "func.h"
|
||||
@@ -2898,62 +2899,62 @@ PrintStmt::PrintStmt(const std::string &f, Expr *v, SourcePos p) : Stmt(p, Print
|
||||
/* Because the pointers to values that are passed to __do_print() are all
|
||||
void *s (and because ispc print() formatting strings statements don't
|
||||
encode types), we pass along a string to __do_print() where the i'th
|
||||
character encodes the type of the i'th value to be printed. Needless to
|
||||
say, the encoding chosen here and the decoding code in __do_print() need
|
||||
to agree on the below!
|
||||
character encodes the type of the i'th value to be printed. The encoding
|
||||
is defined by getEncoding4Uniform<T> and getEncding4Varying<T> functions.
|
||||
*/
|
||||
static char lEncodeType(const Type *t) {
|
||||
if (Type::Equal(t, AtomicType::UniformBool))
|
||||
return 'b';
|
||||
return PrintInfo::getEncoding4Uniform<bool>();
|
||||
if (Type::Equal(t, AtomicType::VaryingBool))
|
||||
return 'B';
|
||||
return PrintInfo::getEncoding4Varying<bool>();
|
||||
if (Type::Equal(t, AtomicType::UniformInt32))
|
||||
return 'i';
|
||||
return PrintInfo::getEncoding4Uniform<int>();
|
||||
if (Type::Equal(t, AtomicType::VaryingInt32))
|
||||
return 'I';
|
||||
return PrintInfo::getEncoding4Varying<int>();
|
||||
if (Type::Equal(t, AtomicType::UniformUInt32))
|
||||
return 'u';
|
||||
return PrintInfo::getEncoding4Uniform<unsigned>();
|
||||
if (Type::Equal(t, AtomicType::VaryingUInt32))
|
||||
return 'U';
|
||||
return PrintInfo::getEncoding4Varying<unsigned>();
|
||||
if (Type::Equal(t, AtomicType::UniformFloat))
|
||||
return 'f';
|
||||
return PrintInfo::getEncoding4Uniform<float>();
|
||||
if (Type::Equal(t, AtomicType::VaryingFloat))
|
||||
return 'F';
|
||||
return PrintInfo::getEncoding4Varying<float>();
|
||||
if (Type::Equal(t, AtomicType::UniformInt64))
|
||||
return 'l';
|
||||
return PrintInfo::getEncoding4Uniform<long long>();
|
||||
if (Type::Equal(t, AtomicType::VaryingInt64))
|
||||
return 'L';
|
||||
return PrintInfo::getEncoding4Varying<long long>();
|
||||
if (Type::Equal(t, AtomicType::UniformUInt64))
|
||||
return 'v';
|
||||
return PrintInfo::getEncoding4Uniform<unsigned long long>();
|
||||
if (Type::Equal(t, AtomicType::VaryingUInt64))
|
||||
return 'V';
|
||||
return PrintInfo::getEncoding4Varying<unsigned long long>();
|
||||
if (Type::Equal(t, AtomicType::UniformDouble))
|
||||
return 'd';
|
||||
return PrintInfo::getEncoding4Uniform<double>();
|
||||
if (Type::Equal(t, AtomicType::VaryingDouble))
|
||||
return 'D';
|
||||
return PrintInfo::getEncoding4Varying<double>();
|
||||
if (CastType<PointerType>(t) != NULL) {
|
||||
if (t->IsUniformType())
|
||||
return 'p';
|
||||
return PrintInfo::getEncoding4Uniform<void *>();
|
||||
else
|
||||
return 'P';
|
||||
return PrintInfo::getEncoding4Varying<void *>();
|
||||
} else
|
||||
return '\0';
|
||||
}
|
||||
|
||||
/** Given an Expr for a value to be printed, emit the code to evaluate the
|
||||
expression and store the result to alloca'd memory. Update the
|
||||
argTypes string with the type encoding for this expression.
|
||||
*/
|
||||
static llvm::Value *lProcessPrintArg(Expr *expr, FunctionEmitContext *ctx, std::string &argTypes) {
|
||||
struct ExprWithType {
|
||||
Expr *expr;
|
||||
char type;
|
||||
};
|
||||
|
||||
static ExprWithType lProcessPrintArgType(Expr *expr) {
|
||||
const Type *type = expr->GetType();
|
||||
if (type == NULL)
|
||||
return NULL;
|
||||
return {NULL, '\0'};
|
||||
|
||||
if (CastType<ReferenceType>(type) != NULL) {
|
||||
expr = new RefDerefExpr(expr, expr->pos);
|
||||
type = expr->GetType();
|
||||
if (type == NULL)
|
||||
return NULL;
|
||||
return {NULL, '\0'};
|
||||
}
|
||||
|
||||
// Just int8 and int16 types to int32s...
|
||||
@@ -2974,30 +2975,276 @@ static llvm::Value *lProcessPrintArg(Expr *expr, FunctionEmitContext *ctx, std::
|
||||
"Only atomic types are allowed in print statements; "
|
||||
"type \"%s\" is illegal.",
|
||||
type->GetString().c_str());
|
||||
return NULL;
|
||||
} else {
|
||||
if (type->IsBoolType()) {
|
||||
// Blast bools to ints, but do it here to preserve encoding for
|
||||
// printing 'true' or 'false'
|
||||
expr = new TypeCastExpr(type->IsUniformType() ? AtomicType::UniformInt32 : AtomicType::VaryingInt32, expr,
|
||||
expr->pos);
|
||||
type = expr->GetType();
|
||||
}
|
||||
argTypes.push_back(t);
|
||||
|
||||
llvm::Value *ptr = ctx->AllocaInst(type, "print_arg");
|
||||
llvm::Value *val = expr->GetValue(ctx);
|
||||
if (!val)
|
||||
return NULL;
|
||||
ctx->StoreInst(val, ptr, type);
|
||||
|
||||
ptr = ctx->BitCastInst(ptr, LLVMTypes::VoidPointerType);
|
||||
return ptr;
|
||||
return {NULL, '\0'};
|
||||
}
|
||||
if (type->IsBoolType()) {
|
||||
// Blast bools to ints, but do it here to preserve encoding for
|
||||
// printing 'true' or 'false'
|
||||
expr = new TypeCastExpr(type->IsUniformType() ? AtomicType::UniformInt32 : AtomicType::VaryingInt32, expr,
|
||||
expr->pos);
|
||||
}
|
||||
return {expr, t};
|
||||
}
|
||||
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
class PrintArgsBuilder {
|
||||
// properly dereferenced and size extended value expressions
|
||||
std::vector<ExprWithType> argExprs_;
|
||||
FunctionEmitContext *ctx_;
|
||||
|
||||
public:
|
||||
PrintArgsBuilder() : argExprs_(), ctx_(NULL) {}
|
||||
|
||||
template <typename Iter>
|
||||
PrintArgsBuilder(Iter first, Iter last, FunctionEmitContext *ctx) : argExprs_(), ctx_(ctx) {
|
||||
std::transform(first, last, std::back_inserter(argExprs_),
|
||||
[](Expr *expr) { return lProcessPrintArgType(expr); });
|
||||
std::for_each(argExprs_.cbegin(), argExprs_.cend(),
|
||||
[](const ExprWithType &elem) { Assert(elem.expr && "must have all values processed"); });
|
||||
}
|
||||
|
||||
// the string that will later go as TYPES_IDX argument
|
||||
const std::string generateArgTypes() const {
|
||||
std::string argTypes;
|
||||
std::transform(argExprs_.cbegin(), argExprs_.cend(), std::back_inserter(argTypes),
|
||||
[](const ExprWithType &argInfo) { return argInfo.type; });
|
||||
return argTypes;
|
||||
}
|
||||
|
||||
// the value that will later go as ARGS_IDX argument
|
||||
// Splits arguments into set of uints, stores them in uint[], returns pointer to it
|
||||
llvm::Value *emitArgCode() {
|
||||
std::vector<llvm::Value *> argValues(argExprs_.size());
|
||||
std::transform(argExprs_.cbegin(), argExprs_.cend(), argValues.begin(),
|
||||
[this](const ExprWithType &argInfo) { return argInfo.expr->GetValue(ctx_); });
|
||||
std::vector<llvm::Value *> alignedArgValues;
|
||||
createAlignedArgValues(argValues.cbegin(), argValues.cend(), std::back_inserter(alignedArgValues));
|
||||
auto argAlloca = storeAlignedArgValues(alignedArgValues.cbegin(), alignedArgValues.cend());
|
||||
return ctx_->BitCastInst(argAlloca, LLVMTypes::Int32PointerType);
|
||||
}
|
||||
|
||||
// current implementation of __do_print_cm requires the number of uniform arguments with bool excluded
|
||||
int countNonBoolUniformArgs() const {
|
||||
return std::count_if(argExprs_.cbegin(), argExprs_.cend(), [](const ExprWithType &argInfo) {
|
||||
return argInfo.expr->GetType()->IsUniformType() && argInfo.type != PrintInfo::getEncoding4Uniform<bool>();
|
||||
});
|
||||
}
|
||||
|
||||
// current implementation of __do_print_cm requires the number of varying arguments with bool excluded
|
||||
int countNonBoolVaryingArgs() const {
|
||||
return std::count_if(argExprs_.cbegin(), argExprs_.cend(), [](const ExprWithType &argInfo) {
|
||||
return argInfo.expr->GetType()->IsVaryingType() && argInfo.type != PrintInfo::getEncoding4Varying<bool>();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
// Aligned here means that every value is represented by set of i32
|
||||
template <typename InputIter, typename OutputIter>
|
||||
OutputIter createAlignedArgValues(InputIter first, InputIter last, OutputIter dst) {
|
||||
for (; first != last; ++first) {
|
||||
auto arg = *first;
|
||||
if (arg->getType()->isVectorTy())
|
||||
dst = createAlignedVectorArgValue(arg, dst);
|
||||
else
|
||||
dst = createAlignedScalarArgValue(arg, dst);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// vector becomes separate scalars
|
||||
template <typename OutputIter> OutputIter createAlignedVectorArgValue(llvm::Value *vecArg, OutputIter dst) {
|
||||
auto width = g->target->getVectorWidth();
|
||||
for (auto lane = 0; lane < width; ++lane) {
|
||||
auto scalarArg = ctx_->ExtractInst(vecArg, lane);
|
||||
dst = createAlignedScalarArgValue(scalarArg, dst);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// 64-bit values must become 2 i32
|
||||
template <typename OutputIter> OutputIter createAlignedScalarArgValue(llvm::Value *arg, OutputIter dst) {
|
||||
if (arg->getType()->getPrimitiveSizeInBits() == 64) {
|
||||
arg = castToIntIfNeeded(arg, LLVMTypes::Int64Type);
|
||||
auto lowArg = ctx_->TruncInst(arg, LLVMTypes::Int32Type);
|
||||
auto almostHiArg = ctx_->BinaryOperator(llvm::Instruction::LShr, arg, LLVMInt64(32));
|
||||
auto hiArg = ctx_->TruncInst(almostHiArg, LLVMTypes::Int32Type);
|
||||
*dst++ = lowArg;
|
||||
*dst++ = hiArg;
|
||||
return dst;
|
||||
} else {
|
||||
assert(arg->getType()->getPrimitiveSizeInBits() == 32 && "must be either 32 or 64 bit");
|
||||
*dst++ = castToIntIfNeeded(arg, LLVMTypes::Int32Type);
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
// creates alloca, stores values from [first, last) into it
|
||||
// returns alloca value
|
||||
template <typename InputIter> llvm::Value *storeAlignedArgValues(InputIter first, InputIter last) {
|
||||
auto numArgs = last - first;
|
||||
auto arrTy = llvm::ArrayType::get(LLVMTypes::Int32Type, numArgs);
|
||||
auto argAlloca = ctx_->AllocaInst(arrTy, "print_arg_ptrs");
|
||||
for (int i = 0; first != last; ++first, ++i) {
|
||||
auto curArgPtr = ctx_->AddElementOffset(argAlloca, i, NULL);
|
||||
ctx_->StoreInst(*first, curArgPtr, NULL, true);
|
||||
}
|
||||
return argAlloca;
|
||||
}
|
||||
|
||||
llvm::Value *castToIntIfNeeded(llvm::Value *val, llvm::Type *intTy) {
|
||||
assert(intTy->isIntegerTy());
|
||||
assert(val->getType()->getPrimitiveSizeInBits() == intTy->getPrimitiveSizeInBits());
|
||||
if (val->getType() != intTy)
|
||||
if (val->getType()->isPointerTy())
|
||||
return ctx_->PtrToIntInst(val, intTy);
|
||||
return ctx_->BitCastInst(val, intTy);
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
// prepares arguments for __do_print_cm function
|
||||
std::vector<llvm::Value *> PrintStmt::getDoPrintCMArgs(FunctionEmitContext *ctx) const {
|
||||
std::vector<llvm::Value *> doPrintCMArgs(GENX_NUM_IDX);
|
||||
doPrintCMArgs[FORMAT_IDX] = ctx->GetStringPtr(format);
|
||||
doPrintCMArgs[WIDTH_IDX] = LLVMInt32(g->target->getVectorWidth());
|
||||
doPrintCMArgs[MASK_IDX] = ctx->LaneMask(ctx->GenXSimdCFPredicate(LLVMMaskAllOn));
|
||||
if (values == NULL) {
|
||||
doPrintCMArgs[ARGS_IDX] = llvm::Constant::getNullValue(LLVMTypes::Int32PointerType);
|
||||
doPrintCMArgs[TYPES_IDX] = ctx->GetStringPtr("");
|
||||
doPrintCMArgs[UNI_NUM_IDX] = LLVMInt32(0);
|
||||
doPrintCMArgs[VAR_NUM_IDX] = LLVMInt32(0);
|
||||
} else {
|
||||
PrintArgsBuilder argsBuilder;
|
||||
ExprList *elist = llvm::dyn_cast<ExprList>(values);
|
||||
if (elist)
|
||||
argsBuilder = PrintArgsBuilder(elist->exprs.begin(), elist->exprs.end(), ctx);
|
||||
else
|
||||
argsBuilder = PrintArgsBuilder(&values, &values + 1, ctx);
|
||||
doPrintCMArgs[TYPES_IDX] = ctx->GetStringPtr(argsBuilder.generateArgTypes());
|
||||
doPrintCMArgs[ARGS_IDX] = argsBuilder.emitArgCode();
|
||||
doPrintCMArgs[UNI_NUM_IDX] = LLVMInt32(argsBuilder.countNonBoolUniformArgs());
|
||||
doPrintCMArgs[VAR_NUM_IDX] = LLVMInt32(argsBuilder.countNonBoolVaryingArgs());
|
||||
}
|
||||
return doPrintCMArgs;
|
||||
}
|
||||
|
||||
#endif // ISPC_GENX_ENABLED
|
||||
|
||||
/** Given an Expr for a value to be printed, emit the code to evaluate the
|
||||
expression and store the result to alloca's memory. Update the
|
||||
argTypes string with the type encoding for this expression.
|
||||
*/
|
||||
static llvm::Value *lEmitPrintArgCode(Expr *expr, FunctionEmitContext *ctx) {
|
||||
const Type *type = expr->GetType();
|
||||
|
||||
llvm::Type *llvmExprType = type->LLVMType(g->ctx);
|
||||
llvm::Value *ptr = ctx->AllocaInst(llvmExprType, "print_arg");
|
||||
llvm::Value *val = expr->GetValue(ctx);
|
||||
if (!val)
|
||||
return NULL;
|
||||
ctx->StoreInst(val, ptr);
|
||||
|
||||
ptr = ctx->BitCastInst(ptr, LLVMTypes::VoidPointerType);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static bool lProcessPrintArg(Expr *expr, FunctionEmitContext *ctx, llvm::Value *argPtrArray, int offset,
|
||||
std::string &argTypes) {
|
||||
if (!expr)
|
||||
return false;
|
||||
auto exprType = lProcessPrintArgType(expr);
|
||||
expr = exprType.expr;
|
||||
char type = exprType.type;
|
||||
if (!expr)
|
||||
return false;
|
||||
argTypes.push_back(type);
|
||||
llvm::Value *ptr = lEmitPrintArgCode(expr, ctx);
|
||||
if (!ptr)
|
||||
return false;
|
||||
llvm::Value *arrayPtr = ctx->AddElementOffset(argPtrArray, offset, NULL);
|
||||
ctx->StoreInst(ptr, arrayPtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// prepares arguments for __do_print function
|
||||
std::vector<llvm::Value *> PrintStmt::getDoPrintArgs(FunctionEmitContext *ctx) const {
|
||||
std::vector<llvm::Value *> doPrintArgs(STD_NUM_IDX);
|
||||
std::string argTypes;
|
||||
|
||||
if (values == NULL) {
|
||||
llvm::Type *ptrPtrType = llvm::PointerType::get(LLVMTypes::VoidPointerType, 0);
|
||||
doPrintArgs[ARGS_IDX] = llvm::Constant::getNullValue(ptrPtrType);
|
||||
} else {
|
||||
// Get the values passed to the print() statement evaluated and
|
||||
// stored in memory so that we set up the array of pointers to them
|
||||
// for the 5th __do_print() argument
|
||||
ExprList *elist = llvm::dyn_cast<ExprList>(values);
|
||||
int nArgs = elist ? elist->exprs.size() : 1;
|
||||
// Allocate space for the array of pointers to values to be printed
|
||||
llvm::Type *argPtrArrayType = llvm::ArrayType::get(LLVMTypes::VoidPointerType, nArgs);
|
||||
llvm::Value *argPtrArray = ctx->AllocaInst(argPtrArrayType, "print_arg_ptrs");
|
||||
// Store the array pointer as a void **, which is what __do_print()
|
||||
// expects
|
||||
doPrintArgs[ARGS_IDX] = ctx->BitCastInst(argPtrArray, llvm::PointerType::get(LLVMTypes::VoidPointerType, 0));
|
||||
|
||||
// Now, for each of the arguments, emit code to evaluate its value
|
||||
// and store the value into alloca's storage. Then store the
|
||||
// pointer to the alloca's storage into argPtrArray.
|
||||
if (elist) {
|
||||
for (unsigned int i = 0; i < elist->exprs.size(); ++i) {
|
||||
Expr *expr = elist->exprs[i];
|
||||
if (!lProcessPrintArg(expr, ctx, argPtrArray, i, argTypes)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lProcessPrintArg(values, ctx, argPtrArray, 0, argTypes)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
llvm::Value *mask = ctx->GetFullMask();
|
||||
// Set up the rest of the parameters to it
|
||||
doPrintArgs[FORMAT_IDX] = ctx->GetStringPtr(format);
|
||||
doPrintArgs[TYPES_IDX] = ctx->GetStringPtr(argTypes);
|
||||
doPrintArgs[WIDTH_IDX] = LLVMInt32(g->target->getVectorWidth());
|
||||
doPrintArgs[MASK_IDX] = ctx->LaneMask(mask);
|
||||
return doPrintArgs;
|
||||
}
|
||||
|
||||
// prepares arguments for __do_print(_cm) function depending on target
|
||||
std::vector<llvm::Value *> PrintStmt::getPrintImplArgs(FunctionEmitContext *ctx) const {
|
||||
std::vector<llvm::Value *> printImplArgs;
|
||||
switch (g->target->getISA()) {
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
case Target::GENX:
|
||||
printImplArgs = getDoPrintCMArgs(ctx);
|
||||
break;
|
||||
#endif // ISPC_GENX_ENABLED
|
||||
default:
|
||||
printImplArgs = getDoPrintArgs(ctx);
|
||||
break;
|
||||
}
|
||||
return printImplArgs;
|
||||
}
|
||||
|
||||
// Returns pointer to __do_print(_cm) function
|
||||
llvm::Function *getPrintImplFunc() {
|
||||
llvm::Function *printImplFunc = nullptr;
|
||||
switch (g->target->getISA()) {
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
case Target::GENX:
|
||||
printImplFunc = m->module->getFunction("__do_print_cm");
|
||||
break;
|
||||
#endif /* ISPC_GENX_ENABLED */
|
||||
default:
|
||||
printImplFunc = m->module->getFunction("__do_print");
|
||||
break;
|
||||
}
|
||||
return printImplFunc;
|
||||
}
|
||||
/* PrintStmt works closely with the __do_print() function implemented in
|
||||
the builtins-c.c file. In particular, the EmitCode() method here needs to
|
||||
the builtins-c-cpu.cpp file. In particular, the EmitCode() method here needs to
|
||||
take the arguments passed to it from ispc and generate a valid call to
|
||||
__do_print() with the information that __do_print() then needs to do the
|
||||
actual printing work at runtime.
|
||||
@@ -3005,75 +3252,12 @@ static llvm::Value *lProcessPrintArg(Expr *expr, FunctionEmitContext *ctx, std::
|
||||
void PrintStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
if (!ctx->GetCurrentBasicBlock())
|
||||
return;
|
||||
|
||||
ctx->SetDebugPos(pos);
|
||||
|
||||
// __do_print takes 5 arguments; we'll get them stored in the args[] array
|
||||
// in the code emitted below
|
||||
//
|
||||
// 1. the format string
|
||||
// 2. a string encoding the types of the values being printed,
|
||||
// one character per value
|
||||
// 3. the number of running program instances (i.e. the target's
|
||||
// vector width)
|
||||
// 4. the current lane mask
|
||||
// 5. a pointer to an array of pointers to the values to be printed
|
||||
llvm::Value *args[5];
|
||||
std::string argTypes;
|
||||
|
||||
if (values == NULL) {
|
||||
llvm::Type *ptrPtrType = llvm::PointerType::get(LLVMTypes::VoidPointerType, 0);
|
||||
args[4] = llvm::Constant::getNullValue(ptrPtrType);
|
||||
} else {
|
||||
// Get the values passed to the print() statement evaluated and
|
||||
// stored in memory so that we set up the array of pointers to them
|
||||
// for the 5th __do_print() argument
|
||||
ExprList *elist = llvm::dyn_cast<ExprList>(values);
|
||||
int nArgs = elist ? elist->exprs.size() : 1;
|
||||
|
||||
// Allocate space for the array of pointers to values to be printed
|
||||
llvm::Type *argPtrArrayType = llvm::ArrayType::get(LLVMTypes::VoidPointerType, nArgs);
|
||||
llvm::Value *argPtrArray = ctx->AllocaInst(argPtrArrayType, "print_arg_ptrs");
|
||||
// Store the array pointer as a void **, which is what __do_print()
|
||||
// expects
|
||||
args[4] = ctx->BitCastInst(argPtrArray, llvm::PointerType::get(LLVMTypes::VoidPointerType, 0));
|
||||
|
||||
// Now, for each of the arguments, emit code to evaluate its value
|
||||
// and store the value into alloca'd storage. Then store the
|
||||
// pointer to the alloca'd storage into argPtrArray.
|
||||
if (elist) {
|
||||
for (unsigned int i = 0; i < elist->exprs.size(); ++i) {
|
||||
Expr *expr = elist->exprs[i];
|
||||
if (!expr)
|
||||
return;
|
||||
llvm::Value *ptr = lProcessPrintArg(expr, ctx, argTypes);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
llvm::Value *arrayPtr = ctx->AddElementOffset(argPtrArray, i, NULL);
|
||||
ctx->StoreInst(ptr, arrayPtr);
|
||||
}
|
||||
} else {
|
||||
llvm::Value *ptr = lProcessPrintArg(values, ctx, argTypes);
|
||||
if (!ptr)
|
||||
return;
|
||||
llvm::Value *arrayPtr = ctx->AddElementOffset(argPtrArray, 0, NULL);
|
||||
ctx->StoreInst(ptr, arrayPtr);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can emit code to call __do_print()
|
||||
llvm::Function *printFunc = m->module->getFunction("__do_print");
|
||||
AssertPos(pos, printFunc);
|
||||
|
||||
llvm::Value *mask = ctx->GetFullMask();
|
||||
// Set up the rest of the parameters to it
|
||||
args[0] = ctx->GetStringPtr(format);
|
||||
args[1] = ctx->GetStringPtr(argTypes);
|
||||
args[2] = LLVMInt32(g->target->getVectorWidth());
|
||||
args[3] = ctx->LaneMask(mask);
|
||||
std::vector<llvm::Value *> argVec(&args[0], &args[5]);
|
||||
ctx->CallInst(printFunc, NULL, argVec, "");
|
||||
auto printImplArgs = getPrintImplArgs(ctx);
|
||||
Assert(!printImplArgs.empty() && "Haven't managed to produce __do_print(_cm) args");
|
||||
auto printImplFunc = getPrintImplFunc();
|
||||
AssertPos(pos, printImplFunc);
|
||||
ctx->CallInst(printImplFunc, NULL, printImplArgs, "");
|
||||
}
|
||||
|
||||
void PrintStmt::Print(int indent) const { printf("%*cPrint Stmt (%s)", indent, ' ', format.c_str()); }
|
||||
@@ -3098,7 +3282,7 @@ void AssertStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
}
|
||||
bool isUniform = type->IsUniformType();
|
||||
|
||||
// The actual functionality to do the check and then handle falure is
|
||||
// The actual functionality to do the check and then handle failure is
|
||||
// done via a builtin written in bitcode in builtins/util.m4.
|
||||
llvm::Function *assertFunc =
|
||||
isUniform ? m->module->getFunction("__do_assert_uniform") : m->module->getFunction("__do_assert_varying");
|
||||
|
||||
Regular → Executable
+23
@@ -483,6 +483,29 @@ class PrintStmt : public Stmt {
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
std::vector<llvm::Value *> getDoPrintArgs(FunctionEmitContext *ctx) const;
|
||||
std::vector<llvm::Value *> getPrintImplArgs(FunctionEmitContext *ctx) const;
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
std::vector<llvm::Value *> getDoPrintCMArgs(FunctionEmitContext *ctx) const;
|
||||
#endif // ISPC_GENX_ENABLED
|
||||
|
||||
enum {
|
||||
FORMAT_IDX, // the format string
|
||||
TYPES_IDX, // a string encoding the types of the values being printed,
|
||||
// one character per value
|
||||
WIDTH_IDX, // the number of running program instances (i.e. the target's
|
||||
// vector width)
|
||||
MASK_IDX, // the current lane mask
|
||||
ARGS_IDX, // a pointer to an array of pointers to the values to be printed
|
||||
#ifdef ISPC_GENX_ENABLED
|
||||
UNI_NUM_IDX, // number of uniform nonbool values
|
||||
VAR_NUM_IDX, // number of varying nonbool values
|
||||
GENX_NUM_IDX, // number of arguments of __do_print_cm
|
||||
#endif // ISPC_GENX_ENABLED
|
||||
// number of arguments of __do_print
|
||||
STD_NUM_IDX = ARGS_IDX + 1
|
||||
};
|
||||
|
||||
/** Format string for the print() statement. */
|
||||
const std::string format;
|
||||
/** This holds the arguments passed to the print() statement. If more
|
||||
|
||||
Reference in new issue
Block a user