From f3d81674439b6d60e660c2c5428d4cd9e65abb9a Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Tue, 12 Nov 2024 22:40:28 +0000 Subject: [PATCH 1/8] [vm/compiler] Do not convert null-aware int? equality to IfThenElse IfThenElse instruction expects a simple Smi comparison and it doesn't support null-aware int? comparison (although it uses the same kTagged representation). TEST=runtime/tests/vm/dart/regress_b378737064_test.dart Fixes b/378737064 Change-Id: Iaf9243ff5505b986bbfe4510834972e75869dc61 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394570 Commit-Queue: Alexander Markov Reviewed-by: Ryan Macnak --- .../vm/dart/regress_b378737064_test.dart | 22 +++++++++++++++++++ runtime/vm/compiler/backend/il.cc | 7 +++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 runtime/tests/vm/dart/regress_b378737064_test.dart diff --git a/runtime/tests/vm/dart/regress_b378737064_test.dart b/runtime/tests/vm/dart/regress_b378737064_test.dart new file mode 100644 index 000000000000..339dfdca8d74 --- /dev/null +++ b/runtime/tests/vm/dart/regress_b378737064_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Regression test for b/378737064. +// +// Verifies that compiler doesn't crash after it incorrectly converts +// null-aware int? comparison to IfThenElse. + +import 'package:expect/expect.dart'; + +@pragma('vm:never-inline') +int foo(int? x, int? y) => x == y ? 0 : 255; + +main() { + Expect.equals(0, foo(0, 0)); + Expect.equals(255, foo(0, 42)); + Expect.equals(0, foo(null, null)); + Expect.equals(255, foo(null, 0x1234567890)); + Expect.equals(0, foo(0x1234567890, 0x1234567890)); + Expect.equals(255, foo(0x1234567890, 0x1234567891)); +} diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index daaf8e4a8a27..17bca748c0d4 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -6564,7 +6564,12 @@ bool IfThenElseInstr::Supports(ConditionInstr* condition, // by if-conversion. return !strict_compare->needs_number_check(); } - if (auto* comparison = condition->AsComparison()) { + if (auto* equality = condition->AsEqualityCompare()) { + // Non-smi comparisons are not supported by if-conversion. + return (equality->input_representation() == kTagged) && + !equality->is_null_aware(); + } + if (auto* comparison = condition->AsRelationalOp()) { // Non-smi comparisons are not supported by if-conversion. return comparison->input_representation() == kTagged; } From e8417935da088fb6417a22d5e11b5f3d5d338999 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Tue, 12 Nov 2024 23:14:37 +0000 Subject: [PATCH 2/8] [vm] Add the RISC-V Zicond extension. TEST=ci, local qemu Change-Id: I0d367b762d989b3f9bae0937c7670b175d111453 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394620 Reviewed-by: Alexander Aprelev Commit-Queue: Ryan Macnak --- .../vm/compiler/assembler/assembler_riscv.cc | 10 +++++++ .../vm/compiler/assembler/assembler_riscv.h | 6 ++++ .../assembler/assembler_riscv_test.cc | 30 +++++++++++++++++++ .../compiler/assembler/disassembler_riscv.cc | 17 +++++++++++ runtime/vm/constants_riscv.h | 10 +++++-- runtime/vm/simulator_riscv.cc | 20 +++++++++++++ runtime/vm/simulator_riscv.h | 1 + 7 files changed, 92 insertions(+), 2 deletions(-) diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc index ca4c559e73e9..db4a4f80845f 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv.cc @@ -1634,6 +1634,16 @@ void MicroAssembler::bseti(Register rd, Register rs1, intx_t shamt) { EmitRType(BSET, shamt, rs1, F3_BSET, rd, OPIMM); } +void MicroAssembler::czeroeqz(Register rd, Register rs1, Register rs2) { + ASSERT(Supports(RV_Zicond)); + EmitRType(CZERO, rs2, rs1, CZEROEQZ, rd, OP); +} + +void MicroAssembler::czeronez(Register rd, Register rs1, Register rs2) { + ASSERT(Supports(RV_Zicond)); + EmitRType(CZERO, rs2, rs1, CZERONEZ, rd, OP); +} + void MicroAssembler::lb(Register rd, Address addr, std::memory_order order) { ASSERT(addr.offset() == 0); ASSERT((order == std::memory_order_acquire) || diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h index b6d5d07c382b..1adbfe302351 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.h +++ b/runtime/vm/compiler/assembler/assembler_riscv.h @@ -606,6 +606,12 @@ class MicroAssembler : public AssemblerBase { void bset(Register rd, Register rs1, Register rs2); void bseti(Register rd, Register rs1, intx_t shamt); + // ==== Zicond: Integer conditional operations ==== + // rd := rs2 == 0 ? 0 : rs1 + void czeroeqz(Register rd, Register rs1, Register rs2); + // rd := rs2 != 0 ? 0 : rs1 + void czeronez(Register rd, Register rs1, Register rs2); + // ==== Zalasr: Load-acquire, store-release ==== void lb(Register rd, Address addr, std::memory_order order); void lh(Register rd, Address addr, std::memory_order order); diff --git a/runtime/vm/compiler/assembler/assembler_riscv_test.cc b/runtime/vm/compiler/assembler/assembler_riscv_test.cc index 4ecb94557d0d..d115882371c6 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv_test.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv_test.cc @@ -6924,6 +6924,36 @@ ASSEMBLER_TEST_RUN(BitSetImmediate2, test) { EXPECT_EQ(-1, Call(test->entry(), -1)); } +ASSEMBLER_TEST_GENERATE(ConditionalZeroIfEqualsZero, assembler) { + __ SetExtensions(RV_GC | RV_Zicond); + __ czeroeqz(A0, A0, A1); + __ ret(); +} +ASSEMBLER_TEST_RUN(ConditionalZeroIfEqualsZero, test) { + EXPECT_DISASSEMBLY( + "0eb55533 czero.eqz a0, a0, a1\n" + " 8082 ret\n"); + + EXPECT_EQ(0, Call(test->entry(), 42, 0)); + EXPECT_EQ(42, Call(test->entry(), 42, 1)); + EXPECT_EQ(42, Call(test->entry(), 42, -1)); +} + +ASSEMBLER_TEST_GENERATE(ConditionalZeroIfNotEqualsZero, assembler) { + __ SetExtensions(RV_GC | RV_Zicond); + __ czeronez(A0, A0, A1); + __ ret(); +} +ASSEMBLER_TEST_RUN(ConditionalZeroIfNotEqualsZero, test) { + EXPECT_DISASSEMBLY( + "0eb57533 czero.nez a0, a0, a1\n" + " 8082 ret\n"); + + EXPECT_EQ(42, Call(test->entry(), 42, 0)); + EXPECT_EQ(0, Call(test->entry(), 42, 1)); + EXPECT_EQ(0, Call(test->entry(), 42, -1)); +} + ASSEMBLER_TEST_GENERATE(LoadByteAcquire, assembler) { __ SetExtensions(RV_GC | RV_Zalasr); __ lb(A0, Address(A1), std::memory_order_acquire); diff --git a/runtime/vm/compiler/assembler/disassembler_riscv.cc b/runtime/vm/compiler/assembler/disassembler_riscv.cc index b2634a937aec..9c1a41293726 100644 --- a/runtime/vm/compiler/assembler/disassembler_riscv.cc +++ b/runtime/vm/compiler/assembler/disassembler_riscv.cc @@ -69,6 +69,7 @@ class RISCVDisassembler { void DisassembleOP_MINMAXCLMUL(Instr instr); void DisassembleOP_ROTATE(Instr instr); void DisassembleOP_BCLRBEXT(Instr instr); + void DisassembleOP_CZERO(Instr instr); void DisassembleOP32(Instr instr); void DisassembleOP32_0(Instr instr); void DisassembleOP32_SUB(Instr instr); @@ -713,6 +714,9 @@ void RISCVDisassembler::DisassembleOP(Instr instr) { Print("zext.h 'rd, 'rs1", instr, RV_Zbb); break; #endif + case CZERO: + DisassembleOP_CZERO(instr); + break; default: UnknownInstruction(instr); } @@ -886,6 +890,19 @@ void RISCVDisassembler::DisassembleOP_BCLRBEXT(Instr instr) { } } +void RISCVDisassembler::DisassembleOP_CZERO(Instr instr) { + switch (instr.funct3()) { + case CZEROEQZ: + Print("czero.eqz 'rd, 'rs1, 'rs2", instr, RV_Zicond); + break; + case CZERONEZ: + Print("czero.nez 'rd, 'rs1, 'rs2", instr, RV_Zicond); + break; + default: + UnknownInstruction(instr); + } +} + void RISCVDisassembler::DisassembleOP32(Instr instr) { switch (instr.funct7()) { case 0: diff --git a/runtime/vm/constants_riscv.h b/runtime/vm/constants_riscv.h index 8200b1156852..fa1b73dc6063 100644 --- a/runtime/vm/constants_riscv.h +++ b/runtime/vm/constants_riscv.h @@ -838,6 +838,9 @@ enum Funct3 { BEXT = 0b101, F3_BINV = 0b001, F3_BSET = 0b001, + + CZEROEQZ = 0b101, + CZERONEZ = 0b111, }; enum Funct7 { @@ -885,6 +888,8 @@ enum Funct7 { BCLRBEXT = 0b0100100, BINV = 0b0110100, BSET = 0b0010100, + + CZERO = 0b0000111, }; enum Funct5 { @@ -1617,10 +1622,11 @@ static constexpr ExtensionSet RV_GC = RV_G | RV_C; static constexpr Extension RV_Zba(6); // Address generation static constexpr Extension RV_Zbb(7); // Basic bit-manipulation static constexpr Extension RV_Zbs(8); // Single-bit instructions +static constexpr Extension RV_Zbc(9); // Carry-less multiplication static constexpr ExtensionSet RV_B = RV_Zba | RV_Zbb | RV_Zbs; static constexpr ExtensionSet RV_GCB = RV_GC | RV_B; -static constexpr Extension RV_Zbc(9); // Carry-less multiplication -static constexpr Extension RV_Zalasr(10); // Load-acquire, store-release +static constexpr Extension RV_Zicond(10); // Integer conditional operations +static constexpr Extension RV_Zalasr(11); // Load-acquire, store-release #if defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_ANDROID) static constexpr ExtensionSet RV_baseline = RV_GCB; diff --git a/runtime/vm/simulator_riscv.cc b/runtime/vm/simulator_riscv.cc index eb44c257195d..353127a9f1a1 100644 --- a/runtime/vm/simulator_riscv.cc +++ b/runtime/vm/simulator_riscv.cc @@ -1332,6 +1332,9 @@ void Simulator::InterpretOP(Instr instr) { pc_ += instr.length(); break; #endif + case CZERO: + InterpretOP_CZERO(instr); + break; default: IllegalInstruction(instr); } @@ -1663,6 +1666,23 @@ void Simulator::InterpretOP_BCLRBEXT(Instr instr) { pc_ += instr.length(); } +DART_FORCE_INLINE +void Simulator::InterpretOP_CZERO(Instr instr) { + switch (instr.funct3()) { + case CZEROEQZ: + set_xreg(instr.rd(), + get_xreg(instr.rs2()) == 0 ? 0 : get_xreg(instr.rs1())); + break; + case CZERONEZ: + set_xreg(instr.rd(), + get_xreg(instr.rs2()) != 0 ? 0 : get_xreg(instr.rs1())); + break; + default: + IllegalInstruction(instr); + } + pc_ += instr.length(); +} + DART_FORCE_INLINE void Simulator::InterpretOP32(Instr instr) { switch (instr.funct7()) { diff --git a/runtime/vm/simulator_riscv.h b/runtime/vm/simulator_riscv.h index 1f9e6f35541f..ee6558390ad9 100644 --- a/runtime/vm/simulator_riscv.h +++ b/runtime/vm/simulator_riscv.h @@ -211,6 +211,7 @@ class Simulator { void InterpretOP_MINMAXCLMUL(Instr instr); void InterpretOP_ROTATE(Instr instr); void InterpretOP_BCLRBEXT(Instr instr); + void InterpretOP_CZERO(Instr instr); void InterpretOP32(Instr instr); void InterpretOP32_0(Instr instr); void InterpretOP32_SUB(Instr instr); From 9b668be16f27c1f3de3b9f91fe81cb8556a7cfa4 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Tue, 12 Nov 2024 23:50:04 +0000 Subject: [PATCH 3/8] [vm] Add the RISC-V Zcb extension. TEST=ci, local qemu Change-Id: Ic9d7a1019e389a25817390dbb2f2f41daab579a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394621 Reviewed-by: Alexander Aprelev Commit-Queue: Ryan Macnak --- .../vm/compiler/assembler/assembler_riscv.cc | 145 ++++ .../vm/compiler/assembler/assembler_riscv.h | 17 + .../assembler/assembler_riscv_test.cc | 309 +++++++ .../compiler/assembler/disassembler_riscv.cc | 57 ++ runtime/vm/constants_riscv.h | 52 +- runtime/vm/simulator_riscv.cc | 764 ++++++++++-------- 6 files changed, 995 insertions(+), 349 deletions(-) diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc index db4a4f80845f..b67291b7e83b 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv.cc @@ -273,6 +273,11 @@ void MicroAssembler::lb(Register rd, Address addr) { void MicroAssembler::lh(Register rd, Address addr) { ASSERT(Supports(RV_I)); + if (Supports(RV_Zcb) && IsCRdp(rd) && IsCRs1p(addr.base()) && + IsCMem2Imm(addr.offset())) { + c_lh(rd, addr); + return; + } EmitIType(addr.offset(), addr.base(), LH, rd, LOAD); } @@ -293,21 +298,41 @@ void MicroAssembler::lw(Register rd, Address addr) { void MicroAssembler::lbu(Register rd, Address addr) { ASSERT(Supports(RV_I)); + if (Supports(RV_Zcb) && IsCRdp(rd) && IsCRs1p(addr.base()) && + IsCMem1Imm(addr.offset())) { + c_lbu(rd, addr); + return; + } EmitIType(addr.offset(), addr.base(), LBU, rd, LOAD); } void MicroAssembler::lhu(Register rd, Address addr) { ASSERT(Supports(RV_I)); + if (Supports(RV_Zcb) && IsCRdp(rd) && IsCRs1p(addr.base()) && + IsCMem2Imm(addr.offset())) { + c_lhu(rd, addr); + return; + } EmitIType(addr.offset(), addr.base(), LHU, rd, LOAD); } void MicroAssembler::sb(Register rs2, Address addr) { ASSERT(Supports(RV_I)); + if (Supports(RV_Zcb) && IsCRs2p(rs2) && IsCRs1p(addr.base()) && + IsCMem1Imm(addr.offset())) { + c_sb(rs2, addr); + return; + } EmitSType(addr.offset(), rs2, addr.base(), SB, STORE); } void MicroAssembler::sh(Register rs2, Address addr) { ASSERT(Supports(RV_I)); + if (Supports(RV_Zcb) && IsCRs2p(rs2) && IsCRs1p(addr.base()) && + IsCMem2Imm(addr.offset())) { + c_sh(rs2, addr); + return; + } EmitSType(addr.offset(), rs2, addr.base(), SH, STORE); } @@ -371,6 +396,12 @@ void MicroAssembler::sltiu(Register rd, Register rs1, intptr_t imm) { void MicroAssembler::xori(Register rd, Register rs1, intptr_t imm) { ASSERT(Supports(RV_I)); + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1) && (imm == -1)) { + c_not(rd, rs1); + return; + } + } EmitIType(imm, rs1, XORI, rd, OPIMM); } @@ -386,6 +417,12 @@ void MicroAssembler::andi(Register rd, Register rs1, intptr_t imm) { c_andi(rd, rs1, imm); return; } + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1) && (imm == 0xFF)) { + c_zextb(rd, rs1); + return; + } + } } EmitIType(imm, rs1, ANDI, rd, OPIMM); } @@ -702,6 +739,16 @@ void MicroAssembler::sraw(Register rd, Register rs1, Register rs2) { void MicroAssembler::mul(Register rd, Register rs1, Register rs2) { ASSERT(Supports(RV_M)); + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) { + c_mul(rd, rs1, rs2); + return; + } + if ((rd == rs2) && IsCRs1p(rs1) && IsCRs2p(rs2)) { + c_mul(rd, rs2, rs1); + return; + } + } EmitRType(MULDIV, rs2, rs1, MUL, rd, OP); } @@ -1401,6 +1448,11 @@ void MicroAssembler::fmvdx(FRegister rd, Register rs1) { #if XLEN >= 64 void MicroAssembler::adduw(Register rd, Register rs1, Register rs2) { ASSERT(Supports(RV_Zba)); + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1) && (rs2 == ZR)) { + return c_zextw(rd, rs1); + } + } EmitRType(ADDUW, rs2, rs1, F3_0, rd, OP32); } #endif @@ -1514,16 +1566,31 @@ void MicroAssembler::minu(Register rd, Register rs1, Register rs2) { void MicroAssembler::sextb(Register rd, Register rs1) { ASSERT(Supports(RV_Zbb)); + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1)) { + return c_sextb(rd, rs1); + } + } EmitRType((Funct7)0b0110000, 0b00100, rs1, SEXT, rd, OPIMM); } void MicroAssembler::sexth(Register rd, Register rs1) { ASSERT(Supports(RV_Zbb)); + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1)) { + return c_sexth(rd, rs1); + } + } EmitRType((Funct7)0b0110000, 0b00101, rs1, SEXT, rd, OPIMM); } void MicroAssembler::zexth(Register rd, Register rs1) { ASSERT(Supports(RV_Zbb)); + if (Supports(RV_Zcb)) { + if ((rd == rs1) && IsCRs1p(rs1)) { + return c_zexth(rd, rs1); + } + } #if XLEN == 32 EmitRType((Funct7)0b0000100, 0b00000, rs1, ZEXT, rd, OP); #elif XLEN == 64 @@ -1975,6 +2042,84 @@ void MicroAssembler::c_ebreak() { Emit16(C_EBREAK); } +void MicroAssembler::c_lbu(Register rd, Address addr) { + ASSERT(Supports(RV_Zcb)); + Emit16(C_LBU | EncodeCRdp(rd) | EncodeCRs1p(addr.base()) | + EncodeCMem1Imm(addr.offset())); +} + +void MicroAssembler::c_lhu(Register rd, Address addr) { + ASSERT(Supports(RV_Zcb)); + Emit16(C_LHU | EncodeCRdp(rd) | EncodeCRs1p(addr.base()) | + EncodeCMem2Imm(addr.offset())); +} + +void MicroAssembler::c_lh(Register rd, Address addr) { + ASSERT(Supports(RV_Zcb)); + Emit16(C_LH | EncodeCRdp(rd) | EncodeCRs1p(addr.base()) | + EncodeCMem2Imm(addr.offset())); +} + +void MicroAssembler::c_sb(Register rs2, Address addr) { + ASSERT(Supports(RV_Zcb)); + Emit16(C_SB | EncodeCRs1p(addr.base()) | EncodeCRs2p(rs2) | + EncodeCMem1Imm(addr.offset())); +} + +void MicroAssembler::c_sh(Register rs2, Address addr) { + ASSERT(Supports(RV_Zcb)); + Emit16(C_SH | EncodeCRs1p(addr.base()) | EncodeCRs2p(rs2) | + EncodeCMem2Imm(addr.offset())); +} + +void MicroAssembler::c_zextb(Register rd, Register rs1) { + ASSERT(Supports(RV_Zcb)); + ASSERT(rd == rs1); + Emit16(C_ZEXTB | EncodeCRs1p(rs1)); +} + +void MicroAssembler::c_sextb(Register rd, Register rs1) { + ASSERT(Supports(RV_Zcb)); + ASSERT(rd == rs1); + Emit16(C_SEXTB | EncodeCRs1p(rs1)); +} + +void MicroAssembler::c_zexth(Register rd, Register rs1) { + ASSERT(Supports(RV_Zcb)); + ASSERT(Supports(RV_Zbb)); + ASSERT(rd == rs1); + Emit16(C_ZEXTH | EncodeCRs1p(rs1)); +} + +void MicroAssembler::c_sexth(Register rd, Register rs1) { + ASSERT(Supports(RV_Zcb)); + ASSERT(Supports(RV_Zbb)); + ASSERT(rd == rs1); + Emit16(C_SEXTH | EncodeCRs1p(rs1)); +} + +#if XLEN >= 64 +void MicroAssembler::c_zextw(Register rd, Register rs1) { + ASSERT(Supports(RV_Zcb)); + ASSERT(Supports(RV_Zba)); + ASSERT(rd == rs1); + Emit16(C_ZEXTW | EncodeCRs1p(rs1)); +} +#endif + +void MicroAssembler::c_mul(Register rd, Register rs1, Register rs2) { + ASSERT(Supports(RV_Zcb)); + ASSERT(Supports(RV_M)); + ASSERT(rd == rs1); + Emit16(C_MUL | EncodeCRs1p(rs1) | EncodeCRs2p(rs2)); +} + +void MicroAssembler::c_not(Register rd, Register rs1) { + ASSERT(Supports(RV_Zcb)); + ASSERT(rd == rs1); + Emit16(C_NOT | EncodeCRs1p(rs1)); +} + static Funct3 InvertFunct3(Funct3 func) { switch (func) { case BEQ: diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h index 1adbfe302351..8132cb085f2f 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.h +++ b/runtime/vm/compiler/assembler/assembler_riscv.h @@ -174,6 +174,7 @@ class MicroAssembler : public AssemblerBase { void mv(Register rd, Register rs) { addi(rd, rs, 0); } void not_(Register rd, Register rs) { xori(rd, rs, -1); } void neg(Register rd, Register rs) { sub(rd, ZR, rs); } + void zextb(Register rd, Register rs) { andi(rd, rs, 0xFF); } void snez(Register rd, Register rs) { sltu(rd, ZR, rs); } void seqz(Register rd, Register rs) { sltiu(rd, rs, 1); } @@ -696,6 +697,22 @@ class MicroAssembler : public AssemblerBase { void c_nop(); void c_ebreak(); + // ==== Zcb: Additional code-size saving instructions ==== + void c_lbu(Register rd, Address addr); + void c_lh(Register rd, Address addr); + void c_lhu(Register rd, Address addr); + void c_sb(Register rs2, Address addr); + void c_sh(Register rs2, Address addr); + void c_zextb(Register rd, Register rs1); + void c_sextb(Register rd, Register rs1); + void c_zexth(Register rd, Register rs1); + void c_sexth(Register rd, Register rs1); +#if XLEN >= 64 + void c_zextw(Register rd, Register rs1); +#endif + void c_mul(Register rd, Register rs1, Register rs2); + void c_not(Register rd, Register rs1); + protected: intptr_t UpdateCBOffset(intptr_t branch_position, intptr_t new_offset); intptr_t UpdateCJOffset(intptr_t branch_position, intptr_t new_offset); diff --git a/runtime/vm/compiler/assembler/assembler_riscv_test.cc b/runtime/vm/compiler/assembler/assembler_riscv_test.cc index d115882371c6..efee349222db 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv_test.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv_test.cc @@ -6954,6 +6954,313 @@ ASSEMBLER_TEST_RUN(ConditionalZeroIfNotEqualsZero, test) { EXPECT_EQ(0, Call(test->entry(), 42, -1)); } +ASSEMBLER_TEST_GENERATE(CompressedLoadByteUnsigned_0, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ lbu(A0, Address(A0, 0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedLoadByteUnsigned_0, test) { + EXPECT_DISASSEMBLY( + " 8108 lbu a0, 0(a0)\n" + " 8082 ret\n"); + + uint8_t values[3]; + values[0] = 0xAB; + values[1] = 0xCD; + values[2] = 0xEF; + + EXPECT_EQ(0xCD, Call(test->entry(), reinterpret_cast(&values[1]))); +} + +ASSEMBLER_TEST_GENERATE(CompressedLoadByteUnsigned_Pos, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ lbu(A0, Address(A0, 1)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedLoadByteUnsigned_Pos, test) { + EXPECT_DISASSEMBLY( + " 8148 lbu a0, 1(a0)\n" + " 8082 ret\n"); + + uint8_t values[3]; + values[0] = 0xAB; + values[1] = 0xCD; + values[2] = 0xEF; + + EXPECT_EQ(0xEF, Call(test->entry(), reinterpret_cast(&values[1]))); +} + +ASSEMBLER_TEST_GENERATE(CompressedLoadHalfword_0, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ lh(A0, Address(A0, 0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedLoadHalfword_0, test) { + EXPECT_DISASSEMBLY( + " 8548 lh a0, 0(a0)\n" + " 8082 ret\n"); + + uint16_t values[3]; + values[0] = 0xAB01; + values[1] = 0xCD02; + values[2] = 0xEF03; + + EXPECT_EQ(-13054, Call(test->entry(), reinterpret_cast(&values[1]))); +} + +ASSEMBLER_TEST_GENERATE(CompressedLoadHalfword_Pos, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ lh(A0, Address(A0, 2)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedLoadHalfword_Pos, test) { + EXPECT_DISASSEMBLY( + " 8568 lh a0, 2(a0)\n" + " 8082 ret\n"); + + uint16_t values[3]; + values[0] = 0xAB01; + values[1] = 0xCD02; + values[2] = 0xEF03; + + EXPECT_EQ(-4349, Call(test->entry(), reinterpret_cast(&values[1]))); +} + +ASSEMBLER_TEST_GENERATE(CompressedLoadHalfwordUnsigned_0, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ lhu(A0, Address(A0, 0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedLoadHalfwordUnsigned_0, test) { + EXPECT_DISASSEMBLY( + " 8508 lhu a0, 0(a0)\n" + " 8082 ret\n"); + + uint16_t values[3]; + values[0] = 0xAB01; + values[1] = 0xCD02; + values[2] = 0xEF03; + + EXPECT_EQ(0xCD02, Call(test->entry(), reinterpret_cast(&values[1]))); +} + +ASSEMBLER_TEST_GENERATE(CompressedLoadHalfwordUnsigned_Pos, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ lhu(A0, Address(A0, 2)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedLoadHalfwordUnsigned_Pos, test) { + EXPECT_DISASSEMBLY( + " 8528 lhu a0, 2(a0)\n" + " 8082 ret\n"); + + uint16_t values[3]; + values[0] = 0xAB01; + values[1] = 0xCD02; + values[2] = 0xEF03; + + EXPECT_EQ(0xEF03, Call(test->entry(), reinterpret_cast(&values[1]))); +} + +ASSEMBLER_TEST_GENERATE(CompressedStoreByte_0, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ sb(A1, Address(A0, 0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedStoreByte_0, test) { + EXPECT_DISASSEMBLY( + " 890c sb a1, 0(a0)\n" + " 8082 ret\n"); + + uint8_t values[3]; + values[0] = 0; + values[1] = 0; + values[2] = 0; + + Call(test->entry(), reinterpret_cast(&values[1]), 0xCD); + EXPECT_EQ(0, values[0]); + EXPECT_EQ(0xCD, values[1]); + EXPECT_EQ(0, values[2]); +} + +ASSEMBLER_TEST_GENERATE(CompressedStoreByte_Pos, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ sb(A1, Address(A0, 1)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedStoreByte_Pos, test) { + EXPECT_DISASSEMBLY( + " 894c sb a1, 1(a0)\n" + " 8082 ret\n"); + + uint8_t values[3]; + values[0] = 0; + values[1] = 0; + values[2] = 0; + + Call(test->entry(), reinterpret_cast(&values[1]), 0xEF); + EXPECT_EQ(0, values[0]); + EXPECT_EQ(0, values[1]); + EXPECT_EQ(0xEF, values[2]); +} + +ASSEMBLER_TEST_GENERATE(CompressedStoreHalfword_0, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ sh(A1, Address(A0, 0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedStoreHalfword_0, test) { + EXPECT_DISASSEMBLY( + " 8d0c sh a1, 0(a0)\n" + " 8082 ret\n"); + + uint16_t values[3]; + values[0] = 0; + values[1] = 0; + values[2] = 0; + + Call(test->entry(), reinterpret_cast(&values[1]), 0xCD02); + EXPECT_EQ(0, values[0]); + EXPECT_EQ(0xCD02, values[1]); + EXPECT_EQ(0, values[2]); +} + +ASSEMBLER_TEST_GENERATE(CompressedStoreHalfword_Pos, assembler) { + __ SetExtensions(RV_GC | RV_Zcb); + __ sh(A1, Address(A0, 2)); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedStoreHalfword_Pos, test) { + EXPECT_DISASSEMBLY( + " 8d2c sh a1, 2(a0)\n" + " 8082 ret\n"); + + uint16_t values[3]; + values[0] = 0; + values[1] = 0; + values[2] = 0; + + Call(test->entry(), reinterpret_cast(&values[1]), 0xEF03); + EXPECT_EQ(0, values[0]); + EXPECT_EQ(0, values[1]); + EXPECT_EQ(0xEF03, values[2]); +} + +ASSEMBLER_TEST_GENERATE(CompressedSignExtendByte, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ sextb(A0, A0); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedSignExtendByte, test) { + EXPECT_DISASSEMBLY( + " 9d65 sext.b a0, a0\n" + " 8082 ret\n"); + + EXPECT_EQ(1, Call(test->entry(), 1)); + EXPECT_EQ(127, Call(test->entry(), 127)); + EXPECT_EQ(-128, Call(test->entry(), 128)); +} + +ASSEMBLER_TEST_GENERATE(CompressedZeroExtendByte, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ zextb(A0, A0); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedZeroExtendByte, test) { + EXPECT_DISASSEMBLY( + " 9d61 zext.b a0, a0\n" + " 8082 ret\n"); + + EXPECT_EQ(1, Call(test->entry(), 1)); + EXPECT_EQ(0xCD, Call(test->entry(), 0x1234ABCD)); + EXPECT_EQ(0xFF, Call(test->entry(), 0xFF)); + EXPECT_EQ(0xFF, Call(test->entry(), -1)); +} + +ASSEMBLER_TEST_GENERATE(CompressedSignExtendHalfword, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ sexth(A0, A0); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedSignExtendHalfword, test) { + EXPECT_DISASSEMBLY( + " 9d6d sext.h a0, a0\n" + " 8082 ret\n"); + + EXPECT_EQ(1, Call(test->entry(), 1)); + EXPECT_EQ(0x7BCD, Call(test->entry(), 0x12347BCD)); + EXPECT_EQ(-1, Call(test->entry(), 0xFFFF)); + EXPECT_EQ(-1, Call(test->entry(), -1)); +} + +ASSEMBLER_TEST_GENERATE(CompressedZeroExtendHalfword, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ zexth(A0, A0); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedZeroExtendHalfword, test) { + EXPECT_DISASSEMBLY( + " 9d69 zext.h a0, a0\n" + " 8082 ret\n"); + + EXPECT_EQ(0, Call(test->entry(), 0)); + EXPECT_EQ(0xABCD, Call(test->entry(), 0x1234ABCD)); + EXPECT_EQ(0xFFFF, Call(test->entry(), 0xFFFF)); + EXPECT_EQ(0xFFFF, Call(test->entry(), -1)); +} + +#if XLEN >= 64 +ASSEMBLER_TEST_GENERATE(CompressedZeroExtendWord, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ zextw(A0, A0); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedZeroExtendWord, test) { + EXPECT_DISASSEMBLY( + " 9d71 zext.w a0, a0\n" + " 8082 ret\n"); + + EXPECT_EQ(0, Call(test->entry(), 0)); + EXPECT_EQ(0x1234ABCD, Call(test->entry(), 0x11234ABCD)); + EXPECT_EQ(0xFFFFFFFF, Call(test->entry(), 0xFFFFFFFF)); + EXPECT_EQ(0xFFFFFFFF, Call(test->entry(), -1)); +} +#endif // XLEN >= 64 + +ASSEMBLER_TEST_GENERATE(CompressedNot, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ not_(A0, A0); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedNot, test) { + EXPECT_DISASSEMBLY( + " 9d75 not a0, a0\n" + " 8082 ret\n"); + + EXPECT_EQ(~42, Call(test->entry(), 42)); + EXPECT_EQ(~-42, Call(test->entry(), -42)); +} + +ASSEMBLER_TEST_GENERATE(CompressedMultiply, assembler) { + __ SetExtensions(RV_GCB | RV_Zcb); + __ mul(A0, A0, A1); + __ ret(); +} +ASSEMBLER_TEST_RUN(CompressedMultiply, test) { + EXPECT_DISASSEMBLY( + " 9d4d mul a0, a0, a1\n" + " 8082 ret\n"); + + EXPECT_EQ(68, Call(test->entry(), 4, 17)); + EXPECT_EQ(-68, Call(test->entry(), -4, 17)); + EXPECT_EQ(-68, Call(test->entry(), 4, -17)); + EXPECT_EQ(68, Call(test->entry(), -4, -17)); + EXPECT_EQ(68, Call(test->entry(), 17, 4)); + EXPECT_EQ(-68, Call(test->entry(), -17, 4)); + EXPECT_EQ(-68, Call(test->entry(), 17, -4)); + EXPECT_EQ(68, Call(test->entry(), -17, -4)); +} + ASSEMBLER_TEST_GENERATE(LoadByteAcquire, assembler) { __ SetExtensions(RV_GC | RV_Zalasr); __ lb(A0, Address(A1), std::memory_order_acquire); @@ -7570,6 +7877,8 @@ TEST_ENCODING(intptr_t, CSPLoad4Imm) TEST_ENCODING(intptr_t, CSPLoad8Imm) TEST_ENCODING(intptr_t, CSPStore4Imm) TEST_ENCODING(intptr_t, CSPStore8Imm) +TEST_ENCODING(intptr_t, CMem1Imm) +TEST_ENCODING(intptr_t, CMem2Imm) TEST_ENCODING(intptr_t, CMem4Imm) TEST_ENCODING(intptr_t, CMem8Imm) TEST_ENCODING(intptr_t, CJImm) diff --git a/runtime/vm/compiler/assembler/disassembler_riscv.cc b/runtime/vm/compiler/assembler/disassembler_riscv.cc index 9c1a41293726..d16bc4a345e2 100644 --- a/runtime/vm/compiler/assembler/disassembler_riscv.cc +++ b/runtime/vm/compiler/assembler/disassembler_riscv.cc @@ -369,6 +369,35 @@ void RISCVDisassembler::DisassembleInstruction(CInstr instr) { Print("subw 'rs1p, 'rs1p, 'rs2p", instr, RV_C); break; #endif + case C_MUL: + Print("mul 'rs1p, 'rs1p, 'rs2p", instr, RV_Zcb); + break; + case C_EXT: + switch (instr.encoding() & C_EXT_MASK) { + case C_ZEXTB: + Print("zext.b 'rs1p, 'rs1p", instr, RV_Zcb); + break; + case C_SEXTB: + Print("sext.b 'rs1p, 'rs1p", instr, RV_Zcb); + break; + case C_ZEXTH: + Print("zext.h 'rs1p, 'rs1p", instr, RV_Zcb); + break; + case C_SEXTH: + Print("sext.h 'rs1p, 'rs1p", instr, RV_Zcb); + break; +#if XLEN >= 64 + case C_ZEXTW: + Print("zext.w 'rs1p, 'rs1p", instr, RV_Zcb); + break; +#endif + case C_NOT: + Print("not 'rs1p, 'rs1p", instr, RV_Zcb); + break; + default: + UnknownInstruction(instr); + } + break; default: UnknownInstruction(instr); } @@ -377,6 +406,28 @@ void RISCVDisassembler::DisassembleInstruction(CInstr instr) { UnknownInstruction(instr); } break; + case C_LBU: + switch (instr.encoding() & 0b1111110000000011) { + case C_LBU: + Print("lbu 'rdp, 'mem1imm('rs1p)", instr, RV_Zcb); + break; + case C_LHU: + if ((instr.encoding() & 0b1000000) == 0) { + Print("lhu 'rdp, 'mem2imm('rs1p)", instr, RV_Zcb); + } else { + Print("lh 'rdp, 'mem2imm('rs1p)", instr, RV_Zcb); + } + break; + case C_SB: + Print("sb 'rs2p, 'mem1imm('rs1p)", instr, RV_Zcb); + break; + case C_SH: + Print("sh 'rs2p, 'mem2imm('rs1p)", instr, RV_Zcb); + break; + default: + UnknownInstruction(instr); + } + break; default: if ((instr.encoding() == 0) || (instr.encoding() == static_cast(-1))) { @@ -1806,6 +1857,12 @@ const char* RISCVDisassembler::PrintOption(const char* format, CInstr instr) { } else if (STRING_STARTS_WITH(format, "spstore8imm")) { Printf("%" Pd, static_cast(instr.spstore8_imm())); return format + 11; + } else if (STRING_STARTS_WITH(format, "mem1imm")) { + Printf("%" Pd, static_cast(instr.mem1_imm())); + return format + 7; + } else if (STRING_STARTS_WITH(format, "mem2imm")) { + Printf("%" Pd, static_cast(instr.mem2_imm())); + return format + 7; } else if (STRING_STARTS_WITH(format, "mem4imm")) { Printf("%" Pd, static_cast(instr.mem4_imm())); return format + 7; diff --git a/runtime/vm/constants_riscv.h b/runtime/vm/constants_riscv.h index fa1b73dc6063..3e24d690e85c 100644 --- a/runtime/vm/constants_riscv.h +++ b/runtime/vm/constants_riscv.h @@ -1263,6 +1263,38 @@ inline intx_t DecodeCSPStore8Imm(uint32_t encoding) { return imm; } +inline bool IsCMem1Imm(intptr_t imm) { + return Utils::IsUint(2, imm) && Utils::IsAligned(imm, 1); +} +inline uint32_t EncodeCMem1Imm(intptr_t imm) { + ASSERT(IsCMem1Imm(imm)); + uint32_t encoding = 0; + encoding |= ((imm >> 1) & 0x1) << 5; + encoding |= ((imm >> 0) & 0x1) << 6; + return encoding; +} +inline intx_t DecodeCMem1Imm(uint32_t encoding) { + uint32_t imm = 0; + imm |= ((encoding >> 5) & 0x1) << 1; + imm |= ((encoding >> 6) & 0x1) << 0; + return imm; +} + +inline bool IsCMem2Imm(intptr_t imm) { + return Utils::IsUint(2, imm) && Utils::IsAligned(imm, 2); +} +inline uint32_t EncodeCMem2Imm(intptr_t imm) { + ASSERT(IsCMem1Imm(imm)); + uint32_t encoding = 0; + encoding |= ((imm >> 1) & 0x1) << 5; + return encoding; +} +inline intx_t DecodeCMem2Imm(uint32_t encoding) { + uint32_t imm = 0; + imm |= ((encoding >> 5) & 0x1) << 1; + return imm; +} + inline bool IsCMem4Imm(intptr_t imm) { return Utils::IsUint(7, imm) && Utils::IsAligned(imm, 4); } @@ -1479,6 +1511,15 @@ enum COpcode { C_AND = 0b1000110001100001, C_SUBW = 0b1001110000000001, C_ADDW = 0b1001110000100001, + C_MUL = 0b1001110001000001, + C_EXT = 0b1001110001100001, + C_EXT_MASK = 0b1111110001111111, + C_ZEXTB = 0b1001110001100001, + C_SEXTB = 0b1001110001100101, + C_ZEXTH = 0b1001110001101001, + C_SEXTH = 0b1001110001101101, + C_ZEXTW = 0b1001110001110001, + C_NOT = 0b1001110001110101, C_J = 0b1010000000000001, C_BEQZ = 0b1100000000000001, @@ -1500,6 +1541,12 @@ enum COpcode { C_NOP = 0b0000000000000001, C_EBREAK = 0b1001000000000010, + + C_LBU = 0b1000000000000000, + C_LH = 0b1000010001000000, + C_LHU = 0b1000010000000000, + C_SB = 0b1000100000000000, + C_SH = 0b1000110000000000, }; class CInstr { @@ -1529,6 +1576,8 @@ class CInstr { intx_t spload8_imm() { return DecodeCSPLoad8Imm(encoding_); } intx_t spstore4_imm() { return DecodeCSPStore4Imm(encoding_); } intx_t spstore8_imm() { return DecodeCSPStore8Imm(encoding_); } + intx_t mem1_imm() { return DecodeCMem1Imm(encoding_); } + intx_t mem2_imm() { return DecodeCMem2Imm(encoding_); } intx_t mem4_imm() { return DecodeCMem4Imm(encoding_); } intx_t mem8_imm() { return DecodeCMem8Imm(encoding_); } intx_t j_imm() { return DecodeCJImm(encoding_); } @@ -1626,7 +1675,8 @@ static constexpr Extension RV_Zbc(9); // Carry-less multiplication static constexpr ExtensionSet RV_B = RV_Zba | RV_Zbb | RV_Zbs; static constexpr ExtensionSet RV_GCB = RV_GC | RV_B; static constexpr Extension RV_Zicond(10); // Integer conditional operations -static constexpr Extension RV_Zalasr(11); // Load-acquire, store-release +static constexpr Extension RV_Zcb(11); // More compressed instructions +static constexpr Extension RV_Zalasr(12); // Load-acquire, store-release #if defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_ANDROID) static constexpr ExtensionSet RV_baseline = RV_GCB; diff --git a/runtime/vm/simulator_riscv.cc b/runtime/vm/simulator_riscv.cc index 353127a9f1a1..b3d593833cf2 100644 --- a/runtime/vm/simulator_riscv.cc +++ b/runtime/vm/simulator_riscv.cc @@ -580,6 +580,309 @@ void Simulator::Interpret(Instr instr) { } } +static intx_t mul(intx_t a, intx_t b) { + return static_cast(a) * static_cast(b); +} + +static intx_t mulh(intx_t a, intx_t b) { + const uintx_t kLoMask = (static_cast(1) << (XLEN / 2)) - 1; + const uintx_t kHiShift = XLEN / 2; + + uintx_t a_lo = a & kLoMask; + intx_t a_hi = a >> kHiShift; + uintx_t b_lo = b & kLoMask; + intx_t b_hi = b >> kHiShift; + + uintx_t x = a_lo * b_lo; + intx_t y = a_hi * b_lo; + intx_t z = a_lo * b_hi; + intx_t w = a_hi * b_hi; + + intx_t r0 = (x >> kHiShift) + y; + intx_t r1 = (r0 & kLoMask) + z; + return w + (r0 >> kHiShift) + (r1 >> kHiShift); +} + +static uintx_t mulhu(uintx_t a, uintx_t b) { + const uintx_t kLoMask = (static_cast(1) << (XLEN / 2)) - 1; + const uintx_t kHiShift = XLEN / 2; + + uintx_t a_lo = a & kLoMask; + uintx_t a_hi = a >> kHiShift; + uintx_t b_lo = b & kLoMask; + uintx_t b_hi = b >> kHiShift; + + uintx_t x = a_lo * b_lo; + uintx_t y = a_hi * b_lo; + uintx_t z = a_lo * b_hi; + uintx_t w = a_hi * b_hi; + + uintx_t r0 = (x >> kHiShift) + y; + uintx_t r1 = (r0 & kLoMask) + z; + return w + (r0 >> kHiShift) + (r1 >> kHiShift); +} + +static uintx_t mulhsu(intx_t a, uintx_t b) { + const uintx_t kLoMask = (static_cast(1) << (XLEN / 2)) - 1; + const uintx_t kHiShift = XLEN / 2; + + uintx_t a_lo = a & kLoMask; + intx_t a_hi = a >> kHiShift; + uintx_t b_lo = b & kLoMask; + uintx_t b_hi = b >> kHiShift; + + uintx_t x = a_lo * b_lo; + intx_t y = a_hi * b_lo; + uintx_t z = a_lo * b_hi; + intx_t w = a_hi * b_hi; + + intx_t r0 = (x >> kHiShift) + y; + uintx_t r1 = (r0 & kLoMask) + z; + return w + (r0 >> kHiShift) + (r1 >> kHiShift); +} + +static intx_t div(intx_t a, intx_t b) { + if (b == 0) { + return -1; + } else if (b == -1 && a == kMinIntX) { + return kMinIntX; + } else { + return a / b; + } +} + +static uintx_t divu(uintx_t a, uintx_t b) { + if (b == 0) { + return kMaxUIntX; + } else { + return a / b; + } +} + +static intx_t rem(intx_t a, intx_t b) { + if (b == 0) { + return a; + } else if (b == -1 && a == kMinIntX) { + return 0; + } else { + return a % b; + } +} + +static uintx_t remu(uintx_t a, uintx_t b) { + if (b == 0) { + return a; + } else { + return a % b; + } +} + +#if XLEN >= 64 +static int32_t mulw(int32_t a, int32_t b) { + return a * b; +} + +static int32_t divw(int32_t a, int32_t b) { + if (b == 0) { + return -1; + } else if (b == -1 && a == kMinInt32) { + return kMinInt32; + } else { + return a / b; + } +} + +static uint32_t divuw(uint32_t a, uint32_t b) { + if (b == 0) { + return kMaxUint32; + } else { + return a / b; + } +} + +static int32_t remw(int32_t a, int32_t b) { + if (b == 0) { + return a; + } else if (b == -1 && a == kMinInt32) { + return 0; + } else { + return a % b; + } +} + +static uint32_t remuw(uint32_t a, uint32_t b) { + if (b == 0) { + return a; + } else { + return a % b; + } +} +#endif // XLEN >= 64 + +static uintx_t clz(uintx_t a) { + for (int bit = XLEN - 1; bit >= 0; bit--) { + if ((a & (static_cast(1) << bit)) != 0) { + return XLEN - bit - 1; + } + } + return XLEN; +} + +static uintx_t ctz(uintx_t a) { + for (int bit = 0; bit < XLEN; bit++) { + if ((a & (static_cast(1) << bit)) != 0) { + return bit; + } + } + return XLEN; +} + +static uintx_t cpop(uintx_t a) { + uintx_t count = 0; + for (int bit = 0; bit < XLEN; bit++) { + if ((a & (static_cast(1) << bit)) != 0) { + count++; + } + } + return count; +} + +static uintx_t clzw(uint32_t a) { + for (int bit = 32 - 1; bit >= 0; bit--) { + if ((a & (static_cast(1) << bit)) != 0) { + return 32 - bit - 1; + } + } + return 32; +} + +static uintx_t ctzw(uint32_t a) { + for (int bit = 0; bit < 32; bit++) { + if ((a & (static_cast(1) << bit)) != 0) { + return bit; + } + } + return 32; +} + +static uintx_t cpopw(uint32_t a) { + uintx_t count = 0; + for (int bit = 0; bit < 32; bit++) { + if ((a & (static_cast(1) << bit)) != 0) { + count++; + } + } + return count; +} + +static intx_t max(intx_t a, intx_t b) { + return a > b ? a : b; +} +static uintx_t maxu(uintx_t a, uintx_t b) { + return a > b ? a : b; +} +static intx_t min(intx_t a, intx_t b) { + return a < b ? a : b; +} +static uintx_t minu(uintx_t a, uintx_t b) { + return a < b ? a : b; +} +static uintx_t clmul(uintx_t a, uintx_t b) { + uintx_t result = 0; + for (int bit = 0; bit < XLEN; bit++) { + if (((b >> bit) & 1) != 0) { + result ^= a << bit; + } + } + return result; +} +static uintx_t clmulh(uintx_t a, uintx_t b) { + uintx_t result = 0; + for (int bit = 1; bit < XLEN; bit++) { + if (((b >> bit) & 1) != 0) { + result ^= a >> (XLEN - bit); + } + } + return result; +} +static uintx_t clmulr(uintx_t a, uintx_t b) { + uintx_t result = 0; + for (int bit = 0; bit < XLEN; bit++) { + if (((b >> bit) & 1) != 0) { + result ^= a >> (XLEN - bit - 1); + } + } + return result; +} +static uintx_t sextb(uintx_t a) { + return static_cast(a << (XLEN - 8)) >> (XLEN - 8); +} +static uintx_t zextb(uintx_t a) { + return a << (XLEN - 8) >> (XLEN - 8); +} +static uintx_t sexth(uintx_t a) { + return static_cast(a << (XLEN - 16)) >> (XLEN - 16); +} +static uintx_t zexth(uintx_t a) { + return a << (XLEN - 16) >> (XLEN - 16); +} +#if XLEN >= 64 +static uintx_t zextw(uintx_t a) { + return a << (XLEN - 32) >> (XLEN - 32); +} +#endif +static uintx_t ror(uintx_t a, uintx_t b) { + uintx_t r = b & (XLEN - 1); + uintx_t l = (XLEN - r) & (XLEN - 1); + return (a << l) | (a >> r); +} +static uintx_t rol(uintx_t a, uintx_t b) { + uintx_t l = b & (XLEN - 1); + uintx_t r = (XLEN - l) & (XLEN - 1); + return (a << l) | (a >> r); +} +static uintx_t rorw(uintx_t a, uintx_t b) { + uint32_t r = b & (XLEN - 1); + uint32_t l = (XLEN - r) & (XLEN - 1); + uint32_t x = a; + return sign_extend((x << l) | (x >> r)); +} +static uintx_t rolw(uintx_t a, uintx_t b) { + uint32_t l = b & (XLEN - 1); + uint32_t r = (XLEN - l) & (XLEN - 1); + uint32_t x = a; + return sign_extend((x << l) | (x >> r)); +} +static uintx_t orcb(uintx_t a) { + uintx_t result = 0; + for (int shift = 0; shift < XLEN; shift += 8) { + if (((a >> shift) & 0xFF) != 0) { + result |= static_cast(0xFF) << shift; + } + } + return result; +} +static uintx_t rev8(uintx_t a) { + uintx_t result = 0; + for (int shift = 0; shift < XLEN; shift += 8) { + result <<= 8; + result |= (a >> shift) & 0xFF; + } + return result; +} +static uintx_t bclr(uintx_t a, uintx_t b) { + return a & ~(static_cast(1) << (b & (XLEN - 1))); +} +static uintx_t bext(uintx_t a, uintx_t b) { + return (a >> (b & (XLEN - 1))) & 1; +} +static uintx_t binv(uintx_t a, uintx_t b) { + return a ^ (static_cast(1) << (b & (XLEN - 1))); +} +static uintx_t bset(uintx_t a, uintx_t b) { + return a | (static_cast(1) << (b & (XLEN - 1))); +} + DART_FORCE_INLINE void Simulator::Interpret(CInstr instr) { switch (instr.opcode()) { @@ -825,6 +1128,36 @@ void Simulator::Interpret(CInstr instr) { set_xreg(instr.rs1p(), sign_extend(a - b)); break; } + case C_MUL: + set_xreg(instr.rs1p(), + mul(get_xreg(instr.rs1p()), get_xreg(instr.rs2p()))); + break; + case C_EXT: + switch (instr.encoding() & C_EXT_MASK) { + case C_ZEXTB: + set_xreg(instr.rs1p(), zextb(get_xreg(instr.rs1p()))); + break; + case C_SEXTB: + set_xreg(instr.rs1p(), sextb(get_xreg(instr.rs1p()))); + break; + case C_ZEXTH: + set_xreg(instr.rs1p(), zexth(get_xreg(instr.rs1p()))); + break; + case C_SEXTH: + set_xreg(instr.rs1p(), sexth(get_xreg(instr.rs1p()))); + break; +#if XLEN >= 64 + case C_ZEXTW: + set_xreg(instr.rs1p(), zextw(get_xreg(instr.rs1p()))); + break; +#endif + case C_NOT: + set_xreg(instr.rs1p(), ~get_xreg(instr.rs1p())); + break; + default: + IllegalInstruction(instr); + } + break; default: IllegalInstruction(instr); } @@ -833,6 +1166,36 @@ void Simulator::Interpret(CInstr instr) { IllegalInstruction(instr); } break; + case C_LBU: + switch (instr.encoding() & 0b1111110000000011) { + case C_LBU: { + uintx_t addr = get_xreg(instr.rs1p()) + instr.mem1_imm(); + set_xreg(instr.rdp(), MemoryRead(addr, instr.rs1p())); + break; + } + case C_LHU: { + uintx_t addr = get_xreg(instr.rs1p()) + instr.mem2_imm(); + if ((instr.encoding() & 0b1000000) == 0) { + set_xreg(instr.rdp(), MemoryRead(addr, instr.rs1p())); + } else { + set_xreg(instr.rdp(), MemoryRead(addr, instr.rs1p())); + } + break; + } + case C_SB: { + uintx_t addr = get_xreg(instr.rs1p()) + instr.mem1_imm(); + MemoryWrite(addr, get_xreg(instr.rs2p()), instr.rs1p()); + break; + } + case C_SH: { + uintx_t addr = get_xreg(instr.rs1p()) + instr.mem2_imm(); + MemoryWrite(addr, get_xreg(instr.rs2p()), instr.rs1p()); + break; + } + default: + IllegalInstruction(instr); + } + break; default: IllegalInstruction(instr); } @@ -946,222 +1309,66 @@ void Simulator::InterpretLOAD(Instr instr) { set_xreg(instr.rd(), MemoryRead(addr, instr.rs1())); break; #endif // XLEN >= 64 - default: - IllegalInstruction(instr); - } - pc_ += instr.length(); -} - -DART_FORCE_INLINE -void Simulator::InterpretLOADFP(Instr instr) { - uintx_t addr = get_xreg(instr.rs1()) + instr.itype_imm(); - switch (instr.funct3()) { - case S: - set_fregs(instr.frd(), MemoryRead(addr, instr.rs1())); - break; - case D: - set_fregd(instr.frd(), MemoryRead(addr, instr.rs1())); - break; - default: - IllegalInstruction(instr); - } - pc_ += instr.length(); -} - -DART_FORCE_INLINE -void Simulator::InterpretSTORE(Instr instr) { - uintx_t addr = get_xreg(instr.rs1()) + instr.stype_imm(); - switch (instr.funct3()) { - case SB: - MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); - break; - case SH: - MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); - break; - case SW: - MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); - break; -#if XLEN >= 64 - case SD: - MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); - break; -#endif // XLEN >= 64 - default: - IllegalInstruction(instr); - } - pc_ += instr.length(); -} - -DART_FORCE_INLINE -void Simulator::InterpretSTOREFP(Instr instr) { - uintx_t addr = get_xreg(instr.rs1()) + instr.stype_imm(); - switch (instr.funct3()) { - case S: - MemoryWrite(addr, get_fregs(instr.frs2()), instr.rs1()); - break; - case D: - MemoryWrite(addr, get_fregd(instr.frs2()), instr.rs1()); - break; - default: - IllegalInstruction(instr); - } - pc_ += instr.length(); -} - -static uintx_t clz(uintx_t a) { - for (int bit = XLEN - 1; bit >= 0; bit--) { - if ((a & (static_cast(1) << bit)) != 0) { - return XLEN - bit - 1; - } - } - return XLEN; -} - -static uintx_t ctz(uintx_t a) { - for (int bit = 0; bit < XLEN; bit++) { - if ((a & (static_cast(1) << bit)) != 0) { - return bit; - } - } - return XLEN; -} - -static uintx_t cpop(uintx_t a) { - uintx_t count = 0; - for (int bit = 0; bit < XLEN; bit++) { - if ((a & (static_cast(1) << bit)) != 0) { - count++; - } - } - return count; -} - -static uintx_t clzw(uint32_t a) { - for (int bit = 32 - 1; bit >= 0; bit--) { - if ((a & (static_cast(1) << bit)) != 0) { - return 32 - bit - 1; - } - } - return 32; -} - -static uintx_t ctzw(uint32_t a) { - for (int bit = 0; bit < 32; bit++) { - if ((a & (static_cast(1) << bit)) != 0) { - return bit; - } - } - return 32; -} - -static uintx_t cpopw(uint32_t a) { - uintx_t count = 0; - for (int bit = 0; bit < 32; bit++) { - if ((a & (static_cast(1) << bit)) != 0) { - count++; - } - } - return count; -} - -static intx_t max(intx_t a, intx_t b) { - return a > b ? a : b; -} -static uintx_t maxu(uintx_t a, uintx_t b) { - return a > b ? a : b; -} -static intx_t min(intx_t a, intx_t b) { - return a < b ? a : b; -} -static uintx_t minu(uintx_t a, uintx_t b) { - return a < b ? a : b; -} -static uintx_t clmul(uintx_t a, uintx_t b) { - uintx_t result = 0; - for (int bit = 0; bit < XLEN; bit++) { - if (((b >> bit) & 1) != 0) { - result ^= a << bit; - } - } - return result; -} -static uintx_t clmulh(uintx_t a, uintx_t b) { - uintx_t result = 0; - for (int bit = 1; bit < XLEN; bit++) { - if (((b >> bit) & 1) != 0) { - result ^= a >> (XLEN - bit); - } + default: + IllegalInstruction(instr); } - return result; + pc_ += instr.length(); } -static uintx_t clmulr(uintx_t a, uintx_t b) { - uintx_t result = 0; - for (int bit = 0; bit < XLEN; bit++) { - if (((b >> bit) & 1) != 0) { - result ^= a >> (XLEN - bit - 1); - } + +DART_FORCE_INLINE +void Simulator::InterpretLOADFP(Instr instr) { + uintx_t addr = get_xreg(instr.rs1()) + instr.itype_imm(); + switch (instr.funct3()) { + case S: + set_fregs(instr.frd(), MemoryRead(addr, instr.rs1())); + break; + case D: + set_fregd(instr.frd(), MemoryRead(addr, instr.rs1())); + break; + default: + IllegalInstruction(instr); } - return result; -} -static uintx_t sextb(uintx_t a) { - return static_cast(a << (XLEN - 8)) >> (XLEN - 8); -} -static uintx_t sexth(uintx_t a) { - return static_cast(a << (XLEN - 16)) >> (XLEN - 16); -} -static uintx_t zexth(uintx_t a) { - return a << (XLEN - 16) >> (XLEN - 16); -} -static uintx_t ror(uintx_t a, uintx_t b) { - uintx_t r = b & (XLEN - 1); - uintx_t l = (XLEN - r) & (XLEN - 1); - return (a << l) | (a >> r); -} -static uintx_t rol(uintx_t a, uintx_t b) { - uintx_t l = b & (XLEN - 1); - uintx_t r = (XLEN - l) & (XLEN - 1); - return (a << l) | (a >> r); -} -static uintx_t rorw(uintx_t a, uintx_t b) { - uint32_t r = b & (XLEN - 1); - uint32_t l = (XLEN - r) & (XLEN - 1); - uint32_t x = a; - return sign_extend((x << l) | (x >> r)); -} -static uintx_t rolw(uintx_t a, uintx_t b) { - uint32_t l = b & (XLEN - 1); - uint32_t r = (XLEN - l) & (XLEN - 1); - uint32_t x = a; - return sign_extend((x << l) | (x >> r)); + pc_ += instr.length(); } -static uintx_t orcb(uintx_t a) { - uintx_t result = 0; - for (int shift = 0; shift < XLEN; shift += 8) { - if (((a >> shift) & 0xFF) != 0) { - result |= static_cast(0xFF) << shift; - } + +DART_FORCE_INLINE +void Simulator::InterpretSTORE(Instr instr) { + uintx_t addr = get_xreg(instr.rs1()) + instr.stype_imm(); + switch (instr.funct3()) { + case SB: + MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); + break; + case SH: + MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); + break; + case SW: + MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); + break; +#if XLEN >= 64 + case SD: + MemoryWrite(addr, get_xreg(instr.rs2()), instr.rs1()); + break; +#endif // XLEN >= 64 + default: + IllegalInstruction(instr); } - return result; + pc_ += instr.length(); } -static uintx_t rev8(uintx_t a) { - uintx_t result = 0; - for (int shift = 0; shift < XLEN; shift += 8) { - result <<= 8; - result |= (a >> shift) & 0xFF; + +DART_FORCE_INLINE +void Simulator::InterpretSTOREFP(Instr instr) { + uintx_t addr = get_xreg(instr.rs1()) + instr.stype_imm(); + switch (instr.funct3()) { + case S: + MemoryWrite(addr, get_fregs(instr.frs2()), instr.rs1()); + break; + case D: + MemoryWrite(addr, get_fregd(instr.frs2()), instr.rs1()); + break; + default: + IllegalInstruction(instr); } - return result; -} -static uintx_t bclr(uintx_t a, uintx_t b) { - return a & ~(static_cast(1) << (b & (XLEN - 1))); -} -static uintx_t bext(uintx_t a, uintx_t b) { - return (a >> (b & (XLEN - 1))) & 1; -} -static uintx_t binv(uintx_t a, uintx_t b) { - return a ^ (static_cast(1) << (b & (XLEN - 1))); -} -static uintx_t bset(uintx_t a, uintx_t b) { - return a | (static_cast(1) << (b & (XLEN - 1))); + pc_ += instr.length(); } DART_FORCE_INLINE @@ -1384,145 +1591,6 @@ void Simulator::InterpretOP_0(Instr instr) { pc_ += instr.length(); } -static intx_t mul(intx_t a, intx_t b) { - return static_cast(a) * static_cast(b); -} - -static intx_t mulh(intx_t a, intx_t b) { - const uintx_t kLoMask = (static_cast(1) << (XLEN / 2)) - 1; - const uintx_t kHiShift = XLEN / 2; - - uintx_t a_lo = a & kLoMask; - intx_t a_hi = a >> kHiShift; - uintx_t b_lo = b & kLoMask; - intx_t b_hi = b >> kHiShift; - - uintx_t x = a_lo * b_lo; - intx_t y = a_hi * b_lo; - intx_t z = a_lo * b_hi; - intx_t w = a_hi * b_hi; - - intx_t r0 = (x >> kHiShift) + y; - intx_t r1 = (r0 & kLoMask) + z; - return w + (r0 >> kHiShift) + (r1 >> kHiShift); -} - -static uintx_t mulhu(uintx_t a, uintx_t b) { - const uintx_t kLoMask = (static_cast(1) << (XLEN / 2)) - 1; - const uintx_t kHiShift = XLEN / 2; - - uintx_t a_lo = a & kLoMask; - uintx_t a_hi = a >> kHiShift; - uintx_t b_lo = b & kLoMask; - uintx_t b_hi = b >> kHiShift; - - uintx_t x = a_lo * b_lo; - uintx_t y = a_hi * b_lo; - uintx_t z = a_lo * b_hi; - uintx_t w = a_hi * b_hi; - - uintx_t r0 = (x >> kHiShift) + y; - uintx_t r1 = (r0 & kLoMask) + z; - return w + (r0 >> kHiShift) + (r1 >> kHiShift); -} - -static uintx_t mulhsu(intx_t a, uintx_t b) { - const uintx_t kLoMask = (static_cast(1) << (XLEN / 2)) - 1; - const uintx_t kHiShift = XLEN / 2; - - uintx_t a_lo = a & kLoMask; - intx_t a_hi = a >> kHiShift; - uintx_t b_lo = b & kLoMask; - uintx_t b_hi = b >> kHiShift; - - uintx_t x = a_lo * b_lo; - intx_t y = a_hi * b_lo; - uintx_t z = a_lo * b_hi; - intx_t w = a_hi * b_hi; - - intx_t r0 = (x >> kHiShift) + y; - uintx_t r1 = (r0 & kLoMask) + z; - return w + (r0 >> kHiShift) + (r1 >> kHiShift); -} - -static intx_t div(intx_t a, intx_t b) { - if (b == 0) { - return -1; - } else if (b == -1 && a == kMinIntX) { - return kMinIntX; - } else { - return a / b; - } -} - -static uintx_t divu(uintx_t a, uintx_t b) { - if (b == 0) { - return kMaxUIntX; - } else { - return a / b; - } -} - -static intx_t rem(intx_t a, intx_t b) { - if (b == 0) { - return a; - } else if (b == -1 && a == kMinIntX) { - return 0; - } else { - return a % b; - } -} - -static uintx_t remu(uintx_t a, uintx_t b) { - if (b == 0) { - return a; - } else { - return a % b; - } -} - -#if XLEN >= 64 -static int32_t mulw(int32_t a, int32_t b) { - return a * b; -} - -static int32_t divw(int32_t a, int32_t b) { - if (b == 0) { - return -1; - } else if (b == -1 && a == kMinInt32) { - return kMinInt32; - } else { - return a / b; - } -} - -static uint32_t divuw(uint32_t a, uint32_t b) { - if (b == 0) { - return kMaxUint32; - } else { - return a / b; - } -} - -static int32_t remw(int32_t a, int32_t b) { - if (b == 0) { - return a; - } else if (b == -1 && a == kMinInt32) { - return 0; - } else { - return a % b; - } -} - -static uint32_t remuw(uint32_t a, uint32_t b) { - if (b == 0) { - return a; - } else { - return a % b; - } -} -#endif // XLEN >= 64 - DART_FORCE_INLINE void Simulator::InterpretOP_MULDIV(Instr instr) { switch (instr.funct3()) { From 21a1c6ce6128774c2e88343c3d09a914e59134f1 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Wed, 13 Nov 2024 00:31:18 +0000 Subject: [PATCH 4/8] [vm] Add the RISC-V Zabha extension. TEST=ci, local qemu Change-Id: Ie00951246c3b5eaa8ce8a038b60eea100b24fd6e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394664 Reviewed-by: Alexander Aprelev Commit-Queue: Ryan Macnak --- .../vm/compiler/assembler/assembler_riscv.cc | 162 +++++++++ .../vm/compiler/assembler/assembler_riscv.h | 74 ++++ .../assembler/assembler_riscv_test.cc | 328 ++++++++++++++++++ .../compiler/assembler/disassembler_riscv.cc | 54 +++ runtime/vm/constants_riscv.h | 15 +- runtime/vm/simulator_riscv.cc | 54 +++ 6 files changed, 686 insertions(+), 1 deletion(-) diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc index b67291b7e83b..d23fa248b765 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv.cc @@ -1711,6 +1711,168 @@ void MicroAssembler::czeronez(Register rd, Register rs1, Register rs2) { EmitRType(CZERO, rs2, rs1, CZERONEZ, rd, OP); } +void MicroAssembler::amoswapb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOSWAP, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amoaddb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOADD, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amoxorb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOXOR, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amoandb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOAND, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amoorb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOOR, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amominb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMIN, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amomaxb(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMAX, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amominub(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMINU, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amomaxub(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMAXU, order, rs2, addr.base(), WIDTH8, rd, AMO); +} + +void MicroAssembler::amoswaph(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOSWAP, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amoaddh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOADD, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amoxorh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOXOR, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amoandh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOAND, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amoorh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOOR, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amominh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMIN, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amomaxh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMAX, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amominuh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMINU, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + +void MicroAssembler::amomaxuh(Register rd, + Register rs2, + Address addr, + std::memory_order order) { + ASSERT(addr.offset() == 0); + ASSERT(Supports(RV_Zabha)); + EmitRType(AMOMAXU, order, rs2, addr.base(), WIDTH16, rd, AMO); +} + void MicroAssembler::lb(Register rd, Address addr, std::memory_order order) { ASSERT(addr.offset() == 0); ASSERT((order == std::memory_order_acquire) || diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h index 8132cb085f2f..da43d6a55df2 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.h +++ b/runtime/vm/compiler/assembler/assembler_riscv.h @@ -613,6 +613,80 @@ class MicroAssembler : public AssemblerBase { // rd := rs2 != 0 ? 0 : rs1 void czeronez(Register rd, Register rs1, Register rs2); + // ==== Zabha: Byte and halfword AMOs ==== + void amoswapb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoaddb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoxorb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoandb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoorb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amominb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amomaxb(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amominub(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amomaxub(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoswaph(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoaddh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoxorh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoandh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amoorh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amominh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amomaxh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amominuh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + void amomaxuh(Register rd, + Register rs2, + Address addr, + std::memory_order order = std::memory_order_relaxed); + // ==== Zalasr: Load-acquire, store-release ==== void lb(Register rd, Address addr, std::memory_order order); void lh(Register rd, Address addr, std::memory_order order); diff --git a/runtime/vm/compiler/assembler/assembler_riscv_test.cc b/runtime/vm/compiler/assembler/assembler_riscv_test.cc index efee349222db..76ca48a890f2 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv_test.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv_test.cc @@ -7261,6 +7261,334 @@ ASSEMBLER_TEST_RUN(CompressedMultiply, test) { EXPECT_EQ(68, Call(test->entry(), -17, -4)); } +ASSEMBLER_TEST_GENERATE(AmoSwapByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoswapb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoSwapByte, test) { + EXPECT_DISASSEMBLY( + "08b5052f amoswap.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b1010, value); +} + +ASSEMBLER_TEST_GENERATE(AmoAddByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoaddb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoAddByte, test) { + EXPECT_DISASSEMBLY( + "00b5052f amoadd.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = 42; + + EXPECT_EQ(42, Call(test->entry(), reinterpret_cast(&value), 10)); + EXPECT_EQ(52, value); +} + +ASSEMBLER_TEST_GENERATE(AmoXorByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoxorb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoXorByte, test) { + EXPECT_DISASSEMBLY( + "20b5052f amoxor.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b0110, value); +} + +ASSEMBLER_TEST_GENERATE(AmoAndByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoandb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoAndByte, test) { + EXPECT_DISASSEMBLY( + "60b5052f amoand.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b1000, value); +} + +ASSEMBLER_TEST_GENERATE(AmoOrByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoorb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoOrByte, test) { + EXPECT_DISASSEMBLY( + "40b5052f amoor.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b1110, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMinByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amominb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMinByte, test) { + EXPECT_DISASSEMBLY( + "80b5052f amomin.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-11, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMaxByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amomaxb(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMaxByte, test) { + EXPECT_DISASSEMBLY( + "a0b5052f amomax.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-4, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMinUnsignedByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amominub(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMinUnsignedByte, test) { + EXPECT_DISASSEMBLY( + "c0b5052f amominu.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-11, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMaxUnsignedByte, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amomaxub(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMaxUnsignedByte, test) { + EXPECT_DISASSEMBLY( + "e0b5052f amomaxu.b a0, a1, (a0)\n" + "00008067 ret\n"); + + int8_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-4, value); +} + +ASSEMBLER_TEST_GENERATE(AmoSwapHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoswaph(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoSwapHalfword, test) { + EXPECT_DISASSEMBLY( + "08b5152f amoswap.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b1010, value); +} + +ASSEMBLER_TEST_GENERATE(AmoAddHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoaddh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoAddHalfword, test) { + EXPECT_DISASSEMBLY( + "00b5152f amoadd.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = 42; + + EXPECT_EQ(42, Call(test->entry(), reinterpret_cast(&value), 10)); + EXPECT_EQ(52, value); +} + +ASSEMBLER_TEST_GENERATE(AmoXorHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoxorh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoXorHalfword, test) { + EXPECT_DISASSEMBLY( + "20b5152f amoxor.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b0110, value); +} + +ASSEMBLER_TEST_GENERATE(AmoAndHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoandh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoAndHalfword, test) { + EXPECT_DISASSEMBLY( + "60b5152f amoand.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b1000, value); +} + +ASSEMBLER_TEST_GENERATE(AmoOrHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amoorh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoOrHalfword, test) { + EXPECT_DISASSEMBLY( + "40b5152f amoor.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = 0b1100; + + EXPECT_EQ(0b1100, + Call(test->entry(), reinterpret_cast(&value), 0b1010)); + EXPECT_EQ(0b1110, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMinHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amominh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMinHalfword, test) { + EXPECT_DISASSEMBLY( + "80b5152f amomin.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-11, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMaxHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amomaxh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMaxHalfword, test) { + EXPECT_DISASSEMBLY( + "a0b5152f amomax.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-4, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMinUnsignedHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amominuh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMinUnsignedHalfword, test) { + EXPECT_DISASSEMBLY( + "c0b5152f amominu.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-11, value); +} + +ASSEMBLER_TEST_GENERATE(AmoMaxUnsignedHalfword, assembler) { + __ SetExtensions(RV_G | RV_Zabha); + __ amomaxuh(A0, A1, Address(A0)); + __ ret(); +} +ASSEMBLER_TEST_RUN(AmoMaxUnsignedHalfword, test) { + EXPECT_DISASSEMBLY( + "e0b5152f amomaxu.h a0, a1, (a0)\n" + "00008067 ret\n"); + + int16_t value = -7; + + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -11)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -7)); + EXPECT_EQ(-7, value); + EXPECT_EQ(-7, Call(test->entry(), reinterpret_cast(&value), -4)); + EXPECT_EQ(-4, value); +} + ASSEMBLER_TEST_GENERATE(LoadByteAcquire, assembler) { __ SetExtensions(RV_GC | RV_Zalasr); __ lb(A0, Address(A1), std::memory_order_acquire); diff --git a/runtime/vm/compiler/assembler/disassembler_riscv.cc b/runtime/vm/compiler/assembler/disassembler_riscv.cc index d16bc4a345e2..83405f5c7912 100644 --- a/runtime/vm/compiler/assembler/disassembler_riscv.cc +++ b/runtime/vm/compiler/assembler/disassembler_riscv.cc @@ -1190,6 +1190,33 @@ void RISCVDisassembler::DisassembleAMO(Instr instr) { void RISCVDisassembler::DisassembleAMO8(Instr instr) { switch (instr.funct5()) { + case AMOSWAP: + Print("amoswap.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOADD: + Print("amoadd.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOXOR: + Print("amoxor.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOAND: + Print("amoand.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOOR: + Print("amoor.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMIN: + Print("amomin.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMAX: + Print("amomax.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMINU: + Print("amominu.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMAXU: + Print("amomaxu.b'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; case LOADORDERED: Print("lb'order 'rd, ('rs1)", instr, RV_Zalasr); break; @@ -1203,6 +1230,33 @@ void RISCVDisassembler::DisassembleAMO8(Instr instr) { void RISCVDisassembler::DisassembleAMO16(Instr instr) { switch (instr.funct5()) { + case AMOSWAP: + Print("amoswap.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOADD: + Print("amoadd.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOXOR: + Print("amoxor.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOAND: + Print("amoand.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOOR: + Print("amoor.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMIN: + Print("amomin.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMAX: + Print("amomax.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMINU: + Print("amominu.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; + case AMOMAXU: + Print("amomaxu.h'order 'rd, 'rs2, ('rs1)", instr, RV_Zabha); + break; case LOADORDERED: Print("lh'order 'rd, ('rs1)", instr, RV_Zalasr); break; diff --git a/runtime/vm/constants_riscv.h b/runtime/vm/constants_riscv.h index 3e24d690e85c..96464298b905 100644 --- a/runtime/vm/constants_riscv.h +++ b/runtime/vm/constants_riscv.h @@ -689,12 +689,24 @@ inline int32_t SignExtend(int N, int32_t value) { (32 - N); } +inline intx_t sign_extend(int8_t x) { + return static_cast(x); +} +inline intx_t sign_extend(int16_t x) { + return static_cast(x); +} inline intx_t sign_extend(int32_t x) { return static_cast(x); } inline intx_t sign_extend(int64_t x) { return static_cast(x); } +inline intx_t sign_extend(uint8_t x) { + return static_cast(static_cast(x)); +} +inline intx_t sign_extend(uint16_t x) { + return static_cast(static_cast(x)); +} inline intx_t sign_extend(uint32_t x) { return static_cast(static_cast(x)); } @@ -1676,7 +1688,8 @@ static constexpr ExtensionSet RV_B = RV_Zba | RV_Zbb | RV_Zbs; static constexpr ExtensionSet RV_GCB = RV_GC | RV_B; static constexpr Extension RV_Zicond(10); // Integer conditional operations static constexpr Extension RV_Zcb(11); // More compressed instructions -static constexpr Extension RV_Zalasr(12); // Load-acquire, store-release +static constexpr Extension RV_Zabha(12); // Byte and halfword AMOs +static constexpr Extension RV_Zalasr(13); // Load-acquire, store-release #if defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_ANDROID) static constexpr ExtensionSet RV_baseline = RV_GCB; diff --git a/runtime/vm/simulator_riscv.cc b/runtime/vm/simulator_riscv.cc index b3d593833cf2..5028c1c3f508 100644 --- a/runtime/vm/simulator_riscv.cc +++ b/runtime/vm/simulator_riscv.cc @@ -2346,6 +2346,33 @@ void Simulator::InterpretSTOREORDERED(Instr instr) { void Simulator::InterpretAMO8(Instr instr) { switch (instr.funct5()) { + case AMOSWAP: + InterpretAMOSWAP(instr); + break; + case AMOADD: + InterpretAMOADD(instr); + break; + case AMOXOR: + InterpretAMOXOR(instr); + break; + case AMOAND: + InterpretAMOAND(instr); + break; + case AMOOR: + InterpretAMOOR(instr); + break; + case AMOMIN: + InterpretAMOMIN(instr); + break; + case AMOMAX: + InterpretAMOMAX(instr); + break; + case AMOMINU: + InterpretAMOMIN(instr); + break; + case AMOMAXU: + InterpretAMOMAX(instr); + break; case LOADORDERED: InterpretLOADORDERED(instr); break; @@ -2360,6 +2387,33 @@ void Simulator::InterpretAMO8(Instr instr) { void Simulator::InterpretAMO16(Instr instr) { switch (instr.funct5()) { + case AMOSWAP: + InterpretAMOSWAP(instr); + break; + case AMOADD: + InterpretAMOADD(instr); + break; + case AMOXOR: + InterpretAMOXOR(instr); + break; + case AMOAND: + InterpretAMOAND(instr); + break; + case AMOOR: + InterpretAMOOR(instr); + break; + case AMOMIN: + InterpretAMOMIN(instr); + break; + case AMOMAX: + InterpretAMOMAX(instr); + break; + case AMOMINU: + InterpretAMOMIN(instr); + break; + case AMOMAXU: + InterpretAMOMAX(instr); + break; case LOADORDERED: InterpretLOADORDERED(instr); break; From c94bad54fd986b055d3372717ad3fe7d669fc59d Mon Sep 17 00:00:00 2001 From: pq Date: Wed, 13 Nov 2024 00:55:28 +0000 Subject: [PATCH 5/8] [element model] migrate `avoid_renaming_method_parameters` Interestingly this fixes a few false negatives that are showing up on the bots: Flutter: https://github.com/flutter/packages/blob/main/packages/vector_graphics_compiler/lib/src/svg/visitor.dart#L48 Dart: https://dart-review.googlesource.com/c/sdk/+/394569 Bug: https://github.com/dart-lang/linter/issues/5099 Change-Id: I1ff221bd8591d712caaaa24f15d065bf8b2426ce Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394662 Commit-Queue: Phil Quitslund Reviewed-by: Brian Wilkerson Auto-Submit: Phil Quitslund --- .../avoid_renaming_method_parameters.dart | 76 ++++++++++--------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart b/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart index 579cc3462a8e..c06d7f980494 100644 --- a/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart +++ b/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart @@ -5,9 +5,10 @@ import 'dart:math' as math; import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/element2.dart'; +// ignore: implementation_imports +import 'package:analyzer/src/dart/ast/ast.dart'; import '../analyzer.dart'; import '../extensions.dart'; @@ -29,7 +30,8 @@ class AvoidRenamingMethodParameters extends LintRule { NodeLintRegistry registry, LinterContext context) { if (!context.isInLibDir) return; - var visitor = _Visitor(this, context.libraryElement); + var visitor = + _Visitor(this, context.libraryElement2, context.inheritanceManager); registry.addMethodDeclaration(this, visitor); } } @@ -38,9 +40,11 @@ class _Visitor extends SimpleAstVisitor { /// Whether the `wildcard_variables` feature is enabled. final bool _wildCardVariablesEnabled; + final InheritanceManager3 inheritanceManager; + final LintRule rule; - _Visitor(this.rule, LibraryElement? library) + _Visitor(this.rule, LibraryElement2? library, this.inheritanceManager) : _wildCardVariablesEnabled = library?.featureSet.isEnabled(Feature.wildcard_variables) ?? false; @@ -52,50 +56,47 @@ class _Visitor extends SimpleAstVisitor { if (node.isStatic) return; if (node.documentationComment != null) return; - var parentNode = node.parent; - if (parentNode is! Declaration) { - return; - } - var parentElement = parentNode.declaredElement; - // Note: there are no override semantics with extension methods. - if (parentElement is! InterfaceElement) { - return; - } + var nodeParams = node.parameters; + if (nodeParams == null) return; - var classElement = parentElement; + late List parentParameters; - if (classElement.isPrivate) return; + var previousFragment = node.declaredFragment?.previousFragment; + if (previousFragment == null) { + // If it's the first fragment, check for an inherited member. + var parentNode = node.parent; + if (parentNode is! Declaration) return; - var parentMethod = classElement.lookUpInheritedMethod( - node.name.lexeme, classElement.library); + var parentElement = parentNode.declaredFragment?.element; - // If it's not an inherited method, check for an augmentation. - if (parentMethod == null && node.isAugmentation) { - var element = node.declaredElement; - // Note that we only require an augmentation to conform to the previous - // declaration/augmentation in the chain. - var target = element?.augmentationTarget; - if (target is MethodElement) { - parentMethod = target; - } - } + // Note: there are no override semantics with extension methods. + if (parentElement is! InterfaceElement2) return; + if (parentElement.isPrivate) return; - if (parentMethod == null) return; + var parentMethod = inheritanceManager.getMember4( + parentElement, Name(parentElement.library2.uri, node.name.lexeme), + forSuper: true); + if (parentMethod == null) return; - var nodeParams = node.parameters; - if (nodeParams == null) { - return; + parentParameters = parentMethod.formalParameters.positional; + } else { + if (!node.isAugmentation) return; + + parentParameters = + previousFragment.formalParameters.map((p) => p.element).positional; } var parameters = nodeParams.parameters.where((p) => !p.isNamed).toList(); - var parentParameters = - parentMethod.parameters.where((p) => !p.isNamed).toList(); + var count = math.min(parameters.length, parentParameters.length); for (var i = 0; i < count; i++) { if (parentParameters.length <= i) break; - var parentParameterName = parentParameters[i].name; - if (isWildcardIdentifier(parentParameterName)) continue; + var parentParameterName = parentParameters[i].name3; + if (parentParameterName == null || + isWildcardIdentifier(parentParameterName)) { + continue; + } var parameterName = parameters[i].name; if (parameterName == null) continue; @@ -110,3 +111,8 @@ class _Visitor extends SimpleAstVisitor { } } } + +extension on Iterable { + List get positional => + where((p) => !p.isNamed).toList(); +} From e3987ed32f3e1ac6a71f27b2debd85828cec2629 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Wed, 13 Nov 2024 00:57:46 +0000 Subject: [PATCH 6/8] [vm] Add the RISC-V Zfa extension. TEST=ci, local qemu Change-Id: Ifede459753ed76b3b01f09d0c0cff1c8b09bba04 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394567 Commit-Queue: Ryan Macnak Reviewed-by: Alexander Aprelev --- runtime/platform/assert.h | 19 + .../vm/compiler/assembler/assembler_riscv.cc | 146 ++- .../vm/compiler/assembler/assembler_riscv.h | 25 +- .../assembler/assembler_riscv_test.cc | 890 ++++++++++++++++++ .../compiler/assembler/disassembler_riscv.cc | 121 ++- runtime/vm/compiler/backend/il_riscv.cc | 18 + runtime/vm/constants_riscv.h | 79 +- runtime/vm/simulator_riscv.cc | 165 +++- runtime/vm/simulator_riscv.h | 30 + 9 files changed, 1454 insertions(+), 39 deletions(-) diff --git a/runtime/platform/assert.h b/runtime/platform/assert.h index e2f68a937921..b786413d4853 100644 --- a/runtime/platform/assert.h +++ b/runtime/platform/assert.h @@ -18,6 +18,8 @@ // implemented for all the primitive types we can replace the usage of // sstream by Utils::toString() #if defined(DEBUG) || defined(TESTING) +#include + #include #include #endif @@ -63,6 +65,9 @@ class Expect : public DynamicAssertionHelper { template void NotEquals(const E& not_expected, const A& actual); + template + void BitEquals(const E& expected, const A& actual); + template void FloatEquals(const E& expected, const A& actual, const T& tol); @@ -126,6 +131,17 @@ void Expect::NotEquals(const E& not_expected, const A& actual) { Fail("did not expect: <%s>", nes.c_str()); } +template +void Expect::BitEquals(const E& expected, const A& actual) { + static_assert(sizeof(E) == sizeof(A)); + if (memcmp(&expected, &actual, sizeof(E)) == 0) return; + std::ostringstream ess, ass; + ess << expected; + ass << actual; + std::string es = ess.str(), as = ass.str(); + Fail("expected: <%s> but was: <%s>", es.c_str(), as.c_str()); +} + template void Expect::FloatEquals(const E& expected, const A& actual, const T& tol) { if (((expected - tol) <= actual) && (actual <= (expected + tol))) { @@ -356,6 +372,9 @@ void Expect::Null(const T p) { #define EXPECT_NE(not_expected, actual) \ dart::Expect(__FILE__, __LINE__).NotEquals((not_expected), (actual)) +#define EXPECT_BITEQ(expected, actual) \ + dart::Expect(__FILE__, __LINE__).BitEquals((expected), (actual)) + #define EXPECT_FLOAT_EQ(expected, actual, tol) \ dart::Expect(__FILE__, __LINE__).FloatEquals((expected), (actual), (tol)) diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc index d23fa248b765..e92323e29c41 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv.cc @@ -1175,14 +1175,14 @@ void MicroAssembler::fcvtwus(Register rd, void MicroAssembler::fcvtsw(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_F)); - EmitRType(FCVTSint, FRegister(W), rs1, rounding, rd, OPFP); + EmitRType(FCVTSint, Register(W), rs1, rounding, rd, OPFP); } void MicroAssembler::fcvtswu(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_F)); - EmitRType(FCVTSint, FRegister(WU), rs1, rounding, rd, OPFP); + EmitRType(FCVTSint, Register(WU), rs1, rounding, rd, OPFP); } void MicroAssembler::fmvxw(Register rd, FRegister rs1) { @@ -1192,7 +1192,7 @@ void MicroAssembler::fmvxw(Register rd, FRegister rs1) { void MicroAssembler::fmvwx(FRegister rd, Register rs1) { ASSERT(Supports(RV_F)); - EmitRType(FMVWX, FRegister(0), rs1, F3_0, rd, OPFP); + EmitRType(FMVWX, Register(0), rs1, F3_0, rd, OPFP); } #if XLEN >= 64 @@ -1210,14 +1210,14 @@ void MicroAssembler::fcvtlus(Register rd, void MicroAssembler::fcvtsl(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_F)); - EmitRType(FCVTSint, FRegister(L), rs1, rounding, rd, OPFP); + EmitRType(FCVTSint, Register(L), rs1, rounding, rd, OPFP); } void MicroAssembler::fcvtslu(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_F)); - EmitRType(FCVTSint, FRegister(LU), rs1, rounding, rd, OPFP); + EmitRType(FCVTSint, Register(LU), rs1, rounding, rd, OPFP); } #endif // XLEN >= 64 @@ -1399,14 +1399,14 @@ void MicroAssembler::fcvtwud(Register rd, void MicroAssembler::fcvtdw(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_D)); - EmitRType(FCVTDint, FRegister(W), rs1, rounding, rd, OPFP); + EmitRType(FCVTDint, Register(W), rs1, rounding, rd, OPFP); } void MicroAssembler::fcvtdwu(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_D)); - EmitRType(FCVTDint, FRegister(WU), rs1, rounding, rd, OPFP); + EmitRType(FCVTDint, Register(WU), rs1, rounding, rd, OPFP); } #if XLEN >= 64 @@ -1429,19 +1429,19 @@ void MicroAssembler::fmvxd(Register rd, FRegister rs1) { void MicroAssembler::fcvtdl(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_D)); - EmitRType(FCVTDint, FRegister(L), rs1, rounding, rd, OPFP); + EmitRType(FCVTDint, Register(L), rs1, rounding, rd, OPFP); } void MicroAssembler::fcvtdlu(FRegister rd, Register rs1, RoundingMode rounding) { ASSERT(Supports(RV_D)); - EmitRType(FCVTDint, FRegister(LU), rs1, rounding, rd, OPFP); + EmitRType(FCVTDint, Register(LU), rs1, rounding, rd, OPFP); } void MicroAssembler::fmvdx(FRegister rd, Register rs1) { ASSERT(Supports(RV_D)); - EmitRType(FMVDX, FRegister(0), rs1, F3_0, rd, OPFP); + EmitRType(FMVDX, Register(0), rs1, F3_0, rd, OPFP); } #endif // XLEN >= 64 @@ -1873,6 +1873,102 @@ void MicroAssembler::amomaxuh(Register rd, EmitRType(AMOMAXU, order, rs2, addr.base(), WIDTH16, rd, AMO); } +void MicroAssembler::flis(FRegister rd, intptr_t index) { + ASSERT((index >= 0) && (index < 32)); + ASSERT(Supports(RV_Zfa)); + EmitRType(FMVWX, FRegister(1), FRegister(index), F3_0, rd, OPFP); +} + +void MicroAssembler::flid(FRegister rd, intptr_t index) { + ASSERT((index >= 0) && (index < 32)); + ASSERT(Supports(RV_Zfa)); + EmitRType(FMVDX, FRegister(1), FRegister(index), F3_0, rd, OPFP); +} + +void MicroAssembler::fminms(FRegister rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FMINMAXS, rs2, rs1, FMINM, rd, OPFP); +} +void MicroAssembler::fmaxms(FRegister rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FMINMAXS, rs2, rs1, FMAXM, rd, OPFP); +} + +void MicroAssembler::fminmd(FRegister rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FMINMAXD, rs2, rs1, FMINM, rd, OPFP); +} + +void MicroAssembler::fmaxmd(FRegister rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FMINMAXD, rs2, rs1, FMAXM, rd, OPFP); +} + +void MicroAssembler::frounds(FRegister rd, + FRegister rs1, + RoundingMode rounding) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCVTS, FRegister(4), rs1, rounding, rd, OPFP); +} + +void MicroAssembler::froundnxs(FRegister rd, + FRegister rs1, + RoundingMode rounding) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCVTS, FRegister(5), rs1, rounding, rd, OPFP); +} + +void MicroAssembler::froundd(FRegister rd, + FRegister rs1, + RoundingMode rounding) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCVTD, FRegister(4), rs1, rounding, rd, OPFP); +} + +void MicroAssembler::froundnxd(FRegister rd, + FRegister rs1, + RoundingMode rounding) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCVTD, FRegister(5), rs1, rounding, rd, OPFP); +} + +void MicroAssembler::fcvtmodwd(Register rd, FRegister rs1) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCVTintD, FRegister(8), rs1, RTZ, rd, OPFP); +} + +#if XLEN == 32 +void MicroAssembler::fmvhxd(Register rd, FRegister rs1) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FMVHXD, FRegister(1), rs1, F3_0, rd, OPFP); +} + +void MicroAssembler::fmvpdx(FRegister rd, Register rs1, Register rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FMVPDX, rs2, rs1, F3_0, rd, OPFP); +} +#endif // XLEN == 32 + +void MicroAssembler::fltqs(Register rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCMPS, rs2, rs1, FLTQ, rd, OPFP); +} + +void MicroAssembler::fleqs(Register rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCMPS, rs2, rs1, FLEQ, rd, OPFP); +} + +void MicroAssembler::fltqd(Register rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCMPD, rs2, rs1, FLTQ, rd, OPFP); +} + +void MicroAssembler::fleqd(Register rd, FRegister rs1, FRegister rs2) { + ASSERT(Supports(RV_Zfa)); + EmitRType(FCMPD, rs2, rs1, FLEQ, rd, OPFP); +} + void MicroAssembler::lb(Register rd, Address addr, std::memory_order order) { ASSERT(addr.offset() == 0); ASSERT((order == std::memory_order_acquire) || @@ -2554,14 +2650,14 @@ void MicroAssembler::EmitRType(Funct7 funct7, } void MicroAssembler::EmitRType(Funct7 funct7, - FRegister rs2, + Register rs2, Register rs1, RoundingMode round, FRegister rd, Opcode opcode) { uint32_t e = 0; e |= EncodeFunct7(funct7); - e |= EncodeFRs2(rs2); + e |= EncodeRs2(rs2); e |= EncodeRs1(rs1); e |= EncodeRoundingMode(round); e |= EncodeFRd(rd); @@ -2570,14 +2666,14 @@ void MicroAssembler::EmitRType(Funct7 funct7, } void MicroAssembler::EmitRType(Funct7 funct7, - FRegister rs2, + Register rs2, Register rs1, Funct3 funct3, FRegister rd, Opcode opcode) { uint32_t e = 0; e |= EncodeFunct7(funct7); - e |= EncodeFRs2(rs2); + e |= EncodeRs2(rs2); e |= EncodeRs1(rs1); e |= EncodeFunct3(funct3); e |= EncodeFRd(rd); @@ -4074,10 +4170,19 @@ void Assembler::LoadImmediate(Register reg, intx_t imm) { } void Assembler::LoadSImmediate(FRegister reg, float imms) { - int32_t imm = bit_cast(imms); + uint32_t imm = bit_cast(imms); if (imm == 0) { fmvwx(reg, ZR); // bit_cast uint32_t -> float } else { + if (Supports(RV_Zfa)) { + for (intptr_t i = 0; i < 32; i++) { + if (kFlisConstants[i] == imm) { + flis(reg, i); + return; + } + } + } + ASSERT(constant_pool_allowed()); intptr_t index = object_pool_builder().FindImmediate(imm); intptr_t offset = target::ObjectPool::element_offset(index); @@ -4086,7 +4191,7 @@ void Assembler::LoadSImmediate(FRegister reg, float imms) { } void Assembler::LoadDImmediate(FRegister reg, double immd) { - int64_t imm = bit_cast(immd); + uint64_t imm = bit_cast(immd); if (imm == 0) { #if XLEN >= 64 fmvdx(reg, ZR); // bit_cast uint64_t -> double @@ -4094,6 +4199,15 @@ void Assembler::LoadDImmediate(FRegister reg, double immd) { fcvtdwu(reg, ZR); // static_cast uint32_t -> double #endif } else { + if (Supports(RV_Zfa)) { + for (intptr_t i = 0; i < 32; i++) { + if (kFlidConstants[i] == imm) { + flid(reg, i); + return; + } + } + } + ASSERT(constant_pool_allowed()); intptr_t index = object_pool_builder().FindImmediate64(imm); intptr_t offset = target::ObjectPool::element_offset(index); diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h index da43d6a55df2..d77da40d1e5b 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv.h +++ b/runtime/vm/compiler/assembler/assembler_riscv.h @@ -687,6 +687,27 @@ class MicroAssembler : public AssemblerBase { Address addr, std::memory_order order = std::memory_order_relaxed); + // ==== Zfa: Additional floating-point instructions ==== + void flis(FRegister rd, intptr_t index); + void flid(FRegister rd, intptr_t index); + void fminms(FRegister rd, FRegister rs1, FRegister rs2); + void fmaxms(FRegister rd, FRegister rs1, FRegister rs2); + void fminmd(FRegister rd, FRegister rs1, FRegister rs2); + void fmaxmd(FRegister rd, FRegister rs1, FRegister rs2); + void frounds(FRegister rd, FRegister rs1, RoundingMode rounding = RNE); + void froundd(FRegister rd, FRegister rs1, RoundingMode rounding = RNE); + void froundnxs(FRegister rd, FRegister rs1, RoundingMode rounding = RNE); + void froundnxd(FRegister rd, FRegister rs1, RoundingMode rounding = RNE); + void fcvtmodwd(Register rd, FRegister rs1); +#if XLEN == 32 + void fmvhxd(Register rd, FRegister rs1); + void fmvpdx(FRegister rd, Register rs1, Register rs2); +#endif + void fltqs(Register rd, FRegister rs1, FRegister rs2); + void fleqs(Register rd, FRegister rs1, FRegister rs2); + void fltqd(Register rd, FRegister rs1, FRegister rs2); + void fleqd(Register rd, FRegister rs1, FRegister rs2); + // ==== Zalasr: Load-acquire, store-release ==== void lb(Register rd, Address addr, std::memory_order order); void lh(Register rd, Address addr, std::memory_order order); @@ -830,13 +851,13 @@ class MicroAssembler : public AssemblerBase { FRegister rd, Opcode opcode); void EmitRType(Funct7 funct7, - FRegister rs2, + Register rs2, Register rs1, RoundingMode round, FRegister rd, Opcode opcode); void EmitRType(Funct7 funct7, - FRegister rs2, + Register rs2, Register rs1, Funct3 funct3, FRegister rd, diff --git a/runtime/vm/compiler/assembler/assembler_riscv_test.cc b/runtime/vm/compiler/assembler/assembler_riscv_test.cc index 76ca48a890f2..e9cfccaeed11 100644 --- a/runtime/vm/compiler/assembler/assembler_riscv_test.cc +++ b/runtime/vm/compiler/assembler/assembler_riscv_test.cc @@ -129,6 +129,16 @@ static double CallD(intx_t entry, intx_t arg0) { return reinterpret_cast(entry)(arg0); #endif } +#if XLEN == 32 +static double CallD(intx_t entry, int64_t arg0) { +#if defined(USING_SIMULATOR) + return Simulator::Current()->CallD(entry, arg0); +#else + typedef double (*F)(int64_t); + return reinterpret_cast(entry)(arg0); +#endif +} +#endif static double CallD(intx_t entry, intx_t arg0, double arg1) { #if defined(USING_SIMULATOR) return Simulator::Current()->CallD(entry, arg0, arg1); @@ -185,6 +195,16 @@ static intx_t CallI(intx_t entry, double arg0, double arg1) { return reinterpret_cast(entry)(arg0, arg1); #endif } +#if XLEN == 32 +static int64_t CallI64(intx_t entry, double arg0, double arg1 = 0.0) { +#if defined(USING_SIMULATOR) + return Simulator::Current()->CallI64(entry, arg0, arg1); +#else + typedef int64_t (*F)(double, double); + return reinterpret_cast(entry)(arg0, arg1); +#endif +} +#endif ASSEMBLER_TEST_GENERATE(LoadUpperImmediate, assembler) { __ SetExtensions(RV_G); @@ -7589,6 +7609,876 @@ ASSEMBLER_TEST_RUN(AmoMaxUnsignedHalfword, test) { EXPECT_EQ(-4, value); } +ASSEMBLER_TEST_GENERATE(FloatLoadImmediateSingle, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + for (intptr_t i = 0; i < 32; i++) { + __ flis(FRegister(i), i); + } +} +ASSEMBLER_TEST_RUN(FloatLoadImmediateSingle, test) { + EXPECT_DISASSEMBLY( + "f0100053 flis ft0, -1.000000\n" + "f01080d3 flis ft1, min\n" + "f0110153 flis ft2, 0.000015\n" + "f01181d3 flis ft3, 0.000031\n" + "f0120253 flis ft4, 0.003906\n" + "f01282d3 flis ft5, 0.007812\n" + "f0130353 flis ft6, 0.062500\n" + "f01383d3 flis ft7, 0.125000\n" + "f0140453 flis fs0, 0.250000\n" + "f01484d3 flis fs1, 0.312500\n" + "f0150553 flis fa0, 0.375000\n" + "f01585d3 flis fa1, 0.437500\n" + "f0160653 flis fa2, 0.500000\n" + "f01686d3 flis fa3, 0.625000\n" + "f0170753 flis fa4, 0.750000\n" + "f01787d3 flis fa5, 0.875000\n" + "f0180853 flis fa6, 1.000000\n" + "f01888d3 flis fa7, 1.250000\n" + "f0190953 flis fs2, 1.500000\n" + "f01989d3 flis fs3, 1.750000\n" + "f01a0a53 flis fs4, 2.000000\n" + "f01a8ad3 flis fs5, 2.500000\n" + "f01b0b53 flis fs6, 3.000000\n" + "f01b8bd3 flis fs7, 4.000000\n" + "f01c0c53 flis fs8, 8.000000\n" + "f01c8cd3 flis fs9, 16.000000\n" + "f01d0d53 flis fs10, 128.000000\n" + "f01d8dd3 flis fs11, 256.000000\n" + "f01e0e53 flis ft8, 32768.000000\n" + "f01e8ed3 flis ft9, 65536.000000\n" + "f01f0f53 flis ft10, inf\n" + "f01f8fd3 flis ft11, nan\n"); +} + +ASSEMBLER_TEST_GENERATE(FloatLoadImmediateDouble, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + for (intptr_t i = 0; i < 32; i++) { + __ flid(FRegister(i), i); + } +} +ASSEMBLER_TEST_RUN(FloatLoadImmediateDouble, test) { + EXPECT_DISASSEMBLY( + "f2100053 flid ft0, -1.000000\n" + "f21080d3 flid ft1, min\n" + "f2110153 flid ft2, 0.000015\n" + "f21181d3 flid ft3, 0.000031\n" + "f2120253 flid ft4, 0.003906\n" + "f21282d3 flid ft5, 0.007812\n" + "f2130353 flid ft6, 0.062500\n" + "f21383d3 flid ft7, 0.125000\n" + "f2140453 flid fs0, 0.250000\n" + "f21484d3 flid fs1, 0.312500\n" + "f2150553 flid fa0, 0.375000\n" + "f21585d3 flid fa1, 0.437500\n" + "f2160653 flid fa2, 0.500000\n" + "f21686d3 flid fa3, 0.625000\n" + "f2170753 flid fa4, 0.750000\n" + "f21787d3 flid fa5, 0.875000\n" + "f2180853 flid fa6, 1.000000\n" + "f21888d3 flid fa7, 1.250000\n" + "f2190953 flid fs2, 1.500000\n" + "f21989d3 flid fs3, 1.750000\n" + "f21a0a53 flid fs4, 2.000000\n" + "f21a8ad3 flid fs5, 2.500000\n" + "f21b0b53 flid fs6, 3.000000\n" + "f21b8bd3 flid fs7, 4.000000\n" + "f21c0c53 flid fs8, 8.000000\n" + "f21c8cd3 flid fs9, 16.000000\n" + "f21d0d53 flid fs10, 128.000000\n" + "f21d8dd3 flid fs11, 256.000000\n" + "f21e0e53 flid ft8, 32768.000000\n" + "f21e8ed3 flid ft9, 65536.000000\n" + "f21f0f53 flid ft10, inf\n" + "f21f8fd3 flid ft11, nan\n"); +} + +ASSEMBLER_TEST_GENERATE(SingleMinM, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fminms(FA0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(SingleMinM, test) { + EXPECT_DISASSEMBLY( + "28b52553 fminm.s fa0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(1.0f, CallF(test->entry(), 3.0f, 1.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, 3.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, 5.0f)); + EXPECT_EQ(-1.0f, CallF(test->entry(), 3.0f, -1.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), 3.0f, -3.0f)); + EXPECT_EQ(-5.0f, CallF(test->entry(), 3.0f, -5.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, 1.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, 3.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, 5.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, -1.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, -3.0f)); + EXPECT_EQ(-5.0f, CallF(test->entry(), -3.0f, -5.0f)); + + EXPECT_BITEQ(-0.0f, CallF(test->entry(), 0.0f, -0.0f)); + EXPECT_BITEQ(-0.0f, CallF(test->entry(), -0.0f, 0.0f)); + + float qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_BITEQ(qNAN, CallF(test->entry(), 3.0f, qNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, 3.0f)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), -3.0f, qNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, -3.0f)); + + float sNAN = std::numeric_limits::signaling_NaN(); + EXPECT_BITEQ(qNAN, CallF(test->entry(), 3.0f, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, 3.0f)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), -3.0f, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, -3.0f)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, qNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, qNAN)); +} + +ASSEMBLER_TEST_GENERATE(SingleMaxM, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fmaxms(FA0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(SingleMaxM, test) { + EXPECT_DISASSEMBLY( + "28b53553 fmaxm.s fa0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, 1.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, 3.0f)); + EXPECT_EQ(5.0f, CallF(test->entry(), 3.0f, 5.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, -1.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, -3.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), 3.0f, -5.0f)); + EXPECT_EQ(1.0f, CallF(test->entry(), -3.0f, 1.0f)); + EXPECT_EQ(3.0f, CallF(test->entry(), -3.0f, 3.0f)); + EXPECT_EQ(5.0f, CallF(test->entry(), -3.0f, 5.0f)); + EXPECT_EQ(-1.0f, CallF(test->entry(), -3.0f, -1.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, -3.0f)); + EXPECT_EQ(-3.0f, CallF(test->entry(), -3.0f, -5.0f)); + + EXPECT_BITEQ(0.0f, CallF(test->entry(), 0.0f, -0.0f)); + EXPECT_BITEQ(0.0f, CallF(test->entry(), -0.0f, 0.0f)); + + float qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_BITEQ(qNAN, CallF(test->entry(), 3.0f, qNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, 3.0f)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), -3.0f, qNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, -3.0f)); + + float sNAN = std::numeric_limits::signaling_NaN(); + EXPECT_BITEQ(qNAN, CallF(test->entry(), 3.0f, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, 3.0f)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), -3.0f, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, -3.0f)); + + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, qNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), qNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallF(test->entry(), sNAN, qNAN)); +} + +ASSEMBLER_TEST_GENERATE(DoubleMinM, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fminmd(FA0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(DoubleMinM, test) { + EXPECT_DISASSEMBLY( + "2ab52553 fminm.d fa0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(1.0, CallD(test->entry(), 3.0, 1.0)); + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, 3.0)); + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, 5.0)); + EXPECT_EQ(-1.0, CallD(test->entry(), 3.0, -1.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), 3.0, -3.0)); + EXPECT_EQ(-5.0, CallD(test->entry(), 3.0, -5.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, 1.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, 3.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, 5.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, -1.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, -3.0)); + EXPECT_EQ(-5.0, CallD(test->entry(), -3.0, -5.0)); + + EXPECT_BITEQ(-0.0, CallD(test->entry(), 0.0, -0.0)); + EXPECT_BITEQ(-0.0, CallD(test->entry(), -0.0, 0.0)); + + double qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_BITEQ(qNAN, CallD(test->entry(), 3.0, qNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, 3.0)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), -3.0, qNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, -3.0)); + + double sNAN = std::numeric_limits::signaling_NaN(); + EXPECT_BITEQ(qNAN, CallD(test->entry(), 3.0, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, 3.0)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), -3.0, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, -3.0)); + + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, qNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, qNAN)); +} + +ASSEMBLER_TEST_GENERATE(DoubleMaxM, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fmaxmd(FA0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(DoubleMaxM, test) { + EXPECT_DISASSEMBLY( + "2ab53553 fmaxm.d fa0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, 1.0)); + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, 3.0)); + EXPECT_EQ(5.0, CallD(test->entry(), 3.0, 5.0)); + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, -1.0)); + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, -3.0)); + EXPECT_EQ(3.0, CallD(test->entry(), 3.0, -5.0)); + EXPECT_EQ(1.0, CallD(test->entry(), -3.0, 1.0)); + EXPECT_EQ(3.0, CallD(test->entry(), -3.0, 3.0)); + EXPECT_EQ(5.0, CallD(test->entry(), -3.0, 5.0)); + EXPECT_EQ(-1.0, CallD(test->entry(), -3.0, -1.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, -3.0)); + EXPECT_EQ(-3.0, CallD(test->entry(), -3.0, -5.0)); + + EXPECT_BITEQ(0.0, CallD(test->entry(), 0.0, -0.0)); + EXPECT_BITEQ(0.0, CallD(test->entry(), -0.0, 0.0)); + + double qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_BITEQ(qNAN, CallD(test->entry(), 3.0, qNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, 3.0)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), -3.0, qNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, -3.0)); + + double sNAN = std::numeric_limits::signaling_NaN(); + EXPECT_BITEQ(qNAN, CallD(test->entry(), 3.0, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, 3.0)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), -3.0, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, -3.0)); + + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, qNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), qNAN, sNAN)); + EXPECT_BITEQ(qNAN, CallD(test->entry(), sNAN, qNAN)); +} + +ASSEMBLER_TEST_GENERATE(RoundSingle, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ frounds(FA0, FA0); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundSingle, test) { + EXPECT_DISASSEMBLY( + "40450553 fround.s fa0, fa0\n" + "00008067 ret\n"); + + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.6f)); + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.5f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.4f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.0f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -42.6f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.5f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.4f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.0f)); + EXPECT_BITEQ(-0.0f, CallF(test->entry(), -0.0f)); + EXPECT_BITEQ(+0.0f, CallF(test->entry(), +0.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.4f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.5f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 42.6f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.0f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.4f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.5f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.6f)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallF(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallF(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundSingle_RTZ, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ frounds(FA0, FA0, RTZ); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundSingle_RTZ, test) { + EXPECT_DISASSEMBLY( + "40451553 fround.s fa0, fa0, rtz\n" + "00008067 ret\n"); + + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.6f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.5f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.4f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.0f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.6f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.5f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.4f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.0f)); + EXPECT_BITEQ(-0.0f, CallF(test->entry(), -0.0f)); + EXPECT_BITEQ(+0.0f, CallF(test->entry(), +0.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.4f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.5f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.6f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.0f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.4f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.5f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.6f)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallF(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallF(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundSingle_RDN, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ frounds(FA0, FA0, RDN); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundSingle_RDN, test) { + EXPECT_DISASSEMBLY( + "40452553 fround.s fa0, fa0, rdn\n" + "00008067 ret\n"); + + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.6f)); + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.5f)); + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.4f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.0f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -42.6f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -42.5f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -42.4f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.0f)); + EXPECT_BITEQ(-0.0f, CallF(test->entry(), -0.0f)); + EXPECT_BITEQ(+0.0f, CallF(test->entry(), +0.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.4f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.5f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.6f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.0f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.4f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.5f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.6f)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallF(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallF(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundSingle_RUP, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ frounds(FA0, FA0, RUP); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundSingle_RUP, test) { + EXPECT_DISASSEMBLY( + "40453553 fround.s fa0, fa0, rup\n" + "00008067 ret\n"); + + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.6f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.5f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.4f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.0f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.6f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.5f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.4f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.0f)); + EXPECT_BITEQ(-0.0f, CallF(test->entry(), -0.0f)); + EXPECT_BITEQ(+0.0f, CallF(test->entry(), +0.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.0f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 42.4f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 42.5f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 42.6f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.0f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.4f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.5f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.6f)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallF(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallF(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundSingle_RMM, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ frounds(FA0, FA0, RMM); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundSingle_RMM, test) { + EXPECT_DISASSEMBLY( + "40454553 fround.s fa0, fa0, rmm\n" + "00008067 ret\n"); + + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.6f)); + EXPECT_EQ(-44.0f, CallF(test->entry(), -43.5f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.4f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -43.0f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -42.6f)); + EXPECT_EQ(-43.0f, CallF(test->entry(), -42.5f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.4f)); + EXPECT_EQ(-42.0f, CallF(test->entry(), -42.0f)); + EXPECT_BITEQ(-0.0f, CallF(test->entry(), -0.0f)); + EXPECT_BITEQ(+0.0f, CallF(test->entry(), +0.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.0f)); + EXPECT_EQ(42.0f, CallF(test->entry(), 42.4f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 42.5f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 42.6f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.0f)); + EXPECT_EQ(43.0f, CallF(test->entry(), 43.4f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.5f)); + EXPECT_EQ(44.0f, CallF(test->entry(), 43.6f)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallF(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallF(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallF(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundDouble, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ froundd(FA0, FA0); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundDouble, test) { + EXPECT_DISASSEMBLY( + "42450553 fround.d fa0, fa0\n" + "00008067 ret\n"); + + EXPECT_EQ(-44.0, CallD(test->entry(), -43.6)); + EXPECT_EQ(-44.0, CallD(test->entry(), -43.5)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.4)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.0)); + EXPECT_EQ(-43.0, CallD(test->entry(), -42.6)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.5)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.4)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.0)); + EXPECT_BITEQ(-0.0, CallD(test->entry(), -0.0)); + EXPECT_BITEQ(+0.0, CallD(test->entry(), +0.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.4)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.5)); + EXPECT_EQ(43.0, CallD(test->entry(), 42.6)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.0)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.4)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.5)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.6)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallD(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallD(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundDouble_RTZ, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ froundd(FA0, FA0, RTZ); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundDouble_RTZ, test) { + EXPECT_DISASSEMBLY( + "42451553 fround.d fa0, fa0, rtz\n" + "00008067 ret\n"); + + EXPECT_EQ(-43.0, CallD(test->entry(), -43.6)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.5)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.4)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.0)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.6)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.5)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.4)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.0)); + EXPECT_BITEQ(-0.0, CallD(test->entry(), -0.0)); + EXPECT_BITEQ(+0.0, CallD(test->entry(), +0.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.4)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.5)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.6)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.0)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.4)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.5)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.6)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallD(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallD(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundDouble_RDN, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ froundd(FA0, FA0, RDN); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundDouble_RDN, test) { + EXPECT_DISASSEMBLY( + "42452553 fround.d fa0, fa0, rdn\n" + "00008067 ret\n"); + + EXPECT_EQ(-44.0, CallD(test->entry(), -43.6)); + EXPECT_EQ(-44.0, CallD(test->entry(), -43.5)); + EXPECT_EQ(-44.0, CallD(test->entry(), -43.4)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.0)); + EXPECT_EQ(-43.0, CallD(test->entry(), -42.6)); + EXPECT_EQ(-43.0, CallD(test->entry(), -42.5)); + EXPECT_EQ(-43.0, CallD(test->entry(), -42.4)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.0)); + EXPECT_BITEQ(-0.0, CallD(test->entry(), -0.0)); + EXPECT_BITEQ(+0.0, CallD(test->entry(), +0.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.4)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.5)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.6)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.0)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.4)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.5)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.6)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallD(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallD(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundDouble_RUP, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ froundd(FA0, FA0, RUP); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundDouble_RUP, test) { + EXPECT_DISASSEMBLY( + "42453553 fround.d fa0, fa0, rup\n" + "00008067 ret\n"); + + EXPECT_EQ(-43.0, CallD(test->entry(), -43.6)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.5)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.4)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.0)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.6)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.5)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.4)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.0)); + EXPECT_BITEQ(-0.0, CallD(test->entry(), -0.0)); + EXPECT_BITEQ(+0.0, CallD(test->entry(), +0.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.0)); + EXPECT_EQ(43.0, CallD(test->entry(), 42.4)); + EXPECT_EQ(43.0, CallD(test->entry(), 42.5)); + EXPECT_EQ(43.0, CallD(test->entry(), 42.6)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.0)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.4)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.5)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.6)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallD(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallD(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(RoundDouble_RMM, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ froundd(FA0, FA0, RMM); + __ ret(); +} +ASSEMBLER_TEST_RUN(RoundDouble_RMM, test) { + EXPECT_DISASSEMBLY( + "42454553 fround.d fa0, fa0, rmm\n" + "00008067 ret\n"); + + EXPECT_EQ(-44.0, CallD(test->entry(), -43.6)); + EXPECT_EQ(-44.0, CallD(test->entry(), -43.5)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.4)); + EXPECT_EQ(-43.0, CallD(test->entry(), -43.0)); + EXPECT_EQ(-43.0, CallD(test->entry(), -42.6)); + EXPECT_EQ(-43.0, CallD(test->entry(), -42.5)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.4)); + EXPECT_EQ(-42.0, CallD(test->entry(), -42.0)); + EXPECT_BITEQ(-0.0, CallD(test->entry(), -0.0)); + EXPECT_BITEQ(+0.0, CallD(test->entry(), +0.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.0)); + EXPECT_EQ(42.0, CallD(test->entry(), 42.4)); + EXPECT_EQ(43.0, CallD(test->entry(), 42.5)); + EXPECT_EQ(43.0, CallD(test->entry(), 42.6)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.0)); + EXPECT_EQ(43.0, CallD(test->entry(), 43.4)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.5)); + EXPECT_EQ(44.0, CallD(test->entry(), 43.6)); + + EXPECT_EQ(-std::numeric_limits::infinity(), + CallD(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(std::numeric_limits::infinity(), + CallD(test->entry(), std::numeric_limits::infinity())); + EXPECT_BITEQ(std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallD(test->entry(), std::numeric_limits::signaling_NaN())); +} + +ASSEMBLER_TEST_GENERATE(ModularConvertDoubleToWord, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fcvtmodwd(A0, FA0); + __ ret(); +} +ASSEMBLER_TEST_RUN(ModularConvertDoubleToWord, test) { + EXPECT_DISASSEMBLY( + "c2851553 fcvtmod.w.d a0, fa0, rtz\n" + "00008067 ret\n"); + + EXPECT_EQ(-42, CallI(test->entry(), -42.0)); + EXPECT_EQ(0, CallI(test->entry(), 0.0)); + EXPECT_EQ(42, CallI(test->entry(), 42.0)); + EXPECT_EQ(sign_extend(kMinInt32), + CallI(test->entry(), static_cast(kMinInt32))); + EXPECT_EQ(sign_extend(kMaxInt32), + CallI(test->entry(), static_cast(kMaxInt32))); + EXPECT_EQ(-1, CallI(test->entry(), static_cast(kMaxUint32))); + EXPECT_EQ(0, CallI(test->entry(), static_cast(kMinInt64))); + EXPECT_EQ(0, CallI(test->entry(), static_cast(kMaxInt64))); + EXPECT_EQ(0, CallI(test->entry(), static_cast(kMaxUint64))); + EXPECT_EQ(0, + CallI(test->entry(), -std::numeric_limits::denorm_min())); + EXPECT_EQ(0, CallI(test->entry(), std::numeric_limits::denorm_min())); + EXPECT_EQ(0, CallI(test->entry(), -std::numeric_limits::infinity())); + EXPECT_EQ(0, CallI(test->entry(), std::numeric_limits::infinity())); + EXPECT_EQ(0, + CallI(test->entry(), std::numeric_limits::signaling_NaN())); +} + +#if XLEN == 32 +ASSEMBLER_TEST_GENERATE(BitCastDoubleToIntegerHigh, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fmvxw(A0, FA0); + __ fmvhxd(A1, FA0); + __ ret(); +} +ASSEMBLER_TEST_RUN(BitCastDoubleToIntegerHigh, test) { + EXPECT_DISASSEMBLY( + "e0050553 fmv.x.w a0, fa0\n" + "e21505d3 fmvh.x.d a1, fa0\n" + "00008067 ret\n"); + + EXPECT_EQ(bit_cast(0.0), CallI64(test->entry(), 0.0)); + EXPECT_EQ(bit_cast(-0.0), CallI64(test->entry(), -0.0)); + EXPECT_EQ(bit_cast(42.0), CallI64(test->entry(), 42.0)); + EXPECT_EQ(bit_cast(-42.0), CallI64(test->entry(), -42.0)); + EXPECT_EQ(bit_cast(std::numeric_limits::quiet_NaN()), + CallI64(test->entry(), std::numeric_limits::quiet_NaN())); + EXPECT_EQ( + bit_cast(std::numeric_limits::signaling_NaN()), + CallI64(test->entry(), std::numeric_limits::signaling_NaN())); + EXPECT_EQ(bit_cast(std::numeric_limits::infinity()), + CallI64(test->entry(), std::numeric_limits::infinity())); + EXPECT_EQ(bit_cast(-std::numeric_limits::infinity()), + CallI64(test->entry(), -std::numeric_limits::infinity())); +} + +ASSEMBLER_TEST_GENERATE(BitCastIntegerPairToDouble, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fmvpdx(FA0, A0, A1); + __ ret(); +} +ASSEMBLER_TEST_RUN(BitCastIntegerPairToDouble, test) { + EXPECT_DISASSEMBLY( + "b2b50553 fmvp.d.x fa0, a0, a1\n" + "00008067 ret\n"); + + EXPECT_BITEQ(0.0, CallD(test->entry(), bit_cast(0.0))); + EXPECT_BITEQ(-0.0, CallD(test->entry(), bit_cast(-0.0))); + EXPECT_EQ(42.0, CallD(test->entry(), bit_cast(42.0))); + EXPECT_EQ(-42.0, CallD(test->entry(), bit_cast(-42.0))); + EXPECT_BITEQ( + std::numeric_limits::quiet_NaN(), + CallD(test->entry(), + bit_cast(std::numeric_limits::quiet_NaN()))); + EXPECT_BITEQ( + std::numeric_limits::signaling_NaN(), + CallD(test->entry(), + bit_cast(std::numeric_limits::signaling_NaN()))); + EXPECT_BITEQ( + std::numeric_limits::infinity(), + CallD(test->entry(), + bit_cast(std::numeric_limits::infinity()))); + EXPECT_BITEQ( + -std::numeric_limits::infinity(), + CallD(test->entry(), + bit_cast(-std::numeric_limits::infinity()))); +} +#endif // XLEN == 32 + +ASSEMBLER_TEST_GENERATE(SingleLessThanQuiet, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fltqs(A0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(SingleLessThanQuiet, test) { + EXPECT_DISASSEMBLY( + "a0b55553 fltq.s a0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(0, CallI(test->entry(), 3.0f, 1.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, 3.0f)); + EXPECT_EQ(1, CallI(test->entry(), 3.0f, 5.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, -1.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, -3.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, -5.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, 1.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, 3.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, 5.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, -1.0f)); + EXPECT_EQ(0, CallI(test->entry(), -3.0f, -3.0f)); + EXPECT_EQ(0, CallI(test->entry(), -3.0f, -5.0f)); + + float qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, 3.0f)); + EXPECT_EQ(0, CallI(test->entry(), -3.0f, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, -3.0f)); +} + +ASSEMBLER_TEST_GENERATE(SingleLessOrEqualQuiet, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fleqs(A0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(SingleLessOrEqualQuiet, test) { + EXPECT_DISASSEMBLY( + "a0b54553 fleq.s a0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(0, CallI(test->entry(), 3.0f, 1.0f)); + EXPECT_EQ(1, CallI(test->entry(), 3.0f, 3.0f)); + EXPECT_EQ(1, CallI(test->entry(), 3.0f, 5.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, -1.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, -3.0f)); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, -5.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, 1.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, 3.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, 5.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, -1.0f)); + EXPECT_EQ(1, CallI(test->entry(), -3.0f, -3.0f)); + EXPECT_EQ(0, CallI(test->entry(), -3.0f, -5.0f)); + + float qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_EQ(0, CallI(test->entry(), 3.0f, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, 3.0f)); + EXPECT_EQ(0, CallI(test->entry(), -3.0f, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, -3.0f)); +} + +ASSEMBLER_TEST_GENERATE(DoubleLessThanQuiet, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fltqd(A0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(DoubleLessThanQuiet, test) { + EXPECT_DISASSEMBLY( + "a2b55553 fltq.d a0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(0, CallI(test->entry(), 3.0, 1.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, 3.0)); + EXPECT_EQ(1, CallI(test->entry(), 3.0, 5.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, -1.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, -3.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, -5.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, 1.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, 3.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, 5.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, -1.0)); + EXPECT_EQ(0, CallI(test->entry(), -3.0, -3.0)); + EXPECT_EQ(0, CallI(test->entry(), -3.0, -5.0)); + + double qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_EQ(0, CallI(test->entry(), 3.0, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, 3.0)); + EXPECT_EQ(0, CallI(test->entry(), -3.0, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, -3.0)); +} + +ASSEMBLER_TEST_GENERATE(DoubleLessOrEqualQuiet, assembler) { + __ SetExtensions(RV_G | RV_Zfa); + __ fleqd(A0, FA0, FA1); + __ ret(); +} +ASSEMBLER_TEST_RUN(DoubleLessOrEqualQuiet, test) { + EXPECT_DISASSEMBLY( + "a2b54553 fleq.d a0, fa0, fa1\n" + "00008067 ret\n"); + + EXPECT_EQ(0, CallI(test->entry(), 3.0, 1.0)); + EXPECT_EQ(1, CallI(test->entry(), 3.0, 3.0)); + EXPECT_EQ(1, CallI(test->entry(), 3.0, 5.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, -1.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, -3.0)); + EXPECT_EQ(0, CallI(test->entry(), 3.0, -5.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, 1.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, 3.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, 5.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, -1.0)); + EXPECT_EQ(1, CallI(test->entry(), -3.0, -3.0)); + EXPECT_EQ(0, CallI(test->entry(), -3.0, -5.0)); + + double qNAN = std::numeric_limits::quiet_NaN(); + EXPECT_EQ(0, CallI(test->entry(), 3.0, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, 3.0)); + EXPECT_EQ(0, CallI(test->entry(), -3.0, qNAN)); + EXPECT_EQ(0, CallI(test->entry(), qNAN, -3.0)); +} + ASSEMBLER_TEST_GENERATE(LoadByteAcquire, assembler) { __ SetExtensions(RV_GC | RV_Zalasr); __ lb(A0, Address(A1), std::memory_order_acquire); diff --git a/runtime/vm/compiler/assembler/disassembler_riscv.cc b/runtime/vm/compiler/assembler/disassembler_riscv.cc index 83405f5c7912..c78efadd00ff 100644 --- a/runtime/vm/compiler/assembler/disassembler_riscv.cc +++ b/runtime/vm/compiler/assembler/disassembler_riscv.cc @@ -1467,6 +1467,12 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case FMAX: Print("fmax.s 'frd, 'frs1, 'frs2", instr, RV_F); break; + case FMINM: + Print("fminm.s 'frd, 'frs1, 'frs2", instr, RV_Zfa); + break; + case FMAXM: + Print("fmaxm.s 'frd, 'frs1, 'frs2", instr, RV_Zfa); + break; default: UnknownInstruction(instr); } @@ -1483,6 +1489,12 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case FLE: Print("fle.s 'rd, 'frs1, 'frs2", instr, RV_F); break; + case FLTQ: + Print("fltq.s 'rd, 'frs1, 'frs2", instr, RV_Zfa); + break; + case FLEQ: + Print("fleq.s 'rd, 'frs1, 'frs2", instr, RV_Zfa); + break; default: UnknownInstruction(instr); } @@ -1541,7 +1553,16 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { } break; case FMVWX: - Print("fmv.w.x 'frd, 'rs1", instr, RV_F); + switch (instr.frs2()) { + case 0: + Print("fmv.w.x 'frd, 'rs1", instr, RV_F); + break; + case 1: + Print("flis 'frd, 'flis", instr, RV_Zfa); + break; + default: + UnknownInstruction(instr); + } break; case FADDD: Print("fadd.d 'frd, 'frs1, 'frs2'round", instr, RV_D); @@ -1594,6 +1615,12 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case FMAX: Print("fmax.d 'frd, 'frs1, 'frs2", instr, RV_D); break; + case FMINM: + Print("fminm.d 'frd, 'frs1, 'frs2", instr, RV_Zfa); + break; + case FMAXM: + Print("fmaxm.d 'frd, 'frs1, 'frs2", instr, RV_Zfa); + break; default: UnknownInstruction(instr); } @@ -1604,6 +1631,12 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case 1: Print("fcvt.s.d 'frd, 'frs1'round", instr, RV_D); break; + case 4: + Print("fround.s 'frd, 'frs1'round", instr, RV_Zfa); + break; + case 5: + Print("froundnx.s 'frd, 'frs1'round", instr, RV_Zfa); + break; default: UnknownInstruction(instr); } @@ -1614,6 +1647,12 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case 0: Print("fcvt.d.s 'frd, 'frs1'round", instr, RV_D); break; + case 4: + Print("fround.d 'frd, 'frs1'round", instr, RV_Zfa); + break; + case 5: + Print("froundnx.d 'frd, 'frs1'round", instr, RV_Zfa); + break; default: UnknownInstruction(instr); } @@ -1630,6 +1669,12 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case FLE: Print("fle.d 'rd, 'frs1, 'frs2", instr, RV_D); break; + case FLTQ: + Print("fltq.d 'rd, 'frs1, 'frs2", instr, RV_Zfa); + break; + case FLEQ: + Print("fleq.d 'rd, 'frs1, 'frs2", instr, RV_Zfa); + break; default: UnknownInstruction(instr); } @@ -1640,20 +1685,34 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { case 1: Print("fclass.d 'rd, 'frs1", instr, RV_D); break; -#if XLEN >= 64 case 0: - Print("fmv.x.d 'rd, 'frs1", instr, RV_D); - break; + switch (static_cast(instr.rs2())) { +#if XLEN >= 64 + case 0: + Print("fmv.x.d 'rd, 'frs1", instr, RV_D); + break; +#endif +#if XLEN == 32 + case 1: + Print("fmvh.x.d 'rd, 'frs1", instr, RV_Zfa); + break; #endif + default: + UnknownInstruction(instr); + } + break; default: UnknownInstruction(instr); } break; case FCVTintD: - switch (static_cast(instr.rs2())) { + switch (static_cast(instr.rs2())) { case W: Print("fcvt.w.d 'rd, 'frs1'round", instr, RV_D); break; + case 8: + Print("fcvtmod.w.d 'rd, 'frs1'round", instr, RV_Zfa); + break; case WU: Print("fcvt.wu.d 'rd, 'frs1'round", instr, RV_D); break; @@ -1689,9 +1748,23 @@ void RISCVDisassembler::DisassembleOPFP(Instr instr) { UnknownInstruction(instr); } break; -#if XLEN >= 64 case FMVDX: - Print("fmv.d.x 'frd, 'rs1", instr, RV_D); + switch (static_cast(instr.frs2())) { +#if XLEN >= 64 + case 0: + Print("fmv.d.x 'frd, 'rs1", instr, RV_D); + break; +#endif + case 1: + Print("flid 'frd, 'flid", instr, RV_Zfa); + break; + default: + UnknownInstruction(instr); + } + break; +#if XLEN == 32 + case FMVPDX: + Print("fmvp.d.x 'frd, 'rs1, 'rs2", instr, RV_Zfa); break; #endif default: @@ -1856,6 +1929,40 @@ const char* RISCVDisassembler::PrintOption(const char* format, Instr instr) { } else if (STRING_STARTS_WITH(format, "frs3")) { Printf("%s", fpu_reg_names[instr.frs3()]); return format + 4; + } else if (STRING_STARTS_WITH(format, "flis")) { + intptr_t index = instr.frs1(); + switch (index) { + case 1: + Printf("min"); + break; + case 30: + Printf("inf"); + break; + case 31: + Printf("nan"); + break; + default: + Printf("%f", bit_cast(kFlisConstants[index])); + break; + } + return format + 4; + } else if (STRING_STARTS_WITH(format, "flid")) { + intptr_t index = instr.frs1(); + switch (index) { + case 1: + Printf("min"); + break; + case 30: + Printf("inf"); + break; + case 31: + Printf("nan"); + break; + default: + Printf("%lf", bit_cast(kFlidConstants[index])); + break; + } + return format + 4; } FATAL("Bad format %s\n", format); diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc index cb4e6416c669..c1e15b285b33 100644 --- a/runtime/vm/compiler/backend/il_riscv.cc +++ b/runtime/vm/compiler/backend/il_riscv.cc @@ -4371,6 +4371,15 @@ void UnaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) { case Token::kSQUARE: __ fmuld(result, value, value); break; + case Token::kTRUNCATE: + __ froundd(result, value, RTZ); + break; + case Token::kFLOOR: + __ froundd(result, value, RDN); + break; + case Token::kCEILING: + __ froundd(result, value, RUP); + break; default: UNREACHABLE(); } @@ -4400,6 +4409,15 @@ void UnaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) { case Token::kSQUARE: __ fmuls(result, value, value); break; + case Token::kTRUNCATE: + __ frounds(result, value, RTZ); + break; + case Token::kFLOOR: + __ frounds(result, value, RDN); + break; + case Token::kCEILING: + __ frounds(result, value, RUP); + break; default: UNREACHABLE(); } diff --git a/runtime/vm/constants_riscv.h b/runtime/vm/constants_riscv.h index 96464298b905..a2ef58c5096e 100644 --- a/runtime/vm/constants_riscv.h +++ b/runtime/vm/constants_riscv.h @@ -822,9 +822,13 @@ enum Funct3 { JX = 0b010, FMIN = 0b000, FMAX = 0b001, + FMINM = 0b010, + FMAXM = 0b011, FEQ = 0b010, FLT = 0b001, FLE = 0b000, + FLTQ = 0b101, + FLEQ = 0b100, SH1ADD = 0b010, SH2ADD = 0b100, @@ -890,6 +894,8 @@ enum Funct7 { FCVTDint = 0b1101001, FMVXD = 0b1110001, FMVDX = 0b1111001, + FMVHXD = 0b1110001, + FMVPDX = 0b1011001, ADDUW = 0b0000100, SHADD = 0b0010000, @@ -967,6 +973,76 @@ enum HartEffects { const intptr_t kReleaseShift = 25; const intptr_t kAcquireShift = 26; +constexpr uint32_t kFlisConstants[32] = { + 0xbf800000, // -1.0 + 0x00800000, // min positive normal + 0x37800000, // 2^-16 + 0x38000000, // 2^-15 + 0x3b800000, // 2^-8 + 0x3c000000, // 2^-7 + 0x3d800000, // 0.0625 + 0x3e000000, // 0.125 + 0x3e800000, // 0.25 + 0x3ea00000, // 0.3125 + 0x3ec00000, // 0.375 + 0x3ee00000, // 0.4375 + 0x3f000000, // 0.5 + 0x3f200000, // 0.625 + 0x3f400000, // 0.75 + 0x3f600000, // 0.875 + 0x3f800000, // 1.0 + 0x3fa00000, // 1.25 + 0x3fc00000, // 1.5 + 0x3fe00000, // 1.75 + 0x40000000, // 2.0 + 0x40200000, // 2.5 + 0x40400000, // 3 + 0x40800000, // 4 + 0x41000000, // 8 + 0x41800000, // 16 + 0x43000000, // 2^7 + 0x43800000, // 2^8 + 0x47000000, // 2^15 + 0x47800000, // 2^16 + 0x7f800000, // positive infinity + 0x7fc00000, // canonical NaN +}; + +constexpr uint64_t kFlidConstants[32] = { + 0xbff0000000000000, // -1.0 + 0x0010000000000000, // min positive normal + 0x3ef0000000000000, // 2^-16 + 0x3f00000000000000, // 2^-15 + 0x3f70000000000000, // 2^-8 + 0x3f80000000000000, // 2^7 + 0x3fb0000000000000, // 0.0625 + 0x3fc0000000000000, // 0.125 + 0x3fd0000000000000, // 0.25 + 0x3fd4000000000000, // 0.3125 + 0x3fd8000000000000, // 0.375 + 0x3fdc000000000000, // 0.4375 + 0x3fe0000000000000, // 0.5 + 0x3fe4000000000000, // 0.625 + 0x3fe8000000000000, // 0.75 + 0x3fec000000000000, // 0.875 + 0x3ff0000000000000, // 1.0 + 0x3ff4000000000000, // 1.25 + 0x3ff8000000000000, // 1.5 + 0x3ffc000000000000, // 1.75 + 0x4000000000000000, // 2.0 + 0x4004000000000000, // 2.5 + 0x4008000000000000, // 3 + 0x4010000000000000, // 4 + 0x4020000000000000, // 8 + 0x4030000000000000, // 16 + 0x4060000000000000, // 2^7 + 0x4070000000000000, // 2^8 + 0x40e0000000000000, // 2^15 + 0x40f0000000000000, // 2^16 + 0x7ff0000000000000, // positive infinity + 0x7ff8000000000000, // canonical NaN +}; + #define DEFINE_REG_ENCODING(type, name, shift) \ inline bool Is##name(type r) { \ return static_cast(r) < 32; \ @@ -1689,7 +1765,8 @@ static constexpr ExtensionSet RV_GCB = RV_GC | RV_B; static constexpr Extension RV_Zicond(10); // Integer conditional operations static constexpr Extension RV_Zcb(11); // More compressed instructions static constexpr Extension RV_Zabha(12); // Byte and halfword AMOs -static constexpr Extension RV_Zalasr(13); // Load-acquire, store-release +static constexpr Extension RV_Zfa(13); // Additional floating-point +static constexpr Extension RV_Zalasr(14); // Load-acquire, store-release #if defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_ANDROID) static constexpr ExtensionSet RV_baseline = RV_GCB; diff --git a/runtime/vm/simulator_riscv.cc b/runtime/vm/simulator_riscv.cc index 5028c1c3f508..8eae7f80ab82 100644 --- a/runtime/vm/simulator_riscv.cc +++ b/runtime/vm/simulator_riscv.cc @@ -2646,6 +2646,33 @@ static float rv_fmaxf(float x, float y) { return fmaxf(x, y); } +// "The FMINM.S and FMAXM.S instructions are defined like the FMIN.S and FMAX.S +// instructions, except that if either input is NaN, the result is the +// canonical NaN." +static double rv_fminm(double x, double y) { + if (isnan(x) || isnan(y)) return std::numeric_limits::quiet_NaN(); + if (x == y) return signbit(x) ? x : y; + return fmin(x, y); +} + +static double rv_fmaxm(double x, double y) { + if (isnan(x) || isnan(y)) return std::numeric_limits::quiet_NaN(); + if (x == y) return signbit(x) ? y : x; + return fmax(x, y); +} + +static float rv_fminmf(float x, float y) { + if (isnan(x) || isnan(y)) return std::numeric_limits::quiet_NaN(); + if (x == y) return signbit(x) ? x : y; + return fminf(x, y); +} + +static float rv_fmaxmf(float x, float y) { + if (isnan(x) || isnan(y)) return std::numeric_limits::quiet_NaN(); + if (x == y) return signbit(x) ? y : x; + return fmaxf(x, y); +} + static bool is_quiet(float x) { // Warning: This is true on Intel/ARM, but not everywhere. return (bit_cast(x) & (static_cast(1) << 22)) != 0; @@ -2729,6 +2756,13 @@ static double roundeven(double x) { } static float Round(float x, RoundingMode rounding) { + switch (fpclassify(x)) { + case FP_INFINITE: + case FP_ZERO: + return x; + case FP_NAN: + return std::numeric_limits::quiet_NaN(); + } switch (rounding) { case RNE: // Round to Nearest, ties to Even return roundevenf(x); @@ -2748,6 +2782,13 @@ static float Round(float x, RoundingMode rounding) { } static double Round(double x, RoundingMode rounding) { + switch (fpclassify(x)) { + case FP_INFINITE: + case FP_ZERO: + return x; + case FP_NAN: + return std::numeric_limits::quiet_NaN(); + } switch (rounding) { case RNE: // Round to Nearest, ties to Even return roundeven(x); @@ -2818,6 +2859,34 @@ static int32_t fcvtwd(double x, RoundingMode rounding) { return kMaxInt32; // Positive infinity, NaN. } +static int32_t fcvtmodwd(double x, RoundingMode rounding) { + ASSERT(rounding == RTZ); + switch (fpclassify(x)) { + case FP_INFINITE: + case FP_NAN: + case FP_ZERO: + case FP_SUBNORMAL: + return 0; + } + int biased_exp = (bit_cast(x) & 0x7FF0000000000000) >> 52; + int exponent = biased_exp - 0x3FF - 52; + int64_t significand = bit_cast(x) & 0x000FFFFFFFFFFFFF; + significand += 0x0010000000000000; + if (x < 0) { + significand = -significand; + } + if (exponent >= 0) { + if (exponent >= 64) { + return 0; + } + return significand << exponent; + } + if (exponent <= -64) { + return significand >> 63; + } + return significand >> -exponent; +} + static uint32_t fcvtwud(double x, RoundingMode rounding) { if (x < static_cast(0)) { return 0; // Negative infinity. @@ -2912,6 +2981,12 @@ void Simulator::InterpretOPFP(Instr instr) { case FMAX: set_fregs(instr.frd(), rv_fmaxf(rs1, rs2)); break; + case FMINM: + set_fregs(instr.frd(), rv_fminmf(rs1, rs2)); + break; + case FMAXM: + set_fregs(instr.frd(), rv_fmaxmf(rs1, rs2)); + break; default: IllegalInstruction(instr); } @@ -2925,9 +3000,11 @@ void Simulator::InterpretOPFP(Instr instr) { set_xreg(instr.rd(), rs1 == rs2 ? 1 : 0); break; case FLT: + case FLTQ: set_xreg(instr.rd(), rs1 < rs2 ? 1 : 0); break; case FLE: + case FLEQ: set_xreg(instr.rd(), rs1 <= rs2 ? 1 : 0); break; default: @@ -2944,7 +3021,7 @@ void Simulator::InterpretOPFP(Instr instr) { case 0: // fmv.x.s set_xreg(instr.rd(), - sign_extend(bit_cast(get_fregs(instr.frs1())))); + sign_extend(bit_cast(get_fregs_raw(instr.frs1())))); break; default: IllegalInstruction(instr); @@ -3003,8 +3080,18 @@ void Simulator::InterpretOPFP(Instr instr) { } break; case FMVWX: - set_fregs(instr.frd(), - bit_cast(static_cast(get_xreg(instr.rs1())))); + switch (static_cast(instr.frs2())) { + case 0: + set_fregs( + instr.frd(), + bit_cast(static_cast(get_xreg(instr.rs1())))); + break; + case 1: + set_fregs(instr.frd(), bit_cast(kFlisConstants[instr.rs1()])); + break; + default: + IllegalInstruction(instr); + } break; case FADDD: { double rs1 = get_fregd(instr.frs1()); @@ -3066,26 +3153,42 @@ void Simulator::InterpretOPFP(Instr instr) { case FMAX: set_fregd(instr.frd(), rv_fmax(rs1, rs2)); break; + case FMINM: + set_fregd(instr.frd(), rv_fminm(rs1, rs2)); + break; + case FMAXM: + set_fregd(instr.frd(), rv_fmaxm(rs1, rs2)); + break; default: IllegalInstruction(instr); } break; } case FCVTS: { - switch (static_cast(instr.rs2())) { + switch (static_cast(instr.rs2())) { case 1: set_fregs(instr.frd(), static_cast(get_fregd(instr.frs1()))); break; + case 4: + case 5: + set_fregs(instr.frd(), + Round(get_fregs(instr.frs1()), instr.rounding())); + break; default: IllegalInstruction(instr); } break; } case FCVTD: { - switch (static_cast(instr.rs2())) { + switch (static_cast(instr.rs2())) { case 0: set_fregd(instr.frd(), static_cast(get_fregs(instr.frs1()))); break; + case 4: + case 5: + set_fregd(instr.frd(), + Round(get_fregd(instr.frs1()), instr.rounding())); + break; default: IllegalInstruction(instr); } @@ -3100,9 +3203,11 @@ void Simulator::InterpretOPFP(Instr instr) { set_xreg(instr.rd(), rs1 == rs2 ? 1 : 0); break; case FLT: + case FLTQ: set_xreg(instr.rd(), rs1 < rs2 ? 1 : 0); break; case FLE: + case FLEQ: set_xreg(instr.rd(), rs1 <= rs2 ? 1 : 0); break; default: @@ -3116,22 +3221,39 @@ void Simulator::InterpretOPFP(Instr instr) { // fclass.d set_xreg(instr.rd(), fclass(get_fregd(instr.frs1()))); break; -#if XLEN >= 64 case 0: - // fmv.x.d - set_xreg(instr.rd(), bit_cast(get_fregd(instr.frs1()))); - break; + switch (static_cast(instr.rs2())) { +#if XLEN >= 64 + case 0: + // fmv.x.d + set_xreg(instr.rd(), bit_cast(get_fregd(instr.frs1()))); + break; #endif // XLEN >= 64 +#if XLEN == 32 + case 1: + // fmvh.x.d + set_xreg(instr.rd(), + bit_cast(get_fregd(instr.frs1())) >> 32); + break; +#endif // XLEN == 32 + default: + IllegalInstruction(instr); + } + break; default: IllegalInstruction(instr); } break; case FCVTintD: - switch (static_cast(instr.rs2())) { + switch (static_cast(instr.rs2())) { case W: set_xreg(instr.rd(), sign_extend(fcvtwd(get_fregd(instr.frs1()), instr.rounding()))); break; + case 8: + set_xreg(instr.rd(), sign_extend(fcvtmodwd(get_fregd(instr.frs1()), + instr.rounding()))); + break; case WU: set_xreg(instr.rd(), sign_extend(fcvtwud(get_fregd(instr.frs1()), instr.rounding()))); @@ -3176,11 +3298,28 @@ void Simulator::InterpretOPFP(Instr instr) { IllegalInstruction(instr); } break; -#if XLEN >= 64 case FMVDX: - set_fregd(instr.frd(), bit_cast(get_xreg(instr.rs1()))); - break; + switch (static_cast(instr.frs2())) { +#if XLEN >= 64 + case 0: + set_fregd(instr.frd(), bit_cast(get_xreg(instr.rs1()))); + break; #endif // XLEN >= 64 + case 1: + set_fregd(instr.frd(), bit_cast(kFlidConstants[instr.rs1()])); + break; + default: + IllegalInstruction(instr); + } + break; +#if XLEN == 32 + case FMVPDX: { + uint64_t hi = static_cast(get_xreg(instr.rs2())); + uint64_t lo = static_cast(get_xreg(instr.rs1())); + set_fregd(instr.frd(), bit_cast((hi << 32) | lo)); + break; + } +#endif default: IllegalInstruction(instr); } diff --git a/runtime/vm/simulator_riscv.h b/runtime/vm/simulator_riscv.h index ee6558390ad9..b301e45a7c7a 100644 --- a/runtime/vm/simulator_riscv.h +++ b/runtime/vm/simulator_riscv.h @@ -61,6 +61,21 @@ class Simulator { return get_xreg(A0); } + int64_t CallI64(intx_t function, double arg0, double arg1 = 0.0) { + PreservedRegisters preserved; + PrepareCall(&preserved); + set_fregd(FA0, arg0); + set_fregd(FA1, arg1); + RunCall(function, &preserved); +#if XLEN == 32 + uint64_t hi = static_cast(get_xreg(A1)); + uint64_t lo = static_cast(get_xreg(A0)); + return (hi << 32) | lo; +#else + return get_xreg(A0); +#endif + } + double CallD(intx_t function, intx_t arg0, intx_t arg1 = 0) { PreservedRegisters preserved; PrepareCall(&preserved); @@ -69,6 +84,16 @@ class Simulator { RunCall(function, &preserved); return get_fregd(FA0); } +#if XLEN == 32 + double CallD(intx_t function, int64_t arg0) { + PreservedRegisters preserved; + PrepareCall(&preserved); + set_xreg(A0, static_cast(arg0) & 0xFFFFFFFF); + set_xreg(A1, static_cast(arg0) >> 32); + RunCall(function, &preserved); + return get_fregd(FA0); + } +#endif double CallD(intx_t function, double arg0, double arg1 = 0.0, @@ -287,6 +312,11 @@ class Simulator { static constexpr uint64_t kNaNBox = 0xFFFFFFFF00000000; + float get_fregs_raw(FRegister rs) const { + uint64_t bits64 = bit_cast(fregs_[rs]); + uint32_t bits32 = static_cast(bits64); + return bit_cast(bits32); + } float get_fregs(FRegister rs) const { uint64_t bits64 = bit_cast(fregs_[rs]); if ((bits64 & kNaNBox) != kNaNBox) { From 7784976d752238e2f36e4b34de406e2b4fda747d Mon Sep 17 00:00:00 2001 From: pq Date: Wed, 13 Nov 2024 01:24:19 +0000 Subject: [PATCH 7/8] [element model] migrate `scope_util_test` Bug: https://github.com/dart-lang/linter/issues/5099 Change-Id: I3a1a0229b59a7b9f83011d0acd6fd6d56415c5ac Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394840 Auto-Submit: Phil Quitslund Commit-Queue: Brian Wilkerson Reviewed-by: Brian Wilkerson --- pkg/linter/analyzer_use_new_elements.txt | 2 -- pkg/linter/test/scope_util_test.dart | 42 +++++++++++------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/pkg/linter/analyzer_use_new_elements.txt b/pkg/linter/analyzer_use_new_elements.txt index 3ad1f39a4d8a..537a4fbdb2de 100644 --- a/pkg/linter/analyzer_use_new_elements.txt +++ b/pkg/linter/analyzer_use_new_elements.txt @@ -14,7 +14,5 @@ lib/src/rules/use_build_context_synchronously.dart lib/src/rules/use_late_for_private_fields_and_variables.dart lib/src/util/dart_type_utilities.dart lib/src/util/flutter_utils.dart -lib/src/util/leak_detector_visitor.dart lib/src/util/scope.dart test/rules/use_build_context_synchronously_test.dart -test/scope_util_test.dart diff --git a/pkg/linter/test/scope_util_test.dart b/pkg/linter/test/scope_util_test.dart index f99575114f87..4ac2fcce1600 100644 --- a/pkg/linter/test/scope_util_test.dart +++ b/pkg/linter/test/scope_util_test.dart @@ -7,8 +7,7 @@ import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/src/test_utilities/find_element.dart'; +import 'package:analyzer/dart/element/element2.dart'; import 'package:analyzer/src/test_utilities/find_element2.dart'; import 'package:analyzer/src/test_utilities/find_node.dart'; import 'package:linter/src/util/scope.dart'; @@ -25,9 +24,7 @@ main() { @reflectiveTest class ResolveNameInScopeTest extends PubPackageResolutionTest { - late FindElement findElement; - - late FindElement2 findElement2; + late FindElement2 findElement; late FindNode findNode; @@ -35,8 +32,7 @@ class ResolveNameInScopeTest extends PubPackageResolutionTest { Future resolveFile(String path) async { var result = await super.resolveFile(path); - findElement = FindElement(result.unit); - findElement2 = FindElement2(result.unit); + findElement = FindElement2(result.unit); findNode = FindNode(result.content, result.unit); return result; } @@ -55,7 +51,7 @@ class B extends A { } } '''); - _checkGetterDifferent(findElement.setter('foo')); + _checkGetterDifferent(findElement.setter('foo=')); } test_class_getter_different_importScope() async { @@ -628,7 +624,7 @@ class A { } } '''); - _checkSetterRequested(findElement.setter('foo')); + _checkSetterRequested(findElement.setter('foo=')); } test_class_setter_requested_thisClass_topLevelFunction() async { @@ -643,7 +639,7 @@ class A { void foo() {} '''); - _checkSetterRequested(findElement.setter('foo')); + _checkSetterRequested(findElement.setter('foo=')); } test_class_typeParameter_inConstructor() async { @@ -779,8 +775,8 @@ void foo(int T) {} void foo(void Function(String T) b) {} '''); var node = findNode.simpleFormalParameter('T)'); - var T = findNode.typeParameter('T>').declaredElement!; - _resultRequested(node, 'T', false, T); + var T = findNode.typeParameter('T>').declaredFragment?.element; + _resultRequested(node, 'T', false, T!); } test_genericTypeAlias_typeParameter() async { @@ -837,7 +833,7 @@ mixin A { _resultRequested(node, 'T', false, findElement.typeParameter('T')); } - void _checkGetterDifferent(Element expected) { + void _checkGetterDifferent(Element2 expected) { var node = findNode.this_('this.foo;'); _resultDifferent(node, 'foo', false, expected); } @@ -847,12 +843,12 @@ mixin A { _resultNone(node, 'foo', false); } - void _checkGetterRequested(Element expected) { + void _checkGetterRequested(Element2 expected) { var node = findNode.this_('this.foo;'); _resultRequested(node, 'foo', false, expected); } - void _checkMethodDifferent(Element expected) { + void _checkMethodDifferent(Element2 expected) { var node = findNode.this_('this.foo()'); _resultDifferent(node, 'foo', false, expected); } @@ -862,7 +858,7 @@ mixin A { _resultNone(node, 'foo', false); } - void _checkMethodRequested(Element expected) { + void _checkMethodRequested(Element2 expected) { var node = findNode.this_('this.foo()'); _resultRequested(node, 'foo', false, expected); } @@ -871,7 +867,7 @@ mixin A { _checkMethodRequested(findElement.localVar('foo')); } - void _checkSetterDifferent(Element expected) { + void _checkSetterDifferent(Element2 expected) { var node = findNode.this_('this.foo = 0;'); _resultDifferent(node, 'foo', true, expected); } @@ -881,14 +877,15 @@ mixin A { _resultNone(node, 'foo', true); } - void _checkSetterRequested(Element expected) { + void _checkSetterRequested(Element2 expected) { var node = findNode.this_('this.foo = 0;'); _resultRequested(node, 'foo', true, expected); } - void _resultDifferent(AstNode node, String id, bool setter, Element element) { + void _resultDifferent( + AstNode node, String id, bool setter, Element2 element) { var result = resolveNameInScope(id, node, shouldResolveSetter: setter); - if (!result.isDifferentName || result.element != element) { + if (!result.isDifferentName || result.element2 != element) { fail('Expected different $element, actual: $result'); } } @@ -900,9 +897,10 @@ mixin A { } } - void _resultRequested(AstNode node, String id, bool setter, Element element) { + void _resultRequested( + AstNode node, String id, bool setter, Element2 element) { var result = resolveNameInScope(id, node, shouldResolveSetter: setter); - if (!result.isRequestedName || result.element != element) { + if (!result.isRequestedName || result.element2 != element) { fail('Expected requested $element, actual: $result'); } } From be6ca13322ca456cfa1bd80ec464ed0908d9e3a7 Mon Sep 17 00:00:00 2001 From: Chloe Stefantsova Date: Wed, 13 Nov 2024 02:51:21 +0000 Subject: [PATCH 8/8] [analyzer][cfe] Share constraint generation for nullable types Part of https://github.com/dart-lang/sdk/issues/54902 Change-Id: I9ce094d28de679002c81b0ff95d94d433369afcf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/393741 Commit-Queue: Chloe Stefantsova Reviewed-by: Paul Berry --- .../type_analyzer_operations.dart | 111 ++++++++++++++++++ .../type_constraint_gatherer_test.dart | 78 ++++++++++++ .../element/type_constraint_gatherer.dart | 68 +---------- .../type_constraint_gatherer.dart | 69 ++--------- 4 files changed, 204 insertions(+), 122 deletions(-) diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart index 204e83c4ec34..d47f83167811 100644 --- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart +++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart @@ -1233,6 +1233,117 @@ abstract class TypeConstraintGenerator< } } + /// Matches [p] against [q] as a subtype against supertype. + /// + /// - If [p] is `p0?` for some `p0` and [p] is a subtype of [q] under some + /// constraints, the constraints making the relation possible are recorded, + /// and `true` is returned. + /// - Otherwise, the constraint state is unchanged (or rolled back using + /// [restoreState]), and `false` is returned. + /// + /// An invariant of the type inference is that only [p] or [q] may be a + /// schema (in other words, may contain the unknown type `_`); the other must + /// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is + /// `false`, [q] may contain `_`. + bool performSubtypeConstraintGenerationForLeftNullableType( + TypeStructure p, TypeStructure q, + {required bool leftSchema, required AstNode? astNodeForTesting}) { + // If `P` is `P0?` the match holds under constraint set `C1 + C2`: + NullabilitySuffix pNullability = p.nullabilitySuffix; + if (pNullability == NullabilitySuffix.question) { + TypeStructure p0 = typeAnalyzerOperations + .withNullabilitySuffix(new SharedTypeView(p), NullabilitySuffix.none) + .unwrapTypeView(); + final TypeConstraintGeneratorState state = currentState; + + // If `P0` is a subtype match for `Q` under constraint set `C1`. + // And if `Null` is a subtype match for `Q` under constraint set `C2`. + if (performSubtypeConstraintGenerationInternal(p0, q, + leftSchema: leftSchema, astNodeForTesting: astNodeForTesting) && + performSubtypeConstraintGenerationInternal( + typeAnalyzerOperations.nullType.unwrapTypeView(), q, + leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) { + return true; + } + + restoreState(state); + } + + return false; + } + + /// Matches [p] against [q] as a subtype against supertype. + /// + /// - If [q] is `q0?` for some `q0` and [p] is a subtype of [q] under some + /// constraints, the constraints making the relation possible are recorded, + /// and `true` is returned. + /// - Otherwise, the constraint state is unchanged (or rolled back using + /// [restoreState]), and `false` is returned. + /// + /// An invariant of the type inference is that only [p] or [q] may be a + /// schema (in other words, may contain the unknown type `_`); the other must + /// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is + /// `false`, [q] may contain `_`. + bool performSubtypeConstraintGenerationForRightNullableType( + TypeStructure p, TypeStructure q, + {required bool leftSchema, required AstNode? astNodeForTesting}) { + // If `Q` is `Q0?` the match holds under constraint set `C`: + NullabilitySuffix qNullability = q.nullabilitySuffix; + if (qNullability == NullabilitySuffix.question) { + TypeStructure q0 = typeAnalyzerOperations + .withNullabilitySuffix(new SharedTypeView(q), NullabilitySuffix.none) + .unwrapTypeView(); + final TypeConstraintGeneratorState state = currentState; + + // If `P` is `P0?` and `P0` is a subtype match for `Q0` under + // constraint set `C`. + NullabilitySuffix pNullability = p.nullabilitySuffix; + if (pNullability == NullabilitySuffix.question) { + TypeStructure p0 = typeAnalyzerOperations + .withNullabilitySuffix( + new SharedTypeView(p), NullabilitySuffix.none) + .unwrapTypeView(); + if (performSubtypeConstraintGenerationInternal(p0, q0, + leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) { + return true; + } + } + + // Or if `P` is `dynamic` or `void` and `Object` is a subtype match + // for `Q0` under constraint set `C`. + if (p is SharedDynamicTypeStructure || p is SharedVoidTypeStructure) { + if (performSubtypeConstraintGenerationInternal( + typeAnalyzerOperations.objectType.unwrapTypeView(), q0, + leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) { + return true; + } + } + + // Or if `P` is a subtype match for `Q0` under non-empty + // constraint set `C`. + bool pMatchesQ0 = performSubtypeConstraintGenerationInternal(p, q0, + leftSchema: leftSchema, astNodeForTesting: astNodeForTesting); + if (pMatchesQ0 && state != currentState) { + return true; + } + + // Or if `P` is a subtype match for `Null` under constraint set `C`. + if (performSubtypeConstraintGenerationInternal( + p, typeAnalyzerOperations.nullType.unwrapTypeView(), + leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) { + return true; + } + + // Or if `P` is a subtype match for `Q0` under empty + // constraint set `C`. + if (pMatchesQ0) { + return true; + } + } + + return false; + } + /// Implementation backing [performSubtypeConstraintGenerationLeftSchema] and /// [performSubtypeConstraintGenerationRightSchema]. /// diff --git a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart index eb489fb2fa49..856f02b5ac40 100644 --- a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart +++ b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart @@ -517,6 +517,74 @@ main() { }); }); + group('performSubtypeConstraintGenerationForLeftNullableType:', () { + test('Nullable matches nullable with constraints based on base types', () { + // `T? <# int?` reduces to `T <# int?` + var tcg = _TypeConstraintGatherer({'T'}); + check(tcg.performSubtypeConstraintGenerationForLeftNullableType( + Type('T?'), Type('Null'), + leftSchema: false, astNodeForTesting: Node.placeholder())) + .isTrue(); + check(tcg._constraints).deepEquals(['T <: Null']); + }); + + test('Nullable does not match Nullable because base types fail to match', + () { + // `int? <# String?` reduces to `int <# String` + var tcg = _TypeConstraintGatherer({}); + check(tcg.performSubtypeConstraintGenerationForLeftNullableType( + Type('int?'), Type('String?'), + leftSchema: false, astNodeForTesting: Node.placeholder())) + .isFalse(); + check(tcg._constraints).isEmpty(); + }); + }); + + group('performSubtypeConstraintGenerationForRightNullableType:', () { + test('Null matches Nullable favoring non-Null branch', () { + // `Null <# T?` could match in two possible ways: + // - `Null <# Null` (taking the "Null" branch of the FutureOr), producing + // the empty constraint set. + // - `Null <# T` (taking the "non-Null" branch of the FutureOr), + // producing `Null <: T` + // In cases where both branches produce a constraint, the "non-Null" + // branch is favored. + var tcg = _TypeConstraintGatherer({'T'}); + check(tcg.performSubtypeConstraintGenerationForRightNullableType( + Type('Null'), Type('T?'), + leftSchema: false, astNodeForTesting: Node.placeholder())) + .isTrue(); + check(tcg._constraints).deepEquals(['Null <: T']); + }); + + test('Type matches Nullable favoring the non-Null branch', () { + // `T <# int?` could match in two possible ways: + // - `T <# Null` (taking the "Null" branch of the Nullable), + // producing `T <: Null` + // - `T <# int` (taking the "non-Null" branch of the Nullable), + // producing `T <: int` + // In cases where both branches produce a constraint, the "non-Null" + // branch is favored. + var tcg = _TypeConstraintGatherer({'T'}); + check(tcg.performSubtypeConstraintGenerationForRightNullableType( + Type('T'), Type('int?'), + leftSchema: false, astNodeForTesting: Node.placeholder())) + .isTrue(); + check(tcg._constraints).deepEquals(['T <: int']); + }); + + test('Null matches Nullable with no constraints', () { + // `Null <# int?` matches (taking the "Null" branch of + // the Nullable) without generating any constraints. + var tcg = _TypeConstraintGatherer({}); + check(tcg.performSubtypeConstraintGenerationForRightNullableType( + Type('Null'), Type('int?'), + leftSchema: false, astNodeForTesting: Node.placeholder())) + .isTrue(); + check(tcg._constraints).isEmpty(); + }); + }); + group('performSubtypeConstraintGenerationForTypeDeclarationTypes', () { group('Same base type on both sides:', () { test('Covariant, matching', () { @@ -768,6 +836,16 @@ class _TypeConstraintGatherer extends TypeConstraintGenerator` the match holds under constraint set `C1 + C2`: @@ -285,21 +241,9 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< } // If `P` is `P0?` the match holds under constraint set `C1 + C2`: - if (P_nullability == NullabilitySuffix.question) { - var P0 = _typeSystemOperations - .withNullabilitySuffix(SharedTypeView(P), NullabilitySuffix.none) - .unwrapTypeView(); - var rewind = _constraints.length; - - // If `P0` is a subtype match for `Q` under constraint set `C1`. - // And if `Null` is a subtype match for `Q` under constraint set `C2`. - if (trySubtypeMatch(P0, Q, leftSchema, nodeForTesting: nodeForTesting) && - trySubtypeMatch(_typeSystem.nullNone, Q, leftSchema, - nodeForTesting: nodeForTesting)) { - return true; - } - - _constraints.length = rewind; + if (performSubtypeConstraintGenerationForLeftNullableType(P, Q, + leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) { + return true; } // If `Q` is `dynamic`, `Object?`, or `void` then the match holds under diff --git a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart index d0239e5a5803..cc7f70166309 100644 --- a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart +++ b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart @@ -304,55 +304,17 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< // Or if P is a subtype match for Q0 under non-empty constraint set C. // Or if P is a subtype match for Null under constraint set C. // Or if P is a subtype match for Q0 under empty constraint set C. - if (qNullability == NullabilitySuffix.question) { - final int baseConstraintCount = _protoConstraints.length; - final DartType rawP = typeOperations - .withNullabilitySuffix(new SharedTypeView(p), NullabilitySuffix.none) - .unwrapTypeView(); - final DartType rawQ = typeOperations - .withNullabilitySuffix(new SharedTypeView(q), NullabilitySuffix.none) - .unwrapTypeView(); - - if (pNullability == NullabilitySuffix.question && - _isNullabilityAwareSubtypeMatch(rawP, rawQ, - constrainSupertype: constrainSupertype, - treeNodeForTesting: treeNodeForTesting)) { - return true; - } - - if ((p is SharedDynamicTypeStructure || p is SharedVoidTypeStructure) && - _isNullabilityAwareSubtypeMatch( - typeOperations.objectType.unwrapTypeView(), rawQ, - constrainSupertype: constrainSupertype, - treeNodeForTesting: treeNodeForTesting)) { - return true; - } - - bool isMatchWithRawQ = _isNullabilityAwareSubtypeMatch(p, rawQ, - constrainSupertype: constrainSupertype, - treeNodeForTesting: treeNodeForTesting); - bool matchWithRawQAddsConstraints = - _protoConstraints.length != baseConstraintCount; - if (isMatchWithRawQ && matchWithRawQAddsConstraints) { - return true; - } - - if (_isNullabilityAwareSubtypeMatch( - p, typeOperations.nullType.unwrapTypeView(), - constrainSupertype: constrainSupertype, - treeNodeForTesting: treeNodeForTesting)) { - return true; - } - - if (isMatchWithRawQ && !matchWithRawQAddsConstraints) { - return true; - } + if (performSubtypeConstraintGenerationForRightNullableType(p, q, + leftSchema: constrainSupertype, + astNodeForTesting: treeNodeForTesting)) { + return true; } // If P is FutureOr the match holds under constraint set C1 + C2: // // If Future is a subtype match for Q under constraint set C1. // And if P0 is a subtype match for Q under constraint set C2. + if (typeOperations.matchFutureOrInternal(p) case DartType p0?) { final int baseConstraintCount = _protoConstraints.length; if (_isNullabilityAwareSubtypeMatch( @@ -372,23 +334,10 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< // // If P0 is a subtype match for Q under constraint set C1. // And if Null is a subtype match for Q under constraint set C2. - if (pNullability == NullabilitySuffix.question) { - final int baseConstraintCount = _protoConstraints.length; - if (_isNullabilityAwareSubtypeMatch( - typeOperations - .withNullabilitySuffix( - new SharedTypeView(p), NullabilitySuffix.none) - .unwrapTypeView(), - q, - constrainSupertype: constrainSupertype, - treeNodeForTesting: treeNodeForTesting) && - _isNullabilityAwareSubtypeMatch( - typeOperations.nullType.unwrapTypeView(), q, - constrainSupertype: constrainSupertype, - treeNodeForTesting: treeNodeForTesting)) { - return true; - } - _protoConstraints.length = baseConstraintCount; + if (performSubtypeConstraintGenerationForLeftNullableType(p, q, + leftSchema: constrainSupertype, + astNodeForTesting: treeNodeForTesting)) { + return true; } // If Q is dynamic, Object?, or void then the match holds under no