Skip to content

Commit 964066f

Browse files
authored
Merge pull request #308 from tactcomplabs/templatize_helpers2
fully templatize floating-point helper functions
2 parents c178e1e + 0169016 commit 964066f

File tree

8 files changed

+216
-194
lines changed

8 files changed

+216
-194
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

+89-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,12 @@ 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+
#if 0
119+
} else if constexpr( std::is_same_v<T, float16> ) {
120+
uint16_t i16;
121+
memcpy( &i16, &val, sizeof( i16 ) );
122+
return ( i16 & uint16_t{ 1 } << 9 ) != 0 ? QuietNaN : SignalingNaN;
123+
#endif
118124
} else {
119125
uint64_t i64;
120126
memcpy( &i64, &val, sizeof( i64 ) );
@@ -443,6 +449,87 @@ bool fnmadd( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) {
443449
return true;
444450
}
445451

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

448535
#endif

include/RevRegFile.h

+71-27
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,68 @@ 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+
#if 0
51+
template<>
52+
struct uint_type<float16> {
53+
using type = uint16_t;
54+
static_assert( sizeof( type ) == sizeof( float16 ) );
55+
};
56+
#endif
57+
58+
template<typename T>
59+
using uint_type_t = typename uint_type<T>::type;
60+
61+
/// BoxNaN: Store a boxed floating point value inside a possibly larger one
62+
template<typename T, typename U, typename = std::enable_if_t<sizeof( T ) >= sizeof( U )>>
63+
inline void BoxNaN( T* dest, const U* value ) {
64+
if constexpr( sizeof( T ) == sizeof( U ) ) {
65+
*dest = *value;
66+
} else {
67+
uint_type_t<U> i;
68+
memcpy( &i, value, sizeof( i ) ); // The value
69+
uint_type_t<T> box = uint_type_t<T>{ i } | ~uint_type_t<T>{ 0 } << sizeof( U ) * 8; // Boxed NaN value
70+
memcpy( dest, &box, sizeof( box ) ); // Store in larger register
71+
static_assert( sizeof( i ) == sizeof( U ) && sizeof( box ) == sizeof( T ) );
72+
}
73+
}
74+
75+
/// UnBoxNaN: Unbox a floating point value into a possibly smaller one
76+
// The second argument indicates whether it is a FMV/FS move/store
77+
// instruction which just transfers bits and not care about NaN-Boxing.
78+
template<typename T, bool FMV_FS = false, typename U, typename = std::enable_if_t<sizeof( T ) <= sizeof( U )>>
79+
inline T UnBoxNaN( const U* val ) {
80+
if constexpr( sizeof( T ) == sizeof( U ) ) {
81+
return *val;
82+
} else {
83+
uint_type_t<U> i;
84+
memcpy( &i, val, sizeof( i ) );
85+
static_assert( sizeof( i ) == sizeof( val ) );
86+
T fp;
87+
if( !FMV_FS && ~i >> sizeof( T ) * 8 ) {
88+
fp = std::numeric_limits<T>::quiet_NaN();
89+
} else {
90+
auto ifp = static_cast<uint_type_t<T>>( i );
91+
memcpy( &fp, &ifp, sizeof( fp ) );
92+
static_assert( sizeof( ifp ) == sizeof( fp ) );
93+
}
94+
return fp;
95+
}
4196
}
4297

4398
/// RISC-V Register Mneumonics
@@ -303,34 +358,23 @@ class RevRegFile {
303358
template<typename T, bool FMV_FS = false, typename U>
304359
T GetFP( U rs ) const {
305360
if constexpr( std::is_same_v<T, double> ) {
306-
return DPF[size_t( rs )]; // The FP64 register's value
361+
return DPF[size_t( rs )];
362+
} else if( HasD ) {
363+
return UnBoxNaN<T, FMV_FS>( &DPF[size_t( rs )] );
307364
} 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
365+
return UnBoxNaN<T, FMV_FS>( &SPF[size_t( rs )] );
322366
}
323367
}
324368

325369
/// SetFP: Set a specific FP register to a floating-point value
326370
template<typename T, typename U>
327371
void SetFP( U rd, T value ) {
328372
if constexpr( std::is_same_v<T, double> ) {
329-
DPF[size_t( rd )] = value; // Store in FP64 register
373+
DPF[size_t( rd )] = value;
330374
} else if( HasD ) {
331-
BoxNaN( &DPF[size_t( rd )], &value ); // Store NaN-boxed float in FP64 register
375+
BoxNaN( &DPF[size_t( rd )], &value );
332376
} else {
333-
SPF[size_t( rd )] = value; // Store in FP32 register
377+
BoxNaN( &SPF[size_t( rd )], &value );
334378
}
335379
}
336380

@@ -425,8 +469,8 @@ class RevRegFile {
425469
FCSR& GetFCSR() { return fcsr; }
426470

427471
// 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 );
472+
template<typename INT, typename FP>
473+
friend bool fcvtif( RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst );
430474

431475
template<typename T>
432476
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)