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:

err msg

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:

14708

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)
  1. 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
  1. In the previous section, the call to U_V(value) invokes the function numeric.c:Perl_cast_uv. By adding specific print statements, it was discovered that the error-causing statement is res=(UV)f;.

Platform Output
6b pp_int8:9223372036854775808
cast_uv1
cast_uv4:res=9223372036854775808
pp_sassign
tmp1=9223372036854775808.00000000000000000000,
tmp2=9223372036854775808,
tmp3=18446744073709551616.00000000000000000000,
tmp4=1.84467440737096e+19, 18446744073709551615
8x pp_int8:9223372036854775807
cast_uv1
cast_uv4:res=9223372036854775807
pp_sassign
tmp1=9223372036854775808.00000000000000000000,
tmp2=9223372036854775807,
tmp3=18446744073709551616.00000000000000000000,
tmp4=1.84467440737096e+19, 18446744073709551613
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