Skip to content
This repository was archived by the owner on Jul 8, 2026. It is now read-only.

Commit 6cc7af8

Browse files
toceanwenchenvincent
authored andcommitted
make extension compilable with AMD GPU
1 parent 11b799d commit 6cc7af8

9 files changed

Lines changed: 710 additions & 11 deletions

File tree

csrc/extensions/include/common.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@
44
#ifndef MSAMP_COMMON_H_
55
#define MSAMP_COMMON_H_
66

7+
#ifndef __HIP_PLATFORM_AMD__
78
#include <cuda_bf16.h>
89
#include <cuda_fp16.h>
910
#include <cuda_fp8.h>
10-
#include <torch/extension.h>
11+
#else
12+
#include <hip/hip_runtime.h>
13+
#include <hip/hip_fp16.h>
14+
#include <hip/hip_bfloat16.h>
15+
#include "hip_float8.h"
16+
#endif
1117

18+
#include <torch/extension.h>
1219
#include <string>
1320

1421
using namespace std;
@@ -17,10 +24,18 @@ using byte = uint8_t;
1724
using int32 = int32_t;
1825
using fp32 = float;
1926
using fp16 = half;
27+
28+
#ifndef __HIP_PLATFORM_AMD__
2029
using bf16 = nv_bfloat16;
2130
using fp8e4m3 = __nv_fp8_e4m3;
2231
using fp8e5m2 = __nv_fp8_e5m2;
2332

