1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
18 #define BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
19 
20 #include <cfenv>
21 #include <cstdint>
22 
23 #include "berberis/base/checks.h"
24 #include "berberis/intrinsics/guest_rounding_modes.h"
25 
26 namespace berberis {
27 
28 namespace FPFlags {
29 
30 inline constexpr uint64_t NV = 1 << 4;
31 inline constexpr uint64_t DZ = 1 << 3;
32 inline constexpr uint64_t OF = 1 << 2;
33 inline constexpr uint64_t UF = 1 << 1;
34 inline constexpr uint64_t NX = 1 << 0;
35 inline constexpr uint64_t RM_POS = 5;
36 inline constexpr uint64_t RM_MASK = 0b111;
37 inline constexpr uint64_t RM_MAX = 0b100;
38 inline constexpr uint64_t RNE = 0b000;
39 inline constexpr uint64_t RTZ = 0b001;
40 inline constexpr uint64_t RDN = 0b010;
41 inline constexpr uint64_t RUP = 0b011;
42 inline constexpr uint64_t RMM = 0b100;
43 inline constexpr uint64_t DYN = 0b111;
44 
45 }  // namespace FPFlags
46 
47 namespace VXRMFlags {
48 
49 inline constexpr uint64_t RNU = 0b00;
50 inline constexpr uint64_t RNE = 0b01;
51 inline constexpr uint64_t RDN = 0b10;
52 inline constexpr uint64_t ROD = 0b11;
53 
54 }  // namespace VXRMFlags
55 
56 namespace intrinsics {
57 
58 // Note that not all RISC-V rounding modes are supported on popular architectures.
59 // FE_TIESAWAY is emulated, but proper emulation needs FE_TOWARDZERO mode.
ToHostRoundingMode(int8_t rm)60 inline int ToHostRoundingMode(int8_t rm) {
61   if (rm == FPFlags::DYN) {
62     return FE_HOSTROUND;
63   }
64   CHECK_GE(rm, 0);
65   CHECK_LE(rm, int8_t{FPFlags::RM_MAX});
66   static constexpr int kRounding[FPFlags::RM_MAX + 1] = {
67       FE_TONEAREST, FE_TOWARDZERO, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO};
68   return kRounding[rm];
69 }
70 
71 // Same as ToHostRoundingMode, but returns pseudo FE_TIESAWAY mode for RMM.
ToIntrinsicRoundingMode(int8_t rm)72 inline int ToIntrinsicRoundingMode(int8_t rm) {
73   if (rm == FPFlags::RMM) {
74     return FE_TIESAWAY;
75   }
76   return ToHostRoundingMode(rm);
77 }
78 
GuestModeFromHostRounding()79 inline uint8_t GuestModeFromHostRounding() {
80   switch (fegetround()) {
81     case FE_TONEAREST:
82       return FPFlags::RNE;
83     case FE_DOWNWARD:
84       return FPFlags::RDN;
85     case FE_UPWARD:
86       return FPFlags::RUP;
87     case FE_TOWARDZERO:
88       return FPFlags::RTZ;
89     default:
90       CHECK(false);
91   }
92 }
93 
94 }  // namespace intrinsics
95 
96 }  // namespace berberis
97 
98 #endif  // BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
99