diff options
author | Martyn Capewell <martyn.capewell@arm.com> | 2016-10-18 13:45:55 +0100 |
---|---|---|
committer | Martyn Capewell <martyn.capewell@arm.com> | 2016-10-18 13:49:14 +0100 |
commit | 491a575777fe21edc15bedd877a288a7f042bf48 (patch) | |
tree | f2ec1c3c22b2513c0a2047a0b4c3ab6d2ea4f0be /test/aarch64 | |
parent | 3976edb2d3b98f0012c4f18b7242a6382c2d136c (diff) | |
download | vixl-491a575777fe21edc15bedd877a288a7f042bf48.tar.gz |
Fix NEON 'across' instruction simulation
The simulation of NEON instructions that operate across a vector, eg. uminv,
was incorrectly clearing its destination register, which was a problem when
source and destination registers aliased.
Fix the simulation and improve the tests to check instances of aliased
registers.
Change-Id: I65717c472e5bfc85258952002b571597d554270d
Diffstat (limited to 'test/aarch64')
-rw-r--r-- | test/aarch64/test-simulator-aarch64.cc | 80 |
1 files changed, 46 insertions, 34 deletions
diff --git a/test/aarch64/test-simulator-aarch64.cc b/test/aarch64/test-simulator-aarch64.cc index bf0daec1..a59519f7 100644 --- a/test/aarch64/test-simulator-aarch64.cc +++ b/test/aarch64/test-simulator-aarch64.cc @@ -1391,21 +1391,26 @@ static void Test1OpAcrossNEON_Helper(Test1OpNEONHelper_t helper, // TODO: Refactor duplicate definitions below with a VRegister::As() routine. const unsigned vd_bits = RegisterSizeInBitsFromFormat(vd_form); - const unsigned vn_bits = RegisterSizeInBitsFromFormat(vn_form); const unsigned vn_lane_count = LaneCountFromFormat(vn_form); const unsigned vn_lane_bytes = LaneSizeInBytesFromFormat(vn_form); const unsigned vn_lane_bytes_log2 = LaneSizeInBytesLog2FromFormat(vn_form); const unsigned vn_lane_bits = LaneSizeInBitsFromFormat(vn_form); + // Test destructive operations by (arbitrarily) using the same register for + // B and S lane sizes. + bool destructive = (vd_bits == kBRegSize) || (vd_bits == kSRegSize); - // These will be either a D- or a Q-register form, with a single lane - // (for use in scalar load and store operations). + // Create two aliases for v0; the first is the destination for the tested + // instruction, the second, the whole Q register to check the results. VRegister vd = VRegister(0, vd_bits); + VRegister vdstr = VRegister(0, kQRegSize); + VRegister vn = VRegister(1, vn_bits); VRegister vntmp = VRegister(3, vn_bits); // These will have the correct format for use when calling 'helper'. + VRegister vd_helper = VRegister(0, vn_bits, vn_lane_count); VRegister vn_helper = VRegister(1, vn_bits, vn_lane_count); // 'v*tmp_single' will be either 'Vt.B', 'Vt.H', 'Vt.S' or 'Vt.D'. @@ -1430,17 +1435,16 @@ static void Test1OpAcrossNEON_Helper(Test1OpNEONHelper_t helper, vn_lane_bytes_log2)); __ Ext(vn_ext, vn_ext, vntmp_ext, vn_lane_bytes); - // Set the destination to zero for tests such as '[r]shrn2'. - // TODO: Setting the destination to values other than zero - // might be a better test for instructions such as sqxtn2 - // which may leave parts of V registers unchanged. - __ Movi(vd.V16B(), 0); - - { + if (destructive) { + __ Mov(vd_helper, vn_helper); + SingleEmissionCheckScope guard(&masm); + (masm.*helper)(vd, vd_helper); + } else { SingleEmissionCheckScope guard(&masm); (masm.*helper)(vd, vn_helper); } - __ Str(vd, MemOperand(out, vd.GetSizeInBytes(), PostIndex)); + + __ Str(vdstr, MemOperand(out, kQRegSizeInBytes, PostIndex)); __ Add(index_n, index_n, 1); __ Cmp(index_n, inputs_n_length); @@ -1463,9 +1467,10 @@ static void Test1OpAcrossNEON(const char * name, Test1OpNEONHelper_t helper, VIXL_ASSERT(inputs_n_length > 0); const unsigned vd_lane_count = LaneCountFromFormat(vd_form); + const unsigned vd_lanes_per_q = MaxLaneCountFromFormat(vd_form); const unsigned results_length = inputs_n_length; - Td* results = new Td[results_length * vd_lane_count]; + Td* results = new Td[results_length * vd_lanes_per_q]; const unsigned lane_bit = sizeof(Td) * 8; const unsigned lane_len_in_hex = MaxHexCharCount<Td, Tn>(); @@ -1505,9 +1510,19 @@ static void Test1OpAcrossNEON(const char * name, Test1OpNEONHelper_t helper, bool error_in_vector = false; for (unsigned lane = 0; lane < vd_lane_count; lane++) { - unsigned output_index = (n * vd_lane_count) + lane; + unsigned expected_index = (n * vd_lane_count) + lane; + unsigned results_index = (n * vd_lanes_per_q) + lane; - if (results[output_index] != expected[output_index]) { + if (results[results_index] != expected[expected_index]) { + error_in_vector = true; + break; + } + } + + // For across operations, the remaining lanes should be zero. + for (unsigned lane = vd_lane_count; lane < vd_lanes_per_q; lane++) { + unsigned results_index = (n * vd_lanes_per_q) + lane; + if (results[results_index] != 0) { error_in_vector = true; break; } @@ -1518,8 +1533,8 @@ static void Test1OpAcrossNEON(const char * name, Test1OpNEONHelper_t helper, printf("%s\n", name); printf(" Vn%.*s| Vd%.*s| Expected\n", - lane_len_in_hex+1, padding, - lane_len_in_hex+1, padding); + lane_len_in_hex + 1, padding, + lane_len_in_hex + 1, padding); // TODO: In case of an error, all tests print out as many elements as // there are lanes in the output or input vectors. This way @@ -1530,28 +1545,25 @@ static void Test1OpAcrossNEON(const char * name, Test1OpNEONHelper_t helper, // This output for the 'Across' category has the required // modifications. for (unsigned lane = 0; lane < vn_lane_count; lane++) { - unsigned output_index = n * vd_lane_count; + unsigned results_index = (n * vd_lanes_per_q) + ((vn_lane_count - 1) - lane); unsigned input_index_n = (inputs_n_length - vn_lane_count + n + 1 + lane) % inputs_n_length; - if (vn_lane_count-1 == lane) { // Is this the last lane? - // Print the result element(s) in the last lane only. - printf("%c0x%0*" PRIx64 " | 0x%0*" PRIx64 " " - "| 0x%0*" PRIx64 "\n", - results[output_index] != expected[output_index] ? '*' : ' ', - lane_len_in_hex, - static_cast<uint64_t>(inputs_n[input_index_n]), - lane_len_in_hex, - static_cast<uint64_t>(results[output_index]), - lane_len_in_hex, - static_cast<uint64_t>(expected[output_index])); - } else { - printf(" 0x%0*" PRIx64 " | %.*s| %.*s\n", - lane_len_in_hex, - static_cast<uint64_t>(inputs_n[input_index_n]), - lane_len_in_hex+1, padding, - lane_len_in_hex+1, padding); + Td expect = 0; + if ((vn_lane_count - 1) == lane) { + // This is the last lane to be printed, ie. the least-significant + // lane, so use the expected value; any other lane should be zero. + unsigned expected_index = n * vd_lane_count; + expect = expected[expected_index]; } + printf("%c0x%0*" PRIx64 " | 0x%0*" PRIx64 " | 0x%0*" PRIx64 "\n", + results[results_index] != expect ? '*' : ' ', + lane_len_in_hex, + static_cast<uint64_t>(inputs_n[input_index_n]), + lane_len_in_hex, + static_cast<uint64_t>(results[results_index]), + lane_len_in_hex, + static_cast<uint64_t>(expect)); } } } |