33+
#else
34+
using bf16 = hip_bfloat16;
35+
using fp8e4m3 = hip_f8<hip_f8_type::fp8>;
36+
using fp8e5m2 = hip_f8<hip_f8_type::bf8>;
37+
#endif
38+
2439
template <typename T>
2540
constexpr T DIVUP(const T &x, const T &y) {
2641
return (((x) + ((y)-1)) / (y));

csrc/extensions/include/concurrency.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ struct DeviceSyncer {
1818
/// Destroy the DeviceSyncer object.
1919
~DeviceSyncer() = default;
2020

21-
#ifdef __CUDACC__
2221
/// Synchronize all threads inside a kernel. Guarantee that all previous work of all threads in cooperating blocks is
2322
/// finished.
2423
/// @param blockNum The number of blocks that will synchronize.
@@ -48,7 +47,6 @@ struct DeviceSyncer {
4847
// the flag is flipped.
4948
__syncthreads();
5049
}
51-
#endif
5250

5351
private:
5452
/// The flag to indicate whether the barrier is reached by the latest thread.
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
/*************************************************************************
2+
* Copyright (c) 2023-2024, Advanced Micro Devices, Inc. All rights reserved.
3+
*
4+
* License for AMD contributions = MIT. See LICENSE for more information
5+
************************************************************************/
6+
7+
namespace hip_f8_impl {
8+
9+
__host__ inline int clz(uint32_t x) { return __builtin_clz(x); }
10+
__device__ inline int clz(uint32_t x) { return __clz(x); }
11+
12+
13+
template <int wm, int we, typename T, bool negative_zero_nan, bool clip>
14+
HIP_HOST_DEVICE
15+
uint8_t cast_to_f8(T _x, bool stoch, uint32_t rng) {
16+
constexpr bool is_half = std::is_same<T,__half>::value;
17+
constexpr bool is_float = std::is_same<T,float>::value;
18+
static_assert(wm+we==7, "wm+we==7");
19+
static_assert(is_half || is_float, "Only half and float can be cast to f8");
20+
21+
//if(sizeof(T)==2 && we==5 && !negative_zero_nan)
22+
//return cast_to_f8_no_range_reduce<2, 5, __half>(_x, stoch, rng);
23+
24+
const int mfmt = (sizeof(T)==4) ? 23 : 10;
25+
uint32_t x;
26+
if(sizeof(T)==4)
27+
x = reinterpret_cast<uint32_t&>(_x);
28+
else
29+
x = reinterpret_cast<uint16_t&>(_x);
30+
31+
uint32_t y, head, mantissa;
32+
int exponent, bias;
33+
uint32_t sign;
34+
35+
if(sizeof(T)==4) {
36+
head = x & 0xFF800000;
37+
mantissa = x & 0x7FFFFF;
38+
exponent = (head>>23) & 0xFF;
39+
sign = head >> 31;
40+
bias = 127;
41+
} else {
42+
head = x & 0xFC00;
43+
mantissa = x & 0x3FF;
44+
exponent = (head>>10) & 0x1F;
45+
sign = head >> 15;
46+
bias = 15;
47+
}
48+
49+
uint32_t signed_inf = (sign<<7) + (((1<<we)-1)<<wm);
50+
51+
// Deal with inf and NaNs
52+
if(negative_zero_nan) {
53+
if(sizeof(T)==4) {
54+
if((x & 0x7F800000) == 0x7F800000)
55+
return 0x80;
56+
} else {
57+
//if(__hisinf(x) || __hisnan(x))
58+
if((x & 0x7C00)==0x7C00)
59+
return 0x80;
60+
}
61+
}
62+
else {
63+
if(sizeof(T)==4) {
64+
if((x & 0x7F800000) == 0x7F800000)
65+
return signed_inf + (mantissa!=0 ? 1 : 0);
66+
} else {
67+
if((x & 0x7C00)==0x7C00)
68+
return signed_inf + (mantissa!=0 ? 1 : 0);
69+
}
70+
}
71+
if(x==0)
72+
return 0;
73+
74+
// First need to check if it is normal or denorm as there is a difference of implict 1
75+
// Then need to adjust the exponent to align with the F8 exponent, in the meanwhile, shift
76+
// The mantissa. Then for stochastic rounding, add rng to mantissa and truncate. And for
77+
// RNE, no need to add rng. Then probably need to check whether there is carry and adjust
78+
// exponent and mantissa again
79+
80+
// For IEEE bias mode, the bias is 2^(k-1) -1 where k is the width of exponent bits
81+
const int f8_bias = ( 1<<(we-1) ) - 1 + ( negative_zero_nan ? 1 : 0 );
82+
const int f8_denormal_act_exponent = 1 - f8_bias; //actual exponent of f8 denormal
83+
// act_exponent is the actual exponent of fp32/fp16 (after subtracting bias)
84+
// f8_exponent is the converted f8 exponent with bias encoding
85+
// exponent_diff is the diff between fp32/fp16 exponent and f8 exponent,
86+
// the difference needs to be adjusted and mantissa shifted
87+
int act_exponent, f8_exponent, exponent_diff;
88+
89+
if (exponent == 0) { // fp32/fp16 is in denormal.
90+
/* fp32 denormal is below 2^-127 so it is usually not a concern here, we mostly concern fp16 here.
91+
In this case, f8 is usually in denormal. But there could be exceptions.
92+
fp16 denormal has exponent bias 15 while bf8 with NANOO has exponent bias 16.
93+
It means that there are some numbers in fp16 denormal but they are bf8 (NANOO) normals - smallest bf8 (NANOO) normal is 2^-15.
94+
fp16 numbers where exponent==0 (actual exponent -14) and highest bit of mantissa is 1 are bf8 (NANOO) normal.
95+
In this case, the fp16 mantissa should be shift left by 1 */
96+
act_exponent = exponent - bias + 1;
97+
exponent_diff = f8_denormal_act_exponent - act_exponent; // actual exponent is exponent-bias+1 as it is denormal
98+
}
99+
else { // fp32/fp16 is normal with implicit 1
100+
act_exponent = exponent - bias;
101+
if (act_exponent <= f8_denormal_act_exponent) {
102+
/* This is the case where fp32/fp16 is normal but it is in f8 denormal range.
103+
For example fp8 nanoo mode, denormal exponent is -7, but if the fp32/fp16
104+
actual exponent is -7, it is actually larger due to the implict 1,
105+
Therefore it needs to be adjust to -6 and mantissa shift right by 1.
106+
So for fp32/fp16, exponent -8 is the cut point to convert to fp8 nanoo */
107+
exponent_diff = f8_denormal_act_exponent - act_exponent;
108+
}
109+
else { //both fp32/fp16 and f8 are in normal range
110+
exponent_diff = 0; // exponent_diff=0 does not mean there is no difference for this case,
111+
//act_exponent could be larger. Just that it does not need shift mantissa
112+
}
113+
mantissa += (1 << mfmt); //Add the implicit 1 into mantissa
114+
}
115+
116+
117+
bool midpoint = (mantissa & ( (1 << (mfmt-wm+exponent_diff)) - 1 )) == ( 1 << (mfmt-wm+exponent_diff-1) );
118+
/* This part is a bit tricky. The judgment of whether it is a tie needs to be done before we shift right
119+
as shift right could rip off some residual part and make something not midpoint look like midpoint.
120+
For example, the fp16 number 0x1002 (0 00100 0000000010), it is larger than midpoint,
121+
but after shift right by 4 bits, it would look like midpoint.
122+
*/
123+
124+
if (exponent_diff>0)
125+
mantissa >>= exponent_diff;
126+
else if (exponent_diff == -1)
127+
mantissa <<= -exponent_diff;
128+
bool implicit_one = mantissa & (1 << mfmt);
129+
//if there is no implict 1, it means the f8 is denormal and need to adjust to denorm exponent
130+
f8_exponent = (act_exponent+exponent_diff) /*actual f8 exponent*/ + f8_bias - (implicit_one?0:1);
131+
132+
//Now we have the exponent and mantissa adjusted
133+
uint32_t drop_mask = (1 << (mfmt-wm)) - 1;
134+
//bool midpoint = (mantissa & drop_mask) == ( 1 << (mfmt-wm-1) );
135+
bool odd = mantissa & (1<< (mfmt-wm)); // if the least significant bit that is not truncated is 1
136+
mantissa += (stoch ? rng : (midpoint?(odd?mantissa:mantissa-1 ) :mantissa) ) & drop_mask;
137+
138+
//Now we deal with overflow
139+
if (f8_exponent == 0) {
140+
if ((1 << mfmt) & mantissa) {
141+
f8_exponent = 1; //denormal overflow to become normal, promote exponent
142+
//mantissa &= (1<<mfmt) -1 ; //No need to make 1 implicit now as it will be addressed later
143+
}
144+
}
145+
else {
146+
if ((1 << (mfmt+1)) & mantissa) {
147+
mantissa >>= 1;
148+
f8_exponent++;
149+
//mantissa &= (1<<mfmt) -1 ; // No need to make 1 implicit now as it will be addressed later
150+
}
151+
}
152+
153+
mantissa >>= (mfmt-wm);
154+
155+
// above range: quantize to maximum possible float of the same sign
156+
const int max_exp = (1<<we)-(negative_zero_nan ? 1 : 2);
157+
if(f8_exponent > max_exp) {
158+
if(clip) {
159+
mantissa = (1<<wm)-1;
160+
f8_exponent = max_exp;
161+
} else {
162+
return signed_inf;
163+
}
164+
}
165+
166+
if(f8_exponent == 0 && mantissa == 0)
167+
return negative_zero_nan? 0 : (sign<<7);
168+
mantissa &= (1<<wm)-1;
169+
return (sign << 7) | (f8_exponent << wm) | mantissa;
170+
171+
}
172+
173+
/* RTC does not have std::conditional so implement it here*/
174+
template<bool B, class T, class F>
175+
struct conditional { typedef T type; };
176+
template<class T, class F>
177+
struct conditional<false, T, F> { typedef F type; };
178+
179+
template <int wm, int we, typename T, bool negative_zero_nan>
180+
HIP_HOST_DEVICE
181+
T cast_from_f8(uint8_t x) {
182+
constexpr bool is_half = std::is_same<T,__half>::value;
183+
constexpr bool is_float = std::is_same<T,float>::value;
184+
constexpr bool is_bf16 = std::is_same<T,hip_bfloat16>::value;
185+
static_assert(is_half || is_float, "only half and float are supported");
186+
187+
constexpr int weo = is_half ? 5 : 8;
188+
constexpr int wmo = is_half ? 10 : (is_float ? 23 : 7);
189+
190+
T fInf, fNegInf, fNaN, fNeg0;
191+
if(is_half) {
192+
const uint16_t ihInf = 0x7C00;
193+
const uint16_t ihNegInf = 0xFC00;
194+
const uint16_t ihNaN = 0x7C01;
195+
const uint16_t ihNeg0 = 0x8000;
196+
fInf = reinterpret_cast<const __half&>(ihInf);
197+
fNegInf = reinterpret_cast<const __half&>(ihNegInf);
198+
fNaN = reinterpret_cast<const __half&>(ihNaN);
199+
fNeg0 = reinterpret_cast<const __half&>(ihNeg0);
200+
} else if(is_float) {
201+
const uint32_t ifInf = 0x7F800000;
202+
const uint32_t ifNegInf = 0xFF800000;
203+
const uint32_t ifNaN = 0x7F800001;
204+
const uint32_t ifNeg0 = 0x80000000;
205+
fInf = reinterpret_cast<const float&>(ifInf);
206+
fNegInf = reinterpret_cast<const float&>(ifNegInf);
207+
fNaN = reinterpret_cast<const float&>(ifNaN);
208+
fNeg0 = reinterpret_cast<const float&>(ifNeg0);
209+
}
210+
211+
if(x==0)
212+
return 0;
213+
214+
uint32_t sign = x>>7;
215+
uint32_t mantissa = x & ((1<<wm)-1);
216+
int exponent = (x & 0x7F) >> wm;
217+
if(negative_zero_nan) {
218+
if(x==0x80)
219+
return fNaN;
220+
} else {
221+
if(x==0x80)
222+
return fNeg0;
223+
if(exponent == ((1<<we)-1))
224+
return (mantissa == 0) ? (sign ? fNegInf : fInf) : fNaN;
225+
}
226+
typename conditional<sizeof(T)==2, uint16_t, uint32_t>::type retval;
227+
if(we==5 && is_half && !negative_zero_nan) {
228+
retval = x<<8;
229+
return reinterpret_cast<const T&>(retval);
230+
}
231+
232+
const int exp_low_cutoff = (1<<(weo-1)) - (1<<(we-1)) + 1 - (negative_zero_nan ? 1 : 0);
233+
234+
//subnormal input
235+
if(exponent == 0) {
236+
//guaranteed mantissa!=0 since cases 0x0 and 0x80 are handled above
237+
int sh = 1 + clz(mantissa) - (32-wm);
238+
mantissa <<= sh;
239+
exponent += 1-sh;
240+
/*
241+
exponent++;
242+
while(mantissa<(1<<wm)) {
243+
mantissa <<= 1;
244+
exponent--;
245+
}
246+
*/
247+
mantissa &= ((1<<wm)-1);
248+
}
249+
exponent += exp_low_cutoff-1;
250+
mantissa <<= wmo - wm;
251+
252+
// subnormal output (occurs when T=half, we=5, negative_zero_nan=true)
253+
if(exponent<=0) {
254+
mantissa |= 1<<wmo;
255+
mantissa >>= 1-exponent;
256+
exponent = 0;
257+
}
258+
259+
if(sizeof(T)==2)
260+
retval = (sign<<15) | (exponent<<10) | mantissa;
261+
else
262+
retval = (sign<<31) | (exponent<<23) | mantissa;
263+
return reinterpret_cast<const T&>(retval);
264+
}
265+
266+
267+
} // namespace hip_f8_impl

0 commit comments

Comments
 (0)