Skip to content

Commit 6c5cec1

Browse files
committed
Introduce --dimParams compiler option, and use it for dynamic backend tests on NNPA to utilze NNPA.
Signed-off-by: Yasushi Negishi <[email protected]>
1 parent b0db459 commit 6c5cec1

13 files changed

+122
-105
lines changed

docs/Testing.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ The onnx node tests usually have known dimension size for input tensors. So, to
5454
IMPORTER_FORCE_DYNAMIC='-1:-1' all dimensions of all the inputs will be changed
5555
IMPORTER_FORCE_DYNAMIC='0:-1' all dimensions of the first input will be changed
5656
IMPORTER_FORCE_DYNAMIC='0:-1|1:0,1' all dimensions of the first input and the 1st and 2nd dimensions of the second input will be changed
57-
IMPORTER_FORCE_DYNAMIC='0:0=a,1=b,2=c|1:0=a,1=b,2=c' the first three dimensions of the first of and the second inputs are changed. And assume that the first dimensions of the first and second arguments are the same, and same for the the second and third dimensions.
5857
```
5958

6059
The Backus-Naur Form (BNF) for `IMPORTER_FORCE_DYNAMIC` is as follows.
@@ -64,11 +63,10 @@ The Backus-Naur Form (BNF) for `IMPORTER_FORCE_DYNAMIC` is as follows.
6463
<inputString ::= <inputIndex> `:` <dimString>
6564
<dimString> ::= <dimIndex> | <dimIndex> `,` <dimString>
6665
<inputIndex> ::= <index>
67-
<dimIndex> ::= <index> | <index> '=' <symbol>
66+
<dimIndex> ::= <index>
6867
<index> ::= -1 | <number>
6968
<number> ::= <digit> | <digit><number>
7069
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
71-
<symbol> ::= 'a', 'b', 'c', ..., 'z'
7270
```
7371
Value `-1` semantically represents all inputs or all dimensions, and it has the highest priority. E.g. `'0: -1, 0'` means all dimensions of the first input will be changed. Input and dimension indices start from 0.
7472

@@ -84,12 +82,10 @@ with `IMPORTER_FORCE_DYNAMIC='0:-1'`, the result is:
8482
```
8583
func @main_graph(%arg0: tensor<?x?x?xf32>, %arg1: tensor<3x4x5xf32>) -> tensor<3x4x5xf32>
8684
```
87-
88-
with `IMPORTER_FORCE_DYNAMIC='0:0=a,2=b|1:1=a'`, the result is:
85+
with `IMPORTER_FORCE_DYNAMIC='0:0,2|1:1'`, the result is:
8986
```
90-
func @main_graph(%arg0: tensor<?x4x?xf32>{onnx.name = "x“, onnx.dim_params = "0:a,2:b"}, %arg1: tensor<3x?x5xf32>{onnx.name = "y“, onnx.dim_params = "1:a"}) -> tensor<3x4x5xf32>
87+
func @main_graph(%arg0: tensor<?x4x?xf32>, %arg1: tensor<3x?x5xf32>) -> tensor<3x4x5xf32>
9188
```
92-
The onnx.dim_params attributes are used to specify the assumptions about dimensions of the first and second arguments.
9389
This is a way to use existing node test for dynamic tensors. Since not all test case can pass with dynamic tensor, there is a list in test/backend/test.py, test_not_for_dynamic, to specify which test can not pass with `IMPORTER_FORCE_DYNAMIC` is defined.
9490

9591
### Tests with constant inputs

src/Builder/FrontendDialectTransformer.cpp

Lines changed: 28 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,27 @@ class FrontendGenImpl {
452452
return attributes;
453453
}
454454

