Skip to content

Commit 1c0ddb0

Browse files
authored
Merge pull request #306 from tactcomplabs/templatize_helpers
Fully templatize floating-point helper functions
2 parents 9af3bd4 + d883ffc commit 1c0ddb0

File tree

7 files changed

+211
-193
lines changed

7 files changed

+211
-193
lines changed

common/include/RevCommon.h

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
namespace SST::RevCPU {
3636

37+
using float16 = _Float16;
38+
3739
/// Zero-extend value of bits size
3840
template<typename T>
3941
constexpr auto ZeroExt( T val, size_t bits ) {

include/RevInstHelpers.h

+87-2
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ inline constexpr double fpmin<double, uint64_t> = 0x0p+0;
6565
/// General template for converting between Floating Point and Integer.
6666
/// FP values outside the range of the target integer type are clipped
6767
/// at the integer type's numerical limits, whether signed or unsigned.
68-
template<typename FP, typename INT>
69-
bool CvtFpToInt( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
68+
template<typename INT, typename FP>
69+
bool fcvtif( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
7070
// Read the FP register. Round to integer according to current rounding mode.
7171
FP fp = std::rint( R->GetFP<FP>( Inst.rs1 ) );
7272

@@ -115,6 +115,10 @@ uint32_t fclass( T val ) {
115115
uint32_t i32;
116116
memcpy( &i32, &val, sizeof( i32 ) );
117117
return ( i32 & uint32_t{ 1 } << 22 ) != 0 ? QuietNaN : SignalingNaN;
118+
} else if constexpr( std::is_same_v<T, float16> ) {
119+
uint16_t i16;
120+
memcpy( &i16, &val, sizeof( i16 ) );
121+
return ( i16 & uint16_t{ 1 } << 9 ) != 0 ? QuietNaN : SignalingNaN;
118122
} else {
119123
uint64_t i64;
120124
memcpy( &i64, &val, sizeof( i64 ) );
@@ -443,6 +447,87 @@ bool fnmadd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
443447
return true;
444448
}
445449

450+
// Square root
451+
template<typename T>
452+
static bool fsqrt( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
453+
R->SetFP( Inst.rd, std::sqrt( R->GetFP<T>( Inst.rs1 ) ) );
454+
R->AdvancePC( Inst );
455+
return true;
456+
}
457+
458+
// Transfer sign bit
459+
template<typename T>
460+
static bool fsgnj( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
461+
R->SetFP( Inst.rd, std::copysign( R->GetFP<T>( Inst.rs1 ), R->GetFP<T>( Inst.rs2 ) ) );
462+
R->AdvancePC( Inst );
463+
return true;
464+
}
465+
466+
// Negated transfer sign bit
467+
template<typename T>
468+
static bool fsgnjn( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
469+
R->SetFP( Inst.rd, std::copysign( R->GetFP<T>( Inst.rs1 ), negate( R->GetFP<T>( Inst.rs2 ) ) ) );
470+
R->AdvancePC( Inst );
471+
return true;
472+
}
473+
474+
// Xor transfer sign bit
475+
template<typename T>
476+
static bool fsgnjx( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
477+
T rs1 = R->GetFP<T>( Inst.rs1 ), rs2 = R->GetFP<T>( Inst.rs2 );
478+
R->SetFP( Inst.rd, std::copysign( rs1, std::signbit( rs1 ) ? negate( rs2 ) : rs2 ) );
479+
R->AdvancePC( Inst );
480+
return true;
481+
}
482+
483+
// Move floating-point register to integer register
484+
template<typename T>
485+
static bool fmvif( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
486+
std::make_signed_t<uint_type_t<T>> i;
487+
T fp = R->GetFP<T, true>( Inst.rs1 ); // The FP value
488+
static_assert( sizeof( i ) == sizeof( fp ) );
489+
memcpy( &i, &fp, sizeof( i ) ); // Reinterpreted as int
490+
R->SetX( Inst.rd, i ); // Copied to the destination register
491+
R->AdvancePC( Inst );
492+
return true;
493+
}
494+
495+
// Move integer register to floating-point register
496+
template<typename T>
497+
static bool fmvfi( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
498+
T fp;
499+
auto i = R->GetX<uint_type_t<T>>( Inst.rs1 ); // The X register
500+
static_assert( sizeof( i ) == sizeof( fp ) );
501+
memcpy( &fp, &i, sizeof( fp ) ); // Reinterpreted as FP
502+
R->SetFP( Inst.rd, fp ); // Copied to the destination register
503+
R->AdvancePC( Inst );
504+
return true;
505+
}
506+
507+
// Floating-point classify
508+
template<typename T>
509+
static bool fclassify( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
510+
R->SetX( Inst.rd, fclass( R->GetFP<T>( Inst.rs1 ) ) );
511+
R->AdvancePC( Inst );
512+
return true;
513+
}
514+
515+
// Convert integer to floating point
516+
template<typename FP, typename INT>
517+
static bool fcvtfi( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
518+
R->SetFP( Inst.rd, static_cast<FP>( R->GetX<INT>( Inst.rs1 ) ) );
519+
R->AdvancePC( Inst );
520+
return true;
521+
}
522+
523+
// Convert floating point to floating point
524+
template<typename FP2, typename FP1>
525+
static bool fcvtff( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
526+
R->SetFP( Inst.rd, static_cast<FP2>( R->GetFP<FP1>( Inst.rs1 ) ) );
527+
R->AdvancePC( Inst );
528+
return true;
529+
}
530+
446531
} // namespace SST::RevCPU
447532

448533
#endif

include/RevRegFile.h

+69-27
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,66 @@ namespace SST::RevCPU {
3131

3232
struct RevInst;
3333

34-
/// BoxNaN: Store a boxed float inside a double
35-
inline void BoxNaN( double* dest, const float* value ) {
36-
uint32_t i32;
37-
memcpy( &i32, value, sizeof( i32 ) ); // The FP32 value
38-
uint64_t i64 = uint64_t{ i32 } | ~uint64_t{ 0 } << 32; // Boxed NaN value
39-
memcpy( dest, &i64, sizeof( i64 ) ); // Store in FP64 register
40-
static_assert( sizeof( i32 ) == sizeof( float ) && sizeof( i64 ) == sizeof( double ) );
34+
// Mappings from floating point to same-sized integer types
35+
template<typename T>
36+
struct uint_type {};
37+
38+
template<>
39+
struct uint_type<double> {
40+
using type = uint64_t;
41+
static_assert( sizeof( type ) == sizeof( double ) );
42+
};
43+
44+
template<>
45+
struct uint_type<float> {
46+
using type = uint32_t;
47+
static_assert( sizeof( type ) == sizeof( float ) );
48+
};
49+
50+
template<>
51+
struct uint_type<float16> {
52+
using type = uint16_t;
53+
static_assert( sizeof( type ) == sizeof( float16 ) );
54+
};
55+
56+
template<typename T>
57+
using uint_type_t = typename uint_type<T>::type;
58+
59+
/// BoxNaN: Store a boxed floating point value inside a possibly larger one
60+
template<typename T, typename U, typename = std::enable_if_t<sizeof( T ) >= sizeof( U )>>
61+
inline void BoxNaN( T* dest, const U* value ) {
62+
if constexpr( sizeof( T ) == sizeof( U ) ) {
63+
*dest = *value;
64+
} else {
65+
uint_type_t<U> i;
66+
memcpy( &i, value, sizeof( i ) ); // The value
67+
uint_type_t<T> box = uint_type_t<T>{ i } | ~uint_type_t<T>{ 0 } << sizeof( U ) * 8; // Boxed NaN value
68+
memcpy( dest, &box, sizeof( box ) ); // Store in larger register
69+
static_assert( sizeof( i ) == sizeof( U ) && sizeof( box ) == sizeof( T ) );
70+
}
71+
}
72+
73+
/// UnBoxNaN: Unbox a floating point value into a possibly smaller one
74+
// The second argument indicates whether it is a FMV/FS move/store
75+
// instruction which just transfers bits and not care about NaN-Boxing.
76+
template<typename T, bool FMV_FS = false, typename U, typename = std::enable_if_t<sizeof( T ) <= sizeof( U )>>
77+
inline T UnBoxNaN( const U* val ) {
78+
if constexpr( sizeof( T ) == sizeof( U ) ) {
79+
return *val;
80+
} else {
81+
uint_type_t<U> i;
82+
memcpy( &i, val, sizeof( i ) );
83+
static_assert( sizeof( i ) == sizeof( val ) );
84+
T fp;
85+
if( !FMV_FS && ~i >> sizeof( T ) * 8 ) {
86+
fp = std::numeric_limits<T>::quiet_NaN();
87+
} else {
88+
auto ifp = static_cast<uint_type_t<T>>( i );
89+
memcpy( &fp, &ifp, sizeof( fp ) );
90+
static_assert( sizeof( ifp ) == sizeof( fp ) );
91+
}
92+
return fp;
93+
}
4194
}
4295

4396
/// RISC-V Register Mneumonics
@@ -303,34 +356,23 @@ class RevRegFile {
303356
template<typename T, bool FMV_FS = false, typename U>
304357
T GetFP( U rs ) const {
305358
if constexpr( std::is_same_v<T, double> ) {
306-
return DPF[size_t( rs )]; // The FP64 register's value
359+
return DPF[size_t( rs )];
360+
} else if( HasD ) {
361+
return UnBoxNaN<T, FMV_FS>( &DPF[size_t( rs )] );
307362
} else {
308-
float fp32;
309-
if( !HasD ) {
310-
fp32 = SPF[size_t( rs )]; // The FP32 register's value
311-
} else {
312-
uint64_t i64;
313-
memcpy( &i64, &DPF[size_t( rs )], sizeof( i64 ) ); // The FP64 register's value
314-
if( !FMV_FS && ~i64 >> 32 ) { // Check for boxed NaN unless FMV/FS
315-
fp32 = NAN; // Return NaN if it's not boxed
316-
} else {
317-
auto i32 = static_cast<uint32_t>( i64 ); // For endian independence on host
318-
memcpy( &fp32, &i32, sizeof( fp32 ) ); // The bottom half of FP64
319-
}
320-
}
321-
return fp32; // Reinterpreted as FP32
363+
return UnBoxNaN<T, FMV_FS>( &SPF[size_t( rs )] );
322364
}
323365
}
324366

325367
/// SetFP: Set a specific FP register to a floating-point value
326368
template<typename T, typename U>
327369
void SetFP( U rd, T value ) {
328370
if constexpr( std::is_same_v<T, double> ) {
329-
DPF[size_t( rd )] = value; // Store in FP64 register
371+
DPF[size_t( rd )] = value;
330372
} else if( HasD ) {
331-
BoxNaN( &DPF[size_t( rd )], &value ); // Store NaN-boxed float in FP64 register
373+
BoxNaN( &DPF[size_t( rd )], &value );
332374
} else {
333-
SPF[size_t( rd )] = value; // Store in FP32 register
375+
BoxNaN( &SPF[size_t( rd )], &value );
334376
}
335377
}
336378

@@ -425,8 +467,8 @@ class RevRegFile {
425467
FCSR& GetFCSR() { return fcsr; }
426468

427469
// Friend functions and classes to access internal register state
428-
template<typename FP, typename INT>
429-
friend bool CvtFpToInt( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst );
470+
template<typename INT, typename FP>
471+
friend bool fcvtif( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst );
430472

431473
template<typename T>
432474
friend bool load( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst );

include/insns/RV32D.h

+20-56
Original file line numberDiff line numberDiff line change
@@ -44,69 +44,33 @@ class RV32D : public RevExt {
4444
static constexpr auto& fled = fcondop<double, std::less_equal>;
4545

4646
// FP to Integer Conversion instructions
47-
static constexpr auto& fcvtwd = CvtFpToInt<double, int32_t>;
48-
static constexpr auto& fcvtwud = CvtFpToInt<double, uint32_t>;
47+
static constexpr auto& fcvtwd = fcvtif<int32_t, double>;
48+
static constexpr auto& fcvtwud = fcvtif<uint32_t, double>;
4949

50-
static bool fsqrtd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
51-
R->SetFP( Inst.rd, std::sqrt( R->GetFP<double>( Inst.rs1 ) ) );
52-
R->AdvancePC( Inst );
53-
return true;
54-
}
50+
// Square root
51+
static constexpr auto& fsqrtd = fsqrt<double>;
5552

56-
static bool fsgnjd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
57-
R->SetFP( Inst.rd, std::copysign( R->GetFP<double>( Inst.rs1 ), R->GetFP<double>( Inst.rs2 ) ) );
58-
R->AdvancePC( Inst );
59-
return true;
60-
}
53+
// Sign transfer
54+
static constexpr auto& fsgnjd = fsgnj<double>;
55+
static constexpr auto& fsgnjnd = fsgnjn<double>;
56+
static constexpr auto& fsgnjxd = fsgnjx<double>;
6157

62-
static bool fsgnjnd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
63-
R->SetFP( Inst.rd, std::copysign( R->GetFP<double>( Inst.rs1 ), -R->GetFP<double>( Inst.rs2 ) ) );
64-
R->AdvancePC( Inst );
65-
return true;
66-
}
58+
// Conversions between single and double precision FP
59+
static constexpr auto& fcvtsd = fcvtff<float, double>;
60+
static constexpr auto& fcvtds = fcvtff<double, float>;
6761

68-
static bool fsgnjxd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
69-
double rs1 = R->GetFP<double>( Inst.rs1 ), rs2 = R->GetFP<double>( Inst.rs2 );
70-
R->SetFP( Inst.rd, std::copysign( rs1, std::signbit( rs1 ) ? -rs2 : rs2 ) );
71-
R->AdvancePC( Inst );
72-
return true;
73-
}
62+
// FP Classify
63+
static constexpr auto& fclassd = fclassify<double>;
7464

75-
static bool fcvtsd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
76-
R->SetFP( Inst.rd, static_cast<float>( R->GetFP<double>( Inst.rs1 ) ) );
77-
R->AdvancePC( Inst );
78-
return true;
79-
}
80-
81-
static bool fcvtds( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
82-
R->SetFP( Inst.rd, static_cast<double>( R->GetFP<float>( Inst.rs1 ) ) );
83-
R->AdvancePC( Inst );
84-
return true;
85-
}
86-
87-
static bool fclassd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
88-
R->SetX( Inst.rd, fclass( R->GetFP<double>( Inst.rs1 ) ) );
89-
R->AdvancePC( Inst );
90-
return true;
91-
}
92-
93-
static bool fcvtdw( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
94-
R->SetFP( Inst.rd, static_cast<double>( R->GetX<int32_t>( Inst.rs1 ) ) );
95-
R->AdvancePC( Inst );
96-
return true;
97-
}
98-
99-
static bool fcvtdwu( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
100-
R->SetFP( Inst.rd, static_cast<double>( R->GetX<uint32_t>( Inst.rs1 ) ) );
101-
R->AdvancePC( Inst );
102-
return true;
103-
}
65+
// Conversion from integer to double
66+
static constexpr auto& fcvtdw = fcvtfi<double, int32_t>;
67+
static constexpr auto& fcvtdwu = fcvtfi<double, uint32_t>;
10468

10569
// Compressed instructions
106-
static constexpr auto& cfldsp = fld;
107-
static constexpr auto& cfsdsp = fsd;
108-
static constexpr auto& cfld = fld;
109-
static constexpr auto& cfsd = fsd;
70+
static constexpr auto& cfldsp = fld;
71+
static constexpr auto& cfsdsp = fsd;
72+
static constexpr auto& cfld = fld;
73+
static constexpr auto& cfsd = fsd;
11074

11175
// ----------------------------------------------------------------------
11276
//

0 commit comments

Comments
 (0)