Skip to content

Commit 957db01

Browse files
committed
cpu/sh: A few cleanups [R. Belmont]
- Flip the interpreter's policy to run primarily in the host's rounding mode and only set ours when necessary - Move m_fpmode into the cache for further x64 safety - Add the necessary #pragma for FP environment changes on Clang As far as I can tell, GCC wants per-file -frounding-math in the build system for this, and ditto drcbec.cpp. That's a relatively big change for two things that won't normally be used though (the SH interpreter and drcbec).
1 parent e9109f8 commit 957db01

3 files changed

Lines changed: 76 additions & 32 deletions

File tree

src/devices/cpu/sh/sh.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class sh_common_execution : public cpu_device
144144
float m_ftrc_smax;
145145
float m_fzero; // FTRV accumulator initialiser
146146
float m_fone; // FSRRA reciprocal numerator
147+
uint8_t m_fpmode[4]; // FPSCR.RM -> UML rounding mode
147148

148149
int m_frt_input;
149150
int m_fpu_sz;

src/devices/cpu/sh/sh4.cpp

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,50 @@
4141
#include <cfenv>
4242
#include <numbers>
4343

44+
#if defined(__clang_major__) && (__clang_major__ < 12)
45+
// can't guarantee floating point environment control works
46+
#else
47+
#pragma STDC FENV_ACCESS ON
48+
#endif
49+
4450
// From FPSCR.RM: 00 = round to nearest, 01 = round to zero; 10/11 are reserved
4551
static const uint8_t fpmode_source[4] = { uml::ROUND_ROUND, uml::ROUND_TRUNC, uml::ROUND_ROUND, uml::ROUND_ROUND };
52+
static const int feround_source[4] = { FE_TONEAREST, FE_TOWARDZERO, FE_TONEAREST, FE_TONEAREST };
53+
54+
namespace {
55+
56+
// For convenience, we run in the host's rounding mode most of the time and only switch during actual FPU ops.
57+
// That shields read/write handlers and the debugger hook from seeing unexpected rounding mode changes.
58+
class host_rounding_scope
59+
{
60+
public:
61+
host_rounding_scope(uint32_t fpscr, int hostmode) :
62+
m_hostmode(hostmode),
63+
m_changed(feround_source[fpscr & 3] != hostmode)
64+
{
65+
if (m_changed)
66+
{
67+
std::fesetround(feround_source[fpscr & 3]);
68+
}
69+
}
70+
71+
~host_rounding_scope()
72+
{
73+
if (m_changed)
74+
{
75+
std::fesetround(m_hostmode);
76+
}
77+
}
78+
79+
host_rounding_scope(const host_rounding_scope &) = delete;
80+
host_rounding_scope &operator=(const host_rounding_scope &) = delete;
81+
82+
private:
83+
const int m_hostmode;
84+
const bool m_changed;
85+
};
86+
87+
} // anonymous namespace
4688

4789

4890
DEFINE_DEVICE_TYPE(SH3, sh3_device, "sh3", "Hitachi SH-3 (Unidentified)")
@@ -1503,10 +1545,6 @@ inline void sh34_base_device::LDSMFPSCR(const uint16_t opcode)
15031545
#endif
15041546
m_sh2_state->m_fpu_sz = (m_sh2_state->m_fpscr & SZ) ? 1 : 0;
15051547
m_sh2_state->m_fpu_pr = (m_sh2_state->m_fpscr & PR) ? 1 : 0;
1506-
if (!m_isdrc)
1507-
{
1508-
sh4_set_host_rounding();
1509-
}
15101548
}
15111549

15121550
/* LDC.L @Rm+,DBR */
@@ -1563,8 +1601,6 @@ inline void sh34_base_device::LDSFPSCR(const uint16_t opcode)
15631601
#endif
15641602
m_sh2_state->m_fpu_sz = (m_sh2_state->m_fpscr & SZ) ? 1 : 0;
15651603
m_sh2_state->m_fpu_pr = (m_sh2_state->m_fpscr & PR) ? 1 : 0;
1566-
if (!m_isdrc)
1567-
sh4_set_host_rounding();
15681604
}
15691605

15701606
/* LDC Rm,DBR */
@@ -2147,11 +2183,14 @@ inline void sh34_base_device::FLOAT(const uint16_t opcode)
21472183
if (n & 1)
21482184
fatalerror("SH-4: FLOAT opcode used with n %d", n);
21492185

