1559 行
49 KiB
C++
1559 行
49 KiB
C++
/* Copyright (c) 2008 - 2021 Advanced Micro Devices, Inc.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE. */
|
|
|
|
#include <string>
|
|
#include <map>
|
|
#include <numeric>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <sstream>
|
|
#include <iterator>
|
|
#include <cassert>
|
|
#include <regex>
|
|
#include "options.hpp"
|
|
|
|
namespace {
|
|
using namespace amd::option;
|
|
|
|
#define SKIP_INTERNAL_OPTION 1
|
|
|
|
/*
|
|
Macro OPTION and NOPTION are general option macros. And FLAG is a
|
|
special, simple macro with the form -<flag>=<val>. The FLAG is similar
|
|
to a flag defined in flags.hpp, so that it is easier for people who are
|
|
familiar with flags.hpp to use FLAG.
|
|
*/
|
|
#define OPTION(type, a, sn, ln, var, ideft, imin, imax, sdeft, desc) \
|
|
{sn, \
|
|
ln, \
|
|
(type) | (a), \
|
|
(uint32_t)reinterpret_cast<size_t>(&(((OptionVariables*)0)->var)), \
|
|
(int64_t)ideft, \
|
|
imin, \
|
|
imax, \
|
|
sdeft, \
|
|
desc},
|
|
|
|
#define NOPTION(type, a, sn, ln, var, ideft, imin, imax, sdeft, desc) \
|
|
{sn, ln, (type) | (a), 0, (int64_t)ideft, imin, imax, sdeft, desc},
|
|
|
|
/*
|
|
FLAG is a special option with the form
|
|
-<flag>=<val>
|
|
*/
|
|
#define FLAG(type, a, sn, var, deft, desc) \
|
|
{sn, \
|
|
NULL, \
|
|
(type) | (a) | OA_RUNTIME | OVA_REQUIRED | OA_SEPARATOR_EQUAL, \
|
|
(uint32_t)reinterpret_cast<size_t>(&(((OptionVariables*)0)->var)), \
|
|
(type == OT_CSTRING) ? (int64_t)0 : (int64_t)deft, \
|
|
(type == OT_INT32) ? -0x80000000LL : 0LL, \
|
|
(type == OT_INT32) ? 0x7FFFFFFFLL : ((type == OT_UINT32) ? 0xFFFFFFFFLL : 0LL), \
|
|
(type == OT_CSTRING) ? (const char*)deft : NULL, \
|
|
desc},
|
|
|
|
|
|
OptionDescriptor OptDescTable[] = {
|
|
#include "OPTIONS.def"
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0} // dummy entry
|
|
};
|
|
|
|
#undef OPTION
|
|
#undef NOPTION
|
|
#undef FLAG
|
|
|
|
#define OPTION_var(ix, ovars) (reinterpret_cast<char*>(ovars) + OPTION_offset(OptDescTable + ix))
|
|
|
|
/*
|
|
[0] : map from option's short name to OptDescTable's index
|
|
[1] : map from option's long name to OptDescTable's index
|
|
|
|
Any prefix option (-f/-fno, -m/-mno) has no long name, and must have
|
|
a value separator if it requires a value.
|
|
*/
|
|
std::map<std::string, int> OptionNameMap[2] ROCCLR_INIT_PRIORITY(101);
|
|
std::map<std::string, int> NoneSeparatorOptionMap[2] ROCCLR_INIT_PRIORITY(101);
|
|
// prefix -f/-fno- options
|
|
std::map<std::string, int> FOptionMap ROCCLR_INIT_PRIORITY(101);
|
|
// prefix -m/-mno- options
|
|
std::map<std::string, int> MOptionMap ROCCLR_INIT_PRIORITY(101);
|
|
|
|
bool setOptionVariable(OptionDescriptor* oDesc, OptionVariables* oVars, int64_t IValue,
|
|
const char* SValue);
|
|
|
|
/*
|
|
Note: When adding any option that has OVA_OPTIONAL as its value attribute,
|
|
make sure that the code for returning the second default is added
|
|
int this function!!!
|
|
|
|
getDefault2() : get the second default value for an option given as
|
|
'OptDescTableIx'.
|
|
|
|
The second default is a default VALUE if an option is PRESENT in the option string
|
|
but its value is NOT GIVEN (its attribute must be OVA_OPTIONAL). This is different
|
|
from the (first) default that is registerred in OptionDescriptor, and the first
|
|
default gives the default value when an option is NOT PRESENT in the option string.
|
|
For instance,
|
|
|
|
-O[0-3] : optimization level
|
|
first default : 3
|
|
This means if there is no -O[0-3] present in the option string, the default
|
|
is 3.
|
|
second default : 3
|
|
This means if -O is present but no level is given, the level is 3 (second
|
|
default).
|
|
|
|
In this case, the first default and the second default is the same, but in general
|
|
they may be different. Take -O in gcc, the first default is 0, the second default
|
|
is 2.
|
|
|
|
We don't expect many options to have attributes OVA_OPTIONAL, therefore, the second
|
|
default is set here in code instead of registerring it in OptionDescriptor. When
|
|
adding an option that needs the second default, make sure this function return
|
|
the second default.
|
|
*/
|
|
void getDefault2(int OptDescTableIx, int64_t& idefault2, const char*& sdefault2) {
|
|
switch (OptDescTableIx) {
|
|
case OID_ShowHelp:
|
|
sdefault2 = "public";
|
|
break;
|
|
|
|
case OID_SaveTemps:
|
|
case OID_SaveTempsAll:
|
|
sdefault2 = NULL;
|
|
break;
|
|
|
|
case OID_OptLevel:
|
|
idefault2 = amd::option::OPT_O3;
|
|
break;
|
|
|
|
case OID_OptUseNative:
|
|
sdefault2 = "all";
|
|
break;
|
|
|
|
default:
|
|
assert(false && "The second default value is not given");
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool setAliasOptionVariable(int OptDescTableIx, Options& Opts, int64_t IValue, const char* SValue) {
|
|
OptionDescriptor* od;
|
|
OptionVariables* oVars = Opts.oVariables;
|
|
|
|
switch (OptDescTableIx) {
|
|
case OID_OptDisable:
|
|
//
|
|
// -cl-opt-disable is equivalent to -O0.
|
|
//
|
|
Opts.setFlag(OID_OptLevel, 1);
|
|
od = &OptDescTable[OID_OptLevel];
|
|
assert((IValue > 0) && "Internal Error: -cl-opt-disable is present, but with wrong value");
|
|
if (!setOptionVariable(od, oVars, (int64_t)amd::option::OPT_O0, NULL)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
case OID_GPU32BitIsa:
|
|
// -m32 == "no -m64", ie, 64-bit code generation off.
|
|
Opts.setFlag(OID_GPU64BitIsa, 1);
|
|
od = &OptDescTable[OID_GPU64BitIsa];
|
|
assert((IValue > 0) && "Internal Error: -m32 is present, but with wrong value");
|
|
if (!setOptionVariable(od, oVars, (int64_t)0, NULL)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
case OID_SaveTemps:
|
|
case OID_SaveTempsAll:
|
|
case OID_Output:
|
|
uint32_t flags;
|
|
if (OptDescTableIx == OID_SaveTemps) {
|
|
// Dump .cl, .i(.ii), .amdil, .isa, .s, dll, calimage
|
|
flags = DUMP_CL | DUMP_I | DUMP_S | DUMP_O | DUMP_DLL | DUMP_CGIL | DUMP_DEBUGIL | DUMP_IL |
|
|
DUMP_ISA;
|
|
} else if (OptDescTableIx == OID_SaveTempsAll) {
|
|
flags = DUMP_ALL;
|
|
} else { // OID_Output
|
|
flags = DUMP_BIF;
|
|
if (SValue == NULL) {
|
|
assert(false && "Missing value for option -o <prefix>");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Make sure flags is or'ed with the previous ones.
|
|
if (Opts.isOptionSeen(OID_DumpFlags)) {
|
|
flags |= oVars->DumpFlags;
|
|
}
|
|
|
|
Opts.setFlag(OID_DumpFlags, 1);
|
|
od = &OptDescTable[OID_DumpFlags];
|
|
if (!setOptionVariable(od, oVars, (int64_t)flags, NULL)) {
|
|
return false;
|
|
}
|
|
|
|
if (SValue != NULL) {
|
|
// Reset DumpPrefix
|
|
Opts.setFlag(OID_DumpPrefix, 1);
|
|
od = &OptDescTable[OID_DumpPrefix];
|
|
if (!setOptionVariable(od, oVars, (int64_t)0, SValue)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ShowOptionsHelp(const char* helpValue, Options& Opts) {
|
|
// -h[--help][=public|all] : public is the second default
|
|
bool showAll = false;
|
|
bool showSupport = false;
|
|
|
|
/* If SKIP_INTERNAL_OPTION is set, only use public options */
|
|
#ifndef SKIP_INTERNAL_OPTION
|
|
if (::strcmp(helpValue, "all") == 0) {
|
|
showAll = true;
|
|
} else if (::strcmp(helpValue, "support") == 0) {
|
|
showSupport = true;
|
|
}
|
|
#endif
|
|
|
|
Opts.optionsLog() =
|
|
"Option Summary:\n"
|
|
"\tUse those options in clBuildProgram() directly in the OpenCL host application or\n"
|
|
"\tduring runtime by setting environment variables : AMD_OCL_BUILD_OPTIONS and \n"
|
|
"\tAMD_OCL_BUILD_OPTIONS_APPEND. AMD_OCL_BUILD_OPTIONS will override the options\n"
|
|
"\tused in the host application, whereas AMD_OCL_BUILD_OPTIONS_APPEND appends to\n"
|
|
"\tthe options. For example, assume '-cl-opt-disable' is used in the host application,\n"
|
|
"\tand with\n"
|
|
"\t\tset AMD_OCL_BUILD_OPTIONS=-O\n"
|
|
"\tit will override '-cl-opt-disable' with '-O'; with\n"
|
|
"\t\tset AMD_OCL_BUILD_OPTIONS_APPEND=-g\n"
|
|
"\tit will append '-g' to '-cl-opt-disable' (ie '-cl-opt-disable -g').\n\n"
|
|
"\tThose environment variables are useful for changing build options without changing\n"
|
|
"\tand re-building (compiling) the host application.\n\n"
|
|
"OPTIONS:\n";
|
|
for (int i = 0; i < OID_LAST; ++i) {
|
|
OptionDescriptor* od = &OptDescTable[i];
|
|
|
|
if ((OPTION_vis(od) != OVIS_PUBLIC) && !showAll &&
|
|
!(showSupport && (OPTION_vis(od) != OVIS_INTERNAL))) {
|
|
continue;
|
|
}
|
|
|
|
// Print Value -- value to be shown in the help message
|
|
std::string pntVal;
|
|
switch (OPTION_type(od)) {
|
|
case OT_CSTRING:
|
|
if ((i == OID_WFComma) || (i == OID_WBComma) || (i == OID_WHComma)) {
|
|
pntVal = "<options>";
|
|
} else if (i == OID_SaveTemps) {
|
|
pntVal = "<prefix>";
|
|
} else {
|
|
pntVal = "<value>";
|
|
}
|
|
break;
|
|
case OT_BOOL:
|
|
pntVal = "0|1";
|
|
break;
|
|
case OT_INT32:
|
|
case OT_UINT32:
|
|
pntVal = "<number>";
|
|
break;
|
|
case OT_UCHAR:
|
|
pntVal = "<0-9 | a-z>";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
const char* sname = OPTION_sname(od);
|
|
const char* lname = OPTION_lname(od);
|
|
size_t sz = 0;
|
|
Opts.optionsLog() += "\t";
|
|
if (OPTION_form(od) == OFA_NORMAL) {
|
|
for (int j = 0; j < 2; ++j) {
|
|
if (j == 0) {
|
|
// Short Name
|
|
if (sname != NULL) {
|
|
Opts.optionsLog() += "-";
|
|
Opts.optionsLog() += sname;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else { // j == 1
|
|
// Long name
|
|
if (lname != NULL) {
|
|
Opts.optionsLog() += (sname != NULL) ? " --" : "--";
|
|
Opts.optionsLog() += lname;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
switch (i) {
|
|
case OID_PP_D:
|
|
Opts.optionsLog() += " name[=<definition>]";
|
|
break;
|
|
|
|
case OID_PP_I:
|
|
Opts.optionsLog() += " dir";
|
|
break;
|
|
|
|
default:
|
|
if ((OPTION_value(od) == OVA_OPTIONAL) || (OPTION_value(od) == OVA_REQUIRED)) {
|
|
if (OPTION_value(od) == OVA_OPTIONAL) {
|
|
Opts.optionsLog() += "[";
|
|
}
|
|
if ((OPTION_info(od) & OA_SEPARATOR_NONE) &&
|
|
(OPTION_info(od) & (OA_SEPARATOR_EQUAL | OA_SEPARATOR_SPACE))) {
|
|
if ((OPTION_info(od) & OA_SEPARATOR_EQUAL) &&
|
|
(OPTION_info(od) & OA_SEPARATOR_SPACE)) {
|
|
Opts.optionsLog() += "[ |=]";
|
|
} else if (OPTION_info(od) & OA_SEPARATOR_EQUAL) {
|
|
Opts.optionsLog() += "[=]";
|
|
} else if (OPTION_info(od) & OA_SEPARATOR_SPACE) {
|
|
Opts.optionsLog() += "[ ]";
|
|
}
|
|
} else if (OPTION_info(od) & (OA_SEPARATOR_EQUAL | OA_SEPARATOR_SPACE)) {
|
|
if ((OPTION_info(od) & OA_SEPARATOR_EQUAL) &&
|
|
(OPTION_info(od) & OA_SEPARATOR_SPACE)) {
|
|
Opts.optionsLog() += "{ |=}";
|
|
} else if (OPTION_info(od) & OA_SEPARATOR_EQUAL) {
|
|
Opts.optionsLog() += "=";
|
|
} else {
|
|
Opts.optionsLog() += " ";
|
|
}
|
|
}
|
|
|
|
Opts.optionsLog() += pntVal.c_str();
|
|
if (OPTION_value(od) == OVA_OPTIONAL) {
|
|
Opts.optionsLog() += "]";
|
|
}
|
|
}
|
|
break;
|
|
} // switch
|
|
}
|
|
} else if ((OPTION_form(od) == OFA_PREFIX_F) || (OPTION_form(od) == OFA_PREFIX_M)) {
|
|
char fORm = ((OPTION_form(od) == OFA_PREFIX_F) ? 'f' : 'm');
|
|
if (OPTION_type(od) == OT_BOOL) {
|
|
Opts.optionsLog() += '-';
|
|
Opts.optionsLog() += fORm;
|
|
Opts.optionsLog() += "[no-]";
|
|
Opts.optionsLog() += sname;
|
|
} else {
|
|
Opts.optionsLog() += fORm;
|
|
Opts.optionsLog() += sname;
|
|
|
|
if ((OPTION_value(od) == OVA_OPTIONAL) || (OPTION_value(od) == OVA_REQUIRED)) {
|
|
assert(!(OPTION_info(od) & OA_SEPARATOR_NONE) &&
|
|
(OPTION_info(od) & (OA_SEPARATOR_EQUAL | OA_SEPARATOR_SPACE)) &&
|
|
"prefix -f/-m need either ' ' or '=' as value separator");
|
|
if ((OPTION_info(od) & OA_SEPARATOR_EQUAL) && (OPTION_info(od) & OA_SEPARATOR_SPACE)) {
|
|
Opts.optionsLog() += "{ |=}";
|
|
} else if (OPTION_info(od) & OA_SEPARATOR_EQUAL) {
|
|
Opts.optionsLog() += "=";
|
|
} else {
|
|
Opts.optionsLog() += " ";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Opts.optionsLog() += "\n\t ";
|
|
Opts.optionsLog() += OPTION_desc(od);
|
|
Opts.optionsLog() += "\n\n";
|
|
}
|
|
}
|
|
|
|
bool setOptionVariable(OptionDescriptor* oDesc, OptionVariables* oVars, int64_t IValue,
|
|
const char* SValue) {
|
|
char* addr = reinterpret_cast<char*>(oVars) + oDesc->OptionOffset;
|
|
if (OPTION_type(oDesc) == OT_BOOL) {
|
|
OT_BOOL_t* o = reinterpret_cast<OT_BOOL_t*>(addr);
|
|
*o = static_cast<OT_BOOL_t>(IValue != 0);
|
|
} else if (OPTION_type(oDesc) == OT_INT32) {
|
|
OT_INT32_t* o = reinterpret_cast<OT_INT32_t*>(addr);
|
|
*o = static_cast<OT_INT32_t>(IValue);
|
|
} else if (OPTION_type(oDesc) == OT_UINT32) {
|
|
OT_UINT32_t* o = reinterpret_cast<OT_UINT32_t*>(addr);
|
|
*o = static_cast<OT_UINT32_t>(IValue);
|
|
} else if (OPTION_type(oDesc) == OT_CSTRING) {
|
|
OT_CSTRING_t* o = reinterpret_cast<OT_CSTRING_t*>(addr);
|
|
*o = static_cast<OT_CSTRING_t>(SValue);
|
|
} else if (OPTION_type(oDesc) == OT_UCHAR) {
|
|
OT_UCHAR_t* o = reinterpret_cast<OT_UCHAR_t*>(addr);
|
|
*o = static_cast<OT_UCHAR_t>(IValue);
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool setOptionVariable(OptionDescriptor* oDesc, OptionVariables* srcOVars,
|
|
OptionVariables* dstOVars) {
|
|
char* srcAddr = reinterpret_cast<char*>(srcOVars) + oDesc->OptionOffset;
|
|
char* dstAddr = reinterpret_cast<char*>(dstOVars) + oDesc->OptionOffset;
|
|
if (OPTION_type(oDesc) == OT_BOOL) {
|
|
OT_BOOL_t* src = reinterpret_cast<OT_BOOL_t*>(srcAddr);
|
|
OT_BOOL_t* dst = reinterpret_cast<OT_BOOL_t*>(dstAddr);
|
|
*dst = *src;
|
|
} else if (OPTION_type(oDesc) == OT_INT32) {
|
|
OT_INT32_t* src = reinterpret_cast<OT_INT32_t*>(srcAddr);
|
|
OT_INT32_t* dst = reinterpret_cast<OT_INT32_t*>(dstAddr);
|
|
*dst = *src;
|
|
} else if (OPTION_type(oDesc) == OT_UINT32) {
|
|
OT_UINT32_t* src = reinterpret_cast<OT_UINT32_t*>(srcAddr);
|
|
OT_UINT32_t* dst = reinterpret_cast<OT_UINT32_t*>(dstAddr);
|
|
*dst = *src;
|
|
} else if (OPTION_type(oDesc) == OT_UCHAR) {
|
|
OT_UCHAR_t* src = reinterpret_cast<OT_UCHAR_t*>(srcAddr);
|
|
OT_UCHAR_t* dst = reinterpret_cast<OT_UCHAR_t*>(dstAddr);
|
|
*dst = *src;
|
|
} else if (OPTION_type(oDesc) == OT_CSTRING) {
|
|
OT_CSTRING_t* src = reinterpret_cast<OT_CSTRING_t*>(srcAddr);
|
|
OT_CSTRING_t* dst = reinterpret_cast<OT_CSTRING_t*>(dstAddr);
|
|
*dst = *src;
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int getOptionDesc(std::string& options, size_t StartPos, bool IsShortForm, OptionForm oForm,
|
|
size_t& EndPos, std::string& Value) {
|
|
int option_ndx = -1;
|
|
EndPos = std::string::npos;
|
|
const int map_ndx = (IsShortForm ? 0 : 1);
|
|
|
|
size_t sPos = StartPos;
|
|
size_t ePos = options.find(' ', sPos);
|
|
size_t pos = options.find('=', sPos);
|
|
if (pos < ePos) {
|
|
ePos = pos;
|
|
}
|
|
// The following should work even if ePos is npos
|
|
size_t len = ePos - sPos;
|
|
|
|
// Handle the special options-passing option : -W<l>,s0,s1,s2,s3,....sn
|
|
// where l is a single letter. And only in this option, comma ',' is treated
|
|
// like a separator.
|
|
if ((oForm == OFA_NORMAL) && (options.size() > (sPos + 3))) {
|
|
char c0 = options[sPos];
|
|
char c1 = options[sPos + 1];
|
|
char c2 = options[sPos + 2];
|
|
if ((c0 == 'W') && (c2 == ',') && (c1 >= 'a') && (c1 <= 'z')) {
|
|
ePos = sPos + 3;
|
|
len = 3;
|
|
}
|
|
}
|
|
std::string name = options.substr(sPos, len);
|
|
|
|
std::map<std::string, int>::const_iterator I, IE;
|
|
switch (oForm) {
|
|
case OFA_NORMAL:
|
|
I = OptionNameMap[map_ndx].find(name);
|
|
IE = OptionNameMap[map_ndx].end();
|
|
break;
|
|
case OFA_PREFIX_F:
|
|
I = FOptionMap.find(name);
|
|
IE = FOptionMap.end();
|
|
break;
|
|
case OFA_PREFIX_M:
|
|
I = MOptionMap.find(name);
|
|
IE = MOptionMap.end();
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (I != IE) {
|
|
// found the exact match, that's it!
|
|
option_ndx = I->second;
|
|
pos = ePos;
|
|
} else if (oForm != OFA_NORMAL) {
|
|
return -1;
|
|
} else {
|
|
I = NoneSeparatorOptionMap[map_ndx].begin();
|
|
IE = NoneSeparatorOptionMap[map_ndx].end();
|
|
size_t len1 = 0;
|
|
for (; I != IE; ++I) {
|
|
std::string namevalue = I->first;
|
|
size_t n = namevalue.size();
|
|
if (n >= len) {
|
|
// Not substr, skip
|
|
continue;
|
|
}
|
|
if (name.compare(0, n, namevalue) == 0) {
|
|
// found the substr
|
|
if (n > len1) {
|
|
len1 = n;
|
|
option_ndx = I->second;
|
|
}
|
|
}
|
|
}
|
|
if (len1 == 0) {
|
|
// no matching
|
|
return -1;
|
|
}
|
|
pos = sPos + len1;
|
|
}
|
|
|
|
OptionDescriptor* od = &OptDescTable[option_ndx];
|
|
if (pos == std::string::npos) {
|
|
if (OPTION_value(od) == OVA_REQUIRED) {
|
|
// illegal option
|
|
return -1;
|
|
}
|
|
EndPos = pos;
|
|
return option_ndx;
|
|
}
|
|
|
|
char next_c = options.at(pos);
|
|
bool optionalHasValue =
|
|
(OPTION_value(od) == OVA_OPTIONAL) &&
|
|
(((OPTION_info(od) & OA_SEPARATOR_EQUAL) && (next_c == '=')) ||
|
|
((OPTION_info(od) & OA_SEPARATOR_NONE) && !OPTION_valueSeparator(next_c)));
|
|
bool hasValue = (OPTION_value(od) == OVA_REQUIRED) || optionalHasValue;
|
|
|
|
if (hasValue) {
|
|
if ((OPTION_info(od) & OA_SEPARATOR_EQUAL) && (next_c == '=')) {
|
|
pos++;
|
|
if (pos == options.size()) {
|
|
return -1;
|
|
}
|
|
} else if ((OPTION_info(od) & OA_SEPARATOR_SPACE) && (next_c == ' ')) {
|
|
pos = options.find_first_not_of(' ', pos);
|
|
} else if ((OPTION_info(od) & OA_SEPARATOR_NONE) && !OPTION_valueSeparator(next_c)) {
|
|
;
|
|
} else {
|
|
// illgel option
|
|
return -1;
|
|
}
|
|
|
|
if (pos == std::string::npos) {
|
|
return -1;
|
|
}
|
|
|
|
if ((OPTION_type(od) == OT_CSTRING) && options.at(pos) == '"') {
|
|
size_t sz = options.size();
|
|
if ((pos + 1) >= sz) {
|
|
return -1;
|
|
}
|
|
/* Handle quoted string value */
|
|
ePos = options.find('"', pos + 1);
|
|
if (ePos == std::string::npos) {
|
|
return -1;
|
|
}
|
|
|
|
/* Advance ePos to the next position or npos */
|
|
if (ePos + 1 < sz) {
|
|
++ePos;
|
|
if (options.at(ePos) != ' ') {
|
|
return -1;
|
|
}
|
|
} else {
|
|
ePos = std::string::npos;
|
|
}
|
|
} else {
|
|
ePos = options.find(' ', pos);
|
|
}
|
|
if (OPTION_info(od) & OA_RUNTIME) {
|
|
Value = options.substr(pos, ePos - pos);
|
|
}
|
|
EndPos = ePos;
|
|
} else {
|
|
if (next_c != ' ') {
|
|
return -1;
|
|
}
|
|
EndPos = pos;
|
|
}
|
|
return option_ndx;
|
|
}
|
|
|
|
bool processOption(int OptDescTableIx, Options& Opts, const std::string& Value, bool IsPrefixOption,
|
|
bool IsOffFlag) {
|
|
OptionVariables* ovars = Opts.oVariables;
|
|
OptionDescriptor* od = &OptDescTable[OptDescTableIx];
|
|
|
|
const char* sval = NULL;
|
|
int64_t ival = 0;
|
|
int otype = OPTION_type(od);
|
|
|
|
if (Value.size() == 0) {
|
|
if (OPTION_value(od) == OVA_OPTIONAL) {
|
|
// Get the second default value !
|
|
getDefault2(OptDescTableIx, ival, sval);
|
|
} else {
|
|
if (otype == OT_BOOL) {
|
|
// This option may be either OVA_REQUIRED or OVA_DISALLOWED !
|
|
ival = (IsOffFlag ? 0 : 1);
|
|
} else {
|
|
// This option is either OT_CSTRING or OT_{U]INT32, and requires its value!
|
|
assert(false && "This option should have a value");
|
|
Opts.optionsLog() = "Value is missing\n";
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
// An explicit value has been provided.
|
|
char* p;
|
|
unsigned long uval;
|
|
|
|
switch (otype) {
|
|
case OT_CSTRING: {
|
|
char* cs = new char[Value.size() + 1];
|
|
Value.copy(cs, Value.size());
|
|
cs[Value.size()] = 0;
|
|
sval = cs;
|
|
|
|
// Need to remember this and free memory later.
|
|
Opts.recordMemoryHandle((char*)sval);
|
|
} break;
|
|
|
|
case OT_UCHAR: {
|
|
ival = Value.at(0);
|
|
} break;
|
|
|
|
case OT_INT32:
|
|
ival = ::strtol(Value.c_str(), &p, 0);
|
|
if (*p != '\0') {
|
|
// invalid value
|
|
Opts.optionsLog() = "Value is wrong\n";
|
|
return false;
|
|
}
|
|
|
|
if ((ival < OPTION_min(od)) || (ival > OPTION_max(od))) {
|
|
std::stringstream msg;
|
|
msg << "Value should be in [" << (int)OPTION_min(od) << ", " << (int)OPTION_max(od)
|
|
<< "]\n";
|
|
Opts.optionsLog() = msg.str().c_str();
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case OT_UINT32:
|
|
case OT_BOOL:
|
|
uval = ::strtoul(Value.c_str(), &p, 0);
|
|
if (*p != '\0') {
|
|
// invalid value
|
|
Opts.optionsLog() = "Value is wrong\n";
|
|
return false;
|
|
}
|
|
|
|
ival = (int64_t)uval;
|
|
|
|
if (otype == OT_BOOL) {
|
|
if ((ival < 0) || (ival > 1)) {
|
|
Opts.optionsLog() = "Value should be either 0 or 1\n";
|
|
return false;
|
|
}
|
|
} else {
|
|
// OT_INT32, OT_UINT32
|
|
if ((ival < OPTION_min(od)) || (ival > OPTION_max(od))) {
|
|
std::stringstream msg;
|
|
msg << "Value should be in [" << (int)OPTION_min(od) << ", " << (int)OPTION_max(od)
|
|
<< "]\n";
|
|
Opts.optionsLog() = msg.str().c_str();
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// First, if it is an alias option, special-process it here.
|
|
if (OPTION_info(od) & OA_MISC_ALIAS) {
|
|
if (!setAliasOptionVariable(OptDescTableIx, Opts, ival, sval)) {
|
|
Opts.optionsLog() = "Wrong value for the option (alias)\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Special procesing of an individual option (non-alias option)
|
|
OptionDescriptor* tod;
|
|
switch ((OptionIdentifier)OptDescTableIx) {
|
|
case OID_ShowHelp:
|
|
if ((strcmp(sval, "all") != 0) && (strcmp(sval, "support") != 0) &&
|
|
(strcmp(sval, "public") != 0)) {
|
|
Opts.optionsLog() = "-h/--help only supports values all|support|public\n";
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case OID_FiniteMathOnly:
|
|
Opts.setFlag(OID_FiniteMathOnly, 1);
|
|
tod = &OptDescTable[OID_FiniteMathOnly];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
Opts.clangOptions.push_back("-cl-finite-math-only");
|
|
break;
|
|
|
|
case OID_NoSignedZeros:
|
|
Opts.setFlag(OID_NoSignedZeros, 1);
|
|
tod = &OptDescTable[OID_NoSignedZeros];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
Opts.clangOptions.push_back("-cl-no-signed-zeros");
|
|
break;
|
|
|
|
case OID_FastRelaxedMath:
|
|
// -cl-fast-relaxed-math implies:
|
|
// -cl-finite-math-only
|
|
// -cl-unsafe-math-optimizations
|
|
Opts.setFlag(OID_FiniteMathOnly, 1);
|
|
tod = &OptDescTable[OID_FiniteMathOnly];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
|
|
Opts.setFlag(OID_UnsafeMathOpt, 1);
|
|
tod = &OptDescTable[OID_UnsafeMathOpt];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
|
|
Opts.clcOptions.append(" -D__FAST_RELAXED_MATH__=1");
|
|
Opts.clangOptions.push_back("-D__FAST_RELAXED_MATH__=1");
|
|
Opts.clangOptions.push_back("-cl-fast-relaxed-math");
|
|
|
|
// fall-through to handle UnsafeMathOpt
|
|
case OID_UnsafeMathOpt:
|
|
// -cl-unsafe-math-optimizations implies
|
|
// -cl-no-signed-zeros
|
|
// -cl-mad-enable.
|
|
Opts.setFlag(OID_NoSignedZeros, 1);
|
|
tod = &OptDescTable[OID_NoSignedZeros];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
|
|
Opts.setFlag(OID_MadEnable, 1);
|
|
tod = &OptDescTable[OID_MadEnable];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
Opts.clangOptions.push_back("-cl-unsafe-math-optimizations");
|
|
break;
|
|
|
|
case OID_DenormsAreZero:
|
|
Opts.setFlag(OID_DenormsAreZero, 1);
|
|
tod = &OptDescTable[OID_DenormsAreZero];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
Opts.clangOptions.push_back("-cl-denorms-are-zero");
|
|
break;
|
|
|
|
case OID_StricAliasing:
|
|
Opts.setFlag(OID_StricAliasing, 1);
|
|
tod = &OptDescTable[OID_StricAliasing];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
Opts.clangOptions.push_back("-cl-strict-aliasing");
|
|
break;
|
|
|
|
case OID_MadEnable:
|
|
Opts.setFlag(OID_MadEnable, 1);
|
|
tod = &OptDescTable[OID_MadEnable];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
Opts.clangOptions.push_back("-cl-mad-enable");
|
|
break;
|
|
|
|
case OID_EnableDebug:
|
|
Opts.clcOptions.append(" -g");
|
|
Opts.clangOptions.push_back("-g");
|
|
break;
|
|
|
|
case OID_SinglePrecisionConstant:
|
|
Opts.clcOptions.append(" --single_precision_constant");
|
|
Opts.clangOptions.push_back("-cl-single-precision-constant");
|
|
break;
|
|
|
|
case OID_FP32RoundDivideSqrt:
|
|
Opts.clcOptions.append(" --precise_fp32_divide_sqrt");
|
|
Opts.clangOptions.push_back("-cl-fp32-correctly-rounded-divide-sqrt");
|
|
Opts.setFlag(OID_FP32RoundDivideSqrt, 1);
|
|
tod = &OptDescTable[OID_FP32RoundDivideSqrt];
|
|
(void)setOptionVariable(tod, ovars, (int64_t)1, NULL);
|
|
break;
|
|
|
|
case OID_EnableC99Inline:
|
|
if (ival != 0) {
|
|
Opts.clangOptions.push_back("-fc99-inline");
|
|
}
|
|
break;
|
|
|
|
case OID_DisableAllWarnings:
|
|
if (ival != 0) {
|
|
Opts.clcOptions.append(" --no_warnings");
|
|
Opts.clangOptions.push_back("-w");
|
|
}
|
|
break;
|
|
|
|
case OID_WarnToError:
|
|
if (ival != 0) {
|
|
Opts.clcOptions.append(" --werror");
|
|
Opts.clangOptions.push_back("-Werror");
|
|
}
|
|
break;
|
|
|
|
case OID_UniformWorkGroupSize:
|
|
Opts.clangOptions.push_back("-cl-uniform-work-group-size");
|
|
break;
|
|
|
|
case OID_WorkGrpSize:
|
|
// -wgs=x[,y[,z]]. The default for missing part is 1.
|
|
if (sval) {
|
|
bool isValueWrong = false;
|
|
const char* bp = sval;
|
|
char* ep = 0;
|
|
int dim = 0;
|
|
while (*bp) {
|
|
uint32_t tval = ::strtoul(bp, &ep, 0);
|
|
if (ep != bp) {
|
|
if (dim > 2) {
|
|
isValueWrong = true;
|
|
break;
|
|
}
|
|
Opts.WorkGroupSize[dim] = tval;
|
|
++dim;
|
|
if (*ep == ',') {
|
|
bp = ep + 1;
|
|
} else if (*ep == 0) {
|
|
break;
|
|
} else {
|
|
isValueWrong = true;
|
|
break;
|
|
}
|
|
} else {
|
|
isValueWrong = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isValueWrong) {
|
|
Opts.optionsLog() = "Wrong option value -wgs=";
|
|
Opts.optionsLog() += sval;
|
|
Opts.optionsLog() += "\n";
|
|
return false;
|
|
}
|
|
|
|
// If there are missing dimention, assume it is 1.
|
|
for (int i = dim; i < 3; ++i) {
|
|
Opts.WorkGroupSize[i] = 1;
|
|
}
|
|
Opts.setDefaultWGS(false);
|
|
}
|
|
break;
|
|
|
|
case OID_OptUseNative:
|
|
Opts.llvmOptions.append(" -mllvm -amdgpu-use-native=");
|
|
Opts.llvmOptions.append(sval);
|
|
break;
|
|
|
|
case OID_WFComma:
|
|
case OID_WBComma:
|
|
case OID_WHComma:
|
|
if (sval != NULL) {
|
|
// we know that sval was new'ed
|
|
for (char* p = (char*)sval; *p; ++p) {
|
|
if (*p == ',') {
|
|
*p = ' ';
|
|
}
|
|
}
|
|
|
|
if ((OptionIdentifier)OptDescTableIx == OID_WFComma) {
|
|
Opts.clcOptions.append(" ");
|
|
Opts.clcOptions.append(sval);
|
|
if (strcmp(sval, "--force_disable_spir") && strcmp(sval, "--single_precision_constant") &&
|
|
strcmp(sval, "--precise_fp32_divide_sqrt"))
|
|
Opts.clangOptions.push_back(sval);
|
|
} else if (((OptionIdentifier)OptDescTableIx) == OID_WBComma) {
|
|
Opts.llvmOptions.append(" ");
|
|
Opts.llvmOptions.append("-mllvm ");
|
|
Opts.llvmOptions.append(sval);
|
|
} else if (((OptionIdentifier)OptDescTableIx) == OID_WHComma) {
|
|
Opts.finalizerOptions.push_back(sval);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OID_XLang:
|
|
#ifndef SKIP_INTERNAL_OPTION
|
|
// Don't expose "-x il | cgil" to mainline
|
|
if ((strcmp(sval, "il") == 0) || (strcmp(sval, "cgil") == 0)) {
|
|
break;
|
|
}
|
|
#endif
|
|
if ((strcmp(sval, "clc") != 0) && (strcmp(sval, "clc++") != 0) &&
|
|
(strcmp(sval, "spir") != 0)) {
|
|
Opts.optionsLog() = "-x only supports values clc, clc++ and spir\n";
|
|
return false;
|
|
}
|
|
|
|
// ToDo: EDG is not ready to produce SPIR
|
|
// do not pass --spir to EDG
|
|
if (strcmp(sval, "spir") != 0) {
|
|
// clcOptions should be --c++, --c or --spir
|
|
|
|
// add "--" prefix for clcOptions
|
|
Opts.clcOptions.append(" --");
|
|
|
|
// skip "cl" prefix in the sval for clcOptions
|
|
Opts.clcOptions.append(sval + (strcmp(sval, "spir") != 0 ? 2 : 0));
|
|
}
|
|
|
|
break;
|
|
|
|
case OID_FSanitize:
|
|
if (sval != NULL) Opts.clangOptions.push_back("-fsanitize=" + std::string(sval));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!setOptionVariable(od, ovars, ival, sval)) {
|
|
Opts.optionsLog() = "Wrong option value\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// the option at "bpos" of the "options" string is an invalid option,
|
|
// log the error message into "log"
|
|
static void logInvalidOption(std::string& options, size_t bpos, std::string& log,
|
|
const std::string& msg) {
|
|
size_t epos = options.find_first_of(' ', bpos);
|
|
log = "Invalid option: ";
|
|
log += options.substr(bpos, (epos == std::string::npos) ? epos : epos - bpos);
|
|
log += msg + "\n";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace amd {
|
|
|
|
namespace option {
|
|
|
|
bool parseAllOptions(std::string& options, Options& Opts, bool linkOptsOnly) {
|
|
Opts.origOptionStr = options;
|
|
OptionVariables* ovars = Opts.oVariables;
|
|
OptionDescriptor* od = OptDescTable;
|
|
|
|
// Initialize all options to the default
|
|
for (int i = 0; i < OID_LAST; ++i, ++od) {
|
|
if (!OPTIONHasOVariable(od)) {
|
|
continue;
|
|
}
|
|
if (!setOptionVariable(od, ovars, OPTION_default(od), OPTION_defaultstr(od))) {
|
|
Opts.optionsLog() = "Internal Error: option processing failed\n";
|
|
return false;
|
|
}
|
|
}
|
|
Opts.clangOptions.push_back("-cl-kernel-arg-info");
|
|
|
|
// Parse options
|
|
if (options.empty()) {
|
|
Opts.postParseInit();
|
|
return true;
|
|
}
|
|
|
|
bool isLibLinkOpts = false; // is this set of options for linking library?
|
|
bool firstOpt = true;
|
|
for (size_t pos = options.find_first_not_of(' ', 0); pos != std::string::npos;
|
|
pos = options.find_first_not_of(" ", pos)) {
|
|
bool isShortName = true; // -: short name; --: long name
|
|
|
|
// For creating option log
|
|
size_t bpos = pos;
|
|
|
|
if (options.at(pos) == '-') {
|
|
pos++;
|
|
} else {
|
|
// options should start with "-"
|
|
logInvalidOption(options, bpos, Opts.optionsLog(), " (expected - at the beginning)");
|
|
return false;
|
|
}
|
|
|
|
if (options.at(pos) == '-') {
|
|
isShortName = false;
|
|
pos++;
|
|
}
|
|
|
|
if ((pos == std::string::npos) || OPTION_valueSeparator(options.at(pos))) {
|
|
logInvalidOption(options, bpos, Opts.optionsLog(), " (expected an option name)");
|
|
return false;
|
|
}
|
|
|
|
bool isPrefix_fno = false;
|
|
bool isPrefix_mno = false;
|
|
bool isPrefix_option = false;
|
|
|
|
std::string value;
|
|
size_t sPos = pos;
|
|
int option_ndx = getOptionDesc(options, sPos, isShortName, OFA_NORMAL, pos, value);
|
|
if (option_ndx < 0) {
|
|
size_t sPos1;
|
|
pos = sPos;
|
|
if (options.at(pos) == 'f') {
|
|
isPrefix_option = true;
|
|
isPrefix_fno = (options.compare(pos + 1, (size_t)3, "no-") == 0);
|
|
|
|
sPos1 = pos + (isPrefix_fno ? 4 : 1);
|
|
option_ndx = getOptionDesc(options, sPos1, isShortName, OFA_PREFIX_F, pos, value);
|
|
} else if (options.at(pos) == 'm') {
|
|
isPrefix_option = true;
|
|
isPrefix_mno = (options.compare(pos + 1, (size_t)3, "no-") == 0);
|
|
|
|
sPos1 = pos + (isPrefix_mno ? 4 : 1);
|
|
option_ndx = getOptionDesc(options, sPos1, isShortName, OFA_PREFIX_M, pos, value);
|
|
}
|
|
|
|
if (option_ndx < 0) {
|
|
logInvalidOption(options, bpos, Opts.optionsLog(), "");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
od = &OptDescTable[option_ndx];
|
|
|
|
#ifdef SKIP_INTERNAL_OPTION
|
|
if (OPTION_vis(od) == OVIS_INTERNAL) {
|
|
// Internal options are not support in the product
|
|
logInvalidOption(options, bpos, Opts.optionsLog(), "");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (!linkOptsOnly && (OPTION_info(od) & OA_CLC)) {
|
|
size_t sPos1 = (isShortName ? sPos - 1 : sPos - 2);
|
|
std::string oStr = options.substr(sPos1, pos - sPos1);
|
|
if (OPTION_info(od) & OA_CLC) {
|
|
Opts.clcOptions.append(" " + oStr);
|
|
if (!oStr.compare(0, 2, "-D") || !oStr.compare(0, 2, "-I")) {
|
|
// strip off the leading whitespaces in macro
|
|
// definition and include path. Clang treats
|
|
// whitespaces as part of macro and include paths.
|
|
size_t vPos1 = oStr.find_first_not_of(" ", 2);
|
|
if (vPos1 == std::string::npos) {
|
|
// Do not allow blank macro and include directories
|
|
logInvalidOption(options, bpos, Opts.optionsLog(), " (expected value)");
|
|
return false;
|
|
}
|
|
std::string vStr = oStr.substr(vPos1, std::string::npos);
|
|
if ((vStr.size() > 2) && (vStr.front() == '\"' && vStr.back() == '\"')) {
|
|
// Unquote string
|
|
vStr = vStr.substr(1, vStr.size() - 2);
|
|
}
|
|
Opts.clangOptions.push_back(oStr.substr(0, 2) + vStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (linkOptsOnly) {
|
|
if (firstOpt) {
|
|
isLibLinkOpts = ((OPTION_info(od) & OA_LINK_LIB)) != 0;
|
|
firstOpt = false;
|
|
}
|
|
|
|
if (!(OPTION_info(od) & OA_RUNTIME) || (!isLibLinkOpts && !(OPTION_info(od) & OA_LINK_EXE)) ||
|
|
(isLibLinkOpts && !(OPTION_info(od) & OA_LINK_LIB))) {
|
|
// Do not allow non-link-time options
|
|
logInvalidOption(options, bpos, Opts.optionsLog(), " (bad link-time option)");
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!(OPTION_info(od) & OA_RUNTIME)) continue;
|
|
}
|
|
|
|
if (!processOption(option_ndx, Opts, value, isPrefix_option, (isPrefix_mno || isPrefix_fno))) {
|
|
// Keep the optionsLog set in processOption().
|
|
std::string tmpStr("Invalid option: ");
|
|
tmpStr += options.substr(bpos, (pos == std::string::npos) ? pos : pos - bpos);
|
|
tmpStr += "\n ";
|
|
Opts.optionsLog().insert(0, tmpStr);
|
|
return false;
|
|
}
|
|
|
|
Opts.setFlag(option_ndx, 1);
|
|
}
|
|
|
|
if (Opts.isOptionSeen(OID_ShowHelp)) {
|
|
OT_CSTRING_t* p = reinterpret_cast<OT_CSTRING_t*>(OPTION_var(OID_ShowHelp, ovars));
|
|
const char* arg = (const char*)*p;
|
|
ShowOptionsHelp(arg, Opts);
|
|
}
|
|
|
|
// Set up llvmargv if llvmOptions is not empty
|
|
if (!Opts.llvmOptions.empty()) {
|
|
std::string* ostr = &Opts.llvmOptions;
|
|
int n;
|
|
size_t pos1, pos2;
|
|
pos1 = ostr->find_first_not_of(' ');
|
|
for (n = 0; pos1 != std::string::npos; ++n) {
|
|
pos2 = ostr->find_first_of(' ', pos1);
|
|
if (pos2 != std::string::npos) {
|
|
pos2 = ostr->find_first_not_of(' ', pos2);
|
|
}
|
|
pos1 = pos2;
|
|
}
|
|
if (n > 0) {
|
|
char* data = new char[sizeof(char*) * (n + 1) + // for argv[0:n]
|
|
ostr->size() + 1]; // for all option chars
|
|
char** t_argv = (char**)data;
|
|
static char pseudoCmdName[] = "llvmOptCodegen";
|
|
|
|
t_argv[0] = &pseudoCmdName[0]; // pseudo command name
|
|
Opts.setLLVMArgs(n + 1, t_argv);
|
|
Opts.recordMemoryHandle(data);
|
|
|
|
// Initialize all arguments
|
|
t_argv++;
|
|
data = (char*)(t_argv + n);
|
|
int i = 0;
|
|
pos1 = ostr->find_first_not_of(' ');
|
|
while (pos1 != std::string::npos) {
|
|
pos2 = ostr->find_first_of(' ', pos1);
|
|
size_t len;
|
|
if (pos2 == std::string::npos) {
|
|
len = ostr->size() - pos1;
|
|
} else {
|
|
len = pos2 - pos1;
|
|
pos2 = ostr->find_first_not_of(' ', pos2);
|
|
}
|
|
ostr->copy(data, len, pos1);
|
|
data[len] = 0;
|
|
t_argv[i++] = data;
|
|
data += (len + 1);
|
|
pos1 = pos2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the set of options is OA_LINK_LIB options, the "-create-library"
|
|
// option should be included
|
|
if (linkOptsOnly && isLibLinkOpts && !Opts.isOptionSeen(OID_clCreateLibrary)) {
|
|
Opts.optionsLog() =
|
|
"Invalid option:"
|
|
" library link options used without -create-library";
|
|
return false;
|
|
}
|
|
|
|
if (Opts.isOptionSeen(OID_UniformWorkGroupSize) && (strcmp(ovars->CLStd, "CL1.2") == 0)) {
|
|
Opts.optionsLog() =
|
|
"Invalid option:"
|
|
" -cl-uniform-work-group-size is not supported in OpenCL 1.x\n";
|
|
return false;
|
|
}
|
|
Opts.postParseInit();
|
|
return true;
|
|
}
|
|
|
|
bool init() {
|
|
// Set up Options Map
|
|
for (int i = 0; i < OID_LAST; ++i) {
|
|
OptionDescriptor* od = &OptDescTable[i];
|
|
const char* sname = OPTION_sname(od);
|
|
const char* lname = OPTION_lname(od);
|
|
|
|
// Make sure alias option is initialized correctly.
|
|
if (OPTION_info(od) & OA_MISC_ALIAS) {
|
|
// alias option must be in runtime group
|
|
OPTION_info(od) |= OA_RUNTIME;
|
|
assert((OPTION_offset(od) == 0) &&
|
|
"Alias option should be RUNTIME option and has offset zero");
|
|
}
|
|
|
|
if (OPTION_form(od) == OFA_NORMAL) {
|
|
if (sname != NULL) {
|
|
OptionNameMap[0][sname] = i;
|
|
}
|
|
if (lname != NULL) {
|
|
OptionNameMap[1][lname] = i;
|
|
}
|
|
if (((OPTION_value(od) == OVA_OPTIONAL) || (OPTION_value(od) == OVA_REQUIRED)) &&
|
|
(OPTION_info(od) & OA_SEPARATOR_NONE)) {
|
|
if (sname != NULL) {
|
|
NoneSeparatorOptionMap[0][sname] = i;
|
|
}
|
|
if (lname != NULL) {
|
|
NoneSeparatorOptionMap[1][sname] = i;
|
|
}
|
|
}
|
|
} else if (OPTION_form(od) == OFA_PREFIX_F) {
|
|
assert(((OPTION_value(od) == OVA_DISALLOWED) || !(OPTION_info(od) & OA_SEPARATOR_NONE)) &&
|
|
(lname == NULL) &&
|
|
"-f/-fno- option may not have a long name, and"
|
|
"must have a value separator if it requires a value");
|
|
FOptionMap[sname] = i;
|
|
} else if (OPTION_form(od) == OFA_PREFIX_M) {
|
|
assert(((OPTION_value(od) == OVA_DISALLOWED) || !(OPTION_info(od) & OA_SEPARATOR_NONE)) &&
|
|
(lname == NULL) &&
|
|
"-m/-mno- option may not have a long name, and"
|
|
"must have a value separator if it requires a value");
|
|
MOptionMap[sname] = i;
|
|
}
|
|
}
|
|
#if 0
|
|
std::map <std::string, int>::const_iterator I, IE;
|
|
IE = OptionNameMap[0].end();
|
|
for (I = OptionNameMap[0].begin(); I != IE; ++I) {
|
|
printf (" %s : %d \n", I->first.c_str(), I->second);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool teardown() { return true; }
|
|
|
|
OptionDescriptor* getOptDescTable() { return &OptDescTable[0]; }
|
|
|
|
Options::Options()
|
|
: oVariables(NULL),
|
|
clcOptions(),
|
|
llvmOptions(),
|
|
kernelArgAlign(0),
|
|
basename_max(0),
|
|
OptionsLog(),
|
|
llvmargc(0),
|
|
llvmargv(NULL),
|
|
buildNo(0),
|
|
dumpFileRoot(),
|
|
currKernelName(NULL),
|
|
encryptCode(0),
|
|
MemoryHandles(),
|
|
libraryType_(amd::LibraryUndefined) {
|
|
oVariables = new OptionVariables();
|
|
::memset(flags, 0, sizeof(flags));
|
|
|
|
// Set -1 to use default
|
|
WorkGroupSize[0] = -1;
|
|
WorkGroupSize[1] = -1;
|
|
WorkGroupSize[2] = -1;
|
|
UseDefaultWGS = true;
|
|
NumAvailGPRs = -1;
|
|
|
|
sourceFileName_.reserve(1024);
|
|
}
|
|
|
|
Options::~Options() {
|
|
for (int i = 0; i < (int)MemoryHandles.size(); ++i) {
|
|
delete[] MemoryHandles[i];
|
|
}
|
|
if (oVariables) {
|
|
delete oVariables;
|
|
}
|
|
}
|
|
|
|
void Options::postParseInit() {
|
|
if (!oVariables->EnableDumpKernel) {
|
|
oVariables->DumpFlags = 0;
|
|
}
|
|
|
|
#if 0
|
|
// set -march=gpu-64 for clc
|
|
if (oVariables->GPU64BitIsa) {
|
|
clcOptions.append(" --march=gpu-64");
|
|
}
|
|
#endif
|
|
|
|
oVariables->UseJIT = oVariables->ForceJIT || (oVariables->UseJIT && !oVariables->EnableDebug);
|
|
#ifdef SKIP_INTERNAL_OPTION
|
|
oVariables->DumpFlags &= DUMP_PRODUCT_FLAGS;
|
|
#endif
|
|
|
|
/*
|
|
if (oVariables->OptEstNumGPRs == -1) {
|
|
oVariables->OptEstNumGPRs = getDefaultGPRs();
|
|
}
|
|
*/
|
|
}
|
|
|
|
void Options::setPerBuildInfo(const char* val, int encrypt, bool device) {
|
|
encryptCode = encrypt;
|
|
setDumpFileName(val);
|
|
|
|
if (device) {
|
|
if (useDefaultWGS()) {
|
|
WorkGroupSize[0] = 256;
|
|
WorkGroupSize[1] = 1;
|
|
WorkGroupSize[2] = 1;
|
|
}
|
|
// Get an estimate number of available GPRs for each thread
|
|
// Can do it more precisely. For now, it is okay doing so.
|
|
int numWF = 4;
|
|
int wfsize = 64;
|
|
if (wfsize > 0) {
|
|
numWF = WorkGroupSize[0] / wfsize;
|
|
}
|
|
if (numWF < 2) {
|
|
NumAvailGPRs = 128;
|
|
} else {
|
|
NumAvailGPRs = 256 / numWF;
|
|
}
|
|
} else {
|
|
// Unused for CPU
|
|
NumAvailGPRs = -1;
|
|
}
|
|
}
|
|
|
|
|
|
// HashString - Hash function for strings.
|
|
// This is the Bernstein hash function.
|
|
static inline unsigned HashString(std::string::const_iterator it, std::string::const_iterator end) {
|
|
auto Bernstein = [](unsigned Result, unsigned char c) { return Result * 33 + c; };
|
|
return std::accumulate(it, end, 0U, Bernstein);
|
|
}
|
|
|
|
static inline std::string to_string(unsigned number) {
|
|
std::ostringstream ss;
|
|
ss << number;
|
|
return ss.str();
|
|
}
|
|
|
|
static void splitFileName(const std::string& fileName, std::string& path, std::string& baseName) {
|
|
std::size_t found = fileName.find_last_of("/\\");
|
|
if (found != std::string::npos) {
|
|
path = fileName.substr(0, found + 1);
|
|
baseName = fileName.substr(found + 1);
|
|
} else {
|
|
baseName = fileName;
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static inline bool isFullPath(const std::string& path) {
|
|
size_t found = path.find(":");
|
|
return found != std::string::npos;
|
|
}
|
|
|
|
static std::string getValidDumpPath(const std::string& path) {
|
|
if (path.empty()) {
|
|
return path;
|
|
}
|
|
|
|
std::string validPath;
|
|
unsigned long pathname_max = _MAX_PATH;
|
|
if (path.size() > pathname_max) {
|
|
// On Windows the max file path is 32,767 if you prefix "\\?\" with full path
|
|
if (isFullPath(path)) {
|
|
const unsigned new_win_path = _MAX_ENV;
|
|
// prefix "\\?\"
|
|
validPath = "\\\\?\\" + path;
|
|
assert(path.size() < new_win_path);
|
|
} else {
|
|
assert(path.size() < pathname_max);
|
|
}
|
|
} else
|
|
validPath = path;
|
|
return validPath;
|
|
}
|
|
|
|
#else
|
|
|
|
static std::string getValidDumpPath(const std::string& path) {
|
|
if (path.empty()) {
|
|
return path;
|
|
}
|
|
|
|
const std::string curPath = path + ".";
|
|
long pathname_max = pathconf(curPath.c_str(), _PC_PATH_MAX);
|
|
assert(pathname_max != -1 && path.size() < static_cast<unsigned long>(pathname_max));
|
|
return path;
|
|
}
|
|
|
|
#endif
|
|
|
|
static std::string getValidDumpBaseName(const std::string& path, const std::string& file,
|
|
long basename_max, const std::string& ext) {
|
|
if (file.size() + ext.size() < static_cast<unsigned long>(basename_max)) {
|
|
return file;
|
|
}
|
|
|
|
// HashString returns unsigned, and the max lengh of unsighed is
|
|
// 10 digits, anything over origDigits will be replaced with its hash value
|
|
size_t origDigits = basename_max - 10 - ext.size();
|
|
std::string truncName = file.substr(0, origDigits);
|
|
unsigned hashVal = HashString(file.begin() + origDigits, file.end());
|
|
|
|
return truncName + to_string(hashVal);
|
|
}
|
|
|
|
void Options::setDumpFileName(const char* val) {
|
|
std::string dumpPrefix = oVariables->DumpPrefix;
|
|
const size_t pidPos = dumpPrefix.find("%pid%");
|
|
if (pidPos != std::string::npos) {
|
|
#ifdef _WIN32
|
|
const std::int32_t pid = _getpid();
|
|
#endif
|
|
#ifdef __linux__
|
|
const std::int32_t pid = getpid();
|
|
#endif
|
|
dumpPrefix.replace(pidPos, 5, std::to_string(pid));
|
|
}
|
|
|
|
std::stringstream prefix;
|
|
// Replace characters in the file name that are not legal with '_'.
|
|
prefix << dumpPrefix << "_" << buildNo << "_" << std::regex_replace(val, std::regex(":"), "_");
|
|
dumpFileRoot = prefix.str();
|
|
|
|
// Check whether the length of path meets the system limits
|
|
splitFileName(dumpFileRoot, fullPath, baseName);
|
|
fullPath = getValidDumpPath(fullPath);
|
|
|
|
#ifdef _WIN32
|
|
basename_max = FILENAME_MAX;
|
|
#endif
|
|
#ifdef __linux__
|
|
const std::string curPath = fullPath + ".";
|
|
basename_max = pathconf(curPath.c_str(), _PC_NAME_MAX);
|
|
assert(basename_max != -1);
|
|
#endif
|
|
|
|
if (!fullPath.empty()) {
|
|
dumpFileRoot = fullPath + baseName;
|
|
} else {
|
|
dumpFileRoot = baseName;
|
|
}
|
|
}
|
|
|
|
std::string Options::getDumpFileName(const std::string& ext) {
|
|
// The length of file name meets the system limits
|
|
if (baseName.size() + ext.size() < static_cast<unsigned long>(basename_max)) {
|
|
return fullPath + baseName + ext;
|
|
}
|
|
|
|
std::size_t found = ext.find_last_of(".");
|
|
std::string kernel_name, extension;
|
|
if (found != std::string::npos) {
|
|
kernel_name = ext.substr(0, found);
|
|
extension = ext.substr(found);
|
|
} else {
|
|
extension = ext;
|
|
}
|
|
|
|
baseName = getValidDumpBaseName(fullPath, baseName + kernel_name, basename_max, extension);
|
|
|
|
std::string fullName;
|
|
if (!fullPath.empty()) {
|
|
fullName = fullPath + baseName + extension;
|
|
} else {
|
|
fullName = baseName + extension;
|
|
}
|
|
return fullName;
|
|
}
|
|
|
|
bool Options::isCStrOptionsEqual(const char* cs1, const char* cs2) const {
|
|
if ((cs1 != NULL) && (cs2 != NULL)) {
|
|
return (strcmp(cs1, cs2) == 0);
|
|
} else if ((cs1 == NULL || strcmp(cs1, "") == 0) && (cs2 == NULL || strcmp(cs2, "") == 0)) {
|
|
// consider empty string and NULL ptr (no string) as equal
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Options::equals(const Options& other, bool ignoreClcOptions) const {
|
|
OptionVariables* ovars = oVariables;
|
|
OptionVariables* ovars2 = other.oVariables;
|
|
OptionDescriptor* od = OptDescTable;
|
|
for (int i = 0; i < OID_LAST; ++i, ++od) {
|
|
if (!OPTIONHasOVariable(od)) {
|
|
continue;
|
|
}
|
|
|
|
char* addr = reinterpret_cast<char*>(ovars) + od->OptionOffset;
|
|
char* addr2 = reinterpret_cast<char*>(ovars2) + od->OptionOffset;
|
|
if (OPTION_type(od) == OT_BOOL) {
|
|
OT_BOOL_t* o = reinterpret_cast<OT_BOOL_t*>(addr);
|
|
OT_BOOL_t* o2 = reinterpret_cast<OT_BOOL_t*>(addr2);
|
|
if (*o != *o2) return false;
|
|
} else if (OPTION_type(od) == OT_INT32) {
|
|
OT_INT32_t* o = reinterpret_cast<OT_INT32_t*>(addr);
|
|
OT_INT32_t* o2 = reinterpret_cast<OT_INT32_t*>(addr2);
|
|
if (*o != *o2) return false;
|
|
} else if (OPTION_type(od) == OT_UINT32) {
|
|
OT_UINT32_t* o = reinterpret_cast<OT_UINT32_t*>(addr);
|
|
OT_UINT32_t* o2 = reinterpret_cast<OT_UINT32_t*>(addr2);
|
|
if (*o != *o2) return false;
|
|
} else if (OPTION_type(od) == OT_CSTRING) {
|
|
OT_CSTRING_t* o = reinterpret_cast<OT_CSTRING_t*>(addr);
|
|
OT_CSTRING_t* o2 = reinterpret_cast<OT_CSTRING_t*>(addr2);
|
|
if (!isCStrOptionsEqual(*o, *o2)) return false;
|
|
} else if (OPTION_type(od) == OT_UCHAR) {
|
|
OT_UCHAR_t* o = reinterpret_cast<OT_UCHAR_t*>(addr);
|
|
OT_UCHAR_t* o2 = reinterpret_cast<OT_UCHAR_t*>(addr2);
|
|
if (*o != *o2) return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!ignoreClcOptions) {
|
|
if (clcOptions.compare(other.clcOptions) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (llvmOptions.compare(other.llvmOptions) != 0) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
if (WorkGroupSize[i] != other.WorkGroupSize[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (NumAvailGPRs != other.NumAvailGPRs) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Options::setOptionVariablesAs(const Options& other) {
|
|
OptionVariables* srcovars = other.oVariables;
|
|
OptionVariables* dstovars = oVariables;
|
|
OptionDescriptor* od = OptDescTable;
|
|
for (int i = 0; i < OID_LAST; ++i, ++od) {
|
|
if (!OPTIONHasOVariable(od)) {
|
|
continue;
|
|
}
|
|
|
|
if (!other.isOptionSeen(i)) {
|
|
continue;
|
|
}
|
|
|
|
if (!setOptionVariable(od, srcovars, dstovars)) {
|
|
optionsLog() = "Wrong option value\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string Options::getStringFromStringVec(std::vector<std::string>& stringVec) {
|
|
const char* const delim = " ";
|
|
std::ostringstream strstr;
|
|
std::copy(stringVec.begin(), stringVec.end(), std::ostream_iterator<std::string>(strstr, delim));
|
|
return strstr.str();
|
|
}
|
|
|
|
} // namespace option
|
|
|
|
} // namespace amd
|