From b41b493bb5244c4d0accb6d1a7ae24f3ac16617f Mon Sep 17 00:00:00 2001 From: Clemens Hammacher Date: Wed, 18 Oct 2017 11:46:57 +0200 Subject: [PATCH] [arm] [simulator] Fix implementation of vabs and vneg They did not preserve the bit pattern of nans before. Now they do. Also, add some tests for these instructions. R=ahaas@chromium.org, rodolph.perfetta@arm.com Bug: v8:6947 Change-Id: I189720cd47e1768194567a41371fc9586b414c45 Reviewed-on: https://chromium-review.googlesource.com/722979 Commit-Queue: Clemens Hammacher Reviewed-by: Rodolph Perfetta Reviewed-by: Andreas Haas Cr-Commit-Position: refs/heads/master@{#48672} --- src/arm/simulator-arm.cc | 36 ++++--- test/cctest/test-simulator-arm.cc | 156 +++++++++++++++++++++++++----- 2 files changed, 150 insertions(+), 42 deletions(-) diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index 6972ba53fb..f64050e120 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc @@ -3227,28 +3227,32 @@ void Simulator::DecodeTypeVFP(Instruction* instr) { } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) { // vabs if (instr->SzValue() == 0x1) { - double dm_value = get_double_from_d_register(vm).get_scalar(); - double dd_value = std::fabs(dm_value); - dd_value = canonicalizeNaN(dd_value); - set_d_register_from_double(vd, dd_value); + Float64 dm = get_double_from_d_register(vm); + constexpr uint64_t kSignBit64 = uint64_t{1} << 63; + Float64 dd = Float64::FromBits(dm.get_bits() & ~kSignBit64); + dd = canonicalizeNaN(dd); + set_d_register_from_double(vd, dd); } else { - float sm_value = get_float_from_s_register(m).get_scalar(); - float sd_value = std::fabs(sm_value); - sd_value = canonicalizeNaN(sd_value); - set_s_register_from_float(d, sd_value); + Float32 sm = get_float_from_s_register(m); + constexpr uint32_t kSignBit32 = uint32_t{1} << 31; + Float32 sd = Float32::FromBits(sm.get_bits() & ~kSignBit32); + sd = canonicalizeNaN(sd); + set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) { // vneg if (instr->SzValue() == 0x1) { - double dm_value = get_double_from_d_register(vm).get_scalar(); - double dd_value = -dm_value; - dd_value = canonicalizeNaN(dd_value); - set_d_register_from_double(vd, dd_value); + Float64 dm = get_double_from_d_register(vm); + constexpr uint64_t kSignBit64 = uint64_t{1} << 63; + Float64 dd = Float64::FromBits(dm.get_bits() ^ kSignBit64); + dd = canonicalizeNaN(dd); + set_d_register_from_double(vd, dd); } else { - float sm_value = get_float_from_s_register(m).get_scalar(); - float sd_value = -sm_value; - sd_value = canonicalizeNaN(sd_value); - set_s_register_from_float(d, sd_value); + Float32 sm = get_float_from_s_register(m); + constexpr uint32_t kSignBit32 = uint32_t{1} << 31; + Float32 sd = Float32::FromBits(sm.get_bits() ^ kSignBit32); + sd = canonicalizeNaN(sd); + set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) { DecodeVCVTBetweenDoubleAndSingle(instr); diff --git a/test/cctest/test-simulator-arm.cc b/test/cctest/test-simulator-arm.cc index 1784427ef8..7404418e84 100644 --- a/test/cctest/test-simulator-arm.cc +++ b/test/cctest/test-simulator-arm.cc @@ -27,6 +27,7 @@ #include "src/v8.h" #include "test/cctest/cctest.h" +#include "test/cctest/compiler/value-helper.h" #include "src/arm/simulator-arm.h" #include "src/assembler-inl.h" @@ -49,6 +50,8 @@ typedef Object* (*F3)(void* p0, int p1, int p2, int p3, int p4); #define __ assm. +namespace { + struct MemoryAccess { enum class Kind { None, @@ -85,9 +88,9 @@ struct TestData { int dummy; }; -static void AssembleMemoryAccess(Assembler* assembler, MemoryAccess access, - Register dest_reg, Register value_reg, - Register addr_reg) { +void AssembleMemoryAccess(Assembler* assembler, MemoryAccess access, + Register dest_reg, Register value_reg, + Register addr_reg) { Assembler& assm = *assembler; __ add(addr_reg, r0, Operand(access.offset)); @@ -167,38 +170,47 @@ static void AssembleMemoryAccess(Assembler* assembler, MemoryAccess access, } } -static void AssembleLoadExcl(Assembler* assembler, MemoryAccess access, - Register value_reg, Register addr_reg) { +void AssembleLoadExcl(Assembler* assembler, MemoryAccess access, + Register value_reg, Register addr_reg) { DCHECK(access.kind == MemoryAccess::Kind::LoadExcl); AssembleMemoryAccess(assembler, access, no_reg, value_reg, addr_reg); } -static void AssembleStoreExcl(Assembler* assembler, MemoryAccess access, - Register dest_reg, Register value_reg, - Register addr_reg) { +void AssembleStoreExcl(Assembler* assembler, MemoryAccess access, + Register dest_reg, Register value_reg, + Register addr_reg) { DCHECK(access.kind == MemoryAccess::Kind::StoreExcl); AssembleMemoryAccess(assembler, access, dest_reg, value_reg, addr_reg); } -static void TestInvalidateExclusiveAccess( - TestData initial_data, MemoryAccess access1, MemoryAccess access2, - MemoryAccess access3, int expected_res, TestData expected_data) { +F3 AssembleCode(std::function assemble) { Isolate* isolate = CcTest::i_isolate(); - HandleScope scope(isolate); - Assembler assm(isolate, nullptr, 0); - AssembleLoadExcl(&assm, access1, r1, r1); - AssembleMemoryAccess(&assm, access2, r3, r2, r1); - AssembleStoreExcl(&assm, access3, r0, r3, r1); + assemble(assm); - __ mov(pc, Operand(lr)); + __ bx(lr); CodeDesc desc; assm.GetCode(isolate, &desc); Handle code = isolate->factory()->NewCode(desc, Code::STUB, Handle()); F3 f = FUNCTION_CAST(code->entry()); + return f; +} + +void TestInvalidateExclusiveAccess(TestData initial_data, MemoryAccess access1, + MemoryAccess access2, MemoryAccess access3, + int expected_res, TestData expected_data) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + F3 f = AssembleCode([&](Assembler& assm) { + AssembleLoadExcl(&assm, access1, r1, r1); + AssembleMemoryAccess(&assm, access2, r3, r2, r1); + AssembleStoreExcl(&assm, access3, r0, r3, r1); + }); + TestData t = initial_data; int res = @@ -219,6 +231,26 @@ static void TestInvalidateExclusiveAccess( } } +std::vector Float32Inputs() { + std::vector inputs; + FOR_FLOAT32_INPUTS(f) { + inputs.push_back(Float32::FromBits(bit_cast(*f))); + } + FOR_UINT32_INPUTS(bits) { inputs.push_back(Float32::FromBits(*bits)); } + return inputs; +} + +std::vector Float64Inputs() { + std::vector inputs; + FOR_FLOAT64_INPUTS(f) { + inputs.push_back(Float64::FromBits(bit_cast(*f))); + } + FOR_UINT64_INPUTS(bits) { inputs.push_back(Float64::FromBits(*bits)); } + return inputs; +} + +} // namespace + TEST(simulator_invalidate_exclusive_access) { using Kind = MemoryAccess::Kind; using Size = MemoryAccess::Size; @@ -258,15 +290,9 @@ TEST(simulator_invalidate_exclusive_access) { static int ExecuteMemoryAccess(Isolate* isolate, TestData* test_data, MemoryAccess access) { HandleScope scope(isolate); - Assembler assm(isolate, nullptr, 0); - AssembleMemoryAccess(&assm, access, r0, r2, r1); - __ bx(lr); - - CodeDesc desc; - assm.GetCode(isolate, &desc); - Handle code = - isolate->factory()->NewCode(desc, Code::STUB, Handle()); - F3 f = FUNCTION_CAST(code->entry()); + F3 f = AssembleCode([&](Assembler& assm) { + AssembleMemoryAccess(&assm, access, r0, r2, r1); + }); return reinterpret_cast( CALL_GENERATED_CODE(isolate, f, test_data, 0, 0, 0, 0)); @@ -387,6 +413,84 @@ TEST(simulator_invalidate_exclusive_access_threaded) { thread.Join(); } +TEST(simulator_vabs_32) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + F3 f = AssembleCode([](Assembler& assm) { + __ vmov(s0, r0); + __ vabs(s0, s0); + __ vmov(r0, s0); + }); + + for (Float32 f32 : Float32Inputs()) { + Float32 res = Float32::FromBits(reinterpret_cast( + CALL_GENERATED_CODE(isolate, f, f32.get_bits(), 0, 0, 0, 0))); + Float32 exp = Float32::FromBits(f32.get_bits() & ~(1 << 31)); + CHECK_EQ(exp.get_bits(), res.get_bits()); + } +} + +TEST(simulator_vabs_64) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + F3 f = AssembleCode([](Assembler& assm) { + __ vmov(d0, r0, r1); + __ vabs(d0, d0); + __ vmov(r1, r0, d0); + }); + + for (Float64 f64 : Float64Inputs()) { + uint32_t p0 = static_cast(f64.get_bits()); + uint32_t p1 = static_cast(f64.get_bits() >> 32); + uint32_t res = reinterpret_cast( + CALL_GENERATED_CODE(isolate, f, p0, p1, 0, 0, 0)); + Float64 exp = Float64::FromBits(f64.get_bits() & ~(1ull << 63)); + // We just get back the top word, so only compare that one. + CHECK_EQ(exp.get_bits() >> 32, res); + } +} + +TEST(simulator_vneg_32) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + F3 f = AssembleCode([](Assembler& assm) { + __ vmov(s0, r0); + __ vneg(s0, s0); + __ vmov(r0, s0); + }); + + for (Float32 f32 : Float32Inputs()) { + Float32 res = Float32::FromBits(reinterpret_cast( + CALL_GENERATED_CODE(isolate, f, f32.get_bits(), 0, 0, 0, 0))); + Float32 exp = Float32::FromBits(f32.get_bits() ^ (1 << 31)); + CHECK_EQ(exp.get_bits(), res.get_bits()); + } +} + +TEST(simulator_vneg_64) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + F3 f = AssembleCode([](Assembler& assm) { + __ vmov(d0, r0, r1); + __ vneg(d0, d0); + __ vmov(r1, r0, d0); + }); + + for (Float64 f64 : Float64Inputs()) { + uint32_t p0 = static_cast(f64.get_bits()); + uint32_t p1 = static_cast(f64.get_bits() >> 32); + uint32_t res = reinterpret_cast( + CALL_GENERATED_CODE(isolate, f, p0, p1, 0, 0, 0)); + Float64 exp = Float64::FromBits(f64.get_bits() ^ (1ull << 63)); + // We just get back the top word, so only compare that one. + CHECK_EQ(exp.get_bits() >> 32, res); + } +} + #undef __ #endif // USE_SIMULATOR