-
Notifications
You must be signed in to change notification settings - Fork 769
[SYCL]Link Fallback Device Libraries On Demand #1787
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 18 commits
c6af296
3eea5dc
5c59cdf
698b13d
ab9618c
35ec2df
61943e4
893b699
8d7ec71
7686038
b8f1037
c093963
8d20764
bfa8c2c
4da5790
8377e11
ac472cb
c7d459c
a7e8ea4
652d15a
117bebf
5445247
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,29 @@ | ||
//=- SYCLRTShared.h - Shared definition between llvm tools and SYCL runtime -=// | ||
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. Thanks! @jinge90 , I suggest to add a comments what this file is
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. This seems OK to me. 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. Hi, @kbobrovs 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. @kbobrovs, thanks for pinging me and sorry for late feedback. I don't fully understand the design of this "linking on demand" solution (it would be great to extend https://github.com/intel/llvm/blob/sycl/sycl/doc/CompilerAndRuntimeDesign.md document), but this concerns me. It would be great to get a community feedback on this approach through RFC. 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.
@bader, under "Intel specific OpenCL extensions" do you mean SYCL as a whole or just the "linking on demand part"? If the latter - then SYCLRTShared.h is not specific to this extension, it serves for sharing some type and string literal definitions between LLVM tools and SYCL runtime.
Do you think this should be done before this PR can be merged or can be done in parallel? 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.
cl_intel_devicelib_assert, cl_intel_devicelib_math, cl_intel_devicelib_math_fp64, cl_intel_devicelib_complex, cl_intel_devicelib_complex_fp64 - are Intel specific OpenCL extensions.
Can we apply the same approach as regular C++ compiler does? I.e. link with the whole C++ standard library (using LLVM linker) + provide an compiler option to disable SYCL extension and skip linking with this libraries? This should much easier to implement and maintain as it should not require extending LLVM functionality.
Ideally the review should happen before the merge, but if there are any time constraints we can merge and re-do later if needed. 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. Hi, @kbobrovs @andykaylor @bader
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'm not convinced that this is the right approach to "fix performance regression". Moving some runtime overhead to compile time (e.g. #1398) seems to be a better option. |
||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
#ifndef LLVM_SUPPORT_SYCLRTSHARED_H | ||
#define LLVM_SUPPORT_SYCLRTSHARED_H | ||
/// Device binary image property set names recognized by the SYCL runtime. | ||
/// Name must be consistent with | ||
/// PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS defined in | ||
/// PropertySetIO.h | ||
#define PI_PROPERTY_SET_SPEC_CONST_MAP "SYCL/specialization constants" | ||
#define PI_PROPERTY_SET_DEVICELIB_REQ_MASK "SYCL/devicelib req mask" | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
namespace llvm { | ||
namespace util { | ||
namespace sycl { | ||
enum DeviceLibExt { | ||
cl_intel_devicelib_assert = 0, | ||
cl_intel_devicelib_math, | ||
cl_intel_devicelib_math_fp64, | ||
cl_intel_devicelib_complex, | ||
cl_intel_devicelib_complex_fp64 | ||
}; | ||
} | ||
} // namespace util | ||
} // namespace llvm | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ | |
#include "llvm/Support/InitLLVM.h" | ||
#include "llvm/Support/Path.h" | ||
#include "llvm/Support/PropertySetIO.h" | ||
#include "llvm/Support/SYCLRTShared.h" | ||
#include "llvm/Support/SimpleTable.h" | ||
#include "llvm/Support/SystemUtils.h" | ||
#include "llvm/Support/WithColor.h" | ||
|
@@ -36,6 +37,11 @@ | |
#include <memory> | ||
|
||
using namespace llvm; | ||
using llvm::util::sycl::cl_intel_devicelib_assert; | ||
using llvm::util::sycl::cl_intel_devicelib_complex; | ||
using llvm::util::sycl::cl_intel_devicelib_complex_fp64; | ||
using llvm::util::sycl::cl_intel_devicelib_math; | ||
using llvm::util::sycl::cl_intel_devicelib_math_fp64; | ||
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. somehow my previous comment on this disappeared. Please don't 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. BTW, I see that you add merge commits when you implement review comments:
This does not seem right to me. Maybe @bader can comment more. I think this can lead to missing review comments. You should only use git push/git pull when working with the review, so that
where is the commit right below the first feature branch commit in 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. Hi, @kbobrovs 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, thank you. |
||
|
||
using string_vector = std::vector<std::string>; | ||
using SpecIDMapTy = std::map<StringRef, unsigned>; | ||
|
@@ -47,7 +53,7 @@ cl::OptionCategory PostLinkCat{"sycl-post-link options"}; | |
static constexpr char COL_CODE[] = "Code"; | ||
static constexpr char COL_SYM[] = "Symbols"; | ||
static constexpr char COL_PROPS[] = "Properties"; | ||
|
||
static constexpr char DEVICELIB_FUNC_PREFIX[] = "__devicelib_"; | ||
// InputFilename - The filename to read from. | ||
static cl::opt<std::string> InputFilename{ | ||
cl::Positional, cl::desc("<input bitcode file>"), cl::init("-"), | ||
|
@@ -104,6 +110,143 @@ static cl::opt<SpecConstMode> SpecConstLower{ | |
"set spec constants to C++ defaults")), | ||
cl::cat(PostLinkCat)}; | ||
|
||
struct ImagePropSaveInfo { | ||
bool NeedDeviceLibReqMask; | ||
bool DoSpecConst; | ||
bool SetSpecConstAtRT; | ||
bool SpecConstsMet; | ||
}; | ||
// Please update DeviceLibFuncMap if any item is added to or removed from | ||
// fallback device libraries in libdevice. | ||
static std::unordered_map<std::string, uint32_t> DeviceLibFuncMap = { | ||
{"__devicelib_acosf", cl_intel_devicelib_math}, | ||
{"__devicelib_acoshf", cl_intel_devicelib_math}, | ||
{"__devicelib_asinf", cl_intel_devicelib_math}, | ||
{"__devicelib_asinhf", cl_intel_devicelib_math}, | ||
{"__devicelib_atan2f", cl_intel_devicelib_math}, | ||
{"__devicelib_atanf", cl_intel_devicelib_math}, | ||
{"__devicelib_atanhf", cl_intel_devicelib_math}, | ||
{"__devicelib_cbrtf", cl_intel_devicelib_math}, | ||
{"__devicelib_cosf", cl_intel_devicelib_math}, | ||
{"__devicelib_coshf", cl_intel_devicelib_math}, | ||
{"__devicelib_erfcf", cl_intel_devicelib_math}, | ||
{"__devicelib_erff", cl_intel_devicelib_math}, | ||
{"__devicelib_exp2f", cl_intel_devicelib_math}, | ||
{"__devicelib_expf", cl_intel_devicelib_math}, | ||
{"__devicelib_expm1f", cl_intel_devicelib_math}, | ||
{"__devicelib_fdimf", cl_intel_devicelib_math}, | ||
{"__devicelib_fmaf", cl_intel_devicelib_math}, | ||
{"__devicelib_fmodf", cl_intel_devicelib_math}, | ||
{"__devicelib_frexpf", cl_intel_devicelib_math}, | ||
{"__devicelib_hypotf", cl_intel_devicelib_math}, | ||
{"__devicelib_ilogbf", cl_intel_devicelib_math}, | ||
{"__devicelib_ldexpf", cl_intel_devicelib_math}, | ||
{"__devicelib_lgammaf", cl_intel_devicelib_math}, | ||
{"__devicelib_log10f", cl_intel_devicelib_math}, | ||
{"__devicelib_log1pf", cl_intel_devicelib_math}, | ||
{"__devicelib_log2f", cl_intel_devicelib_math}, | ||
{"__devicelib_logbf", cl_intel_devicelib_math}, | ||
{"__devicelib_logf", cl_intel_devicelib_math}, | ||
{"__devicelib_modff", cl_intel_devicelib_math}, | ||
{"__devicelib_nextafterf", cl_intel_devicelib_math}, | ||
{"__devicelib_powf", cl_intel_devicelib_math}, | ||
{"__devicelib_remainderf", cl_intel_devicelib_math}, | ||
{"__devicelib_remquof", cl_intel_devicelib_math}, | ||
{"__devicelib_sinf", cl_intel_devicelib_math}, | ||
{"__devicelib_sinhf", cl_intel_devicelib_math}, | ||
{"__devicelib_sqrtf", cl_intel_devicelib_math}, | ||
{"__devicelib_tanf", cl_intel_devicelib_math}, | ||
{"__devicelib_tanhf", cl_intel_devicelib_math}, | ||
{"__devicelib_tgammaf", cl_intel_devicelib_math}, | ||
{"__devicelib_acos", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_acosh", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_asin", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_asinh", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_atan", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_atan2", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_atanh", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_cbrt", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_cos", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_cosh", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_erf", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_erfc", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_exp", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_exp2", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_expm1", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_fdim", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_fma", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_fmod", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_frexp", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_hypot", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_ilogb", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_ldexp", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_lgamma", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_log", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_log10", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_log1p", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_log2", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_logb", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_modf", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_nextafter", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_pow", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_remainder", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_remquo", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_sin", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_sinh", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_sqrt", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_tan", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_tanh", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib_tgamma", cl_intel_devicelib_math_fp64}, | ||
{"__devicelib___divsc3", cl_intel_devicelib_complex}, | ||
{"__devicelib___mulsc3", cl_intel_devicelib_complex}, | ||
{"__devicelib_cabsf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cacosf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cacoshf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cargf", cl_intel_devicelib_complex}, | ||
{"__devicelib_casinf", cl_intel_devicelib_complex}, | ||
{"__devicelib_casinhf", cl_intel_devicelib_complex}, | ||
{"__devicelib_catanf", cl_intel_devicelib_complex}, | ||
{"__devicelib_catanhf", cl_intel_devicelib_complex}, | ||
{"__devicelib_ccosf", cl_intel_devicelib_complex}, | ||
{"__devicelib_ccoshf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cexpf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cimagf", cl_intel_devicelib_complex}, | ||
{"__devicelib_clogf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cpolarf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cpowf", cl_intel_devicelib_complex}, | ||
{"__devicelib_cprojf", cl_intel_devicelib_complex}, | ||
{"__devicelib_crealf", cl_intel_devicelib_complex}, | ||
{"__devicelib_csinf", cl_intel_devicelib_complex}, | ||
{"__devicelib_csinhf", cl_intel_devicelib_complex}, | ||
{"__devicelib_csqrtf", cl_intel_devicelib_complex}, | ||
{"__devicelib_ctanf", cl_intel_devicelib_complex}, | ||
{"__devicelib_ctanhf", cl_intel_devicelib_complex}, | ||
{"__devicelib___divdc3", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib___muldc3", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cabs", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cacos", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cacosh", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_carg", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_casin", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_casinh", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_catan", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_catanh", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_ccos", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_ccosh", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cexp", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cimag", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_clog", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cpolar", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cpow", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_cproj", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_creal", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_csin", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_csinh", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_csqrt", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_ctan", cl_intel_devicelib_complex_fp64}, | ||
{"__devicelib_ctanh", cl_intel_devicelib_complex_fp64}, | ||
}; | ||
|
||
static void error(const Twine &Msg) { | ||
errs() << "sycl-post-link: " << Msg << '\n'; | ||
exit(1); | ||
|
@@ -295,20 +438,74 @@ saveResultModules(std::vector<std::unique_ptr<Module>> &ResModules) { | |
return Res; | ||
} | ||
|
||
static string_vector | ||
saveSpecConstantIDMaps(const std::vector<SpecIDMapTy> &Maps) { | ||
string_vector Res; | ||
// Each fallback device library corresponds to one bit in "require mask" which | ||
kbobrovs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// is an unsigned int32. getDeviceLibBit checks which fallback device library | ||
// is required for FuncName and returns the corresponding bit. The corresponding | ||
// mask for each fallback device library is: | ||
// fallback-cassert: 0x1 | ||
kbobrovs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// fallback-cmath: 0x2 | ||
// fallback-cmath-fp64: 0x4 | ||
// fallback-complex: 0x8 | ||
// fallback-complex-fp64: 0x10 | ||
static uint32_t getDeviceLibBits(const std::string &FuncName) { | ||
auto DeviceLibFuncIter = DeviceLibFuncMap.find(FuncName); | ||
return ((DeviceLibFuncIter == DeviceLibFuncMap.end()) | ||
? 0 | ||
: 0x1 << (DeviceLibFuncIter->second - cl_intel_devicelib_assert)); | ||
} | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// For each device image module, we go through all functions which meets | ||
// 1. The function name has prefix "__devicelib_" | ||
// 2. The function has SPIR_FUNC calling convention | ||
// 3. The function is declaration which means it doesn't have function body | ||
static uint32_t getModuleReqMask(const Module &M) { | ||
// Device libraries will be enabled only for spir-v module. | ||
if (M.getTargetTriple().substr(0, 7) != "spir64-") | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return 0; | ||
// 0x1 means sycl runtime will link and load libsycl-fallback-assert.spv as | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// default. In fact, default link assert spv is not necessary but dramatic | ||
// perf regression is observed if we don't link any device library. The perf | ||
// regression is caused by a clang issue. | ||
uint32_t ReqMask = 0x1; | ||
for (const Function &SF : M) { | ||
if (SF.getName().startswith(DEVICELIB_FUNC_PREFIX) && | ||
(SF.getCallingConv() == CallingConv::SPIR_FUNC) && SF.isDeclaration()) { | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint32_t DeviceLibBits = getDeviceLibBits(SF.getName().str()); | ||
ReqMask |= DeviceLibBits; | ||
} | ||
} | ||
return ReqMask; | ||
} | ||
|
||
for (size_t I = 0; I < Maps.size(); ++I) { | ||
static string_vector saveDeviceImageProperty( | ||
const std::vector<std::unique_ptr<Module>> &ResultModules, | ||
const ImagePropSaveInfo &ImgPSInfo) { | ||
string_vector Res; | ||
for (size_t I = 0; I < ResultModules.size(); ++I) { | ||
std::string SCFile = makeResultFileName(".prop", I); | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
llvm::util::PropertySetRegistry PropSet; | ||
PropSet.add(llvm::util::PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS, | ||
Maps[I]); | ||
if (ImgPSInfo.NeedDeviceLibReqMask) { | ||
uint32_t MRMask = getModuleReqMask(*ResultModules[I]); | ||
std::map<StringRef, uint32_t> RMEntry = {{"DeviceLibReqMask", MRMask}}; | ||
PropSet.add(llvm::util::PropertySetRegistry::SYCL_DEVICELIB_REQ_MASK, | ||
RMEntry); | ||
} | ||
if (ImgPSInfo.DoSpecConst && ImgPSInfo.SetSpecConstAtRT) { | ||
// extract spec constant maps per each module | ||
SpecIDMapTy TmpSpecIDMap; | ||
if (ImgPSInfo.SpecConstsMet) | ||
SpecConstantsPass::collectSpecConstantMetadata(*ResultModules[I].get(), | ||
TmpSpecIDMap); | ||
PropSet.add( | ||
llvm::util::PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS, | ||
TmpSpecIDMap); | ||
} | ||
std::error_code EC; | ||
raw_fd_ostream SCOut(SCFile, EC); | ||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PropSet.write(SCOut); | ||
Res.emplace_back(std::move(SCFile)); | ||
} | ||
|
||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return Res; | ||
} | ||
|
||
|
@@ -412,7 +609,6 @@ int main(int argc, char **argv) { | |
} | ||
|
||
std::vector<std::unique_ptr<Module>> ResultModules; | ||
std::vector<SpecIDMapTy> ResultSpecIDMaps; | ||
string_vector ResultSymbolsLists; | ||
|
||
util::SimpleTable Table; | ||
|
@@ -456,18 +652,15 @@ int main(int argc, char **argv) { | |
Error Err = Table.addColumn(COL_CODE, Files); | ||
CHECK_AND_EXIT(Err); | ||
} | ||
if (DoSpecConst && SetSpecConstAtRT) { | ||
// extract spec constant maps per each module | ||
for (auto &MUptr : ResultModules) { | ||
ResultSpecIDMaps.emplace_back(SpecIDMapTy()); | ||
if (SpecConstsMet) | ||
SpecConstantsPass::collectSpecConstantMetadata(*MUptr.get(), | ||
ResultSpecIDMaps.back()); | ||
} | ||
string_vector Files = saveSpecConstantIDMaps(ResultSpecIDMaps); | ||
|
||
{ | ||
ImagePropSaveInfo ImgPSInfo = {true, DoSpecConst, SetSpecConstAtRT, | ||
SpecConstsMet}; | ||
string_vector Files = saveDeviceImageProperty(ResultModules, ImgPSInfo); | ||
Error Err = Table.addColumn(COL_PROPS, Files); | ||
CHECK_AND_EXIT(Err); | ||
} | ||
|
||
jinge90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (DoSymGen) { | ||
// extract symbols per each module | ||
collectSymbolsLists(GlobalsSet, ResultSymbolsLists); | ||
|
Uh oh!
There was an error while loading. Please reload this page.