Error Analysis of SPEC2017-600.perlbench_s
Problem Description
During the validation of SPEC2017 Base0 integer single-core performance on the 8xTxxx simulator, two errors were found in question 600 as follows:
Among them, the perlio test item has been validated correctly on the Txxxpxx FPGA, so the analysis below is based on the pack test item.
Problem Analysis
The source code of the pack test is a Perl script, mainly testing various boundary conditions for integers and floats, data conversion, and correctness of checksums.
Error Scene of “pack”
Pack includes 14,708 test items, with 28 items having comparison errors (gave ≠ expected), some screenshots are as follows:
Analysis of “pack” Source Code
In this part, we allocate the root cause of the error to one single float calculation instruction fcvtdl_z
.
Introduction to Analysis Methodology
600.perlbench_s_base is a Perl language interpreter, interpreting and executing the Perl script test.pl, which runs multiple test files listed in ../run_base_test_mytest-m64.0000/t/TEST, including op/pack.t.
Op/pack.t defines several functions, testing the correctness of function results by inputting different actual parameters to each function.
For example, a function numbers
and some of its parameter inputs, all at boundary values:
| Function | sub numbers {…} |
| ———————— | ———————————————————— |
| Call and Input Arguments | numbers (‘c’, -128, -1, 0, 1, 127);
numbers (‘C’, 0, 1, 127, 128, 255);
numbers (‘W’, 0, 1, 127, 128, 255, 256, 0x7ff, 0x800, 0xfffd);
…
numbers (‘s’, -32768, -1, 0, 1, 32767); |
Error Localization in the Source Code
First, identify the erroneous statements in op/pack.t, then analyze the statements and instructions in perlbench_s that lead to differences in behavior between 6b and 8x when interpreting and executing erroneous statements, and finally summarize the reasons for this unexpected difference in behavior and the solutions, as detailed in Section Three.
Experiment 1: The 28 erroneous tests were caused by an incorrect assignment of the “max” variable in the “numbers” function (Perl Script Level)
Erroneous Statement is the assignment of the “max” Parameter. The cause of the 28 errors is consistent. The following highlighted statement shows the discrepancies: 18446744073709551615 != 18446744073709551613
For list (xx, xx, xx, xx, xx) (total x) packed with * unpack ‘%64*’ ==gave 18446744073709551615, expected 18446744073709551613==
The number 18446744073709551613 comes from the assignment statement for the “max” parameter in the “numbers” function:
my $max = 1 + 2 * (int (2 ** ($len-1))-1);
Experiment 2: Incorrect Assignment of “max” Originating from the Suboperation (Perl Script Level)
To split the assignment of “max” in Experiment 1, op/pack.t can be reduced to the following Perl script called small.t:
#!./perl -w
{
my $len =64;
my $tmp1 = 2 ** ($len-1);
my $tmp1 = sprintf"%.20f",$tmp1;
my $tmp2 = int ($tmp1);
my $tmp3 = 2 * $tmp2;
my $tmp3 = sprintf"%.20f",$tmp3;
my $tmp4 = 1 + $tmp3;
my $max = 1 + 2 * (int (2 ** ($len-1))-1);
print "tmp1=$tmp1, tmp2=$tmp2, tmp3=$tmp3, tmp4=$tmp4, $max\n";
}
Differences in Behavior of the Suboperation (int) on 6b and 8x Platforms
Platform | Log |
---|---|
6b | tmp1=9223372036854775808.00000000000000000000, tmp2=9223372036854775808, tmp3=18446744073709551616.00000000000000000000, tmp4=1.84467440737096e+19, 18446744073709551615 |
8x | tmp1=9223372036854775808.00000000000000000000, tmp2=9223372036854775807, tmp3=18446744073709551616.00000000000000000000, tmp4=1.84467440737096e+19, 18446744073709551613 |
Experiment 3: Incorrect Interpretation and Execution of the “int” Suboperation in the 600 Test Source Code (C Language Level)
-
Function pp_int would interprete the Perl script small.t.
Printing the op tree of the Perl statement using the following method reveals that the suboperation (int) will be interpreted and executed by the pp_int function in the Perl interpreter perlbench_s:
Perl Instruction | Output |
---|---|
perl -MO=Terse -e 'my $a=9223372036854775808;<br/> my $b=int ($a);' |
LISTOP (0x564ce7c78978) leave [1] OP (0x564ce7c7cd20) enter COP (0x564ce7c789c0) nextstate BINOP (0x564ce7c78x20) sassign SVOP (0x564ce7c78x68) const [4] UV (0x564ce7c6eda0) 9223372036854775808 OP (0x564ce7c78xa8) padsv [1] COP (0x564ce7c7cd68) nextstate BINOP (0x564ce7c7cdc8) sassign //pp_hot.c:pp_sassign UNOP (0x564ce7c7ce10) int [3] //pp.c:pp_int OP (0x564ce7c7ce50) padsv [1] OP (0x564ce7c78910) padsv [2] -e syntax OK |
- In the previous section, the call to
U_V(value)
invokes the functionnumeric.c:Perl_cast_uv
. By adding specific print statements, it was discovered that the error-causing statement isres=(UV)f;
.
Platform | Output |
---|---|
6b | pp_int8:9223372036854775808 |
8x | pp_int8:9223372036854775807 |
Experiment 4: fcvtdl_z instruction error in 6b and 8x (c language level)
The assembly of res=(UV)f
(where f
is double, res
is unsigned long, and UV is unsigned int of 8 bytes) yields different outcomes across platforms xxxxxx, FTD2000, and LS3A5000.
xxxxxx | FTD2000 | LS3A5000 | |
---|---|---|---|
res=(UV)f assembly | omitted | ldr d0, [sp, 24] fcvtzu d0, d0 str d0, [sp, 16] |
slli.d $r12,$r12,32 ftintrz.l.d $f0,$f0 movfr2gr.d $r13,$f0 |
Value in memory | 0x43e0000000000000 | 0x43e0000000000000(sp+24) | 0x8000000000000000(r12) |
Value retrieved from memory into the register | 0x43e0000000000000 | 0x8000000000000000(d0) | 0x8000000000000000(f0) |
Value in the register after rounding towards zero instruction | 0x8000000000000000 | 0x8000000000000000(d0) | 0x8000000000000000(f0) |
Final Output Value | 0x8000000000000000 | 0x8000000000000000(d0) | 0x8000000000000000(r13) |
Analysis of the root cause
Omitted