-
Notifications
You must be signed in to change notification settings - Fork 787
[WIP] [SYCL] Add SYCLConfig helper #859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cb46557
56a5e5a
0304541
d8ec32d
4c1f4d1
6897706
8f5e519
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
//==---------------- config.cpp ---------------------------------*- C++-*---==// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include <CL/sycl/detail/os_util.hpp> | ||
#include <detail/config.hpp> | ||
|
||
#include <cstring> | ||
#include <fstream> | ||
#include <iostream> | ||
#include <limits> | ||
|
||
#define STRINGIFY_LINE_HELP(s) #s | ||
#define STRINGIFY_LINE(s) STRINGIFY_LINE_HELP(s) | ||
|
||
namespace cl { | ||
namespace sycl { | ||
namespace detail { | ||
|
||
#ifndef SYCL_CONFIG_FILE_NAME | ||
#define SYCL_CONFIG_FILE_NAME "sycl.conf" | ||
#endif // SYCL_CONFIG_FILE_NAME | ||
|
||
#define CONFIG(Name, MaxSize, CompileTimeDef) \ | ||
const char *SYCLConfigBase<Name>::MValueFromFile = nullptr; \ | ||
char SYCLConfigBase<Name>::MStorage[MaxSize]; \ | ||
const char *const SYCLConfigBase<Name>::MCompileTimeDef = \ | ||
getStrOrNullptr(STRINGIFY_LINE(CompileTimeDef)); \ | ||
const char *const SYCLConfigBase<Name>::MConfigName = STRINGIFY_LINE(Name); | ||
#include "detail/config.def" | ||
#undef CONFIG | ||
|
||
#define MAX_CONFIG_NAME 256 | ||
|
||
static void initValue(const char *Key, const char *Value) { | ||
#define CONFIG(Name, MaxSize, CompileTimeDef) \ | ||
if (0 == strncmp(Key, SYCLConfigBase<Name>::MConfigName, MAX_CONFIG_NAME)) { \ | ||
strncpy(SYCLConfigBase<Name>::MStorage, Value, MaxSize); \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And what happens when you reach the limit ? A non- There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. Will fix by guaranteeing \0 at the and of the Value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer just using safer C++ instead of lower-level C at the first place... |
||
SYCLConfigBase<Name>::MValueFromFile = SYCLConfigBase<Name>::MStorage; \ | ||
return; \ | ||
} | ||
#include "detail/config.def" | ||
#undef CONFIG | ||
} | ||
|
||
void readConfig() { | ||
static bool Initialized = false; | ||
if (Initialized) | ||
return; | ||
|
||
std::fstream File; | ||
if (const char *ConfigFile = getenv("SYCL_CONFIG_FILE_NAME")) | ||
File.open(ConfigFile, std::ios::in); | ||
else { | ||
const std::string LibSYCLDir = sycl::detail::OSUtil::getCurrentDSODir(); | ||
File.open(LibSYCLDir + sycl::detail::OSUtil::DirSep + SYCL_CONFIG_FILE_NAME, | ||
std::ios::in); | ||
} | ||
|
||
if (File.is_open()) { | ||
// TODO: Use max size from macro instead of 256 | ||
alexbatashev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
char Key[MAX_CONFIG_NAME] = {0}, Value[256] = {0}; | ||
while (!File.eof()) { | ||
// Expected fromat: | ||
// ConfigName=Value\r | ||
// ConfigName=Value | ||
// TODO: Skip spaces before and after '=' | ||
File.getline(Key, sizeof(Key), '='); | ||
if (File.fail()) { | ||
// Fail to process the line. Skip it completely and try next one. | ||
// Do we want to restore here? Or just throw an exception? | ||
File.clear(File.rdstate() & ~std::ios_base::failbit); | ||
File.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | ||
continue; | ||
} | ||
File.getline(Value, sizeof(Value), '\n'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder what happens on Windows. People tend add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a logic which removes \r if it exists. Cannot write proper test as we have no env vars sensitive to the additional character at the end. Did manual testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, \n is the default value of 3rd argument.. Will remove |
||
|
||
if (File.fail()) { | ||
// Fail to process the value while config name is OK. It's likely that | ||
// value is too long. Currently just deal what we have got and ignore | ||
// remaining characters on the line. | ||
// Do we want to restore here? Or just throw an exception? | ||
File.clear(File.rdstate() & ~std::ios_base::failbit); | ||
File.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | ||
} | ||
|
||
// Handle '\r' by nullifying it | ||
const std::streamsize ReadSybmols = File.gcount(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is a Sybmols ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will rename to "characters". |
||
if (ReadSybmols > 1 && '\r' == Value[ReadSybmols - 2]) | ||
Value[ReadSybmols - 2] = '\0'; | ||
|
||
initValue(Key, Value); | ||
} | ||
} | ||
Initialized = true; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All this is super hackish and obfuscated compared to the obvious algorithm: iterating on a plain |
||
|
||
// Prints configs name with their value | ||
void dumpConfig() { | ||
#define CONFIG(Name, MaxSize, CompileTimeDef) \ | ||
const char *Val = SYCLConfig<Name>::get(); \ | ||
std::cerr << SYCLConfigBase<Name>::MConfigName << " : " \ | ||
<< (Val ? Val : "unset") << std::endl; | ||
#include "detail/config.def" | ||
#undef CONFIG | ||
} | ||
|
||
} // namespace cl | ||
} // namespace sycl | ||
} // namespace detail | ||
|
||
#undef STRINGIFY_LINE_HELP | ||
#undef STRINGIFY_LINE |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Syntax: | ||
// CONFIG(CONFIG_NAME, VALUE_MAX_SIZE_IN_BYTES, COMPILE_TIME_CONFIG_NAME) | ||
// | ||
// CONFIG_NAME is used as a specifier to access specific config using | ||
// SYCLConfig class. It is also used in order to determine a config key when | ||
// reading the configuration file. | ||
// VALUE_MAX_SIZE_IN_BYTES is used as a maximum size of a storage when reading | ||
// configs from the configuration file. | ||
// COMPILE_TIME_CONFIG_NAME is a name of compile time macro which can be used | ||
// to set a value of a config. COMPILE_TIME_CONFIG_NAME must start with double | ||
// underscore(__). | ||
|
||
CONFIG(SYCL_PRINT_EXECUTION_GRAPH, 32, __SYCL_PRINT_EXECUTION_GRAPH) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
//==---------------- config.hpp - SYCL context ------------------*- C++-*---==// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#pragma once | ||
|
||
#include <cstdlib> | ||
|
||
namespace cl { | ||
namespace sycl { | ||
namespace detail { | ||
|
||
#ifdef DISABLE_CONFIG_FROM_ENV | ||
constexpr bool ConfigFromEnvEnabled = false; | ||
#else | ||
constexpr bool ConfigFromEnvEnabled = true; | ||
#endif // DISABLE_CONFIG_FROM_ENV | ||
|
||
#ifdef DISABLE_CONFIG_FROM_CONFIG_FILE | ||
constexpr bool ConfigFromFileEnabled = false; | ||
#else | ||
constexpr bool ConfigFromFileEnabled = true; | ||
#endif // DISABLE_CONFIG_FROM_CONFIG_FILE | ||
|
||
#ifdef DISABLE_CONFIG_FROM_COMPILE_TIME | ||
constexpr bool ConfigFromCompileDefEnabled = false; | ||
#else | ||
constexpr bool ConfigFromCompileDefEnabled = true; | ||
#endif // DISABLE_CONFIG_FROM_COMPILE_TIME | ||
|
||
// Enum of config IDs for accessing other arrays | ||
enum ConfigID { | ||
START = 0, | ||
#define CONFIG(name, ...) name, | ||
#include "config.def" | ||
#undef CONFIG | ||
END | ||
}; | ||
|
||
// Consider strings starting with __ as unset | ||
constexpr const char *getStrOrNullptr(const char *Str) { | ||
return (Str[0] == '_' && Str[1] == '_') ? nullptr : Str; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happened if a string has less than 2 characters? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will add check for that. |
||
} | ||
|
||
template <ConfigID Config> class SYCLConfigBase; | ||
|
||
#define CONFIG(Name, MaxSize, CompileTimeDef) \ | ||
template <> class SYCLConfigBase<Name> { \ | ||
public: \ | ||
/*Preallocated storage for config value which is extracted from a config \ | ||
* file*/ \ | ||
static char MStorage[MaxSize]; \ | ||
/*Points to the storage if config is set in the file, nullptr otherwise*/ \ | ||
static const char *MValueFromFile; \ | ||
/*The name of the config*/ \ | ||
static const char *const MConfigName; \ | ||
/*Points to the value which is set during compilation, nullptr otherwise. \ | ||
* Detection of whether a value is set or not is based on checking the \ | ||
* beginning of the string, if it starts with double underscore(__) the \ | ||
* value is not set.*/ \ | ||
static const char *const MCompileTimeDef; \ | ||
}; | ||
#include "config.def" | ||
#undef CONFIG | ||
|
||
// Intializes configs from the configuration file | ||
void readConfig(); | ||
|
||
template <ConfigID Config> class SYCLConfig { | ||
using BaseT = SYCLConfigBase<Config>; | ||
|
||
public: | ||
static const char *get() { | ||
const char *ValStr = getRawValue(); | ||
return ValStr; | ||
} | ||
|
||
private: | ||
static const char *getRawValue() { | ||
if (ConfigFromEnvEnabled) | ||
if (const char *ValStr = getenv(BaseT::MConfigName)) | ||
return ValStr; | ||
|
||
if (ConfigFromFileEnabled) { | ||
readConfig(); | ||
if (BaseT::MValueFromFile) | ||
return BaseT::MValueFromFile; | ||
} | ||
|
||
if (ConfigFromCompileDefEnabled && BaseT::MCompileTimeDef) | ||
return BaseT::MCompileTimeDef; | ||
|
||
return nullptr; | ||
} | ||
}; | ||
|
||
} // namespace cl | ||
} // namespace sycl | ||
} // namespace detail |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
//===----------------------------------------------------------------------===// | ||
|
||
#include <CL/sycl/access/access.hpp> | ||
#include "detail/config.hpp" | ||
#include <CL/sycl/detail/event_impl.hpp> | ||
#include <CL/sycl/detail/memory_manager.hpp> | ||
#include <CL/sycl/detail/queue_impl.hpp> | ||
|
@@ -46,7 +47,7 @@ static bool IsSuitableSubReq(const Requirement *Req) { | |
} | ||
|
||
Scheduler::GraphBuilder::GraphBuilder() { | ||
if (const char *EnvVarCStr = std::getenv("SYCL_PRINT_EXECUTION_GRAPH")) { | ||
if (const char *EnvVarCStr = SYCLConfig<SYCL_PRINT_EXECUTION_GRAPH>::get()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use it for all environment variable (SYCL_DEVICE_TYPE, SYCL_DUMP_IMAGES, etc)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use it for environment variables that are "getenv'ed" inside source directory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to mention this limitation in the commit message. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, will do during merge. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated description with new sentence. |
||
std::string GraphPrintOpts(EnvVarCStr); | ||
bool EnableAlways = GraphPrintOpts.find("always") != std::string::npos; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
//==---- config.cpp --------------------------------------------------------==// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// RUN: %clangxx -g -O0 -fsycl %s -o %t.out | ||
// RUN: echo "SYCL_PRINT_EXECUTION_GRAPH=always" > %t.cfg | ||
// RUN: env SYCL_CONFIG_FILE_NAME=%t.cfg %t.out | ||
// RUN: ls | grep dot | ||
// RUN: rm *.dot | ||
// RUN: env SYCL_PRINT_EXECUTION_GRAPH=always %t.out | ||
// RUN: ls | grep dot | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure that this works on Windows if you don't have GNU coreutils installed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've reused this idea from clang LIT tests so I expect it works on the same configurations as clang LIT tests work. |
||
// RUN: rm *.dot | ||
// RUN: %t.out | ||
// RUN: ls | not grep dot | ||
|
||
#include <CL/sycl.hpp> | ||
|
||
using namespace cl; | ||
|
||
int main() { | ||
sycl::buffer<int, 1> Buf(sycl::range<1>{1}); | ||
auto Acc = Buf.get_access<sycl::access::mode::read>(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All this smells a lot old C...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will replace with constexpr.