455+
// Generate a string vector from the dimParams option string
456+
void getInputDimParamsVecFromOption(std::string optionStr, size_t numOfArgs,
457+
SmallVector<std::string> &paramStrVec) {
458+
std::stringstream paramStrStream;
459+
paramStrStream << optionStr;
460+
std::string dimParamStr;
461+
while (std::getline(paramStrStream, dimParamStr, '|')) {
462+
size_t pos = dimParamStr.find(':');
463+
assert((pos > 0) && "invalid dimParams option string");
464+
int idx = stoi(dimParamStr.substr(0, pos));
465+
dimParamStr = dimParamStr.substr(pos + 1);
466+
std::replace(dimParamStr.begin(), dimParamStr.end(), '=', ':');
467+
if (idx < 0) // set all arguments
468+
for (size_t i = 0; i < numOfArgs; i++)
469+
paramStrVec[i] = dimParamStr;
470+
else
471+
paramStrVec[idx] = dimParamStr;
472+
}
473+
return;
474+
}
475+
455476
/*!
456477
* An alternative graph importing procedure for importing ONNX subgraphs.
457478
* ONNX subgraphs, unlike the main computation graph, are imported as regions
@@ -492,6 +513,10 @@ class FrontendGenImpl {
492513
// See https://github.com/onnx/onnx/blob/main/docs/IR.md for more
493514
// information about dim_param.
494515
llvm::SmallVector<std::string, 4> inputDimParams, outputDimParams;
516+
size_t numOfArgs = graph.input().size();
517+
llvm::SmallVector<std::string> inputDimParamsFromOption(numOfArgs);
518+
getInputDimParamsVecFromOption(
519+
options_.dimParams, numOfArgs, inputDimParamsFromOption);
495520

496521
// Import the input tensor types that are not constant and not initialized.
497522
int inputIndex = 0;
@@ -502,7 +527,9 @@ class FrontendGenImpl {
502527
std::string dimParams = "";
503528
Type argTy = ImportType(input.type(), &dimParams);
504529
argTy = modelInputShaper_.reshape(inputIndex, argTy);
505-
if (!dimParams.empty())
530+
if (!inputDimParamsFromOption[inputIndex].empty())
531+
inputDimParams.emplace_back(inputDimParamsFromOption[inputIndex]);
532+
else if (!dimParams.empty())
506533
inputDimParams.emplace_back(dimParams);
507534

508535
argTypes.emplace_back(argTy);
@@ -1388,56 +1415,6 @@ class FrontendGenImpl {
13881415
ret_vals.push_back(val);
13891416
}
13901417

1391-
void getParamStr(std::string envStr, std::string &paramStr) {
1392-
std::replace(envStr.begin(), envStr.end(), '=', ':');
1393-
paramStr = envStr;
1394-
return;
1395-
}
1396-
1397-
// Generate string for input_dim_params attr from the IMPORTER_FORCE_DYNAMIC
1398-
// string.
1399-
void getInputDimParamStrVec(std::string envInputString, func::FuncOp funcOp,
1400-
size_t numOfArgs, SmallVector<std::string> &paramStrVec) {
1401-
std::stringstream paramStrStream;
1402-
paramStrStream << envInputString;
1403-
std::string envStr;
1404-
while (std::getline(paramStrStream, envStr, '|')) {
1405-
size_t pos = envStr.find(':');
1406-
assert((pos > 0) && "invalid IMPORTER_FORCE_DYNAMIC environment");
1407-
int idx = stoi(envStr.substr(0, pos));
1408-
envStr = envStr.substr(pos + 1);
1409-
if (idx < 0) { // set all arguments
1410-
for (size_t i = 0; i < numOfArgs; i++) {
1411-
getParamStr(envStr, paramStrVec[i]);
1412-
}
1413-
} else {
1414-
getParamStr(envStr, paramStrVec[idx]);
1415-
}
1416-
}
1417-
return;
1418-
}
1419-
1420-
// Get named attributesfrom IMPORTER_FORCE_DYNAMIC environment.
1421-
SmallVector<NamedAttribute> getNamedAttrFromImporterForceDynamic(
1422-
func::FuncOp funcOp, size_t numOfArgs) {
1423-
SmallVector<NamedAttribute> argNamedAttrs;
1424-
const char *envInputString = std::getenv("IMPORTER_FORCE_DYNAMIC");
1425-
if (envInputString == NULL)
1426-
return argNamedAttrs;
1427-
SmallVector<std::string> inputDimParamStrVec(numOfArgs);
1428-
getInputDimParamStrVec(
1429-
envInputString, funcOp, numOfArgs, inputDimParamStrVec);
1430-
for (size_t i = 0; i < numOfArgs; ++i) {
1431-
if (inputDimParamStrVec[i].length() != 0) {
1432-
auto name = builder_.getStringAttr(inputDimParamStrVec[i]);
1433-
NamedAttribute namedAttr =
1434-
builder_.getNamedAttr("onnx.dim_params", name);
1435-
argNamedAttrs.emplace_back(namedAttr);
1436-
}
1437-
}
1438-
return argNamedAttrs;
1439-
}
1440-
14411418
// Move function attributes for argument/result names and dim_params into
14421419
// argument/result attributes.
14431420
void moveFuncAttrsToArgAttrs(func::FuncOp funcOp,
@@ -1448,10 +1425,6 @@ class FrontendGenImpl {
14481425
Operation *op = funcOp.getOperation();
14491426
size_t numOfArgs =
14501427
(isArg) ? funcOp.getNumArguments() : funcOp.getNumResults();
1451-
SmallVector<NamedAttribute> argAttrsFromImporterForceDynamic;
1452-
if (isArg)
1453-
argAttrsFromImporterForceDynamic =
1454-
getNamedAttrFromImporterForceDynamic(funcOp, numOfArgs);
14551428

14561429
// Only move attributes that exists.
14571430
SmallVector<ArrayAttr, 2> funcAttrsToMove;
@@ -1476,8 +1449,6 @@ class FrontendGenImpl {
14761449
argAttrs.emplace_back(namedAttr);
14771450
}
14781451
}
1479-
if (isArg && (i < argAttrsFromImporterForceDynamic.size()))
1480-
argAttrs.emplace_back(argAttrsFromImporterForceDynamic[i]);
14811452
}
14821453
if (!argAttrs.empty()) {
14831454
if (isArg)

src/Builder/FrontendDialectTransformer.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ struct ImportOptions {
5555
// - (arg0: tensor<3x4x5xf32>, arg1: tensor<10x5xf32>)
5656
//
5757
std::string shapeInformation = "";
58+
// Custom onnx.dim_params attributes for the graph inputs for specifying
59+
// relationship among their ranks.
60+
// Its format is 'input_id:dim=sym,dim=sym,dim=sym|input_id:dim=sym,dim=sym..'
61+
// E.g. An ONNX model has two dynamic inputs
62+
// - (arg0: tensor<?x5xf32>, arg1: tensor<?x5xf32>)
63+
// If we want to specify that the first ranks of the first and second
64+
// arguments are the same, we can use:
65+
// - dimParams = '0:0=batch|1:0=batch'
66+
//
67+
std::string dimParams = "";
5868
// Directory to look for external data if any tensor has external
5969
// data location. If empty then external data is disabled.
6070
std::string externalDataDir = "";

src/Builder/ModelInputShaper.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ ModelInputShaper::ModelInputShaper() : force_dim_dynamic_enabled_(false) {
3636
std::string dimIndex;
3737
std::vector<int> dims;
3838
while (std::getline(dimIndices, dimIndex, ',')) {
39-
pos = dimIndex.find('='); // Ignore symbol part after '='
40-
dims.emplace_back(stoi(dimIndex.substr(0, pos)));
39+
dims.emplace_back(stoi(dimIndex));
4140
}
4241
// Default to the all dimensions if dims are not specified.
4342
if (dims.empty())

src/Compiler/CompilerOptions.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ bool preserveMLIR; // onnx-mlir only
5151
bool useOnnxModelTypes; // onnx-mlir only
5252
int repeatOnnxTransform; // onnx-mlir only
5353
std::string shapeInformation; // onnx-mlir only
54+
std::string dimParams; // onnx-mlir only
5455
ModelSize modelSize; // onnx-mlir only
5556
bool storeConstantsToFile; // onnx-mlir only
5657
float constantsToFileTotalThreshold; // onnx-mlir only
@@ -281,6 +282,19 @@ static llvm::cl::opt<std::string, true> shapeInformationOpt("shapeInformation",
281282
llvm::cl::value_desc("value"), llvm::cl::location(shapeInformation),
282283
llvm::cl::cat(OnnxMlirOptions));
283284

285+
static llvm::cl::opt<std::string, true> dimParamsOpt("dimParams",
286+
llvm::cl::desc(
287+
"Custom onnx.dim_params attributes for the inputs of the ONNX model for"
288+
"specifying relationship among ranks of the inputs.\n"
289+
"\"value\" is in the format of "
290+
"\"INPUT_ID1:D1=S1,D2=S2,...,Dn=Sn|INPUT_ID2:D1=T1,D2=T2,...Dn=Tn\""
291+
"where \"INPUT_ID1, INPUT_ID2, ...\" are input indices (starting from "
292+
"0 or being -1 for all input indices), and\n"
293+
"\"S1, S2, ...\" and \"T2, T2, ...\" are symbols to specify that same "
294+
"symbols have the same value"),
295+
llvm::cl::value_desc("value"), llvm::cl::location(dimParams),
296+
llvm::cl::cat(OnnxMlirOptions));
297+
284298
// Default value is defined by the OnnxMlirEnvOptionName constant string
285299
// variable, but the default setting mechanism here cannot be used here as we
286300
// need to evaluate this value prior to the compiler options being set. Proper

src/Compiler/CompilerOptions.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ extern bool preserveMLIR; // onnx-mlir only
9494
extern bool useOnnxModelTypes; // onnx-mlir only
9595
extern int repeatOnnxTransform; // onnx-mlir only
9696
extern std::string shapeInformation; // onnx-mlir only
97+
extern std::string dimParams; // onnx-mlir only
9798
extern ModelSize modelSize; // onnx-mlir only
9899
extern bool storeConstantsToFile; // onnx-mlir only
99100
extern float constantsToFileTotalThreshold; // onnx-mlir only

src/Compiler/CompilerUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ int processInputFile(StringRef inputFilename, mlir::MLIRContext &context,
623623
options.useOnnxModelTypes = useOnnxModelTypes;
624624
options.invokeOnnxVersionConverter = invokeOnnxVersionConverter;
625625
options.shapeInformation = shapeInformation;
626+
options.dimParams = dimParams;
626627
options.allowSorting = allowSorting;
627628
options.externalDataDir = dirName(inputFilename);
628629
options.functionsToDecompose.insert(options.functionsToDecompose.end(),

test/accelerators/NNPA/backend/CMakeLists.txt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,15 @@ endif()
9393
# Followings are test cases of test_to_enable_dict in test/backend/inference_backend.py.
9494
# Only test cases for operations supported by zDNN are listed. Non-supported cases are
9595
# commented out. This list is set in the environment variable `TEST_CASE_BY_USER`. So,
96-
# instruction name and relationship among input arguments are added after test
97-
# case name in each test case if necessary. For example,
96+
# instruction name and dimParams are added after test case name if necessary.
97+
# For example, the following line
9898
# test_add_cpu,zdnn_add,"0:0=a,1=b,2=c|1:0=a,1=b,2=c"
99-
# means that (1) the test name is "test_add_cpu", (2) instruction name to be
100-
# checked if it is used is "zdnn_add", (3) relationship among input arguments
101-
# are "0:0=a,1=b,2=c|1:0=a,1=b,2=c", which means that the first ranks of the
102-
# first, second and third ranks of the first and second input arguments are the
103-
# same respectively.
99+
# means that (1) the test name is "test_add_cpu", (2) instruction name is
100+
# "zdnn_add" to check the function is called, (3) when dimParams is
101+
# "NO_DYNAMIC_SHAPE_TEST", backend test is skipped, otherwise the string is
102+
# passed as --dimParams option. "0:0=a,1=b,2=c|1:0=a,1=b,2=c" means that the
103+
# first ranks of the first, second and third ranks of the first and second
104+
# input arguments are the same respectively.
104105
set(NNPA_TEST_LIST
105106

106107
# ==ARCH== NNPA
@@ -133,8 +134,8 @@ set(NNPA_TEST_LIST
133134
# ==OP== BatchNormalization
134135
# ==MIN== 6
135136
# ==LIM== Input and output tensor must be 4D(N x C x H x W).
136-
test_batchnorm_epsilon_cpu,zdnn_mul,"0:0=a,1=b,2=c,3=d"
137-
test_batchnorm_example_cpu,zdnn_mul,"0:0=a,1=b,2=c,3=d"
137+
test_batchnorm_epsilon_cpu,zdnn_mul,NO_DYNAMIC_SHAPE_TEST
138+
test_batchnorm_example_cpu,zdnn_mul,NO_DYNAMIC_SHAPE_TEST
138139

139140
# ==OP== Conv
140141
# ==MIN== 1

test/backend/common.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ def determine_dynamic_parameters(test_name):
3434
selected_list = {args.input: {args.dim}}
3535
test_name_cpu = test_name + "_cpu"
3636
if test_name_cpu in variables.test_for_dynamic:
37-
if test_name_cpu in test_to_enable_dynshape_dict:
38-
selected_list = variables.test_to_enable_dynshape_dict[test_name_cpu]
39-
elif len(variables.test_to_enable_dict[test_name_cpu]) > 1:
37+
if len(variables.test_to_enable_dict[test_name_cpu]) > 1:
4038
selected_list = variables.test_to_enable_dict[test_name_cpu].get(
4139
DYNAMIC_SHAPE
4240
)
@@ -52,25 +50,28 @@ def execute_commands(cmds, dynamic_inputs_dims):
5250
env_string = ""
5351
if dynamic_inputs_dims is not None:
5452
first_input = True
55-
if isinstance(dynamic_inputs_dims, dict):
56-
for input_index, dim_indices in dynamic_inputs_dims.items():
57-
if first_input:
58-
env_string += str(input_index)
59-
first_input = False
53+
for input_index, dim_indices in dynamic_inputs_dims.items():
54+
if first_input:
55+
env_string += str(input_index)
56+
first_input = False
57+
else:
58+
env_string += "|" + str(input_index)
59+
first_dim = True
60+
for dim_index in dim_indices:
61+
if first_dim:
62+
env_string += ":" + str(dim_index)
63+
first_dim = False
6064
else:
61-
env_string += "|" + str(input_index)
62-
first_dim = True
63-
for dim_index in dim_indices:
64-
if first_dim:
65-
env_string += ":" + str(dim_index)
66-
first_dim = False
67-
else:
68-
env_string += "," + str(dim_index)
69-
else:
70-
env_string = dynamic_inputs_dims
65+
env_string += "," + str(dim_index)
7166
my_env["IMPORTER_FORCE_DYNAMIC"] = env_string
7267
subprocess.run(cmds, env=my_env, check=True)
7368

69+
def get_compile_option(test_name):
70+
if args.dynamic and test_name in variables.test_to_enable_dimparams_dict:
71+
return ["--dimParams=" + variables.test_to_enable_dimparams_dict[test_name]]
72+
else:
73+
return []
74+
7475

7576
def check_instruction(test_name, exec_name):
7677
if args.instruction_check and test_name in variables.test_to_enable_symbol_dict:
@@ -137,6 +138,7 @@ def compile_model(model, emit):
137138
"--constants-to-file-total-threshold="
138139
+ str(args.constants_to_file_total_threshold)
139140
)
141+
command_list += get_compile_option(name + "_cpu")
140142

141143
command_list.append(target[emit])
142144
command_list.append(model_name)

test/backend/inference_backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3538,10 +3538,10 @@ def get_test_models():
35383538
if TEST_CASE_BY_USER is not None and TEST_CASE_BY_USER != "":
35393539
variables.test_by_user = TEST_CASE_BY_USER.split()
35403540
variables.node_test_by_user = [
3541-
x for x in variables.test_by_user if x in node_test_to_enable
3541+
x for x in variables.test_by_user if x.split(",")[0] in node_test_to_enable
35423542
]
35433543
variables.model_test_by_user = [
3544-
x for x in variables.test_by_user if x in model_test_to_enable
3544+
x for x in variables.test_by_user if x.split(",")[0] in model_test_to_enable
35453545
]
35463546

35473547
node_test_to_enable = variables.node_test_by_user

test/backend/test.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,14 @@
8282
quit()
8383

8484
# Ensure that test names specified in test_to_enable actually exist.
85-
for test_name_symbol_dynshape in (
85+
for test_name_symbol_dimparam in (
8686
test_to_enable if not args.type else test_by_type[args.type]
8787
):
88-
test_name_symbol_dynshape_list = test_name_symbol_dynshape.split(",")
89-
test_name = test_name_symbol_dynshape_list[0]
90-
if args.instruction_check and len(test_name_symbol_dynshape_list) >= 2:
88+
test_name_symbol_dimparam_list = test_name_symbol_dimparam.split(",")
89+
test_name = test_name_symbol_dimparam_list[0]
90+
if args.instruction_check and len(test_name_symbol_dimparam_list) >= 2:
9191
variables.test_to_enable_symbol_dict[test_name] = (
92-
test_name_symbol_dynshape_list[1]
92+
test_name_symbol_dimparam_list[1]
9393
)
9494
assert (
9595
test_name in all_test_names
@@ -99,9 +99,9 @@
9999
test_name
100100
)
101101
backend_test.include(r"^{}$".format(test_name))
102-
if len(test_name_symbol_dynshape_list) >= 3:
103-
variables.test_to_enable_dynshape_dict[test_name] = ",".join(
104-
test_name_symbol_dynshape_list[2:]
102+
if len(test_name_symbol_dimparam_list) >= 3:
103+
variables.test_to_enable_dimparams_dict[test_name] = ",".join(
104+
test_name_symbol_dimparam_list[2:]
105105
)
106106

107107
# import all test cases at global scope to make them visible to python.unittest

test/backend/variables.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ def get_runtime_vars():
326326

327327
# test_to_enable_dict
328328
try:
329-
_ = test_to_enable_dict, test_to_enable_symbol_dict, test_to_enable_dynshape_dict
329+
_ = test_to_enable_dict, test_to_enable_symbol_dict, test_to_enable_dimparams_dict
330330
except NameError:
331331
test_to_enable_dict = {}
332332
test_to_enable_symbol_dict = {}
333-
test_to_enable_dynshape_dict = {}
333+
test_to_enable_dimparams_dict = {}

0 commit comments

Comments
 (0)