diff options
Diffstat (limited to 'deps/v8/test/cctest/test-assembler-loong64.cc')
-rw-r--r-- | deps/v8/test/cctest/test-assembler-loong64.cc | 5180 |
1 files changed, 5180 insertions, 0 deletions
diff --git a/deps/v8/test/cctest/test-assembler-loong64.cc b/deps/v8/test/cctest/test-assembler-loong64.cc new file mode 100644 index 0000000000..d9ad4d9015 --- /dev/null +++ b/deps/v8/test/cctest/test-assembler-loong64.cc @@ -0,0 +1,5180 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <iostream> + +#include "src/base/utils/random-number-generator.h" +#include "src/codegen/assembler-inl.h" +#include "src/codegen/macro-assembler.h" +#include "src/diagnostics/disassembler.h" +#include "src/execution/simulator.h" +#include "src/heap/factory.h" +#include "src/init/v8.h" +#include "test/cctest/cctest.h" + +namespace v8 { +namespace internal { + +// Define these function prototypes to match JSEntryFunction in execution.cc. +// TODO(LOONG64): Refine these signatures per test case. +using F1 = void*(int x, int p1, int p2, int p3, int p4); +using F2 = void*(int x, int y, int p2, int p3, int p4); +using F3 = void*(void* p, int p1, int p2, int p3, int p4); +using F4 = void*(int64_t x, int64_t y, int64_t p2, int64_t p3, int64_t p4); +using F5 = void*(void* p0, void* p1, int p2, int p3, int p4); + +#define __ assm. +// v0->a2, v1->a3 +TEST(LA0) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + // Addition. + __ addi_d(a2, a0, 0xC); + + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(0xAB0, 0, 0, 0, 0)); + CHECK_EQ(0xABCL, res); +} + +TEST(LA1) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + Label L, C; + + __ ori(a1, a0, 0); + __ ori(a2, zero_reg, 0); + __ b(&C); + + __ bind(&L); + __ add_d(a2, a2, a1); + __ addi_d(a1, a1, -1); + + __ bind(&C); + __ ori(a3, a1, 0); + + __ Branch(&L, ne, a3, Operand((int64_t)0)); + + __ or_(a0, a2, zero_reg); + __ or_(a1, a3, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F1>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(50, 0, 0, 0, 0)); + CHECK_EQ(1275L, res); +} + +TEST(LA2) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label exit, error; + + __ ori(a4, zero_reg, 0); // 00000000 + __ lu12i_w(a4, 0x12345); // 12345000 + __ ori(a4, a4, 0); // 12345000 + __ ori(a2, a4, 0xF0F); // 12345F0F + __ Branch(&error, ne, a2, Operand(0x12345F0F)); + + __ ori(a4, zero_reg, 0); + __ lu32i_d(a4, 0x12345); // 1 2345 0000 0000 + __ ori(a4, a4, 0xFFF); // 1 2345 0000 0FFF + __ addi_d(a2, a4, 1); + __ Branch(&error, ne, a2, Operand(0x1234500001000)); + + __ ori(a4, zero_reg, 0); + __ lu52i_d(a4, zero_reg, 0x123); // 1230 0000 0000 0000 + __ ori(a4, a4, 0xFFF); // 123F 0000 0000 0FFF + __ addi_d(a2, a4, 1); // 1230 0000 0000 1000 + __ Branch(&error, ne, a2, Operand(0x1230000000001000)); + + __ li(a2, 0x31415926); + __ b(&exit); + + __ bind(&error); + __ li(a2, 0x666); + + __ bind(&exit); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(0, 0, 0, 0, 0)); + + CHECK_EQ(0x31415926L, res); +} + +TEST(LA3) { + // Test 32bit calculate instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label exit, error; + + __ li(a4, 0x00000004); + __ li(a5, 0x00001234); + __ li(a6, 0x12345678); + __ li(a7, 0x7FFFFFFF); + __ li(t0, static_cast<int32_t>(0xFFFFFFFC)); + __ li(t1, static_cast<int32_t>(0xFFFFEDCC)); + __ li(t2, static_cast<int32_t>(0xEDCBA988)); + __ li(t3, static_cast<int32_t>(0x80000000)); + + __ ori(a2, zero_reg, 0); // 0x00000000 + __ add_w(a2, a4, a5); // 0x00001238 + __ sub_w(a2, a2, a4); // 0x00001234 + __ Branch(&error, ne, a2, Operand(0x00001234)); + __ ori(a3, zero_reg, 0); // 0x00000000 + __ add_w(a3, a7, a4); // 32bit addu result is sign-extended into 64bit reg. + __ Branch(&error, ne, a3, Operand(0xFFFFFFFF80000003)); + + __ sub_w(a3, t3, a4); // 0x7FFFFFFC + __ Branch(&error, ne, a3, Operand(0x7FFFFFFC)); + + __ ori(a2, zero_reg, 0); // 0x00000000 + __ ori(a3, zero_reg, 0); // 0x00000000 + __ addi_w(a2, zero_reg, 0x421); // 0x00007421 + __ addi_w(a2, a2, -0x1); // 0x00007420 + __ addi_w(a2, a2, -0x20); // 0x00007400 + __ Branch(&error, ne, a2, Operand(0x0000400)); + __ addi_w(a3, a7, 0x1); // 0x80000000 - result is sign-extended. + __ Branch(&error, ne, a3, Operand(0xFFFFFFFF80000000)); + + __ ori(a2, zero_reg, 0); // 0x00000000 + __ ori(a3, zero_reg, 0); // 0x00000000 + __ alsl_w(a2, a6, a4, 3); // 0xFFFFFFFF91A2B3C4 + __ alsl_w(a2, a2, a4, 2); // 0x468ACF14 + __ Branch(&error, ne, a2, Operand(0x468acf14)); + __ ori(a0, zero_reg, 31); + __ alsl_wu(a3, a6, a4, 3); // 0x91A2B3C4 + __ alsl_wu(a3, a3, a7, 1); // 0xFFFFFFFFA3456787 + __ Branch(&error, ne, a3, Operand(0xA3456787)); + + __ ori(a2, zero_reg, 0); + __ ori(a3, zero_reg, 0); + __ mul_w(a2, a5, a7); + __ div_w(a2, a2, a4); + __ Branch(&error, ne, a2, Operand(0xFFFFFFFFFFFFFB73)); + __ mul_w(a3, a4, t1); + __ Branch(&error, ne, a3, Operand(0xFFFFFFFFFFFFB730)); + __ div_w(a3, t3, a4); + __ Branch(&error, ne, a3, Operand(0xFFFFFFFFE0000000)); + + __ ori(a2, zero_reg, 0); + __ mulh_w(a2, a4, t1); + __ Branch(&error, ne, a2, Operand(0xFFFFFFFFFFFFFFFF)); + __ mulh_w(a2, a4, a6); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + + __ ori(a2, zero_reg, 0); + __ mulh_wu(a2, a4, t1); + __ Branch(&error, ne, a2, Operand(0x3)); + __ mulh_wu(a2, a4, a6); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + + __ ori(a2, zero_reg, 0); + __ mulw_d_w(a2, a4, t1); + __ Branch(&error, ne, a2, Operand(0xFFFFFFFFFFFFB730)); + __ mulw_d_w(a2, a4, a6); + __ Branch(&error, ne, a2, Operand(0x48D159E0)); + + __ ori(a2, zero_reg, 0); + __ mulw_d_wu(a2, a4, t1); + __ Branch(&error, ne, a2, Operand(0x3FFFFB730)); //========0xFFFFB730 + __ ori(a2, zero_reg, 81); + __ mulw_d_wu(a2, a4, a6); + __ Branch(&error, ne, a2, Operand(0x48D159E0)); + + __ ori(a2, zero_reg, 0); + __ div_wu(a2, a7, a5); + __ Branch(&error, ne, a2, Operand(0x70821)); + __ div_wu(a2, t0, a5); + __ Branch(&error, ne, a2, Operand(0xE1042)); + __ div_wu(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0x1)); + + __ ori(a2, zero_reg, 0); + __ mod_w(a2, a6, a5); + __ Branch(&error, ne, a2, Operand(0xDA8)); + __ ori(a2, zero_reg, 0); + __ mod_w(a2, t2, a5); + __ Branch(&error, ne, a2, Operand(0xFFFFFFFFFFFFF258)); + __ ori(a2, zero_reg, 0); + __ mod_w(a2, t2, t1); + __ Branch(&error, ne, a2, Operand(0xFFFFFFFFFFFFF258)); + + __ ori(a2, zero_reg, 0); + __ mod_wu(a2, a6, a5); + __ Branch(&error, ne, a2, Operand(0xDA8)); + __ mod_wu(a2, t2, a5); + __ Branch(&error, ne, a2, Operand(0xF0)); + __ mod_wu(a2, t2, t1); + __ Branch(&error, ne, a2, Operand(0xFFFFFFFFEDCBA988)); + + __ li(a2, 0x31415926); + __ b(&exit); + + __ bind(&error); + __ li(a2, 0x666); + + __ bind(&exit); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(0, 0, 0, 0, 0)); + + CHECK_EQ(0x31415926L, res); +} + +TEST(LA4) { + // Test 64bit calculate instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label exit, error; + + __ li(a4, 0x17312); + __ li(a5, 0x1012131415161718); + __ li(a6, 0x51F4B764A26E7412); + __ li(a7, 0x7FFFFFFFFFFFFFFF); + __ li(t0, static_cast<int64_t>(0xFFFFFFFFFFFFF547)); + __ li(t1, static_cast<int64_t>(0xDF6B8F35A10E205C)); + __ li(t2, static_cast<int64_t>(0x81F25A87C4236841)); + __ li(t3, static_cast<int64_t>(0x8000000000000000)); + + __ ori(a2, zero_reg, 0); + __ add_d(a2, a4, a5); + __ sub_d(a2, a2, a4); + __ Branch(&error, ne, a2, Operand(0x1012131415161718)); + __ ori(a3, zero_reg, 0); + __ add_d(a3, a6, a7); //溢出 + __ Branch(&error, ne, a3, Operand(0xd1f4b764a26e7411)); + __ sub_d(a3, t3, a4); //溢出 + __ Branch(&error, ne, a3, Operand(0x7ffffffffffe8cee)); + + __ ori(a2, zero_reg, 0); + __ addi_d(a2, a5, 0x412); //正值 + __ Branch(&error, ne, a2, Operand(0x1012131415161b2a)); + __ addi_d(a2, a7, 0x547); //负值 + __ Branch(&error, ne, a2, Operand(0x8000000000000546)); + + __ ori(t4, zero_reg, 0); + __ addu16i_d(a2, t4, 0x1234); + __ Branch(&error, ne, a2, Operand(0x12340000)); + __ addu16i_d(a2, a2, 0x9876); + __ Branch(&error, ne, a2, Operand(0xffffffffaaaa0000)); + + __ ori(a2, zero_reg, 0); + __ alsl_d(a2, t2, t0, 3); + __ Branch(&error, ne, a2, Operand(0xf92d43e211b374f)); + + __ ori(a2, zero_reg, 0); + __ mul_d(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(0xdbe6a8729a547fb0)); + __ mul_d(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0x57ad69f40f870584)); + __ mul_d(a2, a4, t0); + __ Branch(&error, ne, a2, Operand(0xfffffffff07523fe)); + + __ ori(a2, zero_reg, 0); + __ mulh_d(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(0x52514c6c6b54467)); + __ mulh_d(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0x15d)); + + __ ori(a2, zero_reg, 0); + __ mulh_du(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(0x52514c6c6b54467)); + __ mulh_du(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0xdf6b8f35a10e1700)); + __ mulh_du(a2, a4, t0); + __ Branch(&error, ne, a2, Operand(0x17311)); + + __ ori(a2, zero_reg, 0); + __ div_d(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + __ div_d(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + __ div_d(a2, t1, a4); + __ Branch(&error, ne, a2, Operand(0xffffe985f631e6d9)); + + __ ori(a2, zero_reg, 0); + __ div_du(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + __ div_du(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0x1)); + __ div_du(a2, t1, a4); + __ Branch(&error, ne, a2, Operand(0x9a22ffd3973d)); + + __ ori(a2, zero_reg, 0); + __ mod_d(a2, a6, a4); + __ Branch(&error, ne, a2, Operand(0x13558)); + __ mod_d(a2, t2, t0); + __ Branch(&error, ne, a2, Operand(0xfffffffffffffb0a)); + __ mod_d(a2, t1, a4); + __ Branch(&error, ne, a2, Operand(0xffffffffffff6a1a)); + + __ ori(a2, zero_reg, 0); + __ mod_du(a2, a6, a4); + __ Branch(&error, ne, a2, Operand(0x13558)); + __ mod_du(a2, t2, t0); + __ Branch(&error, ne, a2, Operand(0x81f25a87c4236841)); + __ mod_du(a2, t1, a4); + __ Branch(&error, ne, a2, Operand(0x1712)); + + // Everything was correctly executed. Load the expected result. + __ li(a2, 0x31415926); + __ b(&exit); + + __ bind(&error); + __ li(a2, 0x666); + // Got an error. Return a wrong result. + + __ bind(&exit); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(0, 0, 0, 0, 0)); + + CHECK_EQ(0x31415926L, res); +} + +TEST(LA5) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label exit, error; + + __ li(a4, 0x17312); + __ li(a5, 0x1012131415161718); + __ li(a6, 0x51F4B764A26E7412); + __ li(a7, 0x7FFFFFFFFFFFFFFF); + __ li(t0, static_cast<int64_t>(0xFFFFFFFFFFFFF547)); + __ li(t1, static_cast<int64_t>(0xDF6B8F35A10E205C)); + __ li(t2, static_cast<int64_t>(0x81F25A87C4236841)); + __ li(t3, static_cast<int64_t>(0x8000000000000000)); + + __ ori(a2, zero_reg, 0); + __ slt(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(0x1)); + __ slt(a2, a7, t0); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + __ slt(a2, t1, t1); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + + __ ori(a2, zero_reg, 0); + __ sltu(a2, a5, a6); + __ Branch(&error, ne, a2, Operand(0x1)); + __ sltu(a2, a7, t0); + __ Branch(&error, ne, a2, Operand(0x1)); + __ sltu(a2, t1, t1); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + + __ ori(a2, zero_reg, 0); + __ slti(a2, a5, 0x123); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + __ slti(a2, t0, 0x123); + __ Branch(&error, ne, a2, Operand(0x1)); + + __ ori(a2, zero_reg, 0); + __ sltui(a2, a5, 0x123); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + __ sltui(a2, t0, 0x123); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + + __ ori(a2, zero_reg, 0); + __ and_(a2, a4, a5); + __ Branch(&error, ne, a2, Operand(0x1310)); + __ and_(a2, a6, a7); + __ Branch(&error, ne, a2, Operand(0x51F4B764A26E7412)); + + __ ori(a2, zero_reg, 0); + __ or_(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0xfffffffffffff55f)); + __ or_(a2, t2, t3); + __ Branch(&error, ne, a2, Operand(0x81f25a87c4236841)); + + __ ori(a2, zero_reg, 0); + __ nor(a2, a4, a5); + __ Branch(&error, ne, a2, Operand(0xefedecebeae888e5)); + __ nor(a2, a6, a7); + __ Branch(&error, ne, a2, Operand(0x8000000000000000)); + + __ ori(a2, zero_reg, 0); + __ xor_(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0x209470ca5ef1d51b)); + __ xor_(a2, t2, t3); + __ Branch(&error, ne, a2, Operand(0x1f25a87c4236841)); + + __ ori(a2, zero_reg, 0); + __ andn(a2, a4, a5); + __ Branch(&error, ne, a2, Operand(0x16002)); + __ andn(a2, a6, a7); + __ Branch(&error, ne, a2, Operand(static_cast<int64_t>(0))); + + __ ori(a2, zero_reg, 0); + __ orn(a2, t0, t1); + __ Branch(&error, ne, a2, Operand(0xffffffffffffffe7)); + __ orn(a2, t2, t3); + __ Branch(&error, ne, a2, Operand(0xffffffffffffffff)); + + __ ori(a2, zero_reg, 0); + __ andi(a2, a4, 0x123); + __ Branch(&error, ne, a2, Operand(0x102)); + __ andi(a2, a6, 0xDCB); + __ Branch(&error, ne, a2, Operand(0x402)); + + __ ori(a2, zero_reg, 0); + __ xori(a2, t0, 0x123); + __ Branch(&error, ne, a2, Operand(0xfffffffffffff464)); + __ xori(a2, t2, 0xDCB); + __ Branch(&error, ne, a2, Operand(0x81f25a87c423658a)); + + // Everything was correctly executed. Load the expected result. + __ li(a2, 0x31415926); + __ b(&exit); + + __ bind(&error); + // Got an error. Return a wrong result. + __ li(a2, 0x666); + + __ bind(&exit); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(0, 0, 0, 0, 0)); + + CHECK_EQ(0x31415926L, res); +} + +TEST(LA6) { + // Test loads and stores instruction. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct T { + int64_t si1; + int64_t si2; + int64_t si3; + int64_t result_ld_b_si1; + int64_t result_ld_b_si2; + int64_t result_ld_h_si1; + int64_t result_ld_h_si2; + int64_t result_ld_w_si1; + int64_t result_ld_w_si2; + int64_t result_ld_d_si1; + int64_t result_ld_d_si3; + int64_t result_ld_bu_si2; + int64_t result_ld_hu_si2; + int64_t result_ld_wu_si2; + int64_t result_st_b; + int64_t result_st_h; + int64_t result_st_w; + }; + T t; + + // Ld_b + __ Ld_b(a4, MemOperand(a0, offsetof(T, si1))); + __ St_d(a4, MemOperand(a0, offsetof(T, result_ld_b_si1))); + + __ Ld_b(a4, MemOperand(a0, offsetof(T, si2))); + __ St_d(a4, MemOperand(a0, offsetof(T, result_ld_b_si2))); + + // Ld_h + __ Ld_h(a5, MemOperand(a0, offsetof(T, si1))); + __ St_d(a5, MemOperand(a0, offsetof(T, result_ld_h_si1))); + + __ Ld_h(a5, MemOperand(a0, offsetof(T, si2))); + __ St_d(a5, MemOperand(a0, offsetof(T, result_ld_h_si2))); + + // Ld_w + __ Ld_w(a6, MemOperand(a0, offsetof(T, si1))); + __ St_d(a6, MemOperand(a0, offsetof(T, result_ld_w_si1))); + + __ Ld_w(a6, MemOperand(a0, offsetof(T, si2))); + __ St_d(a6, MemOperand(a0, offsetof(T, result_ld_w_si2))); + + // Ld_d + __ Ld_d(a7, MemOperand(a0, offsetof(T, si1))); + __ St_d(a7, MemOperand(a0, offsetof(T, result_ld_d_si1))); + + __ Ld_d(a7, MemOperand(a0, offsetof(T, si3))); + __ St_d(a7, MemOperand(a0, offsetof(T, result_ld_d_si3))); + + // Ld_bu + __ Ld_bu(t0, MemOperand(a0, offsetof(T, si2))); + __ St_d(t0, MemOperand(a0, offsetof(T, result_ld_bu_si2))); + + // Ld_hu + __ Ld_hu(t1, MemOperand(a0, offsetof(T, si2))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_ld_hu_si2))); + + // Ld_wu + __ Ld_wu(t2, MemOperand(a0, offsetof(T, si2))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_ld_wu_si2))); + + // St + __ li(t4, 0x11111111); + + // St_b + __ Ld_d(t5, MemOperand(a0, offsetof(T, si3))); + __ St_d(t5, MemOperand(a0, offsetof(T, result_st_b))); + __ St_b(t4, MemOperand(a0, offsetof(T, result_st_b))); + + // St_h + __ Ld_d(t6, MemOperand(a0, offsetof(T, si3))); + __ St_d(t6, MemOperand(a0, offsetof(T, result_st_h))); + __ St_h(t4, MemOperand(a0, offsetof(T, result_st_h))); + + // St_w + __ Ld_d(t7, MemOperand(a0, offsetof(T, si3))); + __ St_d(t7, MemOperand(a0, offsetof(T, result_st_w))); + __ St_w(t4, MemOperand(a0, offsetof(T, result_st_w))); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + t.si1 = 0x11223344; + t.si2 = 0x99AABBCC; + t.si3 = 0x1122334455667788; + f.Call(&t, 0, 0, 0, 0); + + CHECK_EQ(static_cast<int64_t>(0x44), t.result_ld_b_si1); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFFFFFFFCC), t.result_ld_b_si2); + + CHECK_EQ(static_cast<int64_t>(0x3344), t.result_ld_h_si1); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFFFFFBBCC), t.result_ld_h_si2); + + CHECK_EQ(static_cast<int64_t>(0x11223344), t.result_ld_w_si1); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFF99AABBCC), t.result_ld_w_si2); + + CHECK_EQ(static_cast<int64_t>(0x11223344), t.result_ld_d_si1); + CHECK_EQ(static_cast<int64_t>(0x1122334455667788), t.result_ld_d_si3); + + CHECK_EQ(static_cast<int64_t>(0xCC), t.result_ld_bu_si2); + CHECK_EQ(static_cast<int64_t>(0xBBCC), t.result_ld_hu_si2); + CHECK_EQ(static_cast<int64_t>(0x99AABBCC), t.result_ld_wu_si2); + + CHECK_EQ(static_cast<int64_t>(0x1122334455667711), t.result_st_b); + CHECK_EQ(static_cast<int64_t>(0x1122334455661111), t.result_st_h); + CHECK_EQ(static_cast<int64_t>(0x1122334411111111), t.result_st_w); +} + +TEST(LA7) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct T { + int64_t si1; + int64_t si2; + int64_t si3; + int64_t result_ldx_b_si1; + int64_t result_ldx_b_si2; + int64_t result_ldx_h_si1; + int64_t result_ldx_h_si2; + int64_t result_ldx_w_si1; + int64_t result_ldx_w_si2; + int64_t result_ldx_d_si1; + int64_t result_ldx_d_si3; + int64_t result_ldx_bu_si2; + int64_t result_ldx_hu_si2; + int64_t result_ldx_wu_si2; + int64_t result_stx_b; + int64_t result_stx_h; + int64_t result_stx_w; + }; + T t; + + // ldx_b + __ li(a2, static_cast<int64_t>(offsetof(T, si1))); + __ Ld_b(a4, MemOperand(a0, a2)); + __ St_d(a4, MemOperand(a0, offsetof(T, result_ldx_b_si1))); + + __ li(a2, static_cast<int64_t>(offsetof(T, si2))); + __ Ld_b(a4, MemOperand(a0, a2)); + __ St_d(a4, MemOperand(a0, offsetof(T, result_ldx_b_si2))); + + // ldx_h + __ li(a2, static_cast<int64_t>(offsetof(T, si1))); + __ Ld_h(a5, MemOperand(a0, a2)); + __ St_d(a5, MemOperand(a0, offsetof(T, result_ldx_h_si1))); + + __ li(a2, static_cast<int64_t>(offsetof(T, si2))); + __ Ld_h(a5, MemOperand(a0, a2)); + __ St_d(a5, MemOperand(a0, offsetof(T, result_ldx_h_si2))); + + // ldx_w + __ li(a2, static_cast<int64_t>(offsetof(T, si1))); + __ Ld_w(a6, MemOperand(a0, a2)); + __ St_d(a6, MemOperand(a0, offsetof(T, result_ldx_w_si1))); + + __ li(a2, static_cast<int64_t>(offsetof(T, si2))); + __ Ld_w(a6, MemOperand(a0, a2)); + __ St_d(a6, MemOperand(a0, offsetof(T, result_ldx_w_si2))); + + // Ld_d + __ li(a2, static_cast<int64_t>(offsetof(T, si1))); + __ Ld_d(a7, MemOperand(a0, a2)); + __ St_d(a7, MemOperand(a0, offsetof(T, result_ldx_d_si1))); + + __ li(a2, static_cast<int64_t>(offsetof(T, si3))); + __ Ld_d(a7, MemOperand(a0, a2)); + __ St_d(a7, MemOperand(a0, offsetof(T, result_ldx_d_si3))); + + // Ld_bu + __ li(a2, static_cast<int64_t>(offsetof(T, si2))); + __ Ld_bu(t0, MemOperand(a0, a2)); + __ St_d(t0, MemOperand(a0, offsetof(T, result_ldx_bu_si2))); + + // Ld_hu + __ li(a2, static_cast<int64_t>(offsetof(T, si2))); + __ Ld_hu(t1, MemOperand(a0, a2)); + __ St_d(t1, MemOperand(a0, offsetof(T, result_ldx_hu_si2))); + + // Ld_wu + __ li(a2, static_cast<int64_t>(offsetof(T, si2))); + __ Ld_wu(t2, MemOperand(a0, a2)); + __ St_d(t2, MemOperand(a0, offsetof(T, result_ldx_wu_si2))); + + // St + __ li(t4, 0x11111111); + + // St_b + __ Ld_d(t5, MemOperand(a0, offsetof(T, si3))); + __ St_d(t5, MemOperand(a0, offsetof(T, result_stx_b))); + __ li(a2, static_cast<int64_t>(offsetof(T, result_stx_b))); + __ St_b(t4, MemOperand(a0, a2)); + + // St_h + __ Ld_d(t6, MemOperand(a0, offsetof(T, si3))); + __ St_d(t6, MemOperand(a0, offsetof(T, result_stx_h))); + __ li(a2, static_cast<int64_t>(offsetof(T, result_stx_h))); + __ St_h(t4, MemOperand(a0, a2)); + + // St_w + __ Ld_d(t7, MemOperand(a0, offsetof(T, si3))); + __ li(a2, static_cast<int64_t>(offsetof(T, result_stx_w))); + __ St_d(t7, MemOperand(a0, a2)); + __ li(a3, static_cast<int64_t>(offsetof(T, result_stx_w))); + __ St_w(t4, MemOperand(a0, a3)); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + t.si1 = 0x11223344; + t.si2 = 0x99AABBCC; + t.si3 = 0x1122334455667788; + f.Call(&t, 0, 0, 0, 0); + + CHECK_EQ(static_cast<int64_t>(0x44), t.result_ldx_b_si1); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFFFFFFFCC), t.result_ldx_b_si2); + + CHECK_EQ(static_cast<int64_t>(0x3344), t.result_ldx_h_si1); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFFFFFBBCC), t.result_ldx_h_si2); + + CHECK_EQ(static_cast<int64_t>(0x11223344), t.result_ldx_w_si1); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFF99AABBCC), t.result_ldx_w_si2); + + CHECK_EQ(static_cast<int64_t>(0x11223344), t.result_ldx_d_si1); + CHECK_EQ(static_cast<int64_t>(0x1122334455667788), t.result_ldx_d_si3); + + CHECK_EQ(static_cast<int64_t>(0xCC), t.result_ldx_bu_si2); + CHECK_EQ(static_cast<int64_t>(0xBBCC), t.result_ldx_hu_si2); + CHECK_EQ(static_cast<int64_t>(0x99AABBCC), t.result_ldx_wu_si2); + + CHECK_EQ(static_cast<int64_t>(0x1122334455667711), t.result_stx_b); + CHECK_EQ(static_cast<int64_t>(0x1122334455661111), t.result_stx_h); + CHECK_EQ(static_cast<int64_t>(0x1122334411111111), t.result_stx_w); +} + +TEST(LDPTR_STPTR) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + int64_t test[10]; + + __ ldptr_w(a4, a0, 0); + __ stptr_d(a4, a0, 24); // test[3] + + __ ldptr_w(a5, a0, 8); // test[1] + __ stptr_d(a5, a0, 32); // test[4] + + __ ldptr_d(a6, a0, 16); // test[2] + __ stptr_d(a6, a0, 40); // test[5] + + __ li(t0, 0x11111111); + + __ stptr_d(a6, a0, 48); // test[6] + __ stptr_w(t0, a0, 48); // test[6] + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + test[0] = 0x11223344; + test[1] = 0x99AABBCC; + test[2] = 0x1122334455667788; + f.Call(&test, 0, 0, 0, 0); + + CHECK_EQ(static_cast<int64_t>(0x11223344), test[3]); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFF99AABBCC), test[4]); + CHECK_EQ(static_cast<int64_t>(0x1122334455667788), test[5]); + CHECK_EQ(static_cast<int64_t>(0x1122334411111111), test[6]); +} + +TEST(LA8) { + // Test 32bit shift instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + struct T { + int32_t input; + int32_t result_sll_w_0; + int32_t result_sll_w_8; + int32_t result_sll_w_10; + int32_t result_sll_w_31; + int32_t result_srl_w_0; + int32_t result_srl_w_8; + int32_t result_srl_w_10; + int32_t result_srl_w_31; + int32_t result_sra_w_0; + int32_t result_sra_w_8; + int32_t result_sra_w_10; + int32_t result_sra_w_31; + int32_t result_rotr_w_0; + int32_t result_rotr_w_8; + int32_t result_slli_w_0; + int32_t result_slli_w_8; + int32_t result_slli_w_10; + int32_t result_slli_w_31; + int32_t result_srli_w_0; + int32_t result_srli_w_8; + int32_t result_srli_w_10; + int32_t result_srli_w_31; + int32_t result_srai_w_0; + int32_t result_srai_w_8; + int32_t result_srai_w_10; + int32_t result_srai_w_31; + int32_t result_rotri_w_0; + int32_t result_rotri_w_8; + int32_t result_rotri_w_10; + int32_t result_rotri_w_31; + }; + T t; + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + __ Ld_w(a4, MemOperand(a0, offsetof(T, input))); + + // sll_w + __ li(a5, 0); + __ sll_w(t0, a4, a5); + __ li(a5, 0x8); + __ sll_w(t1, a4, a5); + __ li(a5, 0xA); + __ sll_w(t2, a4, a5); + __ li(a5, 0x1F); + __ sll_w(t3, a4, a5); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_sll_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_sll_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_sll_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_sll_w_31))); + + // srl_w + __ li(a5, 0x0); + __ srl_w(t0, a4, a5); + __ li(a5, 0x8); + __ srl_w(t1, a4, a5); + __ li(a5, 0xA); + __ srl_w(t2, a4, a5); + __ li(a5, 0x1F); + __ srl_w(t3, a4, a5); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_srl_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_srl_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_srl_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_srl_w_31))); + + // sra_w + __ li(a5, 0x0); + __ sra_w(t0, a4, a5); + __ li(a5, 0x8); + __ sra_w(t1, a4, a5); + + __ li(a6, static_cast<int32_t>(0x80000000)); + __ add_w(a6, a6, a4); + __ li(a5, 0xA); + __ sra_w(t2, a6, a5); + __ li(a5, 0x1F); + __ sra_w(t3, a6, a5); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_sra_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_sra_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_sra_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_sra_w_31))); + + // rotr + __ li(a5, 0x0); + __ rotr_w(t0, a4, a5); + __ li(a6, 0x8); + __ rotr_w(t1, a4, a6); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_rotr_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_rotr_w_8))); + + // slli_w + __ slli_w(t0, a4, 0); + __ slli_w(t1, a4, 0x8); + __ slli_w(t2, a4, 0xA); + __ slli_w(t3, a4, 0x1F); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_slli_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_slli_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_slli_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_slli_w_31))); + + // srli_w + __ srli_w(t0, a4, 0); + __ srli_w(t1, a4, 0x8); + __ srli_w(t2, a4, 0xA); + __ srli_w(t3, a4, 0x1F); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_srli_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_srli_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_srli_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_srli_w_31))); + + // srai_w + __ srai_w(t0, a4, 0); + __ srai_w(t1, a4, 0x8); + + __ li(a6, static_cast<int32_t>(0x80000000)); + __ add_w(a6, a6, a4); + __ srai_w(t2, a6, 0xA); + __ srai_w(t3, a6, 0x1F); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_srai_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_srai_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_srai_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_srai_w_31))); + + // rotri_w + __ rotri_w(t0, a4, 0); + __ rotri_w(t1, a4, 0x8); + __ rotri_w(t2, a4, 0xA); + __ rotri_w(t3, a4, 0x1F); + + __ St_w(t0, MemOperand(a0, offsetof(T, result_rotri_w_0))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_rotri_w_8))); + __ St_w(t2, MemOperand(a0, offsetof(T, result_rotri_w_10))); + __ St_w(t3, MemOperand(a0, offsetof(T, result_rotri_w_31))); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + t.input = 0x12345678; + f.Call(&t, 0x0, 0, 0, 0); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_sll_w_0); + CHECK_EQ(static_cast<int32_t>(0x34567800), t.result_sll_w_8); + CHECK_EQ(static_cast<int32_t>(0xD159E000), t.result_sll_w_10); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_sll_w_31); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_srl_w_0); + CHECK_EQ(static_cast<int32_t>(0x123456), t.result_srl_w_8); + CHECK_EQ(static_cast<int32_t>(0x48D15), t.result_srl_w_10); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_srl_w_31); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_sra_w_0); + CHECK_EQ(static_cast<int32_t>(0x123456), t.result_sra_w_8); + CHECK_EQ(static_cast<int32_t>(0xFFE48D15), t.result_sra_w_10); + CHECK_EQ(static_cast<int32_t>(0xFFFFFFFF), t.result_sra_w_31); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_rotr_w_0); + CHECK_EQ(static_cast<int32_t>(0x78123456), t.result_rotr_w_8); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_slli_w_0); + CHECK_EQ(static_cast<int32_t>(0x34567800), t.result_slli_w_8); + CHECK_EQ(static_cast<int32_t>(0xD159E000), t.result_slli_w_10); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_slli_w_31); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_srli_w_0); + CHECK_EQ(static_cast<int32_t>(0x123456), t.result_srli_w_8); + CHECK_EQ(static_cast<int32_t>(0x48D15), t.result_srli_w_10); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_srli_w_31); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_srai_w_0); + CHECK_EQ(static_cast<int32_t>(0x123456), t.result_srai_w_8); + CHECK_EQ(static_cast<int32_t>(0xFFE48D15), t.result_srai_w_10); + CHECK_EQ(static_cast<int32_t>(0xFFFFFFFF), t.result_srai_w_31); + + CHECK_EQ(static_cast<int32_t>(0x12345678), t.result_rotri_w_0); + CHECK_EQ(static_cast<int32_t>(0x78123456), t.result_rotri_w_8); + CHECK_EQ(static_cast<int32_t>(0x9E048D15), t.result_rotri_w_10); + CHECK_EQ(static_cast<int32_t>(0x2468ACF0), t.result_rotri_w_31); +} + +TEST(LA9) { + // Test 64bit shift instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + struct T { + int64_t input; + int64_t result_sll_d_0; + int64_t result_sll_d_13; + int64_t result_sll_d_30; + int64_t result_sll_d_63; + int64_t result_srl_d_0; + int64_t result_srl_d_13; + int64_t result_srl_d_30; + int64_t result_srl_d_63; + int64_t result_sra_d_0; + int64_t result_sra_d_13; + int64_t result_sra_d_30; + int64_t result_sra_d_63; + int64_t result_rotr_d_0; + int64_t result_rotr_d_13; + int64_t result_slli_d_0; + int64_t result_slli_d_13; + int64_t result_slli_d_30; + int64_t result_slli_d_63; + int64_t result_srli_d_0; + int64_t result_srli_d_13; + int64_t result_srli_d_30; + int64_t result_srli_d_63; + int64_t result_srai_d_0; + int64_t result_srai_d_13; + int64_t result_srai_d_30; + int64_t result_srai_d_63; + int64_t result_rotri_d_0; + int64_t result_rotri_d_13; + int64_t result_rotri_d_30; + int64_t result_rotri_d_63; + }; + + T t; + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + __ Ld_d(a4, MemOperand(a0, offsetof(T, input))); + + // sll_d + __ li(a5, 0); + __ sll_d(t0, a4, a5); + __ li(a5, 0xD); + __ sll_d(t1, a4, a5); + __ li(a5, 0x1E); + __ sll_d(t2, a4, a5); + __ li(a5, 0x3F); + __ sll_d(t3, a4, a5); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_sll_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_sll_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_sll_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_sll_d_63))); + + // srl_d + __ li(a5, 0x0); + __ srl_d(t0, a4, a5); + __ li(a5, 0xD); + __ srl_d(t1, a4, a5); + __ li(a5, 0x1E); + __ srl_d(t2, a4, a5); + __ li(a5, 0x3F); + __ srl_d(t3, a4, a5); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_srl_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_srl_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_srl_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_srl_d_63))); + + // sra_d + __ li(a5, 0x0); + __ sra_d(t0, a4, a5); + __ li(a5, 0xD); + __ sra_d(t1, a4, a5); + + __ li(a6, static_cast<int64_t>(0x8000000000000000)); + __ add_d(a6, a6, a4); + __ li(a5, 0x1E); + __ sra_d(t2, a6, a5); + __ li(a5, 0x3F); + __ sra_d(t3, a6, a5); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_sra_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_sra_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_sra_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_sra_d_63))); + + // rotr + __ li(a5, 0x0); + __ rotr_d(t0, a4, a5); + __ li(a6, 0xD); + __ rotr_d(t1, a4, a6); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_rotr_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_rotr_d_13))); + + // slli_d + __ slli_d(t0, a4, 0); + __ slli_d(t1, a4, 0xD); + __ slli_d(t2, a4, 0x1E); + __ slli_d(t3, a4, 0x3F); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_slli_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_slli_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_slli_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_slli_d_63))); + + // srli_d + __ srli_d(t0, a4, 0); + __ srli_d(t1, a4, 0xD); + __ srli_d(t2, a4, 0x1E); + __ srli_d(t3, a4, 0x3F); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_srli_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_srli_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_srli_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_srli_d_63))); + + // srai_d + __ srai_d(t0, a4, 0); + __ srai_d(t1, a4, 0xD); + + __ li(a6, static_cast<int64_t>(0x8000000000000000)); + __ add_d(a6, a6, a4); + __ srai_d(t2, a6, 0x1E); + __ srai_d(t3, a6, 0x3F); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_srai_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_srai_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_srai_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_srai_d_63))); + + // rotri_d + __ rotri_d(t0, a4, 0); + __ rotri_d(t1, a4, 0xD); + __ rotri_d(t2, a4, 0x1E); + __ rotri_d(t3, a4, 0x3F); + + __ St_d(t0, MemOperand(a0, offsetof(T, result_rotri_d_0))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_rotri_d_13))); + __ St_d(t2, MemOperand(a0, offsetof(T, result_rotri_d_30))); + __ St_d(t3, MemOperand(a0, offsetof(T, result_rotri_d_63))); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + t.input = 0x51F4B764A26E7412; + f.Call(&t, 0, 0, 0, 0); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_sll_d_0); + CHECK_EQ(static_cast<int64_t>(0x96ec944dce824000), t.result_sll_d_13); + CHECK_EQ(static_cast<int64_t>(0x289b9d0480000000), t.result_sll_d_30); + CHECK_EQ(static_cast<int64_t>(0x0), t.result_sll_d_63); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_srl_d_0); + CHECK_EQ(static_cast<int64_t>(0x28fa5bb251373), t.result_srl_d_13); + CHECK_EQ(static_cast<int64_t>(0x147d2dd92), t.result_srl_d_30); + CHECK_EQ(static_cast<int64_t>(0x0), t.result_srl_d_63); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_sra_d_0); + CHECK_EQ(static_cast<int64_t>(0x28fa5bb251373), t.result_sra_d_13); + CHECK_EQ(static_cast<int64_t>(0xffffffff47d2dd92), t.result_sra_d_30); + CHECK_EQ(static_cast<int64_t>(0xffffffffffffffff), t.result_sra_d_63); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_rotr_d_0); + CHECK_EQ(static_cast<int64_t>(0xa0928fa5bb251373), t.result_rotr_d_13); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_slli_d_0); + CHECK_EQ(static_cast<int64_t>(0x96ec944dce824000), t.result_slli_d_13); + CHECK_EQ(static_cast<int64_t>(0x289b9d0480000000), t.result_slli_d_30); + CHECK_EQ(static_cast<int64_t>(0x0), t.result_slli_d_63); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_srli_d_0); + CHECK_EQ(static_cast<int64_t>(0x28fa5bb251373), t.result_srli_d_13); + CHECK_EQ(static_cast<int64_t>(0x147d2dd92), t.result_srli_d_30); + CHECK_EQ(static_cast<int64_t>(0x0), t.result_srli_d_63); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_srai_d_0); + CHECK_EQ(static_cast<int64_t>(0x28fa5bb251373), t.result_srai_d_13); + CHECK_EQ(static_cast<int64_t>(0xffffffff47d2dd92), t.result_srai_d_30); + CHECK_EQ(static_cast<int64_t>(0xffffffffffffffff), t.result_srai_d_63); + + CHECK_EQ(static_cast<int64_t>(0x51f4b764a26e7412), t.result_rotri_d_0); + CHECK_EQ(static_cast<int64_t>(0xa0928fa5bb251373), t.result_rotri_d_13); + CHECK_EQ(static_cast<int64_t>(0x89b9d04947d2dd92), t.result_rotri_d_30); + CHECK_EQ(static_cast<int64_t>(0xa3e96ec944dce824), t.result_rotri_d_63); +} + +TEST(LA10) { + // Test 32bit bit operation instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct T { + int64_t si1; + int64_t si2; + int32_t result_ext_w_b_si1; + int32_t result_ext_w_b_si2; + int32_t result_ext_w_h_si1; + int32_t result_ext_w_h_si2; + int32_t result_clo_w_si1; + int32_t result_clo_w_si2; + int32_t result_clz_w_si1; + int32_t result_clz_w_si2; + int32_t result_cto_w_si1; + int32_t result_cto_w_si2; + int32_t result_ctz_w_si1; + int32_t result_ctz_w_si2; + int32_t result_bytepick_w_si1; + int32_t result_bytepick_w_si2; + int32_t result_revb_2h_si1; + int32_t result_revb_2h_si2; + int32_t result_bitrev_4b_si1; + int32_t result_bitrev_4b_si2; + int32_t result_bitrev_w_si1; + int32_t result_bitrev_w_si2; + int32_t result_bstrins_w_si1; + int32_t result_bstrins_w_si2; + int32_t result_bstrpick_w_si1; + int32_t result_bstrpick_w_si2; + }; + T t; + + __ Ld_d(a4, MemOperand(a0, offsetof(T, si1))); + __ Ld_d(a5, MemOperand(a0, offsetof(T, si2))); + + // ext_w_b + __ ext_w_b(t0, a4); + __ ext_w_b(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_ext_w_b_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_ext_w_b_si2))); + + // ext_w_h + __ ext_w_h(t0, a4); + __ ext_w_h(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_ext_w_h_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_ext_w_h_si2))); + + /* //clo_w + __ clo_w(t0, a4); + __ clo_w(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_clo_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_clo_w_si2)));*/ + + // clz_w + __ clz_w(t0, a4); + __ clz_w(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_clz_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_clz_w_si2))); + + /* //cto_w + __ cto_w(t0, a4); + __ cto_w(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_cto_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_cto_w_si2)));*/ + + // ctz_w + __ ctz_w(t0, a4); + __ ctz_w(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_ctz_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_ctz_w_si2))); + + // bytepick_w + __ bytepick_w(t0, a4, a5, 0); + __ bytepick_w(t1, a5, a4, 2); + __ St_w(t0, MemOperand(a0, offsetof(T, result_bytepick_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_bytepick_w_si2))); + + // revb_2h + __ revb_2h(t0, a4); + __ revb_2h(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_revb_2h_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_revb_2h_si2))); + + // bitrev + __ bitrev_4b(t0, a4); + __ bitrev_4b(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_bitrev_4b_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_bitrev_4b_si2))); + + // bitrev_w + __ bitrev_w(t0, a4); + __ bitrev_w(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_bitrev_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_bitrev_w_si2))); + + // bstrins + __ or_(t0, zero_reg, zero_reg); + __ or_(t1, zero_reg, zero_reg); + __ bstrins_w(t0, a4, 0xD, 0x4); + __ bstrins_w(t1, a5, 0x16, 0x5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_bstrins_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_bstrins_w_si2))); + + // bstrpick + __ or_(t0, zero_reg, zero_reg); + __ or_(t1, zero_reg, zero_reg); + __ bstrpick_w(t0, a4, 0xD, 0x4); + __ bstrpick_w(t1, a5, 0x16, 0x5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_bstrpick_w_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_bstrpick_w_si2))); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + t.si1 = 0x51F4B764A26E7412; + t.si2 = 0x81F25A87C423B891; + f.Call(&t, 0, 0, 0, 0); + + CHECK_EQ(static_cast<int32_t>(0x12), t.result_ext_w_b_si1); + CHECK_EQ(static_cast<int32_t>(0xffffff91), t.result_ext_w_b_si2); + CHECK_EQ(static_cast<int32_t>(0x7412), t.result_ext_w_h_si1); + CHECK_EQ(static_cast<int32_t>(0xffffb891), t.result_ext_w_h_si2); + // CHECK_EQ(static_cast<int32_t>(0x1), t.result_clo_w_si1); + // CHECK_EQ(static_cast<int32_t>(0x2), t.result_clo_w_si2); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_clz_w_si1); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_clz_w_si2); + // CHECK_EQ(static_cast<int32_t>(0x0), t.result_cto_w_si1); + // CHECK_EQ(static_cast<int32_t>(0x1), t.result_cto_w_si2); + CHECK_EQ(static_cast<int32_t>(0x1), t.result_ctz_w_si1); + CHECK_EQ(static_cast<int32_t>(0x0), t.result_ctz_w_si2); + CHECK_EQ(static_cast<int32_t>(0xc423b891), t.result_bytepick_w_si1); + CHECK_EQ(static_cast<int32_t>(0x7412c423), + t.result_bytepick_w_si2); // 0xffffc423 + CHECK_EQ(static_cast<int32_t>(0x6ea21274), t.result_revb_2h_si1); + CHECK_EQ(static_cast<int32_t>(0x23c491b8), t.result_revb_2h_si2); + CHECK_EQ(static_cast<int32_t>(0x45762e48), t.result_bitrev_4b_si1); + CHECK_EQ(static_cast<int32_t>(0x23c41d89), t.result_bitrev_4b_si2); + CHECK_EQ(static_cast<int32_t>(0x482e7645), t.result_bitrev_w_si1); + CHECK_EQ(static_cast<int32_t>(0x891dc423), t.result_bitrev_w_si2); + CHECK_EQ(static_cast<int32_t>(0x120), t.result_bstrins_w_si1); + CHECK_EQ(static_cast<int32_t>(0x771220), t.result_bstrins_w_si2); + CHECK_EQ(static_cast<int32_t>(0x341), t.result_bstrpick_w_si1); + CHECK_EQ(static_cast<int32_t>(0x11dc4), t.result_bstrpick_w_si2); +} + +TEST(LA11) { + // Test 64bit bit operation instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct T { + int64_t si1; + int64_t si2; + int64_t result_clo_d_si1; + int64_t result_clo_d_si2; + int64_t result_clz_d_si1; + int64_t result_clz_d_si2; + int64_t result_cto_d_si1; + int64_t result_cto_d_si2; + int64_t result_ctz_d_si1; + int64_t result_ctz_d_si2; + int64_t result_bytepick_d_si1; + int64_t result_bytepick_d_si2; + int64_t result_revb_4h_si1; + int64_t result_revb_4h_si2; + int64_t result_revb_2w_si1; + int64_t result_revb_2w_si2; + int64_t result_revb_d_si1; + int64_t result_revb_d_si2; + int64_t result_revh_2w_si1; + int64_t result_revh_2w_si2; + int64_t result_revh_d_si1; + int64_t result_revh_d_si2; + int64_t result_bitrev_8b_si1; + int64_t result_bitrev_8b_si2; + int64_t result_bitrev_d_si1; + int64_t result_bitrev_d_si2; + int64_t result_bstrins_d_si1; + int64_t result_bstrins_d_si2; + int64_t result_bstrpick_d_si1; + int64_t result_bstrpick_d_si2; + int64_t result_maskeqz_si1; + int64_t result_maskeqz_si2; + int64_t result_masknez_si1; + int64_t result_masknez_si2; + }; + + T t; + + __ Ld_d(a4, MemOperand(a0, offsetof(T, si1))); + __ Ld_d(a5, MemOperand(a0, offsetof(T, si2))); + + /* //clo_d + __ clo_d(t0, a4); + __ clo_d(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_clo_d_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_clo_d_si2)));*/ + + // clz_d + __ or_(t0, zero_reg, zero_reg); + __ clz_d(t0, a4); + __ clz_d(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_clz_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_clz_d_si2))); + + /* //cto_d + __ cto_d(t0, a4); + __ cto_d(t1, a5); + __ St_w(t0, MemOperand(a0, offsetof(T, result_cto_d_si1))); + __ St_w(t1, MemOperand(a0, offsetof(T, result_cto_d_si2)));*/ + + // ctz_d + __ ctz_d(t0, a4); + __ ctz_d(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_ctz_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_ctz_d_si2))); + + // bytepick_d + __ bytepick_d(t0, a4, a5, 0); + __ bytepick_d(t1, a5, a4, 5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_bytepick_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_bytepick_d_si2))); + + // revb_4h + __ revb_4h(t0, a4); + __ revb_4h(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_revb_4h_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_revb_4h_si2))); + + // revb_2w + __ revb_2w(t0, a4); + __ revb_2w(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_revb_2w_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_revb_2w_si2))); + + // revb_d + __ revb_d(t0, a4); + __ revb_d(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_revb_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_revb_d_si2))); + + // revh_2w + __ revh_2w(t0, a4); + __ revh_2w(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_revh_2w_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_revh_2w_si2))); + + // revh_d + __ revh_d(t0, a4); + __ revh_d(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_revh_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_revh_d_si2))); + + // bitrev_8b + __ bitrev_8b(t0, a4); + __ bitrev_8b(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_bitrev_8b_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_bitrev_8b_si2))); + + // bitrev_d + __ bitrev_d(t0, a4); + __ bitrev_d(t1, a5); + __ St_d(t0, MemOperand(a0, offsetof(T, result_bitrev_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_bitrev_d_si2))); + + // bstrins_d + __ or_(t0, zero_reg, zero_reg); + __ or_(t1, zero_reg, zero_reg); + __ bstrins_d(t0, a4, 5, 0); + __ bstrins_d(t1, a5, 39, 12); + __ St_d(t0, MemOperand(a0, offsetof(T, result_bstrins_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_bstrins_d_si2))); + + // bstrpick_d + __ or_(t0, zero_reg, zero_reg); + __ or_(t1, zero_reg, zero_reg); + __ bstrpick_d(t0, a4, 5, 0); + __ bstrpick_d(t1, a5, 63, 48); + __ St_d(t0, MemOperand(a0, offsetof(T, result_bstrpick_d_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_bstrpick_d_si2))); + + // maskeqz + __ maskeqz(t0, a4, a4); + __ maskeqz(t1, a5, zero_reg); + __ St_d(t0, MemOperand(a0, offsetof(T, result_maskeqz_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_maskeqz_si2))); + + // masknez + __ masknez(t0, a4, a4); + __ masknez(t1, a5, zero_reg); + __ St_d(t0, MemOperand(a0, offsetof(T, result_masknez_si1))); + __ St_d(t1, MemOperand(a0, offsetof(T, result_masknez_si2))); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + t.si1 = 0x10C021098B710CDE; + t.si2 = 0xFB8017FF781A15C3; + f.Call(&t, 0, 0, 0, 0); + + // CHECK_EQ(static_cast<int64_t>(0x0), t.result_clo_d_si1); + // CHECK_EQ(static_cast<int64_t>(0x5), t.result_clo_d_si2); + CHECK_EQ(static_cast<int64_t>(0x3), t.result_clz_d_si1); + CHECK_EQ(static_cast<int64_t>(0x0), t.result_clz_d_si2); + // CHECK_EQ(static_cast<int64_t>(0x0), t.result_cto_d_si1); + // CHECK_EQ(static_cast<int64_t>(0x2), t.result_cto_d_si2); + CHECK_EQ(static_cast<int64_t>(0x1), t.result_ctz_d_si1); + CHECK_EQ(static_cast<int64_t>(0x0), t.result_ctz_d_si2); + CHECK_EQ(static_cast<int64_t>(0xfb8017ff781a15c3), t.result_bytepick_d_si1); + CHECK_EQ(static_cast<int64_t>(0x710cdefb8017ff78), t.result_bytepick_d_si2); + CHECK_EQ(static_cast<int64_t>(0xc0100921718bde0c), t.result_revb_4h_si1); + CHECK_EQ(static_cast<int64_t>(0x80fbff171a78c315), t.result_revb_4h_si2); + CHECK_EQ(static_cast<int64_t>(0x921c010de0c718b), t.result_revb_2w_si1); + CHECK_EQ(static_cast<int64_t>(0xff1780fbc3151a78), t.result_revb_2w_si2); + CHECK_EQ(static_cast<int64_t>(0xde0c718b0921c010), t.result_revb_d_si1); + CHECK_EQ(static_cast<int64_t>(0xc3151a78ff1780fb), t.result_revb_d_si2); + CHECK_EQ(static_cast<int64_t>(0x210910c00cde8b71), t.result_revh_2w_si1); + CHECK_EQ(static_cast<int64_t>(0x17fffb8015c3781a), t.result_revh_2w_si2); + CHECK_EQ(static_cast<int64_t>(0xcde8b71210910c0), t.result_revh_d_si1); + CHECK_EQ(static_cast<int64_t>(0x15c3781a17fffb80), t.result_revh_d_si2); + CHECK_EQ(static_cast<int64_t>(0x8038490d18e307b), t.result_bitrev_8b_si1); + CHECK_EQ(static_cast<int64_t>(0xdf01e8ff1e58a8c3), t.result_bitrev_8b_si2); + CHECK_EQ(static_cast<int64_t>(0x7b308ed190840308), t.result_bitrev_d_si1); + CHECK_EQ(static_cast<int64_t>(0xc3a8581effe801df), t.result_bitrev_d_si2); + CHECK_EQ(static_cast<int64_t>(0x1e), t.result_bstrins_d_si1); + CHECK_EQ(static_cast<int64_t>(0x81a15c3000), t.result_bstrins_d_si2); + CHECK_EQ(static_cast<int64_t>(0x1e), t.result_bstrpick_d_si1); + CHECK_EQ(static_cast<int64_t>(0xfb80), t.result_bstrpick_d_si2); + CHECK_EQ(static_cast<int64_t>(0), t.result_maskeqz_si1); + CHECK_EQ(static_cast<int64_t>(0xFB8017FF781A15C3), t.result_maskeqz_si2); + CHECK_EQ(static_cast<int64_t>(0x10C021098B710CDE), t.result_masknez_si1); + CHECK_EQ(static_cast<int64_t>(0), t.result_masknez_si2); +} + +uint64_t run_beq(int64_t value1, int64_t value2, int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ beq(a0, a1, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value1, value2, 0, 0, 0)); + + return res; +} + +TEST(BEQ) { + CcTest::InitializeVM(); + struct TestCaseBeq { + int64_t value1; + int64_t value2; + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBeq tc[] = { + // value1, value2, offset, expected_res + { 0, 0, -6, 0x3 }, + { 1, 1, -3, 0x30 }, + { -2, -2, 3, 0x300 }, + { 3, -3, 6, 0 }, + { 4, 4, 6, 0x700 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBeq); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_beq(tc[i].value1, tc[i].value2, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bne(int64_t value1, int64_t value2, int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ bne(a0, a1, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value1, value2, 0, 0, 0)); + + return res; +} + +TEST(BNE) { + CcTest::InitializeVM(); + struct TestCaseBne { + int64_t value1; + int64_t value2; + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBne tc[] = { + // value1, value2, offset, expected_res + { 1, -1, -6, 0x3 }, + { 2, -2, -3, 0x30 }, + { 3, -3, 3, 0x300 }, + { 4, -4, 6, 0x700 }, + { 0, 0, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBne); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bne(tc[i].value1, tc[i].value2, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_blt(int64_t value1, int64_t value2, int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ blt(a0, a1, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value1, value2, 0, 0, 0)); + + return res; +} + +TEST(BLT) { + CcTest::InitializeVM(); + struct TestCaseBlt { + int64_t value1; + int64_t value2; + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBlt tc[] = { + // value1, value2, offset, expected_res + { -1, 1, -6, 0x3 }, + { -2, 2, -3, 0x30 }, + { -3, 3, 3, 0x300 }, + { -4, 4, 6, 0x700 }, + { 5, -5, 6, 0 }, + { 0, 0, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBlt); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_blt(tc[i].value1, tc[i].value2, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bge(uint64_t value1, uint64_t value2, int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ bge(a0, a1, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value1, value2, 0, 0, 0)); + + return res; +} + +TEST(BGE) { + CcTest::InitializeVM(); + struct TestCaseBge { + int64_t value1; + int64_t value2; + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBge tc[] = { + // value1, value2, offset, expected_res + { 0, 0, -6, 0x3 }, + { 1, 1, -3, 0x30 }, + { 2, -2, 3, 0x300 }, + { 3, -3, 6, 0x700 }, + { -4, 4, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBge); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bge(tc[i].value1, tc[i].value2, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bltu(int64_t value1, int64_t value2, int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ bltu(a0, a1, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value1, value2, 0, 0, 0)); + + return res; +} + +TEST(BLTU) { + CcTest::InitializeVM(); + struct TestCaseBltu { + int64_t value1; + int64_t value2; + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBltu tc[] = { + // value1, value2, offset, expected_res + { 0, 1, -6, 0x3 }, + { 1, -1, -3, 0x30 }, + { 2, -2, 3, 0x300 }, + { 3, -3, 6, 0x700 }, + { 4, 4, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBltu); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bltu(tc[i].value1, tc[i].value2, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bgeu(int64_t value1, int64_t value2, int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ bgeu(a0, a1, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value1, value2, 0, 0, 0)); + + return res; +} + +TEST(BGEU) { + CcTest::InitializeVM(); + struct TestCaseBgeu { + int64_t value1; + int64_t value2; + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBgeu tc[] = { + // value1, value2, offset, expected_res + { 0, 0, -6, 0x3 }, + { -1, 1, -3, 0x30 }, + { -2, 2, 3, 0x300 }, + { -3, 3, 6, 0x700 }, + { 4, -4, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBgeu); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bgeu(tc[i].value1, tc[i].value2, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_beqz(int64_t value, int32_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(&L); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ beqz(a0, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(&L); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value, 0, 0, 0, 0)); + + return res; +} + +TEST(BEQZ) { + CcTest::InitializeVM(); + struct TestCaseBeqz { + int64_t value; + int32_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBeqz tc[] = { + // value, offset, expected_res + { 0, -6, 0x3 }, + { 0, -3, 0x30 }, + { 0, 3, 0x300 }, + { 0, 6, 0x700 }, + { 1, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBeqz); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_beqz(tc[i].value, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bnez_b(int64_t value, int32_t offset) { + // bnez, b. + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0l); + __ b(&main_block); + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ b(5); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ b(2); + + // Block 3 (Main) + __ bind(&main_block); + __ bnez(a0, offset); + __ bind(&L); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ b(-4); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ b(-7); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(value, 0, 0, 0, 0)); + + return res; +} + +TEST(BNEZ_B) { + CcTest::InitializeVM(); + struct TestCaseBnez { + int64_t value; + int32_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBnez tc[] = { + // value, offset, expected_res + { 1, -6, 0x3 }, + { -2, -3, 0x30 }, + { 3, 3, 0x300 }, + { -4, 6, 0x700 }, + { 0, 6, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBnez); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bnez_b(tc[i].value, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bl(int32_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block; + __ li(a2, 0l); + __ Push(ra); // Push is implemented by two instructions, addi_d and st_d + __ b(&main_block); + + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ jirl(zero_reg, ra, 0); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ jirl(zero_reg, ra, 0); + + // Block 3 (Main) + __ bind(&main_block); + __ bl(offset); + __ or_(a0, a2, zero_reg); + __ Pop(ra); // Pop is implemented by two instructions, ld_d and addi_d. + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ jirl(zero_reg, ra, 0); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(0, 0, 0, 0, 0)); + + return res; +} + +TEST(BL) { + CcTest::InitializeVM(); + struct TestCaseBl { + int32_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBl tc[] = { + // offset, expected_res + { -6, 0x3 }, + { -3, 0x30 }, + { 5, 0x300 }, + { 8, 0x700 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBl); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bl(tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +TEST(PCADD) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label exit, error; + __ Push(ra); + + // pcaddi + __ li(a4, 0x1FFFFC); + __ li(a5, 0); + __ li(a6, static_cast<int32_t>(0xFFE00000)); + + __ bl(1); + __ pcaddi(a3, 0x7FFFF); + __ add_d(a2, ra, a4); + __ Branch(&error, ne, a2, Operand(a3)); + + __ bl(1); + __ pcaddi(a3, 0); + __ add_d(a2, ra, a5); + __ Branch(&error, ne, a2, Operand(a3)); + + __ bl(1); + __ pcaddi(a3, 0x80000); + __ add_d(a2, ra, a6); + __ Branch(&error, ne, a2, Operand(a3)); + + // pcaddu12i + __ li(a4, 0x7FFFF000); + __ li(a5, 0); + __ li(a6, static_cast<int32_t>(0x80000000)); + + __ bl(1); + __ pcaddu12i(a2, 0x7FFFF); + __ add_d(a3, ra, a4); + __ Branch(&error, ne, a2, Operand(a3)); + __ bl(1); + __ pcaddu12i(a2, 0); + __ add_d(a3, ra, a5); + __ Branch(&error, ne, a2, Operand(a3)); + __ bl(1); + __ pcaddu12i(a2, 0x80000); + __ add_d(a3, ra, a6); + __ Branch(&error, ne, a2, Operand(a3)); + + // pcaddu18i + __ li(a4, 0x1FFFFC0000); + __ li(a5, 0); + __ li(a6, static_cast<int64_t>(0xFFFFFFE000000000)); + + __ bl(1); + __ pcaddu18i(a2, 0x7FFFF); + __ add_d(a3, ra, a4); + __ Branch(&error, ne, a2, Operand(a3)); + + __ bl(1); + __ pcaddu18i(a2, 0); + __ add_d(a3, ra, a5); + __ Branch(&error, ne, a2, Operand(a3)); + + __ bl(1); + __ pcaddu18i(a2, 0x80000); + __ add_d(a3, ra, a6); + __ Branch(&error, ne, a2, Operand(a3)); + + // pcalau12i + __ li(a4, 0x7FFFF000); + __ li(a5, 0); + __ li(a6, static_cast<int32_t>(0x80000000)); + __ li(a7, static_cast<int64_t>(0xFFFFFFFFFFFFF000)); + + __ bl(1); + __ pcalau12i(a3, 0x7FFFF); + __ add_d(a2, ra, a4); + __ and_(t0, a2, a7); + __ and_(t1, a3, a7); + __ Branch(&error, ne, t0, Operand(t1)); + + __ bl(1); + __ pcalau12i(a3, 0); + __ add_d(a2, ra, a5); + __ and_(t0, a2, a7); + __ and_(t1, a3, a7); + __ Branch(&error, ne, t0, Operand(t1)); + + __ bl(1); + __ pcalau12i(a2, 0x80000); + __ add_d(a3, ra, a6); + __ and_(t0, a2, a7); + __ and_(t1, a3, a7); + __ Branch(&error, ne, t0, Operand(t1)); + + __ li(a0, 0x31415926); + __ b(&exit); + + __ bind(&error); + __ li(a0, 0x666); + + __ bind(&exit); + __ Pop(ra); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + int64_t res = reinterpret_cast<int64_t>(f.Call(0, 0, 0, 0, 0)); + + CHECK_EQ(0x31415926L, res); +} + +uint64_t run_jirl(int16_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block; + __ li(a2, 0l); + __ Push(ra); + __ b(&main_block); + + // Block 1 + __ addi_d(a2, a2, 0x1); + __ addi_d(a2, a2, 0x2); + __ jirl(zero_reg, ra, 0); + + // Block 2 + __ addi_d(a2, a2, 0x10); + __ addi_d(a2, a2, 0x20); + __ jirl(zero_reg, ra, 0); + + // Block 3 (Main) + __ bind(&main_block); + __ pcaddi(a3, 1); + __ jirl(ra, a3, offset); + __ or_(a0, a2, zero_reg); + __ Pop(ra); // Pop is implemented by two instructions, ld_d and addi_d. + __ jirl(zero_reg, ra, 0); + + // Block 4 + __ addi_d(a2, a2, 0x100); + __ addi_d(a2, a2, 0x200); + __ jirl(zero_reg, ra, 0); + + // Block 5 + __ addi_d(a2, a2, 0x300); + __ addi_d(a2, a2, 0x400); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(0, 0, 0, 0, 0)); + + return res; +} + +TEST(JIRL) { + CcTest::InitializeVM(); + struct TestCaseJirl { + int16_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseJirl tc[] = { + // offset, expected_res + { -7, 0x3 }, + { -4, 0x30 }, + { 5, 0x300 }, + { 8, 0x700 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseJirl); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_jirl(tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +TEST(LA12) { + // Test floating point calculate instructions. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + struct T { + double a; + double b; + double c; + double d; + double e; + double f; + double result_fadd_d; + double result_fsub_d; + double result_fmul_d; + double result_fdiv_d; + double result_fmadd_d; + double result_fmsub_d; + double result_fnmadd_d; + double result_fnmsub_d; + double result_fsqrt_d; + double result_frecip_d; + double result_frsqrt_d; + double result_fscaleb_d; + double result_flogb_d; + double result_fcopysign_d; + double result_fclass_d; + }; + T t; + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + // Double precision floating point instructions. + __ Fld_d(f8, MemOperand(a0, offsetof(T, a))); + __ Fld_d(f9, MemOperand(a0, offsetof(T, b))); + + __ fneg_d(f10, f8); + __ fadd_d(f11, f9, f10); + __ Fst_d(f11, MemOperand(a0, offsetof(T, result_fadd_d))); + __ fabs_d(f11, f11); + __ fsub_d(f12, f11, f9); + __ Fst_d(f12, MemOperand(a0, offsetof(T, result_fsub_d))); + + __ Fld_d(f13, MemOperand(a0, offsetof(T, c))); + __ Fld_d(f14, MemOperand(a0, offsetof(T, d))); + __ Fld_d(f15, MemOperand(a0, offsetof(T, e))); + + __ fmin_d(f16, f13, f14); + __ fmul_d(f17, f15, f16); + __ Fst_d(f17, MemOperand(a0, offsetof(T, result_fmul_d))); + __ fmax_d(f18, f13, f14); + __ fdiv_d(f19, f15, f18); + __ Fst_d(f19, MemOperand(a0, offsetof(T, result_fdiv_d))); + + __ fmina_d(f16, f13, f14); + __ fmadd_d(f18, f17, f15, f16); + __ Fst_d(f18, MemOperand(a0, offsetof(T, result_fmadd_d))); + __ fnmadd_d(f19, f17, f15, f16); + __ Fst_d(f19, MemOperand(a0, offsetof(T, result_fnmadd_d))); + __ fmaxa_d(f16, f13, f14); + __ fmsub_d(f20, f17, f15, f16); + __ Fst_d(f20, MemOperand(a0, offsetof(T, result_fmsub_d))); + __ fnmsub_d(f21, f17, f15, f16); + __ Fst_d(f21, MemOperand(a0, offsetof(T, result_fnmsub_d))); + + __ Fld_d(f8, MemOperand(a0, offsetof(T, f))); + __ fsqrt_d(f10, f8); + __ Fst_d(f10, MemOperand(a0, offsetof(T, result_fsqrt_d))); + //__ frecip_d(f11, f10); + //__ frsqrt_d(f12, f8); + //__ Fst_d(f11, MemOperand(a0, offsetof(T, result_frecip_d))); + //__ Fst_d(f12, MemOperand(a0, offsetof(T, result_frsqrt_d))); + + /*__ fscaleb_d(f16, f13, f15); + __ flogb_d(f17, f15); + __ fcopysign_d(f18, f8, f9); + __ fclass_d(f19, f9); + __ Fst_d(f16, MemOperand(a0, offsetof(T, result_fscaleb_d))); + __ Fst_d(f17, MemOperand(a0, offsetof(T, result_flogb_d))); + __ Fst_d(f18, MemOperand(a0, offsetof(T, result_fcopysign_d))); + __ Fst_d(f19, MemOperand(a0, offsetof(T, result_fclass_d)));*/ + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + // Double test values. + t.a = 1.5e14; + t.b = -2.75e11; + t.c = 1.5; + t.d = -2.75; + t.e = 120.0; + t.f = 120.44; + f.Call(&t, 0, 0, 0, 0); + + CHECK_EQ(static_cast<double>(-1.502750e14), t.result_fadd_d); + CHECK_EQ(static_cast<double>(1.505500e14), t.result_fsub_d); + CHECK_EQ(static_cast<double>(-3.300000e02), t.result_fmul_d); + CHECK_EQ(static_cast<double>(8.000000e01), t.result_fdiv_d); + CHECK_EQ(static_cast<double>(-3.959850e04), t.result_fmadd_d); + CHECK_EQ(static_cast<double>(-3.959725e04), t.result_fmsub_d); + CHECK_EQ(static_cast<double>(3.959850e04), t.result_fnmadd_d); + CHECK_EQ(static_cast<double>(3.959725e04), t.result_fnmsub_d); + CHECK_EQ(static_cast<double>(10.97451593465515908537), t.result_fsqrt_d); + // CHECK_EQ(static_cast<double>( 8.164965e-08), t.result_frecip_d); + // CHECK_EQ(static_cast<double>( 8.164966e-08), t.result_frsqrt_d); + // CHECK_EQ(static_cast<double>(), t.result_fscaleb_d); + // CHECK_EQ(static_cast<double>( 6.906891), t.result_flogb_d); + // CHECK_EQ(static_cast<double>( 2.75e11), t.result_fcopysign_d); + // CHECK_EQ(static_cast<double>(), t.result_fclass_d); +} + +TEST(LA13) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + struct T { + float a; + float b; + float c; + float d; + float e; + float result_fadd_s; + float result_fsub_s; + float result_fmul_s; + float result_fdiv_s; + float result_fmadd_s; + float result_fmsub_s; + float result_fnmadd_s; + float result_fnmsub_s; + float result_fsqrt_s; + float result_frecip_s; + float result_frsqrt_s; + float result_fscaleb_s; + float result_flogb_s; + float result_fcopysign_s; + float result_fclass_s; + }; + T t; + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + // Float precision floating point instructions. + __ Fld_s(f8, MemOperand(a0, offsetof(T, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(T, b))); + + __ fneg_s(f10, f8); + __ fadd_s(f11, f9, f10); + __ Fst_s(f11, MemOperand(a0, offsetof(T, result_fadd_s))); + __ fabs_s(f11, f11); + __ fsub_s(f12, f11, f9); + __ Fst_s(f12, MemOperand(a0, offsetof(T, result_fsub_s))); + + __ Fld_s(f13, MemOperand(a0, offsetof(T, c))); + __ Fld_s(f14, MemOperand(a0, offsetof(T, d))); + __ Fld_s(f15, MemOperand(a0, offsetof(T, e))); + + __ fmin_s(f16, f13, f14); + __ fmul_s(f17, f15, f16); + __ Fst_s(f17, MemOperand(a0, offsetof(T, result_fmul_s))); + __ fmax_s(f18, f13, f14); + __ fdiv_s(f19, f15, f18); + __ Fst_s(f19, MemOperand(a0, offsetof(T, result_fdiv_s))); + + __ fmina_s(f16, f13, f14); + __ fmadd_s(f18, f17, f15, f16); + __ Fst_s(f18, MemOperand(a0, offsetof(T, result_fmadd_s))); + __ fnmadd_s(f19, f17, f15, f16); + __ Fst_s(f19, MemOperand(a0, offsetof(T, result_fnmadd_s))); + __ fmaxa_s(f16, f13, f14); + __ fmsub_s(f20, f17, f15, f16); + __ Fst_s(f20, MemOperand(a0, offsetof(T, result_fmsub_s))); + __ fnmsub_s(f21, f17, f15, f16); + __ Fst_s(f21, MemOperand(a0, offsetof(T, result_fnmsub_s))); + + __ fsqrt_s(f10, f8); + //__ frecip_s(f11, f10); + //__ frsqrt_s(f12, f8); + __ Fst_s(f10, MemOperand(a0, offsetof(T, result_fsqrt_s))); + //__ Fst_s(f11, MemOperand(a0, offsetof(T, result_frecip_s))); + //__ Fst_s(f12, MemOperand(a0, offsetof(T, result_frsqrt_s))); + + /*__ fscaleb_s(f16, f13, f15); + __ flogb_s(f17, f15); + __ fcopysign_s(f18, f8, f9); + __ fclass_s(f19, f9); + __ Fst_s(f16, MemOperand(a0, offsetof(T, result_fscaleb_s))); + __ Fst_s(f17, MemOperand(a0, offsetof(T, result_flogb_s))); + __ Fst_s(f18, MemOperand(a0, offsetof(T, result_fcopysign_s))); + __ Fst_s(f19, MemOperand(a0, offsetof(T, result_fclass_s)));*/ + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + // Float test values. + t.a = 1.5e6; + t.b = -2.75e4; + t.c = 1.5; + t.d = -2.75; + t.e = 120.0; + f.Call(&t, 0, 0, 0, 0); + + CHECK_EQ(static_cast<float>(-1.527500e06), t.result_fadd_s); + CHECK_EQ(static_cast<float>(1.555000e06), t.result_fsub_s); + CHECK_EQ(static_cast<float>(-3.300000e02), t.result_fmul_s); + CHECK_EQ(static_cast<float>(8.000000e01), t.result_fdiv_s); + CHECK_EQ(static_cast<float>(-3.959850e04), t.result_fmadd_s); + CHECK_EQ(static_cast<float>(-3.959725e04), t.result_fmsub_s); + CHECK_EQ(static_cast<float>(3.959850e04), t.result_fnmadd_s); + CHECK_EQ(static_cast<float>(3.959725e04), t.result_fnmsub_s); + CHECK_EQ(static_cast<float>(1224.744873), t.result_fsqrt_s); + // CHECK_EQ(static_cast<float>( 8.164966e-04), t.result_frecip_s); + // CHECK_EQ(static_cast<float>( 8.164966e-04), t.result_frsqrt_s); + // CHECK_EQ(static_cast<float>(), t.result_fscaleb_s); + // CHECK_EQ(static_cast<float>( 6.906890), t.result_flogb_s); + // CHECK_EQ(static_cast<float>( 2.75e4), t.result_fcopysign_s); + // CHECK_EQ(static_cast<float>(), t.result_fclass_s); +} + +TEST(FCMP_COND) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + double dTrue; + double dFalse; + double dOp1; + double dOp2; + double dCaf; + double dCun; + double dCeq; + double dCueq; + double dClt; + double dCult; + double dCle; + double dCule; + double dCne; + double dCor; + double dCune; + double dSaf; + double dSun; + double dSeq; + double dSueq; + double dSlt; + double dSult; + double dSle; + double dSule; + double dSne; + double dSor; + double dSune; + float fTrue; + float fFalse; + float fOp1; + float fOp2; + float fCaf; + float fCun; + float fCeq; + float fCueq; + float fClt; + float fCult; + float fCle; + float fCule; + float fCne; + float fCor; + float fCune; + float fSaf; + float fSun; + float fSeq; + float fSueq; + float fSlt; + float fSult; + float fSle; + float fSule; + float fSne; + float fSor; + float fSune; + }; + + TestFloat test; + + __ Fld_d(f8, MemOperand(a0, offsetof(TestFloat, dOp1))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, dOp2))); + + __ Fld_s(f10, MemOperand(a0, offsetof(TestFloat, fOp1))); + __ Fld_s(f11, MemOperand(a0, offsetof(TestFloat, fOp2))); + + __ Fld_d(f12, MemOperand(a0, offsetof(TestFloat, dFalse))); + __ Fld_d(f13, MemOperand(a0, offsetof(TestFloat, dTrue))); + + __ Fld_s(f14, MemOperand(a0, offsetof(TestFloat, fFalse))); + __ Fld_s(f15, MemOperand(a0, offsetof(TestFloat, fTrue))); + + __ fcmp_cond_d(CAF, f8, f9, FCC0); + __ fcmp_cond_s(CAF, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCaf))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCaf))); + + __ fcmp_cond_d(CUN, f8, f9, FCC0); + __ fcmp_cond_s(CUN, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCun))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCun))); + + __ fcmp_cond_d(CEQ, f8, f9, FCC0); + __ fcmp_cond_s(CEQ, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCeq))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCeq))); + + __ fcmp_cond_d(CUEQ, f8, f9, FCC0); + __ fcmp_cond_s(CUEQ, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCueq))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCueq))); + + __ fcmp_cond_d(CLT, f8, f9, FCC0); + __ fcmp_cond_s(CLT, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dClt))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fClt))); + + __ fcmp_cond_d(CULT, f8, f9, FCC0); + __ fcmp_cond_s(CULT, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCult))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCult))); + + __ fcmp_cond_d(CLE, f8, f9, FCC0); + __ fcmp_cond_s(CLE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCle))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCle))); + + __ fcmp_cond_d(CULE, f8, f9, FCC0); + __ fcmp_cond_s(CULE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCule))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCule))); + + __ fcmp_cond_d(CNE, f8, f9, FCC0); + __ fcmp_cond_s(CNE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCne))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCne))); + + __ fcmp_cond_d(COR, f8, f9, FCC0); + __ fcmp_cond_s(COR, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCor))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCor))); + + __ fcmp_cond_d(CUNE, f8, f9, FCC0); + __ fcmp_cond_s(CUNE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dCune))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fCune))); + + /* __ fcmp_cond_d(SAF, f8, f9, FCC0); + __ fcmp_cond_s(SAF, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSaf))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fSaf))); + + __ fcmp_cond_d(SUN, f8, f9, FCC0); + __ fcmp_cond_s(SUN, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSun))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fSun))); + + __ fcmp_cond_d(SEQ, f8, f9, FCC0); + __ fcmp_cond_s(SEQ, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSeq))); + __ Fst_f(f17, MemOperand(a0, offsetof(TestFloat, fSeq))); + + __ fcmp_cond_d(SUEQ, f8, f9, FCC0); + __ fcmp_cond_s(SUEQ, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSueq))); + __ Fst_f(f17, MemOperand(a0, offsetof(TestFloat, fSueq))); + + __ fcmp_cond_d(SLT, f8, f9, FCC0); + __ fcmp_cond_s(SLT, f10, f11, FCC1); + __ fsel(f16, f12, f13, FCC0); + __ fsel(f17, f14, f15, FCC1); + __ Fld_d(f16, MemOperand(a0, offsetof(TestFloat, dSlt))); + __ Fst_d(f17, MemOperand(a0, offsetof(TestFloat, fSlt))); + + __ fcmp_cond_d(SULT, f8, f9, FCC0); + __ fcmp_cond_s(SULT, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSult))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fSult))); + + __ fcmp_cond_d(SLE, f8, f9, FCC0); + __ fcmp_cond_s(SLE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSle))); + __ Fst_f(f17, MemOperand(a0, offsetof(TestFloat, fSle))); + + __ fcmp_cond_d(SULE, f8, f9, FCC0); + __ fcmp_cond_s(SULE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSule))); + __ Fst_f(f17, MemOperand(a0, offsetof(TestFloat, fSule))); + + __ fcmp_cond_d(SNE, f8, f9, FCC0); + __ fcmp_cond_s(SNE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSne))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fSne))); + + __ fcmp_cond_d(SOR, f8, f9, FCC0); + __ fcmp_cond_s(SOR, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSor))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fSor))); + + __ fcmp_cond_d(SUNE, f8, f9, FCC0); + __ fcmp_cond_s(SUNE, f10, f11, FCC1); + __ fsel(FCC0, f16, f12, f13); + __ fsel(FCC1, f17, f14, f15); + __ Fst_d(f16, MemOperand(a0, offsetof(TestFloat, dSune))); + __ Fst_s(f17, MemOperand(a0, offsetof(TestFloat, fSune)));*/ + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + test.dTrue = 1234.0; + test.dFalse = 0.0; + test.fTrue = 12.0; + test.fFalse = 0.0; + + test.dOp1 = 2.0; + test.dOp2 = 3.0; + test.fOp1 = 2.0; + test.fOp2 = 3.0; + f.Call(&test, 0, 0, 0, 0); + + CHECK_EQ(test.dCaf, test.dFalse); + CHECK_EQ(test.fCaf, test.fFalse); + CHECK_EQ(test.dCun, test.dFalse); + CHECK_EQ(test.fCun, test.fFalse); + CHECK_EQ(test.dCeq, test.dFalse); + CHECK_EQ(test.fCeq, test.fFalse); + CHECK_EQ(test.dCueq, test.dFalse); + CHECK_EQ(test.fCueq, test.fFalse); + CHECK_EQ(test.dClt, test.dTrue); + CHECK_EQ(test.fClt, test.fTrue); + CHECK_EQ(test.dCult, test.dTrue); + CHECK_EQ(test.fCult, test.fTrue); + CHECK_EQ(test.dCle, test.dTrue); + CHECK_EQ(test.fCle, test.fTrue); + CHECK_EQ(test.dCule, test.dTrue); + CHECK_EQ(test.fCule, test.fTrue); + CHECK_EQ(test.dCne, test.dTrue); + CHECK_EQ(test.fCne, test.fTrue); + CHECK_EQ(test.dCor, test.dTrue); + CHECK_EQ(test.fCor, test.fTrue); + CHECK_EQ(test.dCune, test.dTrue); + CHECK_EQ(test.fCune, test.fTrue); + /* CHECK_EQ(test.dSaf, test.dFalse); + CHECK_EQ(test.fSaf, test.fFalse); + CHECK_EQ(test.dSun, test.dFalse); + CHECK_EQ(test.fSun, test.fFalse); + CHECK_EQ(test.dSeq, test.dFalse); + CHECK_EQ(test.fSeq, test.fFalse); + CHECK_EQ(test.dSueq, test.dFalse); + CHECK_EQ(test.fSueq, test.fFalse); + CHECK_EQ(test.dClt, test.dTrue); + CHECK_EQ(test.fClt, test.fTrue); + CHECK_EQ(test.dCult, test.dTrue); + CHECK_EQ(test.fCult, test.fTrue); + CHECK_EQ(test.dSle, test.dTrue); + CHECK_EQ(test.fSle, test.fTrue); + CHECK_EQ(test.dSule, test.dTrue); + CHECK_EQ(test.fSule, test.fTrue); + CHECK_EQ(test.dSne, test.dTrue); + CHECK_EQ(test.fSne, test.fTrue); + CHECK_EQ(test.dSor, test.dTrue); + CHECK_EQ(test.fSor, test.fTrue); + CHECK_EQ(test.dSune, test.dTrue); + CHECK_EQ(test.fSune, test.fTrue);*/ + + test.dOp1 = std::numeric_limits<double>::max(); + test.dOp2 = std::numeric_limits<double>::min(); + test.fOp1 = std::numeric_limits<float>::min(); + test.fOp2 = -std::numeric_limits<float>::max(); + f.Call(&test, 0, 0, 0, 0); + + CHECK_EQ(test.dCaf, test.dFalse); + CHECK_EQ(test.fCaf, test.fFalse); + CHECK_EQ(test.dCun, test.dFalse); + CHECK_EQ(test.fCun, test.fFalse); + CHECK_EQ(test.dCeq, test.dFalse); + CHECK_EQ(test.fCeq, test.fFalse); + CHECK_EQ(test.dCueq, test.dFalse); + CHECK_EQ(test.fCueq, test.fFalse); + CHECK_EQ(test.dClt, test.dFalse); + CHECK_EQ(test.fClt, test.fFalse); + CHECK_EQ(test.dCult, test.dFalse); + CHECK_EQ(test.fCult, test.fFalse); + CHECK_EQ(test.dCle, test.dFalse); + CHECK_EQ(test.fCle, test.fFalse); + CHECK_EQ(test.dCule, test.dFalse); + CHECK_EQ(test.fCule, test.fFalse); + CHECK_EQ(test.dCne, test.dTrue); + CHECK_EQ(test.fCne, test.fTrue); + CHECK_EQ(test.dCor, test.dTrue); + CHECK_EQ(test.fCor, test.fTrue); + CHECK_EQ(test.dCune, test.dTrue); + CHECK_EQ(test.fCune, test.fTrue); + /* CHECK_EQ(test.dSaf, test.dFalse); + CHECK_EQ(test.fSaf, test.fFalse); + CHECK_EQ(test.dSun, test.dFalse); + CHECK_EQ(test.fSun, test.fFalse); + CHECK_EQ(test.dSeq, test.dFalse); + CHECK_EQ(test.fSeq, test.fFalse); + CHECK_EQ(test.dSueq, test.dFalse); + CHECK_EQ(test.fSueq, test.fFalse); + CHECK_EQ(test.dSlt, test.dFalse); + CHECK_EQ(test.fSlt, test.fFalse); + CHECK_EQ(test.dSult, test.dFalse); + CHECK_EQ(test.fSult, test.fFalse); + CHECK_EQ(test.dSle, test.dFalse); + CHECK_EQ(test.fSle, test.fFalse); + CHECK_EQ(test.dSule, test.dFalse); + CHECK_EQ(test.fSule, test.fFalse); + CHECK_EQ(test.dSne, test.dTrue); + CHECK_EQ(test.fSne, test.fTrue); + CHECK_EQ(test.dSor, test.dTrue); + CHECK_EQ(test.fSor, test.fTrue); + CHECK_EQ(test.dSune, test.dTrue); + CHECK_EQ(test.fSune, test.fTrue);*/ + + test.dOp1 = std::numeric_limits<double>::quiet_NaN(); + test.dOp2 = 0.0; + test.fOp1 = std::numeric_limits<float>::quiet_NaN(); + test.fOp2 = 0.0; + f.Call(&test, 0, 0, 0, 0); + + CHECK_EQ(test.dCaf, test.dFalse); + CHECK_EQ(test.fCaf, test.fFalse); + CHECK_EQ(test.dCun, test.dTrue); + CHECK_EQ(test.fCun, test.fTrue); + CHECK_EQ(test.dCeq, test.dFalse); + CHECK_EQ(test.fCeq, test.fFalse); + CHECK_EQ(test.dCueq, test.dTrue); + CHECK_EQ(test.fCueq, test.fTrue); + CHECK_EQ(test.dClt, test.dFalse); + CHECK_EQ(test.fClt, test.fFalse); + CHECK_EQ(test.dCult, test.dTrue); + CHECK_EQ(test.fCult, test.fTrue); + CHECK_EQ(test.dCle, test.dFalse); + CHECK_EQ(test.fCle, test.fFalse); + CHECK_EQ(test.dCule, test.dTrue); + CHECK_EQ(test.fCule, test.fTrue); + CHECK_EQ(test.dCne, test.dFalse); + CHECK_EQ(test.fCne, test.fFalse); + CHECK_EQ(test.dCor, test.dFalse); + CHECK_EQ(test.fCor, test.fFalse); + CHECK_EQ(test.dCune, test.dTrue); + CHECK_EQ(test.fCune, test.fTrue); + /* CHECK_EQ(test.dSaf, test.dTrue); + CHECK_EQ(test.fSaf, test.fTrue); + CHECK_EQ(test.dSun, test.dTrue); + CHECK_EQ(test.fSun, test.fTrue); + CHECK_EQ(test.dSeq, test.dFalse); + CHECK_EQ(test.fSeq, test.fFalse); + CHECK_EQ(test.dSueq, test.dTrue); + CHECK_EQ(test.fSueq, test.fTrue); + CHECK_EQ(test.dSlt, test.dFalse); + CHECK_EQ(test.fSlt, test.fFalse); + CHECK_EQ(test.dSult, test.dTrue); + CHECK_EQ(test.fSult, test.fTrue); + CHECK_EQ(test.dSle, test.dFalse); + CHECK_EQ(test.fSle, test.fFalse); + CHECK_EQ(test.dSule, test.dTrue); + CHECK_EQ(test.fSule, test.fTrue); + CHECK_EQ(test.dSne, test.dFalse); + CHECK_EQ(test.fSne, test.fFalse); + CHECK_EQ(test.dSor, test.dFalse); + CHECK_EQ(test.fSor, test.fFalse); + CHECK_EQ(test.dSune, test.dTrue); + CHECK_EQ(test.fSune, test.fTrue);*/ +} + +TEST(FCVT) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + float fcvt_d_s_in; + double fcvt_s_d_in; + double fcvt_d_s_out; + float fcvt_s_d_out; + int fcsr; + }; + TestFloat test; + __ xor_(a4, a4, a4); + __ xor_(a5, a5, a5); + __ Ld_w(a4, MemOperand(a0, offsetof(TestFloat, fcsr))); + __ movfcsr2gr(a5); + __ movgr2fcsr(a4); + __ Fld_s(f8, MemOperand(a0, offsetof(TestFloat, fcvt_d_s_in))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, fcvt_s_d_in))); + __ fcvt_d_s(f10, f8); + __ fcvt_s_d(f11, f9); + __ Fst_d(f10, MemOperand(a0, offsetof(TestFloat, fcvt_d_s_out))); + __ Fst_s(f11, MemOperand(a0, offsetof(TestFloat, fcvt_s_d_out))); + __ movgr2fcsr(a5); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + test.fcsr = kRoundToNearest; + + test.fcvt_d_s_in = -0.51; + test.fcvt_s_d_in = -0.51; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.fcvt_d_s_out, static_cast<double>(test.fcvt_d_s_in)); + CHECK_EQ(test.fcvt_s_d_out, static_cast<float>(test.fcvt_s_d_in)); + + test.fcvt_d_s_in = 0.49; + test.fcvt_s_d_in = 0.49; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.fcvt_d_s_out, static_cast<double>(test.fcvt_d_s_in)); + CHECK_EQ(test.fcvt_s_d_out, static_cast<float>(test.fcvt_s_d_in)); + + test.fcvt_d_s_in = std::numeric_limits<float>::max(); + test.fcvt_s_d_in = std::numeric_limits<double>::max(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.fcvt_d_s_out, static_cast<double>(test.fcvt_d_s_in)); + CHECK_EQ(test.fcvt_s_d_out, static_cast<float>(test.fcvt_s_d_in)); + + test.fcvt_d_s_in = -std::numeric_limits<float>::max(); + test.fcvt_s_d_in = -std::numeric_limits<double>::max(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.fcvt_d_s_out, static_cast<double>(test.fcvt_d_s_in)); + CHECK_EQ(test.fcvt_s_d_out, static_cast<float>(test.fcvt_s_d_in)); + + test.fcvt_d_s_in = std::numeric_limits<float>::min(); + test.fcvt_s_d_in = std::numeric_limits<double>::min(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.fcvt_d_s_out, static_cast<double>(test.fcvt_d_s_in)); + CHECK_EQ(test.fcvt_s_d_out, static_cast<float>(test.fcvt_s_d_in)); +} + +TEST(FFINT) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + int32_t ffint_s_w_in; + int64_t ffint_s_l_in; + int32_t ffint_d_w_in; + int64_t ffint_d_l_in; + float ffint_s_w_out; + float ffint_s_l_out; + double ffint_d_w_out; + double ffint_d_l_out; + int fcsr; + }; + TestFloat test; + __ xor_(a4, a4, a4); + __ xor_(a5, a5, a5); + __ Ld_w(a4, MemOperand(a0, offsetof(TestFloat, fcsr))); + __ movfcsr2gr(a5); + __ movgr2fcsr(a4); + __ Fld_s(f8, MemOperand(a0, offsetof(TestFloat, ffint_s_w_in))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, ffint_s_l_in))); + __ Fld_s(f10, MemOperand(a0, offsetof(TestFloat, ffint_d_w_in))); + __ Fld_d(f11, MemOperand(a0, offsetof(TestFloat, ffint_d_l_in))); + __ ffint_s_w(f12, f8); + __ ffint_s_l(f13, f9); + __ ffint_d_w(f14, f10); + __ ffint_d_l(f15, f11); + __ Fst_s(f12, MemOperand(a0, offsetof(TestFloat, ffint_s_w_out))); + __ Fst_s(f13, MemOperand(a0, offsetof(TestFloat, ffint_s_l_out))); + __ Fst_d(f14, MemOperand(a0, offsetof(TestFloat, ffint_d_w_out))); + __ Fst_d(f15, MemOperand(a0, offsetof(TestFloat, ffint_d_l_out))); + __ movgr2fcsr(a5); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + test.fcsr = kRoundToNearest; + + test.ffint_s_w_in = -1; + test.ffint_s_l_in = -1; + test.ffint_d_w_in = -1; + test.ffint_d_l_in = -1; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.ffint_s_w_out, static_cast<float>(test.ffint_s_w_in)); + CHECK_EQ(test.ffint_s_l_out, static_cast<float>(test.ffint_s_l_in)); + CHECK_EQ(test.ffint_d_w_out, static_cast<double>(test.ffint_d_w_in)); + CHECK_EQ(test.ffint_d_l_out, static_cast<double>(test.ffint_d_l_in)); + + test.ffint_s_w_in = 1; + test.ffint_s_l_in = 1; + test.ffint_d_w_in = 1; + test.ffint_d_l_in = 1; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.ffint_s_w_out, static_cast<float>(test.ffint_s_w_in)); + CHECK_EQ(test.ffint_s_l_out, static_cast<float>(test.ffint_s_l_in)); + CHECK_EQ(test.ffint_d_w_out, static_cast<double>(test.ffint_d_w_in)); + CHECK_EQ(test.ffint_d_l_out, static_cast<double>(test.ffint_d_l_in)); + + test.ffint_s_w_in = std::numeric_limits<int32_t>::max(); + test.ffint_s_l_in = std::numeric_limits<int64_t>::max(); + test.ffint_d_w_in = std::numeric_limits<int32_t>::max(); + test.ffint_d_l_in = std::numeric_limits<int64_t>::max(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.ffint_s_w_out, static_cast<float>(test.ffint_s_w_in)); + CHECK_EQ(test.ffint_s_l_out, static_cast<float>(test.ffint_s_l_in)); + CHECK_EQ(test.ffint_d_w_out, static_cast<double>(test.ffint_d_w_in)); + CHECK_EQ(test.ffint_d_l_out, static_cast<double>(test.ffint_d_l_in)); + + test.ffint_s_w_in = std::numeric_limits<int32_t>::min(); + test.ffint_s_l_in = std::numeric_limits<int64_t>::min(); + test.ffint_d_w_in = std::numeric_limits<int32_t>::min(); + test.ffint_d_l_in = std::numeric_limits<int64_t>::min(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.ffint_s_w_out, static_cast<float>(test.ffint_s_w_in)); + CHECK_EQ(test.ffint_s_l_out, static_cast<float>(test.ffint_s_l_in)); + CHECK_EQ(test.ffint_d_w_out, static_cast<double>(test.ffint_d_w_in)); + CHECK_EQ(test.ffint_d_l_out, static_cast<double>(test.ffint_d_l_in)); +} + +TEST(FTINT) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double a; + float b; + int32_t c; + int32_t d; + int64_t e; + int64_t f; + int fcsr; + }; + Test test; + + const int kTableLength = 9; + // clang-format off + double inputs_d[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + float inputs_s[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + double outputs_RN_W[kTableLength] = { + 3.0, 4.0, 4.0, -3.0, -4.0, -4.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_RN_L[kTableLength] = { + 3.0, 4.0, 4.0, -3.0, -4.0, -4.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + double outputs_RZ_W[kTableLength] = { + 3.0, 3.0, 3.0, -3.0, -3.0, -3.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_RZ_L[kTableLength] = { + 3.0, 3.0, 3.0, -3.0, -3.0, -3.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + double outputs_RP_W[kTableLength] = { + 4.0, 4.0, 4.0, -3.0, -3.0, -3.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_RP_L[kTableLength] = { + 4.0, 4.0, 4.0, -3.0, -3.0, -3.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + double outputs_RM_W[kTableLength] = { + 3.0, 3.0, 3.0, -4.0, -4.0, -4.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_RM_L[kTableLength] = { + 3.0, 3.0, 3.0, -4.0, -4.0, -4.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + // clang-format on + + int fcsr_inputs[4] = {kRoundToNearest, kRoundToZero, kRoundToPlusInf, + kRoundToMinusInf}; + double* outputs[8] = { + outputs_RN_W, outputs_RN_L, outputs_RZ_W, outputs_RZ_L, + outputs_RP_W, outputs_RP_L, outputs_RM_W, outputs_RM_L, + }; + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(Test, b))); + __ xor_(a5, a5, a5); + __ Ld_w(a5, MemOperand(a0, offsetof(Test, fcsr))); + __ movfcsr2gr(a4); + __ movgr2fcsr(a5); + __ ftint_w_d(f10, f8); + __ ftint_w_s(f11, f9); + __ ftint_l_d(f12, f8); + __ ftint_l_s(f13, f9); + __ Fst_s(f10, MemOperand(a0, offsetof(Test, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(Test, d))); + __ Fst_d(f12, MemOperand(a0, offsetof(Test, e))); + __ Fst_d(f13, MemOperand(a0, offsetof(Test, f))); + __ movgr2fcsr(a4); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int j = 0; j < 4; j++) { + test.fcsr = fcsr_inputs[j]; + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_d[i]; + test.b = inputs_s[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs[2 * j][i]); + CHECK_EQ(test.d, outputs[2 * j][i]); + CHECK_EQ(test.e, outputs[2 * j + 1][i]); + CHECK_EQ(test.f, outputs[2 * j + 1][i]); + } + } +} + +TEST(FTINTRM) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double a; + float b; + int32_t c; + int32_t d; + int64_t e; + int64_t f; + }; + Test test; + + const int kTableLength = 9; + + // clang-format off + double inputs_d[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + float inputs_s[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + double outputs_w[kTableLength] = { + 3.0, 3.0, 3.0, -4.0, -4.0, -4.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_l[kTableLength] = { + 3.0, 3.0, 3.0, -4.0, -4.0, -4.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(Test, b))); + __ ftintrm_w_d(f10, f8); + __ ftintrm_w_s(f11, f9); + __ ftintrm_l_d(f12, f8); + __ ftintrm_l_s(f13, f9); + __ Fst_s(f10, MemOperand(a0, offsetof(Test, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(Test, d))); + __ Fst_d(f12, MemOperand(a0, offsetof(Test, e))); + __ Fst_d(f13, MemOperand(a0, offsetof(Test, f))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_d[i]; + test.b = inputs_s[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs_w[i]); + CHECK_EQ(test.d, outputs_w[i]); + CHECK_EQ(test.e, outputs_l[i]); + CHECK_EQ(test.f, outputs_l[i]); + } +} + +TEST(FTINTRP) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double a; + float b; + int32_t c; + int32_t d; + int64_t e; + int64_t f; + }; + Test test; + + const int kTableLength = 9; + + // clang-format off + double inputs_d[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + float inputs_s[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + double outputs_w[kTableLength] = { + 4.0, 4.0, 4.0, -3.0, -3.0, -3.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_l[kTableLength] = { + 4.0, 4.0, 4.0, -3.0, -3.0, -3.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(Test, b))); + __ ftintrp_w_d(f10, f8); + __ ftintrp_w_s(f11, f9); + __ ftintrp_l_d(f12, f8); + __ ftintrp_l_s(f13, f9); + __ Fst_s(f10, MemOperand(a0, offsetof(Test, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(Test, d))); + __ Fst_d(f12, MemOperand(a0, offsetof(Test, e))); + __ Fst_d(f13, MemOperand(a0, offsetof(Test, f))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_d[i]; + test.b = inputs_s[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs_w[i]); + CHECK_EQ(test.d, outputs_w[i]); + CHECK_EQ(test.e, outputs_l[i]); + CHECK_EQ(test.f, outputs_l[i]); + } +} + +TEST(FTINTRZ) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double a; + float b; + int32_t c; + int32_t d; + int64_t e; + int64_t f; + }; + Test test; + + const int kTableLength = 9; + + // clang-format off + double inputs_d[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + float inputs_s[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + double outputs_w[kTableLength] = { + 3.0, 3.0, 3.0, -3.0, -3.0, -3.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_l[kTableLength] = { + 3.0, 3.0, 3.0, -3.0, -3.0, -3.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(Test, b))); + __ ftintrz_w_d(f10, f8); + __ ftintrz_w_s(f11, f9); + __ ftintrz_l_d(f12, f8); + __ ftintrz_l_s(f13, f9); + __ Fst_s(f10, MemOperand(a0, offsetof(Test, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(Test, d))); + __ Fst_d(f12, MemOperand(a0, offsetof(Test, e))); + __ Fst_d(f13, MemOperand(a0, offsetof(Test, f))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_d[i]; + test.b = inputs_s[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs_w[i]); + CHECK_EQ(test.d, outputs_w[i]); + CHECK_EQ(test.e, outputs_l[i]); + CHECK_EQ(test.f, outputs_l[i]); + } +} + +TEST(FTINTRNE) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double a; + float b; + int32_t c; + int32_t d; + int64_t e; + int64_t f; + }; + Test test; + + const int kTableLength = 9; + + // clang-format off + double inputs_d[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + float inputs_s[kTableLength] = { + 3.1, 3.6, 3.5, -3.1, -3.6, -3.5, + 2147483648.0, + std::numeric_limits<double>::quiet_NaN(), + std::numeric_limits<double>::infinity() + }; + double outputs_w[kTableLength] = { + 3.0, 4.0, 4.0, -3.0, -4.0, -4.0, + kFPUInvalidResult, 0, + kFPUInvalidResult}; + double outputs_l[kTableLength] = { + 3.0, 4.0, 4.0, -3.0, -4.0, -4.0, + 2147483648.0, 0, + static_cast<double>(kFPU64InvalidResult)}; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(Test, b))); + __ ftintrne_w_d(f10, f8); + __ ftintrne_w_s(f11, f9); + __ ftintrne_l_d(f12, f8); + __ ftintrne_l_s(f13, f9); + __ Fst_s(f10, MemOperand(a0, offsetof(Test, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(Test, d))); + __ Fst_d(f12, MemOperand(a0, offsetof(Test, e))); + __ Fst_d(f13, MemOperand(a0, offsetof(Test, f))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_d[i]; + test.b = inputs_s[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs_w[i]); + CHECK_EQ(test.d, outputs_w[i]); + CHECK_EQ(test.e, outputs_l[i]); + CHECK_EQ(test.f, outputs_l[i]); + } +} + +TEST(FRINT) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double a; + float b; + double c; + float d; + int fcsr; + }; + Test test; + + const int kTableLength = 32; + + // clang-format off + double inputs_d[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E147, + 1.7976931348623157E+308, 6.27463370218383111104242366943E-307, + 309485009821345068724781056.89, + 2.1, 2.6, 2.5, 3.1, 3.6, 3.5, + -2.1, -2.6, -2.5, -3.1, -3.6, -3.5, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<double>::max() - 0.1, + std::numeric_limits<double>::infinity() + }; + float inputs_s[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E37, + 1.7976931348623157E+38, 6.27463370218383111104242366943E-37, + 309485009821345068724781056.89, + 2.1, 2.6, 2.5, 3.1, 3.6, 3.5, + -2.1, -2.6, -2.5, -3.1, -3.6, -3.5, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<float>::lowest() + 0.6, + std::numeric_limits<float>::infinity() + }; + float outputs_RN_S[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E37, + 1.7976931348623157E38, 0, + 309485009821345068724781057.0, + 2.0, 3.0, 2.0, 3.0, 4.0, 4.0, + -2.0, -3.0, -2.0, -3.0, -4.0, -4.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<float>::lowest() + 1, + std::numeric_limits<float>::infinity() + }; + double outputs_RN_D[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E147, + 1.7976931348623157E308, 0, + 309485009821345068724781057.0, + 2.0, 3.0, 2.0, 3.0, 4.0, 4.0, + -2.0, -3.0, -2.0, -3.0, -4.0, -4.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<double>::max(), + std::numeric_limits<double>::infinity() + }; + float outputs_RZ_S[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E37, + 1.7976931348623157E38, 0, + 309485009821345068724781057.0, + 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, + -2.0, -2.0, -2.0, -3.0, -3.0, -3.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<float>::lowest() + 1, + std::numeric_limits<float>::infinity() + }; + double outputs_RZ_D[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E147, + 1.7976931348623157E308, 0, + 309485009821345068724781057.0, + 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, + -2.0, -2.0, -2.0, -3.0, -3.0, -3.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<double>::max() - 1, + std::numeric_limits<double>::infinity() + }; + float outputs_RP_S[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E37, + 1.7976931348623157E38, 1, + 309485009821345068724781057.0, + 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, + -2.0, -2.0, -2.0, -3.0, -3.0, -3.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<float>::lowest() + 1, + std::numeric_limits<float>::infinity() + }; + double outputs_RP_D[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E147, + 1.7976931348623157E308, 1, + 309485009821345068724781057.0, + 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, + -2.0, -2.0, -2.0, -3.0, -3.0, -3.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<double>::max(), + std::numeric_limits<double>::infinity() + }; + float outputs_RM_S[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E37, + 1.7976931348623157E38, 0, + 309485009821345068724781057.0, + 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, + -3.0, -3.0, -3.0, -4.0, -4.0, -4.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<float>::lowest() + 1, + std::numeric_limits<float>::infinity() + }; + double outputs_RM_D[kTableLength] = { + 18446744073709551617.0, 4503599627370496.0, -4503599627370496.0, + 1.26782468584154733584017312973E30, 1.44860108245951772690707170478E147, + 1.7976931348623157E308, 0, + 309485009821345068724781057.0, + 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, + -3.0, -3.0, -3.0, -4.0, -4.0, -4.0, + 37778931862957161709568.0, 37778931862957161709569.0, + 37778931862957161709580.0, 37778931862957161709581.0, + 37778931862957161709582.0, 37778931862957161709583.0, + 37778931862957161709584.0, 37778931862957161709585.0, + 37778931862957161709586.0, 37778931862957161709587.0, + std::numeric_limits<double>::max(), + std::numeric_limits<double>::infinity() + }; + // clang-format on + + int fcsr_inputs[4] = {kRoundToNearest, kRoundToZero, kRoundToPlusInf, + kRoundToMinusInf}; + double* outputs_d[4] = {outputs_RN_D, outputs_RZ_D, outputs_RP_D, + outputs_RM_D}; + float* outputs_s[4] = {outputs_RN_S, outputs_RZ_S, outputs_RP_S, + outputs_RM_S}; + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(Test, b))); + __ xor_(a5, a5, a5); + __ Ld_w(a5, MemOperand(a0, offsetof(Test, fcsr))); + __ movfcsr2gr(a4); + __ movgr2fcsr(a5); + __ frint_d(f10, f8); + __ frint_s(f11, f9); + __ Fst_d(f10, MemOperand(a0, offsetof(Test, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(Test, d))); + __ movgr2fcsr(a4); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int j = 0; j < 4; j++) { + test.fcsr = fcsr_inputs[j]; + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_d[i]; + test.b = inputs_s[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs_d[j][i]); + CHECK_EQ(test.d, outputs_s[j][i]); + } + } +} + +TEST(FMOV) { + const int kTableLength = 7; + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + double a; + float b; + double c; + float d; + }; + + TestFloat test; + + // clang-format off + double inputs_D[kTableLength] = { + 5.3, -5.3, 0.29, -0.29, 0, + std::numeric_limits<double>::max(), + -std::numeric_limits<double>::max() + }; + float inputs_S[kTableLength] = { + 4.8, -4.8, 0.29, -0.29, 0, + std::numeric_limits<float>::max(), + -std::numeric_limits<float>::max() + }; + + double outputs_D[kTableLength] = { + 5.3, -5.3, 0.29, -0.29, 0, + std::numeric_limits<double>::max(), + -std::numeric_limits<double>::max() + }; + + float outputs_S[kTableLength] = { + 4.8, -4.8, 0.29, -0.29, 0, + std::numeric_limits<float>::max(), + -std::numeric_limits<float>::max() + }; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ fmov_d(f10, f8); + __ fmov_s(f11, f9); + __ Fst_d(f10, MemOperand(a0, offsetof(TestFloat, c))); + __ Fst_s(f11, MemOperand(a0, offsetof(TestFloat, d))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputs_D[i]; + test.b = inputs_S[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, outputs_D[i]); + CHECK_EQ(test.d, outputs_S[i]); + } +} + +TEST(LA14) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + struct T { + double a; + double b; + double c; + double d; + int64_t high; + int64_t low; + }; + T t; + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + __ Fld_d(f8, MemOperand(a0, offsetof(T, a))); + __ Fld_d(f9, MemOperand(a0, offsetof(T, b))); + + __ movfr2gr_s(a4, f8); + __ movfrh2gr_s(a5, f8); + __ movfr2gr_d(a6, f9); + + __ movgr2fr_w(f9, a4); + __ movgr2frh_w(f9, a5); + __ movgr2fr_d(f8, a6); + + __ Fst_d(f8, MemOperand(a0, offsetof(T, a))); + __ Fst_d(f9, MemOperand(a0, offsetof(T, c))); + + __ Fld_d(f8, MemOperand(a0, offsetof(T, d))); + __ movfrh2gr_s(a4, f8); + __ movfr2gr_s(a5, f8); + + __ St_d(a4, MemOperand(a0, offsetof(T, high))); + __ St_d(a5, MemOperand(a0, offsetof(T, low))); + + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + + t.a = 1.5e22; + t.b = 2.75e11; + t.c = 17.17; + t.d = -2.75e11; + f.Call(&t, 0, 0, 0, 0); + CHECK_EQ(2.75e11, t.a); + CHECK_EQ(2.75e11, t.b); + CHECK_EQ(1.5e22, t.c); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFC25001D1L), t.high); + CHECK_EQ(static_cast<int64_t>(0xFFFFFFFFBF800000L), t.low); + + t.a = -1.5e22; + t.b = -2.75e11; + t.c = 17.17; + t.d = 274999868928.0; + f.Call(&t, 0, 0, 0, 0); + CHECK_EQ(-2.75e11, t.a); + CHECK_EQ(-2.75e11, t.b); + CHECK_EQ(-1.5e22, t.c); + CHECK_EQ(static_cast<int64_t>(0x425001D1L), t.high); + CHECK_EQ(static_cast<int64_t>(0x3F800000L), t.low); +} + +uint64_t run_bceqz(int fcc_value, int32_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0); + __ li(t0, fcc_value); + __ b(&main_block); + // Block 1 + for (int32_t i = -104; i <= -55; ++i) { + __ addi_d(a2, a2, 0x1); + } + __ b(&L); + + // Block 2 + for (int32_t i = -53; i <= -4; ++i) { + __ addi_d(a2, a2, 0x10); + } + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ movcf2gr(t1, FCC0); + __ movgr2cf(FCC0, t0); + __ bceqz(FCC0, offset); + __ bind(&L); + __ movgr2cf(FCC0, t1); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + for (int32_t i = 4; i <= 53; ++i) { + __ addi_d(a2, a2, 0x100); + } + __ b(&L); + + // Block 5 + for (int32_t i = 55; i <= 104; ++i) { + __ addi_d(a2, a2, 0x300); + } + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(0, 0, 0, 0, 0)); + + return res; +} + +TEST(BCEQZ) { + CcTest::InitializeVM(); + struct TestCaseBceqz { + int fcc; + int32_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBceqz tc[] = { + // fcc, offset, expected_res + { 0, -90, 0x24 }, + { 0, -27, 0x180 }, + { 0, 47, 0x700 }, + { 0, 70, 0x6900 }, + { 1, -27, 0 }, + { 1, 47, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBceqz); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bceqz(tc[i].fcc, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +uint64_t run_bcnez(int fcc_value, int32_t offset) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label main_block, L; + __ li(a2, 0); + __ li(t0, fcc_value); + __ b(&main_block); + // Block 1 + for (int32_t i = -104; i <= -55; ++i) { + __ addi_d(a2, a2, 0x1); + } + __ b(&L); + + // Block 2 + for (int32_t i = -53; i <= -4; ++i) { + __ addi_d(a2, a2, 0x10); + } + __ b(&L); + + // Block 3 (Main) + __ bind(&main_block); + __ movcf2gr(t1, FCC0); + __ movgr2cf(FCC0, t0); + __ bcnez(FCC0, offset); + __ bind(&L); + __ movgr2cf(FCC0, t1); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + // Block 4 + for (int32_t i = 4; i <= 53; ++i) { + __ addi_d(a2, a2, 0x100); + } + __ b(&L); + + // Block 5 + for (int32_t i = 55; i <= 104; ++i) { + __ addi_d(a2, a2, 0x300); + } + __ b(&L); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + uint64_t res = reinterpret_cast<uint64_t>(f.Call(0, 0, 0, 0, 0)); + + return res; +} + +TEST(BCNEZ) { + CcTest::InitializeVM(); + struct TestCaseBcnez { + int fcc; + int32_t offset; + uint64_t expected_res; + }; + + // clang-format off + struct TestCaseBcnez tc[] = { + // fcc, offset, expected_res + { 1, -90, 0x24 }, + { 1, -27, 0x180 }, + { 1, 47, 0x700 }, + { 1, 70, 0x6900 }, + { 0, -27, 0 }, + { 0, 47, 0 }, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCaseBcnez); + for (size_t i = 0; i < nr_test_cases; ++i) { + uint64_t res = run_bcnez(tc[i].fcc, tc[i].offset); + CHECK_EQ(tc[i].expected_res, res); + } +} + +TEST(jump_tables1) { + // Test jump tables with forward jumps. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + const int kNumCases = 512; + int values[kNumCases]; + isolate->random_number_generator()->NextBytes(values, sizeof(values)); + Label labels[kNumCases]; + + __ addi_d(sp, sp, -8); + __ St_d(ra, MemOperand(sp, 0)); + __ Align(8); + + Label done; + { + __ BlockTrampolinePoolFor(kNumCases * 2 + 6); + __ pcaddi(ra, 2); + __ slli_d(t7, a0, 3); + __ add_d(t7, t7, ra); + __ Ld_d(t7, MemOperand(t7, 4 * kInstrSize)); + __ jirl(zero_reg, t7, 0); + __ nop(); + for (int i = 0; i < kNumCases; ++i) { + __ dd(&labels[i]); + } + } + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + __ lu12i_w(a2, (values[i] >> 12) & 0xFFFFF); + __ ori(a2, a2, values[i] & 0xFFF); + __ b(&done); + __ nop(); + } + + __ bind(&done); + __ Ld_d(ra, MemOperand(sp, 0)); + __ addi_d(sp, sp, 8); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CHECK_EQ(0, assm.UnboundLabelsCount()); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + auto f = GeneratedCode<F1>::FromCode(*code); + for (int i = 0; i < kNumCases; ++i) { + int64_t res = reinterpret_cast<int64_t>(f.Call(i, 0, 0, 0, 0)); + ::printf("f(%d) = %" PRId64 "\n", i, res); + CHECK_EQ((values[i]), static_cast<int>(res)); + } +} + +TEST(jump_tables2) { + // Test jump tables with backward jumps. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + const int kNumCases = 512; + int values[kNumCases]; + isolate->random_number_generator()->NextBytes(values, sizeof(values)); + Label labels[kNumCases]; + + __ addi_d(sp, sp, -8); + __ St_d(ra, MemOperand(sp, 0)); + + Label done, dispatch; + __ b(&dispatch); + __ nop(); + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + __ lu12i_w(a2, (values[i] >> 12) & 0xFFFFF); + __ ori(a2, a2, values[i] & 0xFFF); + __ b(&done); + __ nop(); + } + + __ Align(8); + __ bind(&dispatch); + { + __ BlockTrampolinePoolFor(kNumCases * 2 + 6); + __ pcaddi(ra, 2); + __ slli_d(t7, a0, 3); + __ add_d(t7, t7, ra); + __ Ld_d(t7, MemOperand(t7, 4 * kInstrSize)); + __ jirl(zero_reg, t7, 0); + __ nop(); + for (int i = 0; i < kNumCases; ++i) { + __ dd(&labels[i]); + } + } + + __ bind(&done); + __ Ld_d(ra, MemOperand(sp, 0)); + __ addi_d(sp, sp, 8); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + auto f = GeneratedCode<F1>::FromCode(*code); + for (int i = 0; i < kNumCases; ++i) { + int64_t res = reinterpret_cast<int64_t>(f.Call(i, 0, 0, 0, 0)); + ::printf("f(%d) = %" PRId64 "\n", i, res); + CHECK_EQ(values[i], res); + } +} + +TEST(jump_tables3) { + // Test jump tables with backward jumps and embedded heap objects. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + const int kNumCases = 512; + Handle<Object> values[kNumCases]; + for (int i = 0; i < kNumCases; ++i) { + double value = isolate->random_number_generator()->NextDouble(); + values[i] = isolate->factory()->NewHeapNumber<AllocationType::kOld>(value); + } + Label labels[kNumCases]; + Object obj; + int64_t imm64; + + __ addi_d(sp, sp, -8); + __ St_d(ra, MemOperand(sp, 0)); + + Label done, dispatch; + __ b(&dispatch); + __ nop(); + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + obj = *values[i]; + imm64 = obj.ptr(); + __ lu12i_w(a2, (imm64 >> 12) & 0xFFFFF); + __ ori(a2, a2, imm64 & 0xFFF); + __ lu32i_d(a2, (imm64 >> 32) & 0xFFFFF); + __ lu52i_d(a2, a2, (imm64 >> 52) & 0xFFF); + __ b(&done); + } + + __ Align(8); + __ bind(&dispatch); + { + __ BlockTrampolinePoolFor(kNumCases * 2 + 6); + __ pcaddi(ra, 2); + __ slli_d(t7, a0, 3); // In delay slot. + __ add_d(t7, t7, ra); + __ Ld_d(t7, MemOperand(t7, 4 * kInstrSize)); + __ jirl(zero_reg, t7, 0); + __ nop(); + for (int i = 0; i < kNumCases; ++i) { + __ dd(&labels[i]); + } + } + __ bind(&done); + __ Ld_d(ra, MemOperand(sp, 0)); + __ addi_d(sp, sp, 8); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + auto f = GeneratedCode<F1>::FromCode(*code); + for (int i = 0; i < kNumCases; ++i) { + Handle<Object> result( + Object(reinterpret_cast<Address>(f.Call(i, 0, 0, 0, 0))), isolate); +#ifdef OBJECT_PRINT + ::printf("f(%d) = ", i); + result->Print(std::cout); + ::printf("\n"); +#endif + CHECK(values[i].is_identical_to(result)); + } +} + +uint64_t run_li_macro(int64_t imm, LiFlags mode, int32_t num_instr = 0) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + Label code_start; + __ bind(&code_start); + __ li(a2, imm, mode); + if (num_instr > 0) { + CHECK_EQ(assm.InstructionsGeneratedSince(&code_start), num_instr); + CHECK_EQ(__ InstrCountForLi64Bit(imm), num_instr); + } + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + auto f = GeneratedCode<F2>::FromCode(*code); + + uint64_t res = reinterpret_cast<uint64_t>(f.Call(0, 0, 0, 0, 0)); + + return res; +} + +TEST(li_macro) { + CcTest::InitializeVM(); + + // Test li macro-instruction for border cases. + + struct TestCase_li { + uint64_t imm; + int32_t num_instr; + }; + // clang-format off + struct TestCase_li tc[] = { + // imm, num_instr + {0xFFFFFFFFFFFFF800, 1}, // min_int12 + // The test case above generates addi_d instruction. + // This is int12 value and we can load it using just addi_d. + { 0x800, 1}, // max_int12 + 1 + // Generates ori + // max_int12 + 1 is not int12 but is uint12, just use ori. + {0xFFFFFFFFFFFFF7FF, 2}, // min_int12 - 1 + // Generates lu12i + ori + // We load int32 value using lu12i_w + ori. + { 0x801, 1}, // max_int12 + 2 + // Generates ori + // Also an uint1 value, use ori. + { 0x00001000, 1}, // max_uint12 + 1 + // Generates lu12i_w + // Low 12 bits are 0, load value using lu12i_w. + { 0x00001001, 2}, // max_uint12 + 2 + // Generates lu12i_w + ori + // We have to generate two instructions in this case. + {0x00000000FFFFFFFF, 2}, // max_uint32 + // addi_w + lu32i_d + {0x00000000FFFFFFFE, 2}, // max_uint32 - 1 + // addi_w + lu32i_d + {0xFFFFFFFF80000000, 1}, // min_int32 + // lu12i_w + {0x0000000080000000, 2}, // max_int32 + 1 + // lu12i_w + lu32i_d + {0xFFFF0000FFFF8765, 3}, + // lu12i_w + ori + lu32i_d + {0x1234ABCD87654321, 4}, + // lu12i_w + ori + lu32i_d + lu52i_d + {0xFFFF789100000000, 2}, + // xor + lu32i_d + {0xF12F789100000000, 3}, + // xor + lu32i_d + lu52i_d + {0xF120000000000800, 2}, + // ori + lu52i_d + {0xFFF0000000000000, 1}, + // lu52i_d + {0xF100000000000000, 1}, + {0x0122000000000000, 2}, + {0x1234FFFF77654321, 4}, + {0x1230000077654321, 3}, + }; + // clang-format on + + size_t nr_test_cases = sizeof(tc) / sizeof(TestCase_li); + for (size_t i = 0; i < nr_test_cases; ++i) { + CHECK_EQ(tc[i].imm, + run_li_macro(tc[i].imm, OPTIMIZE_SIZE, tc[i].num_instr)); + CHECK_EQ(tc[i].imm, run_li_macro(tc[i].imm, CONSTANT_SIZE)); + if (is_int48(tc[i].imm)) { + CHECK_EQ(tc[i].imm, run_li_macro(tc[i].imm, ADDRESS_LOAD)); + } + } +} + +TEST(FMIN_FMAX) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + double a; + double b; + float c; + float d; + double e; + double f; + float g; + float h; + }; + + TestFloat test; + const double dnan = std::numeric_limits<double>::quiet_NaN(); + const double dinf = std::numeric_limits<double>::infinity(); + const double dminf = -std::numeric_limits<double>::infinity(); + const float fnan = std::numeric_limits<float>::quiet_NaN(); + const float finf = std::numeric_limits<float>::infinity(); + const float fminf = -std::numeric_limits<float>::infinity(); + const int kTableLength = 13; + + // clang-format off + double inputsa[kTableLength] = {2.0, 3.0, dnan, 3.0, -0.0, 0.0, dinf, + dnan, 42.0, dinf, dminf, dinf, dnan}; + double inputsb[kTableLength] = {3.0, 2.0, 3.0, dnan, 0.0, -0.0, dnan, + dinf, dinf, 42.0, dinf, dminf, dnan}; + double outputsdmin[kTableLength] = {2.0, 2.0, 3.0, 3.0, -0.0, + -0.0, dinf, dinf, 42.0, 42.0, + dminf, dminf, dnan}; + double outputsdmax[kTableLength] = {3.0, 3.0, 3.0, 3.0, 0.0, 0.0, dinf, + dinf, dinf, dinf, dinf, dinf, dnan}; + + float inputsc[kTableLength] = {2.0, 3.0, fnan, 3.0, -0.0, 0.0, finf, + fnan, 42.0, finf, fminf, finf, fnan}; + float inputsd[kTableLength] = {3.0, 2.0, 3.0, fnan, 0.0, -0.0, fnan, + finf, finf, 42.0, finf, fminf, fnan}; + float outputsfmin[kTableLength] = {2.0, 2.0, 3.0, 3.0, -0.0, + -0.0, finf, finf, 42.0, 42.0, + fminf, fminf, fnan}; + float outputsfmax[kTableLength] = {3.0, 3.0, 3.0, 3.0, 0.0, 0.0, finf, + finf, finf, finf, finf, finf, fnan}; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ Fld_s(f10, MemOperand(a0, offsetof(TestFloat, c))); + __ Fld_s(f11, MemOperand(a0, offsetof(TestFloat, d))); + __ fmin_d(f12, f8, f9); + __ fmax_d(f13, f8, f9); + __ fmin_s(f14, f10, f11); + __ fmax_s(f15, f10, f11); + __ Fst_d(f12, MemOperand(a0, offsetof(TestFloat, e))); + __ Fst_d(f13, MemOperand(a0, offsetof(TestFloat, f))); + __ Fst_s(f14, MemOperand(a0, offsetof(TestFloat, g))); + __ Fst_s(f15, MemOperand(a0, offsetof(TestFloat, h))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 4; i < kTableLength; i++) { + test.a = inputsa[i]; + test.b = inputsb[i]; + test.c = inputsc[i]; + test.d = inputsd[i]; + + f.Call(&test, 0, 0, 0, 0); + + CHECK_EQ(0, memcmp(&test.e, &outputsdmin[i], sizeof(test.e))); + CHECK_EQ(0, memcmp(&test.f, &outputsdmax[i], sizeof(test.f))); + CHECK_EQ(0, memcmp(&test.g, &outputsfmin[i], sizeof(test.g))); + CHECK_EQ(0, memcmp(&test.h, &outputsfmax[i], sizeof(test.h))); + } +} + +TEST(FMINA_FMAXA) { + const int kTableLength = 23; + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + const double dnan = std::numeric_limits<double>::quiet_NaN(); + const double dinf = std::numeric_limits<double>::infinity(); + const double dminf = -std::numeric_limits<double>::infinity(); + const float fnan = std::numeric_limits<float>::quiet_NaN(); + const float finf = std::numeric_limits<float>::infinity(); + const float fminf = std::numeric_limits<float>::infinity(); + + struct TestFloat { + double a; + double b; + double resd1; + double resd2; + float c; + float d; + float resf1; + float resf2; + }; + + TestFloat test; + // clang-format off + double inputsa[kTableLength] = { + 5.3, 4.8, 6.1, 9.8, 9.8, 9.8, -10.0, -8.9, -9.8, -10.0, -8.9, -9.8, + dnan, 3.0, -0.0, 0.0, dinf, dnan, 42.0, dinf, dminf, dinf, dnan}; + double inputsb[kTableLength] = { + 4.8, 5.3, 6.1, -10.0, -8.9, -9.8, 9.8, 9.8, 9.8, -9.8, -11.2, -9.8, + 3.0, dnan, 0.0, -0.0, dnan, dinf, dinf, 42.0, dinf, dminf, dnan}; + double resd1[kTableLength] = { + 4.8, 4.8, 6.1, 9.8, -8.9, -9.8, 9.8, -8.9, -9.8, -9.8, -8.9, -9.8, + 3.0, 3.0, -0.0, -0.0, dinf, dinf, 42.0, 42.0, dminf, dminf, dnan}; + double resd2[kTableLength] = { + 5.3, 5.3, 6.1, -10.0, 9.8, 9.8, -10.0, 9.8, 9.8, -10.0, -11.2, -9.8, + 3.0, 3.0, 0.0, 0.0, dinf, dinf, dinf, dinf, dinf, dinf, dnan}; + float inputsc[kTableLength] = { + 5.3, 4.8, 6.1, 9.8, 9.8, 9.8, -10.0, -8.9, -9.8, -10.0, -8.9, -9.8, + fnan, 3.0, -0.0, 0.0, finf, fnan, 42.0, finf, fminf, finf, fnan}; + float inputsd[kTableLength] = { + 4.8, 5.3, 6.1, -10.0, -8.9, -9.8, 9.8, 9.8, 9.8, -9.8, -11.2, -9.8, + 3.0, fnan, -0.0, 0.0, fnan, finf, finf, 42.0, finf, fminf, fnan}; + float resf1[kTableLength] = { + 4.8, 4.8, 6.1, 9.8, -8.9, -9.8, 9.8, -8.9, -9.8, -9.8, -8.9, -9.8, + 3.0, 3.0, -0.0, -0.0, finf, finf, 42.0, 42.0, fminf, fminf, fnan}; + float resf2[kTableLength] = { + 5.3, 5.3, 6.1, -10.0, 9.8, 9.8, -10.0, 9.8, 9.8, -10.0, -11.2, -9.8, + 3.0, 3.0, 0.0, 0.0, finf, finf, finf, finf, finf, finf, fnan}; + // clang-format on + + __ Fld_d(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ Fld_s(f10, MemOperand(a0, offsetof(TestFloat, c))); + __ Fld_s(f11, MemOperand(a0, offsetof(TestFloat, d))); + __ fmina_d(f12, f8, f9); + __ fmaxa_d(f13, f8, f9); + __ fmina_s(f14, f10, f11); + __ fmaxa_s(f15, f10, f11); + __ Fst_d(f12, MemOperand(a0, offsetof(TestFloat, resd1))); + __ Fst_d(f13, MemOperand(a0, offsetof(TestFloat, resd2))); + __ Fst_s(f14, MemOperand(a0, offsetof(TestFloat, resf1))); + __ Fst_s(f15, MemOperand(a0, offsetof(TestFloat, resf2))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputsa[i]; + test.b = inputsb[i]; + test.c = inputsc[i]; + test.d = inputsd[i]; + f.Call(&test, 0, 0, 0, 0); + if (i < kTableLength - 1) { + CHECK_EQ(test.resd1, resd1[i]); + CHECK_EQ(test.resd2, resd2[i]); + CHECK_EQ(test.resf1, resf1[i]); + CHECK_EQ(test.resf2, resf2[i]); + } else { + CHECK(std::isnan(test.resd1)); + CHECK(std::isnan(test.resd2)); + CHECK(std::isnan(test.resf1)); + CHECK(std::isnan(test.resf2)); + } + } +} + +TEST(FADD) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + double a; + double b; + double c; + float d; + float e; + float f; + }; + + TestFloat test; + + __ Fld_d(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ fadd_d(f10, f8, f9); + __ Fst_d(f10, MemOperand(a0, offsetof(TestFloat, c))); + + __ Fld_s(f11, MemOperand(a0, offsetof(TestFloat, d))); + __ Fld_s(f12, MemOperand(a0, offsetof(TestFloat, e))); + __ fadd_s(f13, f11, f12); + __ Fst_s(f13, MemOperand(a0, offsetof(TestFloat, f))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + test.a = 2.0; + test.b = 3.0; + test.d = 2.0; + test.e = 3.0; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, 5.0); + CHECK_EQ(test.f, 5.0); + + test.a = std::numeric_limits<double>::max(); + test.b = -std::numeric_limits<double>::max(); // lowest() + test.d = std::numeric_limits<float>::max(); + test.e = -std::numeric_limits<float>::max(); // lowest() + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.c, 0.0); + CHECK_EQ(test.f, 0.0); + + test.a = std::numeric_limits<double>::max(); + test.b = std::numeric_limits<double>::max(); + test.d = std::numeric_limits<float>::max(); + test.e = std::numeric_limits<float>::max(); + f.Call(&test, 0, 0, 0, 0); + CHECK(!std::isfinite(test.c)); + CHECK(!std::isfinite(test.f)); + + test.a = 5.0; + test.b = std::numeric_limits<double>::signaling_NaN(); + test.d = 5.0; + test.e = std::numeric_limits<float>::signaling_NaN(); + f.Call(&test, 0, 0, 0, 0); + CHECK(std::isnan(test.c)); + CHECK(std::isnan(test.f)); +} + +TEST(FSUB) { + const int kTableLength = 12; + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + float a; + float b; + float resultS; + double c; + double d; + double resultD; + }; + + TestFloat test; + + // clang-format off + double inputfs_D[kTableLength] = { + 5.3, 4.8, 2.9, -5.3, -4.8, -2.9, + 5.3, 4.8, 2.9, -5.3, -4.8, -2.9 + }; + double inputft_D[kTableLength] = { + 4.8, 5.3, 2.9, 4.8, 5.3, 2.9, + -4.8, -5.3, -2.9, -4.8, -5.3, -2.9 + }; + double outputs_D[kTableLength] = { + 0.5, -0.5, 0.0, -10.1, -10.1, -5.8, + 10.1, 10.1, 5.8, -0.5, 0.5, 0.0 + }; + float inputfs_S[kTableLength] = { + 5.3, 4.8, 2.9, -5.3, -4.8, -2.9, + 5.3, 4.8, 2.9, -5.3, -4.8, -2.9 + }; + float inputft_S[kTableLength] = { + 4.8, 5.3, 2.9, 4.8, 5.3, 2.9, + -4.8, -5.3, -2.9, -4.8, -5.3, -2.9 + }; + float outputs_S[kTableLength] = { + 0.5, -0.5, 0.0, -10.1, -10.1, -5.8, + 10.1, 10.1, 5.8, -0.5, 0.5, 0.0 + }; + // clang-format on + + __ Fld_s(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ Fld_d(f10, MemOperand(a0, offsetof(TestFloat, c))); + __ Fld_d(f11, MemOperand(a0, offsetof(TestFloat, d))); + __ fsub_s(f12, f8, f9); + __ fsub_d(f13, f10, f11); + __ Fst_s(f12, MemOperand(a0, offsetof(TestFloat, resultS))); + __ Fst_d(f13, MemOperand(a0, offsetof(TestFloat, resultD))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputfs_S[i]; + test.b = inputft_S[i]; + test.c = inputfs_D[i]; + test.d = inputft_D[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.resultS, outputs_S[i]); + CHECK_EQ(test.resultD, outputs_D[i]); + } +} + +TEST(FMUL) { + const int kTableLength = 4; + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + float a; + float b; + float resultS; + double c; + double d; + double resultD; + }; + + TestFloat test; + // clang-format off + double inputfs_D[kTableLength] = { + 5.3, -5.3, 5.3, -2.9 + }; + double inputft_D[kTableLength] = { + 4.8, 4.8, -4.8, -0.29 + }; + + float inputfs_S[kTableLength] = { + 5.3, -5.3, 5.3, -2.9 + }; + float inputft_S[kTableLength] = { + 4.8, 4.8, -4.8, -0.29 + }; + // clang-format on + __ Fld_s(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ Fld_d(f10, MemOperand(a0, offsetof(TestFloat, c))); + __ Fld_d(f11, MemOperand(a0, offsetof(TestFloat, d))); + __ fmul_s(f12, f8, f9); + __ fmul_d(f13, f10, f11); + __ Fst_s(f12, MemOperand(a0, offsetof(TestFloat, resultS))); + __ Fst_d(f13, MemOperand(a0, offsetof(TestFloat, resultD))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + for (int i = 0; i < kTableLength; i++) { + test.a = inputfs_S[i]; + test.b = inputft_S[i]; + test.c = inputfs_D[i]; + test.d = inputft_D[i]; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.resultS, inputfs_S[i] * inputft_S[i]); + CHECK_EQ(test.resultD, inputfs_D[i] * inputft_D[i]); + } +} + +TEST(FDIV) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct Test { + double dOp1; + double dOp2; + double dRes; + float fOp1; + float fOp2; + float fRes; + }; + + Test test; + + __ movfcsr2gr(a4); + __ movgr2fcsr(zero_reg); + + __ Fld_d(f8, MemOperand(a0, offsetof(Test, dOp1))); + __ Fld_d(f9, MemOperand(a0, offsetof(Test, dOp2))); + __ Fld_s(f10, MemOperand(a0, offsetof(Test, fOp1))); + __ Fld_s(f11, MemOperand(a0, offsetof(Test, fOp2))); + __ fdiv_d(f12, f8, f9); + __ fdiv_s(f13, f10, f11); + __ Fst_d(f12, MemOperand(a0, offsetof(Test, dRes))); + __ Fst_s(f13, MemOperand(a0, offsetof(Test, fRes))); + + __ movgr2fcsr(a4); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + f.Call(&test, 0, 0, 0, 0); + const int test_size = 3; + // clang-format off + double dOp1[test_size] = { + 5.0, DBL_MAX, DBL_MAX}; + + double dOp2[test_size] = { + 2.0, 2.0, -DBL_MAX}; + + double dRes[test_size] = { + 2.5, DBL_MAX / 2.0, -1.0}; + + float fOp1[test_size] = { + 5.0, FLT_MAX, FLT_MAX}; + + float fOp2[test_size] = { + 2.0, 2.0, -FLT_MAX}; + + float fRes[test_size] = { + 2.5, FLT_MAX / 2.0, -1.0}; + // clang-format on + + for (int i = 0; i < test_size; i++) { + test.dOp1 = dOp1[i]; + test.dOp2 = dOp2[i]; + test.fOp1 = fOp1[i]; + test.fOp2 = fOp2[i]; + + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.dRes, dRes[i]); + CHECK_EQ(test.fRes, fRes[i]); + } + + test.dOp1 = DBL_MAX; + test.dOp2 = -0.0; + test.fOp1 = FLT_MAX; + test.fOp2 = -0.0; + + f.Call(&test, 0, 0, 0, 0); + CHECK(!std::isfinite(test.dRes)); + CHECK(!std::isfinite(test.fRes)); + + test.dOp1 = 0.0; + test.dOp2 = -0.0; + test.fOp1 = 0.0; + test.fOp2 = -0.0; + + f.Call(&test, 0, 0, 0, 0); + CHECK(std::isnan(test.dRes)); + CHECK(std::isnan(test.fRes)); + + test.dOp1 = std::numeric_limits<double>::quiet_NaN(); + test.dOp2 = -5.0; + test.fOp1 = std::numeric_limits<float>::quiet_NaN(); + test.fOp2 = -5.0; + + f.Call(&test, 0, 0, 0, 0); + CHECK(std::isnan(test.dRes)); + CHECK(std::isnan(test.fRes)); +} + +TEST(FABS) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + double a; + float b; + }; + + TestFloat test; + + __ movfcsr2gr(a4); + __ movgr2fcsr(zero_reg); + + __ Fld_d(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_s(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ fabs_d(f10, f8); + __ fabs_s(f11, f9); + __ Fst_d(f10, MemOperand(a0, offsetof(TestFloat, a))); + __ Fst_s(f11, MemOperand(a0, offsetof(TestFloat, b))); + + __ movgr2fcsr(a4); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + test.a = -2.0; + test.b = -2.0; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.a, 2.0); + CHECK_EQ(test.b, 2.0); + + test.a = 2.0; + test.b = 2.0; + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.a, 2.0); + CHECK_EQ(test.b, 2.0); + + // Testing biggest positive number + test.a = std::numeric_limits<double>::max(); + test.b = std::numeric_limits<float>::max(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.a, std::numeric_limits<double>::max()); + CHECK_EQ(test.b, std::numeric_limits<float>::max()); + + // Testing smallest negative number + test.a = -std::numeric_limits<double>::max(); // lowest() + test.b = -std::numeric_limits<float>::max(); // lowest() + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.a, std::numeric_limits<double>::max()); + CHECK_EQ(test.b, std::numeric_limits<float>::max()); + + // Testing smallest positive number + test.a = -std::numeric_limits<double>::min(); + test.b = -std::numeric_limits<float>::min(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.a, std::numeric_limits<double>::min()); + CHECK_EQ(test.b, std::numeric_limits<float>::min()); + + // Testing infinity + test.a = + -std::numeric_limits<double>::max() / std::numeric_limits<double>::min(); + test.b = + -std::numeric_limits<float>::max() / std::numeric_limits<float>::min(); + f.Call(&test, 0, 0, 0, 0); + CHECK_EQ(test.a, std::numeric_limits<double>::max() / + std::numeric_limits<double>::min()); + CHECK_EQ(test.b, std::numeric_limits<float>::max() / + std::numeric_limits<float>::min()); + + test.a = std::numeric_limits<double>::quiet_NaN(); + test.b = std::numeric_limits<float>::quiet_NaN(); + f.Call(&test, 0, 0, 0, 0); + CHECK(std::isnan(test.a)); + CHECK(std::isnan(test.b)); + + test.a = std::numeric_limits<double>::signaling_NaN(); + test.b = std::numeric_limits<float>::signaling_NaN(); + f.Call(&test, 0, 0, 0, 0); + CHECK(std::isnan(test.a)); + CHECK(std::isnan(test.b)); +} + +template <class T> +struct TestCaseMaddMsub { + T fj, fk, fa, fd_fmadd, fd_fmsub, fd_fnmadd, fd_fnmsub; +}; + +template <typename T, typename F> +void helper_fmadd_fmsub_fnmadd_fnmsub(F func) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + T x = std::sqrt(static_cast<T>(2.0)); + T y = std::sqrt(static_cast<T>(3.0)); + T z = std::sqrt(static_cast<T>(5.0)); + T x2 = 11.11, y2 = 22.22, z2 = 33.33; + // clang-format off + TestCaseMaddMsub<T> test_cases[] = { + {x, y, z, 0.0, 0.0, 0.0, 0.0}, + {x, y, -z, 0.0, 0.0, 0.0, 0.0}, + {x, -y, z, 0.0, 0.0, 0.0, 0.0}, + {x, -y, -z, 0.0, 0.0, 0.0, 0.0}, + {-x, y, z, 0.0, 0.0, 0.0, 0.0}, + {-x, y, -z, 0.0, 0.0, 0.0, 0.0}, + {-x, -y, z, 0.0, 0.0, 0.0, 0.0}, + {-x, -y, -z, 0.0, 0.0, 0.0, 0.0}, + {-3.14, 0.2345, -123.000056, 0.0, 0.0, 0.0, 0.0}, + {7.3, -23.257, -357.1357, 0.0, 0.0, 0.0, 0.0}, + {x2, y2, z2, 0.0, 0.0, 0.0, 0.0}, + {x2, y2, -z2, 0.0, 0.0, 0.0, 0.0}, + {x2, -y2, z2, 0.0, 0.0, 0.0, 0.0}, + {x2, -y2, -z2, 0.0, 0.0, 0.0, 0.0}, + {-x2, y2, z2, 0.0, 0.0, 0.0, 0.0}, + {-x2, y2, -z2, 0.0, 0.0, 0.0, 0.0}, + {-x2, -y2, z2, 0.0, 0.0, 0.0, 0.0}, + {-x2, -y2, -z2, 0.0, 0.0, 0.0, 0.0}, + }; + // clang-format on + if (std::is_same<T, float>::value) { + __ Fld_s(f8, MemOperand(a0, offsetof(TestCaseMaddMsub<T>, fj))); + __ Fld_s(f9, MemOperand(a0, offsetof(TestCaseMaddMsub<T>, fk))); + __ Fld_s(f10, MemOperand(a0, offsetof(TestCaseMaddMsub<T>, fa))); + } else if (std::is_same<T, double>::value) { + __ Fld_d(f8, MemOperand(a0, offsetof(TestCaseMaddMsub<T>, fj))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestCaseMaddMsub<T>, fk))); + __ Fld_d(f10, MemOperand(a0, offsetof(TestCaseMaddMsub<T>, fa))); + } else { + UNREACHABLE(); + } + + func(assm); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F3>::FromCode(*code); + + const size_t kTableLength = sizeof(test_cases) / sizeof(TestCaseMaddMsub<T>); + TestCaseMaddMsub<T> tc; + for (size_t i = 0; i < kTableLength; i++) { + tc.fj = test_cases[i].fj; + tc.fk = test_cases[i].fk; + tc.fa = test_cases[i].fa; + + f.Call(&tc, 0, 0, 0, 0); + + T res_fmadd; + T res_fmsub; + T res_fnmadd; + T res_fnmsub; + res_fmadd = std::fma(tc.fj, tc.fk, tc.fa); + res_fmsub = std::fma(tc.fj, tc.fk, -tc.fa); + res_fnmadd = -std::fma(tc.fj, tc.fk, tc.fa); + res_fnmsub = -std::fma(tc.fj, tc.fk, -tc.fa); + + CHECK_EQ(tc.fd_fmadd, res_fmadd); + CHECK_EQ(tc.fd_fmsub, res_fmsub); + CHECK_EQ(tc.fd_fnmadd, res_fnmadd); + CHECK_EQ(tc.fd_fnmsub, res_fnmsub); + } +} + +TEST(FMADD_FMSUB_FNMADD_FNMSUB_S) { + helper_fmadd_fmsub_fnmadd_fnmsub<float>([](MacroAssembler& assm) { + __ fmadd_s(f11, f8, f9, f10); + __ Fst_s(f11, MemOperand(a0, offsetof(TestCaseMaddMsub<float>, fd_fmadd))); + __ fmsub_s(f12, f8, f9, f10); + __ Fst_s(f12, MemOperand(a0, offsetof(TestCaseMaddMsub<float>, fd_fmsub))); + __ fnmadd_s(f13, f8, f9, f10); + __ Fst_s(f13, MemOperand(a0, offsetof(TestCaseMaddMsub<float>, fd_fnmadd))); + __ fnmsub_s(f14, f8, f9, f10); + __ Fst_s(f14, MemOperand(a0, offsetof(TestCaseMaddMsub<float>, fd_fnmsub))); + }); +} + +TEST(FMADD_FMSUB_FNMADD_FNMSUB_D) { + helper_fmadd_fmsub_fnmadd_fnmsub<double>([](MacroAssembler& assm) { + __ fmadd_d(f11, f8, f9, f10); + __ Fst_d(f11, MemOperand(a0, offsetof(TestCaseMaddMsub<double>, fd_fmadd))); + __ fmsub_d(f12, f8, f9, f10); + __ Fst_d(f12, MemOperand(a0, offsetof(TestCaseMaddMsub<double>, fd_fmsub))); + __ fnmadd_d(f13, f8, f9, f10); + __ Fst_d(f13, + MemOperand(a0, offsetof(TestCaseMaddMsub<double>, fd_fnmadd))); + __ fnmsub_d(f14, f8, f9, f10); + __ Fst_d(f14, + MemOperand(a0, offsetof(TestCaseMaddMsub<double>, fd_fnmsub))); + }); +} + +/* +TEST(FSQRT_FRSQRT_FRECIP) { + const int kTableLength = 4; + const double deltaDouble = 2E-15; + const float deltaFloat = 2E-7; + const float sqrt2_s = sqrt(2); + const double sqrt2_d = sqrt(2); + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + + struct TestFloat { + float a; + float resultS1; + float resultS2; + float resultS3; + double b; + double resultD1; + double resultD2; + double resultD3; + }; + TestFloat test; + // clang-format off + double inputs_D[kTableLength] = { + 0.0L, 4.0L, 2.0L, 4e-28L + }; + + double outputs_D[kTableLength] = { + 0.0L, 2.0L, sqrt2_d, 2e-14L + }; + float inputs_S[kTableLength] = { + 0.0, 4.0, 2.0, 4e-28 + }; + + float outputs_S[kTableLength] = { + 0.0, 2.0, sqrt2_s, 2e-14 + }; + // clang-format on + __ Fld_s(f8, MemOperand(a0, offsetof(TestFloat, a))); + __ Fld_d(f9, MemOperand(a0, offsetof(TestFloat, b))); + __ fsqrt_s(f10, f8); + __ fsqrt_d(f11, f9); + __ frsqrt_s(f12, f8); + __ frsqrt_d(f13, f9); + __ frecip_s(f14, f8); + __ frecip_d(f15, f9); + __ Fst_s(f10, MemOperand(a0, offsetof(TestFloat, resultS1))); + __ Fst_d(f11, MemOperand(a0, offsetof(TestFloat, resultD1))); + __ Fst_s(f12, MemOperand(a0, offsetof(TestFloat, resultS2))); + __ Fst_d(f13, MemOperand(a0, offsetof(TestFloat, resultD2))); + __ Fst_s(f14, MemOperand(a0, offsetof(TestFloat, resultS3))); + __ Fst_d(f15, MemOperand(a0, offsetof(TestFloat, resultD3))); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = Factory::CodeBuilder(isolate, desc, +CodeKind::STUB).Build(); auto f = GeneratedCode<F3>::FromCode(*code); + + for (int i = 0; i < kTableLength; i++) { + float f1; + double d1; + test.a = inputs_S[i]; + test.b = inputs_D[i]; + + f.Call(&test, 0, 0, 0, 0); + + CHECK_EQ(test.resultS1, outputs_S[i]); + CHECK_EQ(test.resultD1, outputs_D[i]); + + if (i != 0) { + f1 = test.resultS2 - 1.0F/outputs_S[i]; + f1 = (f1 < 0) ? f1 : -f1; + CHECK(f1 <= deltaFloat); + d1 = test.resultD2 - 1.0L/outputs_D[i]; + d1 = (d1 < 0) ? d1 : -d1; + CHECK(d1 <= deltaDouble); + f1 = test.resultS3 - 1.0F/inputs_S[i]; + f1 = (f1 < 0) ? f1 : -f1; + CHECK(f1 <= deltaFloat); + d1 = test.resultD3 - 1.0L/inputs_D[i]; + d1 = (d1 < 0) ? d1 : -d1; + CHECK(d1 <= deltaDouble); + } else { + CHECK_EQ(test.resultS2, 1.0F/outputs_S[i]); + CHECK_EQ(test.resultD2, 1.0L/outputs_D[i]); + CHECK_EQ(test.resultS3, 1.0F/inputs_S[i]); + CHECK_EQ(test.resultD3, 1.0L/inputs_D[i]); + } + } +}*/ + +TEST(LA15) { + // Test chaining of label usages within instructions (issue 1644). + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + Assembler assm(AssemblerOptions{}); + + Label target; + __ beq(a0, a1, &target); + __ nop(); + __ bne(a0, a1, &target); + __ nop(); + __ bind(&target); + __ nop(); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + f.Call(1, 1, 0, 0, 0); +} + +TEST(Trampoline) { + static const int kMaxBranchOffset = (1 << (18 - 1)) - 1; + + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes); + Label done; + size_t nr_calls = kMaxBranchOffset / kInstrSize + 5; + + __ xor_(a2, a2, a2); + __ BranchShort(&done, eq, a0, Operand(a1)); + for (size_t i = 0; i < nr_calls; ++i) { + __ addi_d(a2, a2, 1); + } + __ bind(&done); + __ or_(a0, a2, zero_reg); + __ jirl(zero_reg, ra, 0); + + CodeDesc desc; + assm.GetCode(isolate, &desc); + Handle<Code> code = + Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); + auto f = GeneratedCode<F2>::FromCode(*code); + + int64_t res = reinterpret_cast<int64_t>(f.Call(42, 42, 0, 0, 0)); + CHECK_EQ(0, res); +} + +#undef __ + +} // namespace internal +} // namespace v8 |