2186+
// int32 -> double is an exact conversion so the rounding mode doesn't matter
21502187
n = n & 14;
21512188
FP_RFD(n) = (double)*((int32_t *)&m_sh2_state->m_fpul);
21522189
}
21532190
else /* PR = 0 */
21542191
{
2192+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2193+
21552194
FP_RFS(n) = (float)*((int32_t *)&m_sh2_state->m_fpul);
21562195
}
21572196
}
@@ -2251,6 +2290,8 @@ inline void sh34_base_device::FCNVDS(const uint16_t opcode)
22512290

22522291
if (m_sh2_state->m_fpu_pr) /* PR = 1 */
22532292
{
2293+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2294+
22542295
n = n & 14;
22552296
if (m_sh2_state->m_fpscr & RM)
22562297
m_sh2_state->m_fr[n | NATIVE_ENDIAN_VALUE_LE_BE(0, 1)] &= 0xe0000000; /* round toward zero*/
@@ -2277,6 +2318,8 @@ inline void sh34_base_device::FADD(const uint16_t opcode)
22772318
uint32_t m = REG_M;
22782319
uint32_t n = REG_N;
22792320

2321+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2322+
22802323
if (m_sh2_state->m_fpu_pr) /* PR = 1 */
22812324
{
22822325
n = n & 14;
@@ -2296,6 +2339,8 @@ inline void sh34_base_device::FSUB(const uint16_t opcode)
22962339
uint32_t m = REG_M;
22972340
uint32_t n = REG_N;
22982341

2342+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2343+
22992344
if (m_sh2_state->m_fpu_pr) /* PR = 1 */
23002345
{
23012346
n = n & 14;
@@ -2316,6 +2361,8 @@ inline void sh34_base_device::FMUL(const uint16_t opcode)
23162361
uint32_t m = REG_M;
23172362
uint32_t n = REG_N;
23182363

2364+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2365+
23192366
if (m_sh2_state->m_fpu_pr) /* PR = 1 */
23202367
{
23212368
n = n & 14;
@@ -2335,6 +2382,8 @@ inline void sh34_base_device::FDIV(const uint16_t opcode)
23352382
uint32_t m = REG_M;
23362383
uint32_t n = REG_N;
23372384

2385+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2386+
23382387
if (m_sh2_state->m_fpu_pr) /* PR = 1 */
23392388
{
23402389
n = n & 14;
@@ -2353,6 +2402,8 @@ inline void sh34_base_device::FMAC(const uint16_t opcode)
23532402
uint32_t m = REG_M;
23542403
uint32_t n = REG_N;
23552404

2405+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2406+
23562407
if (m_sh2_state->m_fpu_pr == 0) /* PR = 0 */
23572408
{
23582409
const float p = FP_RFS(0) * FP_RFS(m);
@@ -2366,6 +2417,8 @@ inline void sh34_base_device::FSQRT(const uint16_t opcode)
23662417
{
23672418
uint32_t n = REG_N;
23682419

2420+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2421+
23692422
if (m_sh2_state->m_fpu_pr) /* PR = 1 */
23702423
{
23712424
n = n & 14;
@@ -2382,10 +2435,13 @@ inline void sh34_base_device::FSRRA(const uint16_t opcode)
23822435
{
23832436
uint32_t n = REG_N;
23842437

2438+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2439+
23852440
FP_RFS(n) = 1.0f / sqrtf(FP_RFS(n));
23862441
}
23872442

23882443
/* FSSCA FPUL,FRn PR=0 1111nnn011111101 */
2444+
// sinf/cosf are libm calls so we *don't* override the rounding mode here.
23892445
void sh34_base_device::FSSCA(const uint16_t opcode)
23902446
{
23912447
uint32_t n = REG_N;
@@ -2402,6 +2458,8 @@ inline void sh34_base_device::FIPR(const uint16_t opcode)
24022458
uint32_t m = (n & 3) << 2;
24032459
n = n & 12;
24042460

2461+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2462+
24052463
float ml[4];
24062464
for (int a = 0; a < 4; a++)
24072465
ml[a] = FP_RFS(n + a) * FP_RFS(m + a);
@@ -2414,6 +2472,8 @@ void sh34_base_device::FTRV(const uint16_t opcode)
24142472
uint32_t n = REG_N;
24152473
n = n & 12;
24162474

2475+
const host_rounding_scope round(m_sh2_state->m_fpscr, m_host_round);
2476+
24172477
float sum[4];
24182478
for (int i = 0; i < 4; i++)
24192479
{
@@ -3022,9 +3082,8 @@ void sh34_base_device::execute_run()
30223082
return;
30233083
}
30243084

3025-
// Save and restore the host's rounding mode since we're going to override it for the FPU
3026-
const int hostround = std::fegetround();
3027-
sh4_set_host_rounding();
3085+
// save the host's rounding mode so the RAII helper can restore it when necessary
3086+
m_host_round = std::fegetround();
30283087

30293088
do
30303089
{
@@ -3053,8 +3112,6 @@ void sh34_base_device::execute_run()
30533112

30543113
m_sh2_state->icount--;
30553114
} while (m_sh2_state->icount > 0);
3056-
3057-
std::fesetround(hostround);
30583115
}
30593116

30603117
void sh3_base_device::device_start()
@@ -3932,7 +3989,8 @@ void sh34_base_device::device_start()
39323989
//state_add(STATE_GENPCBASE, "CURPC", m_sh2_state->m_ppc).noshow();
39333990
state_add(STATE_GENPCBASE, "CURPC", m_sh2_state->pc).callimport().noshow();
39343991

3935-
memcpy(m_fpmode, fpmode_source, sizeof(fpmode_source));
3992+
memcpy(m_sh2_state->m_fpmode, fpmode_source, sizeof(fpmode_source));
3993+
m_host_round = FE_TONEAREST;
39363994

39373995
m_sh2_state->m_fzero = 0.0f;
39383996
m_sh2_state->m_fone = 1.0f;
@@ -3953,7 +4011,7 @@ void sh34_base_device::device_start()
39534011

39544012
drc_start();
39554013

3956-
m_drcuml->symbol_add(&m_fpmode, sizeof(m_fpmode), "fpmode");
4014+
m_drcuml->symbol_add(&m_sh2_state->m_fpmode, sizeof(m_sh2_state->m_fpmode), "fpmode");
39574015
}
39584016

39594017
void sh34_base_device::state_import(const device_state_entry &entry)
@@ -4352,20 +4410,9 @@ void sh34_base_device::generate_update_cycles(drcuml_block &block, compiler_stat
43524410

43534411
void sh34_base_device::generate_set_fmod(drcuml_block &block)
43544412
{
4355-
UML_AND(block, I0, mem(&m_sh2_state->m_fpscr), 3); // and i0,fpscr,3
4356-
UML_LOAD(block, I0, &m_fpmode[0], I0, SIZE_BYTE, SCALE_x1); // load i0,fpmode,i0,byte
4357-
UML_SETFMOD(block, I0); // setfmod i0
4358-
}
4359-
4360-
/*-------------------------------------------------
4361-
sh4_set_host_rounding - point the host FPU at
4362-
the mode FPSCR selects, for the interpreter
4363-
-------------------------------------------------*/
4364-
4365-
void sh34_base_device::sh4_set_host_rounding()
4366-
{
4367-
static const int feround[4] = { FE_TONEAREST, FE_TOWARDZERO, FE_TONEAREST, FE_TONEAREST };
4368-
std::fesetround(feround[m_sh2_state->m_fpscr & 3]);
4413+
UML_AND(block, I0, mem(&m_sh2_state->m_fpscr), 3); // and i0,fpscr,3
4414+
UML_LOAD(block, I0, &m_sh2_state->m_fpmode[0], I0, SIZE_BYTE, SCALE_x1); // load i0,fpmode,i0,byte
4415+
UML_SETFMOD(block, I0); // setfmod i0
43694416
}
43704417

43714418
/*-------------------------------------------------
@@ -5918,11 +5965,8 @@ bool sh34_base_device::generate_group_15_op1111_0x13_op1111_0xf13_FTRV(drcuml_bl
59185965
return true;
59195966
}
59205967

5921-
// The backend restores the host default rounding mode around a C call, so FPSCR.RM has to
5922-
// be re-applied here
59235968
void sh34_base_device::func_FSSCA()
59245969
{
5925-
sh4_set_host_rounding();
59265970
FSSCA(m_sh2_state->arg0);
59275971
}
59285972
static void cfunc_FSSCA(void *param) { ((sh34_base_device *)param)->func_FSSCA(); };

src/devices/cpu/sh/sh4.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ class sh34_base_device : public sh_common_execution
187187
void generate_pop(drcuml_block &block, uint32_t reg, const uml::parameter &dst);
188188
void generate_fmov_single(drcuml_block &block, uint32_t reg, bool load);
189189
void generate_fmov_pair(drcuml_block &block, uint32_t reg, bool load);
190-
void sh4_set_host_rounding();
191190
void func_FSSCA();
192191
void func_LDTLB();
193192

@@ -223,7 +222,7 @@ class sh34_base_device : public sh_common_execution
223222
address_space_config m_program_config;
224223
address_space_config m_io_config;
225224

226-
uint8_t m_fpmode[4]; // FPSCR.RM -> host rounding mode table
225+
int m_host_round; // host rounding mode the interpreter has to leave in place
227226
uml::parameter m_fs_regmap[16];
228227
uml::parameter m_fd_regmap[16];
229228

0 commit comments

Comments
 (0)