kernel.py 243 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067
  1. """CuTeDSL W4A16 NVFP4/BF16 W4A16 MoE kernels."""
  2. from __future__ import annotations
  3. import math
  4. import os
  5. from dataclasses import dataclass, replace
  6. from typing import NamedTuple
  7. import cuda.bindings.driver as cuda
  8. import cutlass
  9. import cutlass.cute as cute
  10. import torch
  11. from cutlass.cutlass_dsl import Int32, Int64, Uint32
  12. from b12x.cute.compiler import KernelCompileSpec, compile as b12x_compile
  13. from b12x.cute.fp4 import (
  14. atomic_add_global_i32,
  15. bf16_mma_m16n8k16_f32,
  16. bf16_mma_rhs_fragments_as_mma_a_m16n8k16_f32,
  17. bfloat2_broadcast_lane,
  18. bfloat2_mul,
  19. bfloat2_to_float2_scaled,
  20. broadcast_f32_to_half2,
  21. broadcast_f32_to_bfloat2,
  22. cp_async4_shared_global,
  23. cp_async4_shared_global_pred,
  24. fabs_f32,
  25. fmax_f32,
  26. f16_mma_m16n8k16_f32,
  27. f16_mma_rhs_fragments_as_mma_a_m16n8k16_f32,
  28. half2_to_float2_scaled,
  29. get_ptr_as_int64,
  30. half2_mul,
  31. ld_global_acquire_i32,
  32. ld_global_nc_u32,
  33. ld_global_v4_f32,
  34. ld_shared_f32,
  35. ld_shared_i32_relaxed,
  36. ld_shared_u32,
  37. ld_shared_v2_u32,
  38. ld_shared_v4_f32,
  39. ld_shared_v4_u32,
  40. ldmatrix_m8n8x4_b16,
  41. ldmatrix_m8n8x2_b16,
  42. packed_dequant_e2m1x4_to_bfloat2x2,
  43. packed_dequant_e2m1x4_to_half2x2,
  44. packed_dequant_e4m3x4_to_bfloat2x2,
  45. packed_dequant_e4m3x4_to_half2x2,
  46. packed_dequant_e8m0x4_to_bfloat2x2,
  47. packed_dequant_e8m0x4_to_half2x2,
  48. packed_dequant_nf3x8_to_bfloat2x4,
  49. nf3_codebook_pools,
  50. pack_f32x2_to_bfloat2,
  51. pack_f32x2_to_f16x2,
  52. red_add_global_bf16x2,
  53. red_add_global_release_i32,
  54. red_max_global_f32_nonnegative,
  55. shared_ptr_to_u32,
  56. st_global_v4_u32,
  57. st_global_i32,
  58. st_global_v4_f32,
  59. st_shared_bf16_from_f32,
  60. st_shared_f16_from_f32,
  61. st_shared_f32,
  62. st_shared_i32,
  63. st_shared_u32,
  64. st_shared_v4_f32,
  65. st_shared_v4_u32,
  66. threadfence,
  67. warp_reduce,
  68. )
  69. from b12x.cute.utils import current_cuda_stream, make_ptr
  70. from b12x.moe.fused.w4a16.route_pack import (
  71. pack_topk_routes_by_expert as _pack_topk_routes_by_expert,
  72. )
  73. from b12x.moe.fused.w4a16.host import (
  74. _W4A16_ALLOWED_ROUTED_SIZES,
  75. max_packed_route_slots,
  76. packed_gemm_scratch_elements,
  77. plan_w4a16_buffers,
  78. select_route_block_size_m,
  79. validate_activation,
  80. )
  81. from b12x.cute.runtime_control import raise_if_kernel_resolution_frozen
  82. from b12x.moe.fused.activations import (
  83. SWIGLUOAI_UNINTERLEAVE,
  84. is_gated_moe_activation,
  85. normalize_moe_activation,
  86. normalize_swiglu_alpha_for_activation,
  87. normalize_swiglu_beta_for_activation,
  88. normalize_swiglu_limit_for_activation,
  89. )
  90. from b12x.moe.fused.micro import MoEMicroKernelBackend
  91. _ALLOWED_ROUTED_SIZES = _W4A16_ALLOWED_ROUTED_SIZES
  92. _PACK_FACTOR = 8
  93. _STAGES = 4
  94. _E8M0_LOGICAL_TAIL_SCALE_N_ALIGNMENT = 64
  95. _DEVICE_MAX_REG_BYTES = 255 * 1024
  96. _DEFAULT_MAX_SHARED_MEM = 101_376
  97. _WEIGHT_LAYOUTS = {"packed", "modelopt", "nf3_2p1"}
  98. _MODEL_OPT_W13_LAYOUTS = {"w13", "w31"}
  99. # NF3 codebook: 8 bf16 codepoints for the 3-bit ("nf3_2p1") weight layout. The
  100. # device dequant reads these as prmt pools (see nf3_codebook_pools); the host
  101. # injects the 4 pool words as compile-time constants into the GEMM.
  102. _NF3_CODEBOOK = (-1.0, -0.6047, -0.3563, -0.1275, 0.1275, 0.3563, 0.6047, 1.0)
  103. _SCALE_FORMATS = {
  104. "e4m3_k16": "e4m3_k16",
  105. "e8m0_k32": "e8m0_k32",
  106. "e4m3_k32": "e4m3_k32",
  107. }
  108. _E8M0_K32_FP16_GLOBAL_COMPENSATION = float(2.0**7)
  109. _E8M0_K32_BF16_GLOBAL_COMPENSATION = float(2.0**119)
  110. _MAX_DIRECT_TOPK_ROUTE_M = 6
  111. _W4A16_SMALL_M_DIRECT_MAX_M = 8
  112. # TC-decode: a small-M decode specialization that runs on the PACKED W4A16
  113. # object (the same weights/scales the prefill GEMM uses). It reuses the packed
  114. # tensor-core MMA inner loop but folds the top-k sum into the FC2 store
  115. # epilogue (dropping the separate top-k-sum launch). It never regresses vs the
  116. # packed GEMM within its supported M range, so it is ALWAYS used for the small-M
  117. # direct-topk decode sizes — there is no opt-in/opt-out switch.
  118. # TC-decode is available for the whole small-M direct-topk range. Its only hard
  119. # ceiling is the direct-topk route cap (above it, expert route-packing wins).
  120. # _TC_DECODE_M is retained for callers/tests that enumerate the supported sizes.
  121. _TC_DECODE_MAX_M = _W4A16_SMALL_M_DIRECT_MAX_M
  122. _TC_DECODE_M = tuple(range(1, _TC_DECODE_MAX_M + 1))
  123. def _m_specialization_key(size_m: int) -> int:
  124. """M bucket for branches that genuinely specialize on token count."""
  125. return 1 if int(size_m) == 1 else 0
  126. def _fake_m_for_specialization(size_m: int) -> int:
  127. return 1 if _m_specialization_key(size_m) == 1 else 2
  128. # The W4A16 launch model chooses blocks/SM from static resource usage
  129. # for each specialization. These register counts were measured from the local
  130. # SM121 JIT output and keep launch occupancy stable across refactors.
  131. _W4A16_REGS_SM121 = {
  132. (256, 1, 8, 8, True): 118,
  133. (256, 1, 16, 4, True): 118,
  134. (256, 1, 16, 8, True): 118,
  135. (256, 1, 32, 2, True): 118,
  136. (128, 1, 4, 8, True): 118,
  137. (128, 1, 8, 4, True): 120,
  138. (256, 1, 8, 8, False): 158,
  139. (128, 1, 4, 8, False): 154,
  140. (128, 1, 8, 4, False): 143,
  141. (256, 2, 16, 4, False): 212,
  142. (128, 2, 4, 8, False): 215,
  143. (128, 2, 8, 4, False): 214,
  144. (256, 3, 16, 4, False): 249,
  145. (128, 3, 4, 8, False): 249,
  146. (128, 3, 8, 4, False): 250,
  147. (256, 4, 16, 4, False): 255,
  148. (128, 4, 4, 8, False): 255,
  149. (128, 4, 8, 4, False): 255,
  150. }
  151. _SMALL_BATCH_TILE_CONFIGS = (
  152. (128, 128, 256),
  153. (64, 128, 128),
  154. (128, 64, 128),
  155. )
  156. _LARGE_BATCH_TILE_CONFIGS = (
  157. (64, 256, 256),
  158. (64, 128, 128),
  159. (128, 64, 128),
  160. )
  161. def _covering_count(total: int, quantum: int) -> int:
  162. return (total + quantum - 1) // quantum
  163. def _e8m0_logical_tail_scale_n(size_n: int) -> int:
  164. return (
  165. (int(size_n) + _E8M0_LOGICAL_TAIL_SCALE_N_ALIGNMENT - 1)
  166. // _E8M0_LOGICAL_TAIL_SCALE_N_ALIGNMENT
  167. ) * _E8M0_LOGICAL_TAIL_SCALE_N_ALIGNMENT
  168. def _normalize_swiglu_limit(swiglu_limit: float | None) -> float | None:
  169. return normalize_swiglu_limit_for_activation("silu", swiglu_limit)
  170. def _normalize_activation_swiglu_params(
  171. activation: str,
  172. swiglu_limit: float | None,
  173. swiglu_alpha: float | None,
  174. swiglu_beta: float | None,
  175. ) -> tuple[float | None, float, float]:
  176. activation = normalize_moe_activation(activation)
  177. return (
  178. normalize_swiglu_limit_for_activation(activation, swiglu_limit),
  179. normalize_swiglu_alpha_for_activation(activation, swiglu_alpha),
  180. normalize_swiglu_beta_for_activation(activation, swiglu_beta),
  181. )
  182. def _w4a16_num_regs(
  183. *,
  184. cta_threads: int,
  185. cta_m_blocks: int,
  186. cta_n_blocks: int,
  187. cta_k_blocks: int,
  188. uses_m_block_8: bool,
  189. ) -> int:
  190. key = (
  191. int(cta_threads),
  192. int(cta_m_blocks),
  193. int(cta_n_blocks),
  194. int(cta_k_blocks),
  195. bool(uses_m_block_8),
  196. )
  197. try:
  198. return _W4A16_REGS_SM121[key]
  199. except KeyError as exc:
  200. raise ValueError(
  201. f"missing W4A16 register count for NVFP4 BF16 specialization {key}"
  202. ) from exc
  203. def _shared_memory_footprint(
  204. *,
  205. cta_m_blocks: int,
  206. tile_n: int,
  207. tile_k: int,
  208. scale_format: str = "e4m3_k16",
  209. ) -> int:
  210. cta_m = int(cta_m_blocks) * 16
  211. cta_n = int(tile_n)
  212. cta_k = int(tile_k)
  213. sh_block_meta_size = cta_m * 16
  214. sh_a_size = _STAGES * (cta_m * cta_k) * 2
  215. sh_b_size = _STAGES * (cta_k * cta_n // _PACK_FACTOR) * 4
  216. sh_red_size = cta_m * (cta_n + 8) * 2
  217. sh_bias_size = cta_n * 2
  218. tmp_size = min(sh_b_size, sh_red_size) + sh_bias_size
  219. tmp_size = max(max(sh_b_size, sh_red_size), tmp_size)
  220. sh_s_size = (
  221. _covering_count(cta_k, _scale_group_size(scale_format)) * cta_n * 2 * _STAGES
  222. )
  223. return tmp_size + sh_a_size + sh_s_size + sh_block_meta_size
  224. def _determine_blocks_per_sm(
  225. *,
  226. problem_m: int,
  227. problem_n: int,
  228. top_k: int,
  229. cta_threads: int,
  230. cta_m_blocks: int,
  231. tile_n: int,
  232. tile_k: int,
  233. uses_m_block_8: bool,
  234. sms: int,
  235. max_shared_mem: int,
  236. scale_format: str = "e4m3_k16",
  237. ) -> int:
  238. num_regs = _w4a16_num_regs(
  239. cta_threads=cta_threads,
  240. cta_m_blocks=cta_m_blocks,
  241. cta_n_blocks=tile_n // 16,
  242. cta_k_blocks=tile_k // 16,
  243. uses_m_block_8=uses_m_block_8,
  244. )
  245. register_bytes = max(num_regs, 1) * int(cta_threads) * 4
  246. smem_bytes = _shared_memory_footprint(
  247. cta_m_blocks=cta_m_blocks,
  248. tile_n=tile_n,
  249. tile_k=tile_k,
  250. scale_format=scale_format,
  251. )
  252. blocks_per_sm_limit = min(
  253. _DEVICE_MAX_REG_BYTES // register_bytes,
  254. int(max_shared_mem) // (smem_bytes + 1536),
  255. )
  256. if uses_m_block_8:
  257. # Small-M (moe_block_size==8) TC-decode is weight-bandwidth/overhead
  258. # bound, not parallelism bound (only m*top_k route-blocks of GEMM work).
  259. # The fused FC1->activation->FC2 path crosses several grid barriers whose
  260. # tid==0 atomic-counter increment serializes across all grid_x CTAs, so an
  261. # oversized grid pays barrier-atomic latency proportional to grid_x for no
  262. # extra GEMM throughput. Pin one persistent CTA per SM to minimize the
  263. # barrier participant count while still covering the machine for the
  264. # I_tp=1024 GEMMs. The split-K persistent loop is grid_x-agnostic, so this
  265. # is numerically identical.
  266. blocks_per_sm_limit = 1
  267. elif cta_m_blocks == 1:
  268. blocks_per_sm_limit = max(min(blocks_per_sm_limit, 4), 1)
  269. else:
  270. blocks_per_sm_limit = max(min(blocks_per_sm_limit, 2), 1)
  271. work_cta_count = (int(problem_n) // int(tile_n)) * int(problem_m) * int(top_k) * 4
  272. if work_cta_count < int(sms) * blocks_per_sm_limit:
  273. blocks_per_sm_limit = max(work_cta_count // int(sms), 1)
  274. return int(blocks_per_sm_limit)
  275. def _candidate_tile_fits(
  276. *,
  277. problem_n: int,
  278. problem_k: int,
  279. cta_m_blocks: int,
  280. tile_n: int,
  281. tile_k: int,
  282. cta_threads: int,
  283. max_shared_mem: int,
  284. scale_format: str = "e4m3_k16",
  285. allow_logical_tail: bool = False,
  286. ) -> bool:
  287. if int(tile_k) == -1 or int(tile_n) == -1 or int(cta_threads) == -1:
  288. return False
  289. scale_group_size = _scale_group_size(scale_format)
  290. exact_n = int(problem_n) % int(tile_n) == 0
  291. exact_k = int(problem_k) % int(tile_k) == 0
  292. exact_scale_k = int(problem_k) % scale_group_size == 0
  293. if not allow_logical_tail:
  294. if not exact_n or not exact_k:
  295. return False
  296. if not exact_scale_k or int(tile_k) % scale_group_size != 0:
  297. return False
  298. elif (
  299. _normalize_scale_format(scale_format) != "e8m0_k32"
  300. or int(problem_n) % 16 != 0
  301. or int(problem_k) % 8 != 0
  302. or int(tile_n) % 16 != 0
  303. or int(tile_k) % scale_group_size != 0
  304. ):
  305. return False
  306. if int(tile_n) < 64 or int(tile_k) < 64 or int(cta_threads) < 128:
  307. return False
  308. smem_bytes = _shared_memory_footprint(
  309. cta_m_blocks=cta_m_blocks,
  310. tile_n=tile_n,
  311. tile_k=tile_k,
  312. scale_format=scale_format,
  313. )
  314. return smem_bytes <= int(max_shared_mem)
  315. def _select_tile_config(
  316. *,
  317. problem_m: int,
  318. problem_n: int,
  319. problem_k: int,
  320. top_k: int,
  321. moe_block_size: int,
  322. sms: int,
  323. max_shared_mem: int,
  324. required_cta_threads: int | None = None,
  325. scale_format: str = "e4m3_k16",
  326. allow_logical_tail: bool = False,
  327. ) -> tuple[int, int, int, int]:
  328. cta_m_blocks = _covering_count(moe_block_size, 16)
  329. uses_m_block_8 = moe_block_size == 8
  330. configs = (
  331. _LARGE_BATCH_TILE_CONFIGS if cta_m_blocks > 1 else _SMALL_BATCH_TILE_CONFIGS
  332. )
  333. best_blocks_per_sm = 0
  334. best_tile_config: tuple[int, int, int, int] | None = None
  335. for tile_k, tile_n, cta_threads in configs:
  336. if required_cta_threads is not None and int(cta_threads) != int(
  337. required_cta_threads
  338. ):
  339. continue
  340. if not _candidate_tile_fits(
  341. problem_n=problem_n,
  342. problem_k=problem_k,
  343. cta_m_blocks=cta_m_blocks,
  344. tile_n=tile_n,
  345. tile_k=tile_k,
  346. cta_threads=cta_threads,
  347. max_shared_mem=int(max_shared_mem) - 512,
  348. scale_format=scale_format,
  349. allow_logical_tail=allow_logical_tail,
  350. ):
  351. continue
  352. occupancy_problem_n = (
  353. _covering_count(int(problem_n), int(tile_n)) * int(tile_n)
  354. if allow_logical_tail
  355. else int(problem_n)
  356. )
  357. blocks_per_sm_limit = _determine_blocks_per_sm(
  358. problem_m=problem_m,
  359. problem_n=occupancy_problem_n,
  360. top_k=top_k,
  361. cta_threads=cta_threads,
  362. cta_m_blocks=cta_m_blocks,
  363. tile_n=tile_n,
  364. tile_k=tile_k,
  365. uses_m_block_8=uses_m_block_8,
  366. sms=sms,
  367. max_shared_mem=max_shared_mem,
  368. scale_format=scale_format,
  369. )
  370. if blocks_per_sm_limit > best_blocks_per_sm:
  371. best_blocks_per_sm = blocks_per_sm_limit
  372. best_tile_config = (tile_k, tile_n, cta_threads, blocks_per_sm_limit)
  373. if best_tile_config is None:
  374. cta_thread_msg = (
  375. ""
  376. if required_cta_threads is None
  377. else f", required_cta_threads={required_cta_threads}"
  378. )
  379. raise ValueError(
  380. "no valid W4A16 tile config for "
  381. f"M/N/K={problem_m}/{problem_n}/{problem_k}, moe_block_size={moe_block_size}"
  382. f"{cta_thread_msg}"
  383. )
  384. return best_tile_config
  385. @dataclass(frozen=True)
  386. class W4A16GemmCompileResult:
  387. compiled: object
  388. tile_n: int
  389. tile_k: int
  390. moe_block_size: int
  391. max_m_blocks: int
  392. blocks_per_sm: int
  393. weight_layout: str = "packed"
  394. scale_format: str = "e4m3_k16"
  395. w13_layout: str = "w13"
  396. @dataclass(frozen=True)
  397. class W4A16ActivationCompileResult:
  398. compiled: object
  399. rows: int
  400. intermediate_size: int
  401. activation: str
  402. swiglu_limit: float | None
  403. swiglu_alpha: float
  404. swiglu_beta: float
  405. @dataclass(frozen=True)
  406. class W4A16TopKSumCompileResult:
  407. compiled: object
  408. m: int
  409. topk: int
  410. hidden_size: int
  411. @dataclass(frozen=True)
  412. class W4A16FusedMoeCompileResult:
  413. compiled: object
  414. size_m: int
  415. hidden_size: int
  416. intermediate_size: int
  417. num_experts: int
  418. top_k: int
  419. activation: str
  420. apply_router_weight_on_input: bool
  421. zero_fc2_output: bool
  422. element_dtype: str
  423. fast_math: bool
  424. swiglu_limit: float | None
  425. swiglu_alpha: float
  426. swiglu_beta: float
  427. fc1_tile_n: int
  428. fc1_tile_k: int
  429. fc2_tile_n: int
  430. fc2_tile_k: int
  431. moe_block_size: int
  432. max_m_blocks: int
  433. blocks_per_sm: int
  434. weight_layout: str = "packed"
  435. w13_layout: str = "w13"
  436. direct_topk_routes: bool = False
  437. scale_format: str = "e4m3_k16"
  438. tc_decode_fused_sum: bool = False
  439. collect_activation_amax: bool = False
  440. @dataclass(frozen=True)
  441. class _W4A16GemmLaunch:
  442. kernel: W4A16GemmCompileResult
  443. c_tmp: torch.Tensor
  444. class _W4A16SmallMDirectLaunch(NamedTuple):
  445. compiled: object
  446. grid_x: int
  447. m: int
  448. hidden_size: int
  449. intermediate_size: int
  450. num_experts: int
  451. topk: int
  452. activation: str
  453. fast_math: bool
  454. topk_ids_dtype: torch.dtype
  455. class _W4A16SmallMDirectKernel(MoEMicroKernelBackend):
  456. """Decode-sized W4A16 specialization using the native ModelOpt layout."""
  457. _SUPPORTED_M = tuple(range(1, _W4A16_SMALL_M_DIRECT_MAX_M + 1))
  458. @classmethod
  459. def is_supported(
  460. cls,
  461. *,
  462. m: int,
  463. hidden_size: int,
  464. intermediate_size: int,
  465. topk: int,
  466. num_experts: int,
  467. scale_format: str = "e4m3_k16",
  468. ) -> bool:
  469. # E8M0 reads the shared packed scale grid. Both block axes must be /32.
  470. if scale_format == "e8m0_k32":
  471. scale_block_ok = (
  472. int(hidden_size) % 32 == 0
  473. and int(intermediate_size) % 32 == 0
  474. )
  475. else:
  476. scale_block_ok = True
  477. return (
  478. int(m) in cls._SUPPORTED_M
  479. and int(m) <= _W4A16_SMALL_M_DIRECT_MAX_M
  480. and int(hidden_size) > 0
  481. and int(hidden_size) % 128 == 0
  482. and int(intermediate_size) > 0
  483. and int(intermediate_size) % 16 == 0
  484. and scale_block_ok
  485. and 0 < int(topk) <= 32
  486. and int(num_experts) > 0
  487. and MoEMicroKernelBackend.is_supported(
  488. int(m),
  489. int(hidden_size),
  490. int(intermediate_size),
  491. int(topk),
  492. int(num_experts),
  493. )
  494. )
  495. def __init__(
  496. self,
  497. *,
  498. activation: str,
  499. fast_math: bool,
  500. share_input_across_experts: bool,
  501. share_expert_scales: bool,
  502. single_token: bool,
  503. scale_format: str = "e4m3_k16",
  504. swiglu_limit: float | None = None,
  505. swiglu_alpha: float | None = None,
  506. swiglu_beta: float | None = None,
  507. w13_layout: str = "w13",
  508. ):
  509. super().__init__(
  510. sf_vec_size=16,
  511. mma_tiler_mn=(64, 128),
  512. output_tile_count_n=1,
  513. fast_math=fast_math,
  514. activation=activation,
  515. share_input_across_experts=share_input_across_experts,
  516. share_expert_scales=share_expert_scales,
  517. single_token=single_token,
  518. dynamic_down_scale=False,
  519. w4a16_mode=True,
  520. scale_format=scale_format,
  521. swiglu_limit=swiglu_limit,
  522. swiglu_alpha=swiglu_alpha,
  523. swiglu_beta=swiglu_beta,
  524. w13_layout=w13_layout,
  525. )
  526. class W4A16GemmKernel:
  527. def __init__(
  528. self,
  529. *,
  530. size_m: int,
  531. size_n: int,
  532. size_k: int,
  533. num_experts: int,
  534. top_k: int,
  535. mul_topk_weights: bool,
  536. tile_n: int,
  537. tile_k: int,
  538. moe_block_size: int,
  539. max_m_blocks: int,
  540. element_dtype: str = "bf16",
  541. epilogue_activation: str | None = None,
  542. weight_layout: str = "packed",
  543. scale_format: str = "e4m3_k16",
  544. w13_layout: str = "w13",
  545. source_n_rotation: int = 0,
  546. single_token_route_fast_path: bool = False,
  547. direct_topk_routes: bool = False,
  548. fused_topk_sum: bool = False,
  549. fused_sum_topk: int = 1,
  550. ):
  551. if element_dtype not in {"bf16", "fp16"}:
  552. raise ValueError(f"unsupported element_dtype {element_dtype!r}")
  553. if weight_layout not in _WEIGHT_LAYOUTS:
  554. raise ValueError(f"unsupported W4A16 weight_layout {weight_layout!r}")
  555. scale_format = _normalize_scale_format(scale_format)
  556. if weight_layout == "modelopt":
  557. if w13_layout not in _MODEL_OPT_W13_LAYOUTS:
  558. raise ValueError(f"unsupported W4A16 w13_layout {w13_layout!r}")
  559. else:
  560. w13_layout = "packed"
  561. source_n_rotation = 0
  562. if weight_layout == "nf3_2p1":
  563. if scale_format != "e4m3_k32":
  564. raise ValueError(
  565. "nf3_2p1 W4A16 weights require scale_format='e4m3_k32'"
  566. )
  567. if element_dtype != "bf16":
  568. raise ValueError(
  569. "nf3_2p1 W4A16 weights are bf16-activation only (v1)"
  570. )
  571. if epilogue_activation not in (None, "relu2"):
  572. raise ValueError(
  573. "W4A16 GEMM epilogue activation currently supports only relu2"
  574. )
  575. if tile_n % 16 != 0 or tile_k % 16 != 0:
  576. raise ValueError("tile_n/tile_k must be multiples of 16")
  577. scale_group_size = _scale_group_size(scale_format)
  578. has_n_tile_tail = int(size_n) % int(tile_n) != 0
  579. has_k_tile_tail = int(size_k) % int(tile_k) != 0
  580. has_scale_k_tail = int(size_k) % scale_group_size != 0
  581. has_logical_tail = (
  582. has_n_tile_tail or has_k_tile_tail or has_scale_k_tail
  583. )
  584. if has_logical_tail:
  585. if weight_layout != "modelopt" or scale_format != "e8m0_k32":
  586. raise ValueError(
  587. "W4A16 logical tail tiles are only supported for native "
  588. "E8M0 K/32 weights"
  589. )
  590. if int(size_n) % 16 != 0:
  591. raise ValueError(
  592. "native E8M0 W4A16 tail tiles require size_n % 16 == 0"
  593. )
  594. if int(size_k) % 8 != 0:
  595. raise ValueError(
  596. "native E8M0 W4A16 tail tiles require size_k % 8 == 0"
  597. )
  598. if int(tile_k) % scale_group_size != 0:
  599. raise ValueError(
  600. "native E8M0 W4A16 tail tiles require tile_k multiples of 32"
  601. )
  602. else:
  603. if size_n % tile_n != 0:
  604. raise ValueError("size_n must be divisible by tile_n")
  605. if size_k % tile_k != 0:
  606. raise ValueError("size_k must be divisible by tile_k")
  607. if scale_format == "e8m0_k32" and (
  608. size_k % 32 != 0 or tile_k % 32 != 0
  609. ):
  610. raise ValueError(
  611. "E8M0 K/32 W4A16 scales require size_k/tile_k multiples of 32"
  612. )
  613. if scale_format == "e4m3_k32" and (
  614. size_k % 32 != 0 or tile_k % 32 != 0
  615. ):
  616. raise ValueError(
  617. "E4M3 K/32 W4A16 scales require size_k/tile_k multiples of 32"
  618. )
  619. if moe_block_size not in _ALLOWED_ROUTED_SIZES:
  620. raise ValueError(f"unsupported moe_block_size {moe_block_size}")
  621. if moe_block_size != 8 and moe_block_size % 16 != 0:
  622. raise ValueError("moe_block_size must be 8 or a multiple of 16")
  623. cta_threads = tile_n * tile_k // 64
  624. if cta_threads not in (128, 256):
  625. raise ValueError("W4A16 GEMM expects 128 or 256 CTA threads")
  626. self.size_m = int(size_m)
  627. self.size_n = int(size_n)
  628. self.size_k = int(size_k)
  629. self.num_experts = int(num_experts)
  630. self.top_k = int(top_k)
  631. self.mul_topk_weights = bool(mul_topk_weights)
  632. self.tile_n = int(tile_n)
  633. self.tile_k = int(tile_k)
  634. self.cta_n_blocks = int(tile_n // 16)
  635. self.cta_k_blocks = int(tile_k // 16)
  636. self.cta_threads = int(cta_threads)
  637. self.moe_block_size = int(moe_block_size)
  638. self.element_dtype = element_dtype
  639. self.is_fp16 = element_dtype == "fp16"
  640. self.epilogue_relu2 = epilogue_activation == "relu2"
  641. self.weight_layout = weight_layout
  642. self.weight_layout_nf3 = weight_layout == "nf3_2p1"
  643. self.scale_format = scale_format
  644. self.scale_format_e8m0_k32 = scale_format == "e8m0_k32"
  645. # k32 scale cadence (two K16 rows share one scale group). Shared by the
  646. # native E8M0 K/32 path and the NF3 e4m3-style K/32 path; the scale
  647. # DECODE stays keyed on scale_format_e8m0_k32 (e4m3_k32 uses the e4m3
  648. # decode arm, its 2**116 compensation lives in the global_scale tensor).
  649. self.scale_k32 = scale_format in ("e8m0_k32", "e4m3_k32")
  650. # NF3 codebook prmt pools (host-computed compile-time constants injected
  651. # into the device dequant). Only consumed by the nf3_2p1 path; cheap to
  652. # compute unconditionally.
  653. (
  654. self._nf3_cb0,
  655. self._nf3_cb1,
  656. self._nf3_cb2,
  657. self._nf3_cb3,
  658. ) = nf3_codebook_pools(_NF3_CODEBOOK)
  659. self.scale_group_size = int(scale_group_size)
  660. self.scale_k_groups = _covering_count(self.size_k, self.scale_group_size)
  661. self.n_tiles = _covering_count(self.size_n, self.tile_n)
  662. self.k_tiles = _covering_count(self.size_k, self.tile_k)
  663. self.covered_size_n = self.n_tiles * self.tile_n
  664. self.covered_size_k = self.k_tiles * self.tile_k
  665. self.has_n_tile_tail = bool(has_n_tile_tail)
  666. self.has_k_tile_tail = bool(has_k_tile_tail)
  667. self.has_scale_k_tail = bool(has_scale_k_tail)
  668. self.has_logical_tail = bool(has_logical_tail)
  669. self.scale_size_n = (
  670. _e8m0_logical_tail_scale_n(self.size_n)
  671. if self.scale_format_e8m0_k32 and self.has_n_tile_tail
  672. else self.size_n
  673. )
  674. self.scale_n_groups = self.scale_size_n // 16
  675. self.w13_layout = w13_layout
  676. self.source_n_rotation = int(source_n_rotation)
  677. self.single_token_route_fast_path = bool(single_token_route_fast_path)
  678. self.direct_topk_routes = bool(direct_topk_routes)
  679. self.fused_topk_sum = bool(fused_topk_sum)
  680. self.fused_sum_topk = int(fused_sum_topk)
  681. if self.fused_topk_sum and not self.direct_topk_routes:
  682. raise ValueError("fused_topk_sum requires direct_topk_routes")
  683. if self.fused_topk_sum and self.fused_sum_topk < 1:
  684. raise ValueError("fused_sum_topk must be >= 1")
  685. self.cta_m_blocks = int(_covering_count(moe_block_size, 16))
  686. self.uses_m_block_8 = moe_block_size == 8
  687. self.max_m_blocks = int(max_m_blocks)
  688. if torch.cuda.is_available():
  689. props = torch.cuda.get_device_properties(torch.cuda.current_device())
  690. self.sms = int(props.multi_processor_count)
  691. max_shared_mem = int(
  692. getattr(props, "shared_memory_per_block_optin", _DEFAULT_MAX_SHARED_MEM)
  693. )
  694. else:
  695. self.sms = 120
  696. max_shared_mem = _DEFAULT_MAX_SHARED_MEM
  697. self.blocks_per_sm = _determine_blocks_per_sm(
  698. problem_m=self.size_m,
  699. problem_n=self.covered_size_n,
  700. top_k=self.top_k,
  701. cta_threads=self.cta_threads,
  702. cta_m_blocks=self.cta_m_blocks,
  703. tile_n=self.tile_n,
  704. tile_k=self.tile_k,
  705. uses_m_block_8=self.uses_m_block_8,
  706. sms=self.sms,
  707. max_shared_mem=max_shared_mem,
  708. scale_format=scale_format,
  709. )
  710. # W4A16 shared-memory geometry, in int4 units unless noted.
  711. self.a_sh_stride = 16 * self.cta_k_blocks // 8
  712. self.a_sh_stage = self.a_sh_stride * (16 * self.cta_m_blocks)
  713. self.a_gl_rd_delta_o = 16 * self.cta_k_blocks // 8
  714. self.a_sh_wr_delta = self.a_sh_stride * (
  715. self.cta_threads // self.a_gl_rd_delta_o
  716. )
  717. self.a_sh_wr_iters = _covering_count(self.a_sh_stage, self.a_sh_wr_delta)
  718. self.a_sh_rd_delta_i = self.a_sh_stride * 16
  719. self.b_sh_stride = ((self.cta_n_blocks * 16) * 16 // _PACK_FACTOR) // 4
  720. self.b_thread_vecs = 1
  721. self.b_sh_stride_threads = self.b_sh_stride
  722. self.b_sh_stage = self.b_sh_stride * self.cta_k_blocks
  723. self.b_sh_wr_iters = self.b_sh_stage // self.cta_threads
  724. # NF3 ("nf3_2p1") stages 12-byte triples per 32-code unit instead of the
  725. # 16-byte int4 unit; the per-thread unit count is unchanged, only the
  726. # bytes-per-unit and the 16-byte cp.async chunk count differ.
  727. self.b_unit_bytes = 12 if self.weight_layout_nf3 else 16
  728. self.b_sh_stage_bytes = self.b_sh_stage * self.b_unit_bytes
  729. if self.weight_layout_nf3:
  730. if self.b_sh_stage_bytes % 16 != 0:
  731. raise ValueError(
  732. "nf3_2p1 B stage bytes must be a multiple of 16; "
  733. f"got {self.b_sh_stage_bytes}"
  734. )
  735. self.b_sh_chunks = self.b_sh_stage_bytes // 16
  736. self.b_sh_wr_iters_nf3 = _covering_count(
  737. self.b_sh_chunks, self.cta_threads
  738. )
  739. else:
  740. self.b_sh_chunks = self.b_sh_stage
  741. self.b_sh_wr_iters_nf3 = self.b_sh_wr_iters
  742. self.s_sh_stride = 16 * self.cta_n_blocks // 16
  743. self.s_tb_groups = (
  744. self.cta_k_blocks // 2 if self.scale_k32 else self.cta_k_blocks
  745. )
  746. self.s_sh_stage = self.s_tb_groups * self.s_sh_stride
  747. self.tb_n_warps = self.cta_n_blocks // 4
  748. sh_block_route_indices = self.moe_block_size // 4
  749. sh_rd_block_route_indices = self.moe_block_size // 4
  750. sh_block_topk_weights = self.moe_block_size // 2
  751. self.sh_valid_count_off = (
  752. sh_block_route_indices + sh_rd_block_route_indices + sh_block_topk_weights
  753. )
  754. self.sh_route_off = 0
  755. self.sh_rd_route_off = sh_block_route_indices
  756. self.sh_topk_off = sh_block_route_indices + sh_rd_block_route_indices
  757. sh_red_size = (2 * self.cta_n_blocks + 1) * 16 * self.cta_m_blocks
  758. # B region size in int4 (16-byte) units. NF3 units are 12 bytes, so the
  759. # region is 0.75x; round the total up to a 16-byte multiple so the
  760. # following SMEM regions keep their 16-byte alignment (exact for all
  761. # supported tiles since b_sh_stage is a multiple of 4).
  762. sh_b_size = _covering_count(_STAGES * self.b_sh_stage_bytes, 16)
  763. sh_size_min = min(sh_red_size, sh_b_size)
  764. sh_size_max = max(sh_red_size, sh_b_size)
  765. sh_bias_size = self.cta_n_blocks * 16 // 8
  766. sh_b_red_bias_size = max(sh_size_max, sh_size_min + sh_bias_size)
  767. self.sh_b_off = self.sh_valid_count_off
  768. self.sh_red_off = self.sh_valid_count_off
  769. self.sh_s_off = self.sh_valid_count_off + sh_b_red_bias_size
  770. self.sh_a_off = self.sh_s_off + _STAGES * self.s_sh_stage
  771. self.shared_int4 = self.sh_a_off + _STAGES * self.a_sh_stage
  772. self.shared_words = self.shared_int4 * 4
  773. @property
  774. def __cache_key__(self) -> tuple[object, ...]:
  775. return (
  776. self.size_n,
  777. self.size_k,
  778. self.covered_size_n,
  779. self.covered_size_k,
  780. self.scale_k_groups,
  781. self.has_logical_tail,
  782. self.num_experts,
  783. self.top_k,
  784. self.mul_topk_weights,
  785. self.tile_n,
  786. self.tile_k,
  787. self.cta_threads,
  788. self.moe_block_size,
  789. self.element_dtype,
  790. self.epilogue_relu2,
  791. self.weight_layout,
  792. self.scale_format,
  793. self.w13_layout,
  794. self.source_n_rotation,
  795. self.single_token_route_fast_path,
  796. self.direct_topk_routes,
  797. self.fused_topk_sum,
  798. self.fused_sum_topk,
  799. self.cta_m_blocks,
  800. self.uses_m_block_8,
  801. self.shared_words,
  802. # Launch bounds are part of the compiled kernel. Keep binaries
  803. # planned for different residency targets out of the same cache
  804. # entry even when their arithmetic geometry otherwise matches.
  805. self.blocks_per_sm,
  806. )
  807. @cute.jit
  808. def _activation_smem_permuted_offset(self, i: Int32) -> Int32:
  809. row = i // Int32(self.a_gl_rd_delta_o)
  810. return Int32(self.a_gl_rd_delta_o) * row + (
  811. (i - row * Int32(self.a_gl_rd_delta_o)) ^ (row & Int32(7))
  812. )
  813. @cute.jit
  814. def _int4_addr(self, smem_base: Int32, int4_off: Int32) -> Int32:
  815. return smem_base + int4_off * Int32(16)
  816. @cute.jit
  817. def _dequant_e2m1x4_to_elem2x2(self, packed: Uint32):
  818. if cutlass.const_expr(self.is_fp16):
  819. return packed_dequant_e2m1x4_to_half2x2(packed)
  820. return packed_dequant_e2m1x4_to_bfloat2x2(packed)
  821. @cute.jit
  822. def _dequant_e4m3x4_to_elem2x2(self, packed: Uint32):
  823. if cutlass.const_expr(self.is_fp16):
  824. return packed_dequant_e4m3x4_to_half2x2(packed)
  825. return packed_dequant_e4m3x4_to_bfloat2x2(packed)
  826. @cute.jit
  827. def _dequant_scale_x4_to_elem2x2(self, packed: Uint32):
  828. if cutlass.const_expr(self.scale_format_e8m0_k32):
  829. if cutlass.const_expr(self.is_fp16):
  830. return packed_dequant_e8m0x4_to_half2x2(packed)
  831. return packed_dequant_e8m0x4_to_bfloat2x2(packed)
  832. if cutlass.const_expr(self.is_fp16):
  833. return packed_dequant_e4m3x4_to_half2x2(packed)
  834. return packed_dequant_e4m3x4_to_bfloat2x2(packed)
  835. @cute.jit
  836. def _elem2_mul(self, a: Uint32, b: Uint32) -> Uint32:
  837. if cutlass.const_expr(self.is_fp16):
  838. return half2_mul(a, b)
  839. return bfloat2_mul(a, b)
  840. @cute.jit
  841. def _broadcast_f32_to_elem2(self, x: cutlass.Float32) -> Uint32:
  842. if cutlass.const_expr(self.is_fp16):
  843. return broadcast_f32_to_half2(x)
  844. return broadcast_f32_to_bfloat2(x)
  845. @cute.jit
  846. def _pack_f32x2_to_elem2(self, x0: cutlass.Float32, x1: cutlass.Float32) -> Uint32:
  847. if cutlass.const_expr(self.is_fp16):
  848. return pack_f32x2_to_f16x2(x0, x1)
  849. return pack_f32x2_to_bfloat2(x0, x1)
  850. @cute.jit
  851. def _elem2_to_f32x2(self, packed: Uint32):
  852. if cutlass.const_expr(self.is_fp16):
  853. return half2_to_float2_scaled(packed, cutlass.Float32(1.0))
  854. return bfloat2_to_float2_scaled(packed, cutlass.Float32(1.0))
  855. @cute.jit
  856. def _relu2_elem2(self, packed: Uint32) -> Uint32:
  857. x0, x1 = self._elem2_to_f32x2(packed)
  858. if x0 < cutlass.Float32(0.0):
  859. x0 = cutlass.Float32(0.0)
  860. if x1 < cutlass.Float32(0.0):
  861. x1 = cutlass.Float32(0.0)
  862. return self._pack_f32x2_to_elem2(x0 * x0, x1 * x1)
  863. @cute.jit
  864. def _st_shared_elem_from_f32(self, addr: Int32, val: cutlass.Float32):
  865. if cutlass.const_expr(self.is_fp16):
  866. st_shared_f16_from_f32(addr, val)
  867. else:
  868. st_shared_bf16_from_f32(addr, val)
  869. @cute.jit
  870. def _mma_m16n8k16_f32(
  871. self,
  872. d0: cutlass.Float32,
  873. d1: cutlass.Float32,
  874. d2: cutlass.Float32,
  875. d3: cutlass.Float32,
  876. a0: Uint32,
  877. a1: Uint32,
  878. a2: Uint32,
  879. a3: Uint32,
  880. b0: Uint32,
  881. b1: Uint32,
  882. ):
  883. if cutlass.const_expr(self.is_fp16):
  884. return f16_mma_m16n8k16_f32(d0, d1, d2, d3, a0, a1, a2, a3, b0, b1)
  885. return bf16_mma_m16n8k16_f32(d0, d1, d2, d3, a0, a1, a2, a3, b0, b1)
  886. @cute.jit
  887. def _mma_rhs_fragments_as_mma_a_m16n8k16_f32(
  888. self,
  889. d0: cutlass.Float32,
  890. d1: cutlass.Float32,
  891. d2: cutlass.Float32,
  892. d3: cutlass.Float32,
  893. b0_0: Uint32,
  894. b1_0: Uint32,
  895. b0_1: Uint32,
  896. b1_1: Uint32,
  897. a0: Uint32,
  898. a1: Uint32,
  899. ):
  900. if cutlass.const_expr(self.is_fp16):
  901. return f16_mma_rhs_fragments_as_mma_a_m16n8k16_f32(
  902. d0, d1, d2, d3, b0_0, b1_0, b0_1, b1_1, a0, a1
  903. )
  904. return bf16_mma_rhs_fragments_as_mma_a_m16n8k16_f32(
  905. d0, d1, d2, d3, b0_0, b1_0, b0_1, b1_1, a0, a1
  906. )
  907. @cute.jit
  908. def __call__(
  909. self,
  910. a_bf16_flat: cute.Tensor,
  911. b_i32_flat: cute.Tensor,
  912. c_bf16_flat: cute.Tensor,
  913. scales_i32_flat: cute.Tensor,
  914. global_scale: cute.Tensor,
  915. packed_route_indices: cute.Tensor,
  916. block_expert_ids: cute.Tensor,
  917. packed_route_count: cute.Tensor,
  918. topk_weights_flat: cute.Tensor,
  919. c_tmp_f32_flat: cute.Tensor,
  920. locks_i32_flat: cute.Tensor,
  921. active_m: cutlass.Int32,
  922. grid_x: cutlass.Int32,
  923. stream: cuda.CUstream,
  924. ):
  925. grid = (grid_x, 1, 1)
  926. self.kernel(
  927. a_bf16_flat,
  928. b_i32_flat,
  929. c_bf16_flat,
  930. scales_i32_flat,
  931. global_scale,
  932. packed_route_indices,
  933. block_expert_ids,
  934. packed_route_count,
  935. topk_weights_flat,
  936. c_tmp_f32_flat,
  937. locks_i32_flat,
  938. active_m,
  939. ).launch(
  940. grid=grid,
  941. block=[self.cta_threads, 1, 1],
  942. min_blocks_per_mp=self.blocks_per_sm,
  943. stream=stream,
  944. )
  945. @cute.kernel
  946. def kernel(
  947. self,
  948. a_bf16_flat: cute.Tensor,
  949. b_i32_flat: cute.Tensor,
  950. c_bf16_flat: cute.Tensor,
  951. scales_i32_flat: cute.Tensor,
  952. global_scale: cute.Tensor,
  953. packed_route_indices: cute.Tensor,
  954. block_expert_ids: cute.Tensor,
  955. packed_route_count: cute.Tensor,
  956. topk_weights_flat: cute.Tensor,
  957. c_tmp_f32_flat: cute.Tensor,
  958. locks_i32_flat: cute.Tensor,
  959. active_m: cutlass.Int32,
  960. ):
  961. tidx, _, _ = cute.arch.thread_idx()
  962. bidx, _, _ = cute.arch.block_idx()
  963. tid = Int32(tidx)
  964. cta = Int32(bidx)
  965. smem = cutlass.utils.SmemAllocator()
  966. @cute.struct
  967. class Storage:
  968. words: cute.struct.Align[
  969. cute.struct.MemRange[cutlass.Uint32, self.shared_words],
  970. 1024,
  971. ]
  972. storage = smem.allocate(Storage)
  973. smem_base = shared_ptr_to_u32(storage.words.data_ptr())
  974. grid_x, _, _ = cute.arch.grid_dim()
  975. self._run_persistent_gemm(
  976. a_bf16_flat,
  977. b_i32_flat,
  978. c_bf16_flat,
  979. scales_i32_flat,
  980. global_scale,
  981. packed_route_indices,
  982. block_expert_ids,
  983. packed_route_count,
  984. topk_weights_flat,
  985. c_tmp_f32_flat,
  986. locks_i32_flat,
  987. smem_base,
  988. tid,
  989. cta,
  990. Int32(grid_x),
  991. Int32(active_m),
  992. )
  993. @cute.jit
  994. def _run_persistent_gemm(
  995. self,
  996. a_bf16_flat: cute.Tensor,
  997. b_i32_flat: cute.Tensor,
  998. c_bf16_flat: cute.Tensor,
  999. scales_i32_flat: cute.Tensor,
  1000. global_scale: cute.Tensor,
  1001. packed_route_indices: cute.Tensor,
  1002. block_expert_ids: cute.Tensor,
  1003. packed_route_count: cute.Tensor,
  1004. topk_weights_flat: cute.Tensor,
  1005. c_tmp_f32_flat: cute.Tensor,
  1006. locks_i32_flat: cute.Tensor,
  1007. smem_base: Int32,
  1008. tid: Int32,
  1009. cta: Int32,
  1010. grid_x: Int32,
  1011. active_size_m: Int32,
  1012. ):
  1013. n_tiles = Int32(self.n_tiles)
  1014. route_blocks = active_size_m * Int32(self.top_k)
  1015. if cutlass.const_expr(not self.direct_topk_routes):
  1016. route_blocks = packed_route_count[Int32(0)].to(Int32) // Int32(
  1017. self.moe_block_size
  1018. )
  1019. k_tiles = Int32(self.k_tiles)
  1020. global_mn_tiles = route_blocks * n_tiles
  1021. tail_mn_tiles = global_mn_tiles
  1022. full_grid_mn_iters = Int32(0)
  1023. force_one_tile_per_cta = Int32(0)
  1024. if cutlass.const_expr(self.uses_m_block_8):
  1025. # TC-decode small-M: when every mn-tile fits inside the launched grid
  1026. # (FC1 has only route_blocks*n_tiles tiles, far fewer than grid_x),
  1027. # the default tail path fans each mn-tile across multiple CTAs along
  1028. # K and pays a lock-serialized cross-CTA split-K finalize plus the
  1029. # reduction-turn handshake. Instead give the first global_mn_tiles
  1030. # CTAs exactly one full mn-tile (all k_tiles, reduce_slice_count==1,
  1031. # no finalize, no lock traffic) and idle the rest. grid_x and the
  1032. # grid-barrier participant count are unchanged (so FC2 coverage is
  1033. # untouched); only FC1's intra-GEMM work partition changes. Numerically
  1034. # identical: a single CTA computes the whole K-reduction per tile.
  1035. if global_mn_tiles <= grid_x:
  1036. force_one_tile_per_cta = Int32(1)
  1037. if force_one_tile_per_cta != Int32(0):
  1038. tail_mn_tiles = Int32(0)
  1039. full_grid_mn_iters = Int32(1)
  1040. elif global_mn_tiles > grid_x:
  1041. tail_mn_tiles = global_mn_tiles - (global_mn_tiles // grid_x) * grid_x
  1042. if tail_mn_tiles * Int32(3) <= grid_x:
  1043. tail_mn_tiles += grid_x
  1044. full_grid_mn_iters = (global_mn_tiles - tail_mn_tiles) // grid_x
  1045. iters = (k_tiles * tail_mn_tiles + grid_x - Int32(1)) // grid_x
  1046. lock_slot = Int32(0)
  1047. if tail_mn_tiles >= grid_x:
  1048. lock_slot = cta
  1049. else:
  1050. lock_slot = (iters * cta) // k_tiles - Int32(1)
  1051. in_tail_region = Int32(0)
  1052. has_work = Int32(1)
  1053. work_mn_tile = cta
  1054. reduce_k_tile = Int32(0)
  1055. route_block_idx = Int32(0)
  1056. output_n_tile = Int32(0)
  1057. if iters == Int32(0) and full_grid_mn_iters == Int32(0):
  1058. has_work = Int32(0)
  1059. while has_work != Int32(0):
  1060. reduce_tile_count = Int32(0)
  1061. reduce_slice_count = Int32(1)
  1062. reduce_slice_idx = Int32(0)
  1063. if in_tail_region == Int32(0) and full_grid_mn_iters > Int32(0):
  1064. route_block_idx = work_mn_tile // n_tiles
  1065. output_n_tile = work_mn_tile - route_block_idx * n_tiles
  1066. reduce_k_tile = Int32(0)
  1067. reduce_tile_count = k_tiles
  1068. full_grid_mn_iters -= Int32(1)
  1069. else:
  1070. if in_tail_region == Int32(0):
  1071. in_tail_region = Int32(1)
  1072. tail_mn_base = global_mn_tiles - tail_mn_tiles
  1073. cta_iter_start = iters * cta
  1074. work_mn_tile = cta_iter_start // k_tiles
  1075. reduce_k_tile = cta_iter_start - work_mn_tile * k_tiles
  1076. global_mn_tile = work_mn_tile + tail_mn_base
  1077. route_block_idx = global_mn_tile // n_tiles
  1078. output_n_tile = global_mn_tile - route_block_idx * n_tiles
  1079. if work_mn_tile < tail_mn_tiles and iters > Int32(0):
  1080. reduce_tile_count = iters * (cta + Int32(1)) - (
  1081. k_tiles * work_mn_tile + reduce_k_tile
  1082. )
  1083. if reduce_tile_count < Int32(0):
  1084. reduce_tile_count = Int32(0)
  1085. if reduce_k_tile + reduce_tile_count > k_tiles:
  1086. reduce_tile_count = k_tiles - reduce_k_tile
  1087. if reduce_tile_count > Int32(0):
  1088. first_reduce_boundary = iters * (
  1089. (k_tiles * work_mn_tile + iters - Int32(1)) // iters
  1090. )
  1091. if first_reduce_boundary <= k_tiles * (work_mn_tile + Int32(1)):
  1092. reduce_boundary_offset = (
  1093. first_reduce_boundary - k_tiles * work_mn_tile
  1094. )
  1095. reduce_slice_count = (
  1096. k_tiles - reduce_boundary_offset + iters - Int32(1)
  1097. ) // iters
  1098. if reduce_boundary_offset > Int32(0):
  1099. reduce_slice_count += Int32(1)
  1100. reduce_boundary_delta = iters * cta - first_reduce_boundary
  1101. if reduce_boundary_delta < Int32(0):
  1102. reduce_slice_idx = reduce_slice_count - Int32(1)
  1103. else:
  1104. if reduce_boundary_offset == Int32(
  1105. 0
  1106. ) and reduce_boundary_delta == Int32(0):
  1107. reduce_slice_idx = reduce_slice_count - Int32(1)
  1108. else:
  1109. reduce_slice_idx = (
  1110. reduce_slice_count
  1111. - Int32(1)
  1112. - reduce_boundary_delta // iters
  1113. )
  1114. if reduce_boundary_offset > Int32(0):
  1115. reduce_slice_idx -= Int32(1)
  1116. if tail_mn_tiles >= grid_x:
  1117. if reduce_slice_count > Int32(
  1118. 1
  1119. ) and reduce_slice_idx == reduce_slice_count - Int32(1):
  1120. lock_slot += Int32(1)
  1121. else:
  1122. lock_slot += Int32(1)
  1123. else:
  1124. has_work = Int32(0)
  1125. else:
  1126. has_work = Int32(0)
  1127. if (
  1128. has_work != Int32(0)
  1129. and reduce_tile_count > Int32(0)
  1130. and route_block_idx < route_blocks
  1131. ):
  1132. if cutlass.const_expr(self.direct_topk_routes):
  1133. expert_idx = packed_route_indices[route_block_idx].to(Int32)
  1134. else:
  1135. expert_idx = block_expert_ids[route_block_idx].to(Int32)
  1136. if expert_idx >= Int32(0):
  1137. self._run_tile(
  1138. a_bf16_flat,
  1139. b_i32_flat,
  1140. c_bf16_flat,
  1141. scales_i32_flat,
  1142. global_scale,
  1143. packed_route_indices,
  1144. topk_weights_flat,
  1145. c_tmp_f32_flat,
  1146. locks_i32_flat,
  1147. smem_base,
  1148. tid,
  1149. route_block_idx,
  1150. expert_idx,
  1151. output_n_tile,
  1152. reduce_k_tile,
  1153. reduce_tile_count,
  1154. reduce_slice_count,
  1155. reduce_slice_idx,
  1156. lock_slot,
  1157. active_size_m,
  1158. )
  1159. if has_work != Int32(0):
  1160. if in_tail_region == Int32(0):
  1161. work_mn_tile += grid_x
  1162. else:
  1163. reduce_k_tile = Int32(0)
  1164. work_mn_tile += Int32(1)
  1165. output_n_tile += Int32(1)
  1166. if output_n_tile == n_tiles:
  1167. output_n_tile = Int32(0)
  1168. route_block_idx += Int32(1)
  1169. @cute.jit
  1170. def _read_moe_block_data(
  1171. self,
  1172. packed_route_indices: cute.Tensor,
  1173. topk_weights_flat: cute.Tensor,
  1174. smem_base: Int32,
  1175. tid: Int32,
  1176. route_block_idx: Int32,
  1177. global_scale_f32: cutlass.Float32,
  1178. active_size_m: Int32,
  1179. ) -> Int32:
  1180. if cutlass.const_expr(self.direct_topk_routes):
  1181. if tid == Int32(0):
  1182. idx = route_block_idx
  1183. st_shared_i32(smem_base + Int32(self.sh_route_off * 16), idx)
  1184. rd_row = idx // Int32(self.top_k)
  1185. st_shared_i32(smem_base + Int32(self.sh_rd_route_off * 16), rd_row)
  1186. if cutlass.const_expr(self.mul_topk_weights):
  1187. topk = topk_weights_flat[idx].to(cutlass.Float32) * global_scale_f32
  1188. st_shared_u32(
  1189. smem_base + Int32(self.sh_topk_off * 16),
  1190. self._broadcast_f32_to_elem2(topk),
  1191. )
  1192. cute.arch.sync_threads()
  1193. return Int32(1)
  1194. if cutlass.const_expr(self.single_token_route_fast_path):
  1195. if tid == Int32(0):
  1196. idx = packed_route_indices[
  1197. route_block_idx * Int32(self.moe_block_size)
  1198. ].to(Int32)
  1199. st_shared_i32(smem_base + Int32(self.sh_route_off * 16), idx)
  1200. rd_row = idx // Int32(self.top_k)
  1201. st_shared_i32(smem_base + Int32(self.sh_rd_route_off * 16), rd_row)
  1202. if cutlass.const_expr(self.mul_topk_weights):
  1203. safe_idx = idx
  1204. if idx >= active_size_m * Int32(self.top_k):
  1205. safe_idx = Int32(0)
  1206. topk = (
  1207. topk_weights_flat[safe_idx].to(cutlass.Float32)
  1208. * global_scale_f32
  1209. )
  1210. st_shared_u32(
  1211. smem_base + Int32(self.sh_topk_off * 16),
  1212. self._broadcast_f32_to_elem2(topk),
  1213. )
  1214. cute.arch.sync_threads()
  1215. return Int32(1)
  1216. route_indices_int4_addr = self._int4_addr(
  1217. smem_base, Int32(self.sh_route_off) + tid
  1218. )
  1219. route_indices_gmem = get_ptr_as_int64(
  1220. packed_route_indices,
  1221. route_block_idx * Int32(self.moe_block_size) + tid * Int32(4),
  1222. )
  1223. cp_async4_shared_global_pred(
  1224. route_indices_int4_addr,
  1225. route_indices_gmem,
  1226. (tid < Int32(self.moe_block_size // 4)).to(Int32),
  1227. )
  1228. cute.arch.cp_async_commit_group()
  1229. cute.arch.cp_async_wait_group(0)
  1230. cute.arch.sync_threads()
  1231. if tid >= Int32(self.cta_threads - 32):
  1232. size_per_thread = _covering_count(self.moe_block_size, 32)
  1233. lane = tid - Int32(self.cta_threads - 32)
  1234. local_count = Int32(0)
  1235. for i in cutlass.range_constexpr(size_per_thread):
  1236. j = lane * Int32(size_per_thread) + Int32(i)
  1237. if j < Int32(self.moe_block_size):
  1238. idx = ld_shared_i32_relaxed(
  1239. smem_base + Int32(self.sh_route_off * 16) + j * Int32(4)
  1240. )
  1241. if idx < active_size_m * Int32(self.top_k):
  1242. local_count += Int32(1)
  1243. valid = cute.arch.warp_redux_sync(local_count, "add")
  1244. if lane == Int32(0):
  1245. st_shared_i32(smem_base + Int32(self.sh_valid_count_off * 16), valid)
  1246. if tid < Int32(self.moe_block_size):
  1247. idx = ld_shared_i32_relaxed(
  1248. smem_base + Int32(self.sh_route_off * 16) + tid * Int32(4)
  1249. )
  1250. rd_row = idx // Int32(self.top_k)
  1251. st_shared_i32(
  1252. smem_base + Int32(self.sh_rd_route_off * 16) + tid * Int32(4),
  1253. rd_row,
  1254. )
  1255. if cutlass.const_expr(self.mul_topk_weights):
  1256. safe_idx = idx
  1257. if idx >= active_size_m * Int32(self.top_k):
  1258. safe_idx = Int32(0)
  1259. topk = (
  1260. topk_weights_flat[safe_idx].to(cutlass.Float32) * global_scale_f32
  1261. )
  1262. packed_topk = self._broadcast_f32_to_elem2(topk)
  1263. # top-k weights are cached as packed element pairs.
  1264. topk_word_addr = (
  1265. smem_base + Int32(self.sh_topk_off * 16) + tid * Int32(4)
  1266. )
  1267. st_shared_u32(topk_word_addr, packed_topk)
  1268. cute.arch.sync_threads()
  1269. valid_count = ld_shared_i32_relaxed(
  1270. smem_base + Int32(self.sh_valid_count_off * 16)
  1271. )
  1272. cute.arch.sync_threads()
  1273. return valid_count
  1274. @cute.jit
  1275. def _run_tile(
  1276. self,
  1277. a_bf16_flat: cute.Tensor,
  1278. b_i32_flat: cute.Tensor,
  1279. c_bf16_flat: cute.Tensor,
  1280. scales_i32_flat: cute.Tensor,
  1281. global_scale: cute.Tensor,
  1282. packed_route_indices: cute.Tensor,
  1283. topk_weights_flat: cute.Tensor,
  1284. c_tmp_f32_flat: cute.Tensor,
  1285. locks_i32_flat: cute.Tensor,
  1286. smem_base: Int32,
  1287. tid: Int32,
  1288. route_block_idx: Int32,
  1289. expert_idx: Int32,
  1290. output_n_tile: Int32,
  1291. reduce_k_tile: Int32,
  1292. reduce_tile_count: Int32,
  1293. reduce_slice_count: Int32,
  1294. reduce_slice_idx: Int32,
  1295. lock_slot: Int32,
  1296. active_size_m: Int32,
  1297. ):
  1298. if cutlass.const_expr(self.uses_m_block_8):
  1299. self._run_tile_m8(
  1300. a_bf16_flat,
  1301. b_i32_flat,
  1302. c_bf16_flat,
  1303. scales_i32_flat,
  1304. global_scale,
  1305. packed_route_indices,
  1306. topk_weights_flat,
  1307. c_tmp_f32_flat,
  1308. locks_i32_flat,
  1309. smem_base,
  1310. tid,
  1311. route_block_idx,
  1312. expert_idx,
  1313. output_n_tile,
  1314. reduce_k_tile,
  1315. reduce_tile_count,
  1316. reduce_slice_count,
  1317. reduce_slice_idx,
  1318. lock_slot,
  1319. active_size_m,
  1320. )
  1321. else:
  1322. self._run_tile_large_m(
  1323. a_bf16_flat,
  1324. b_i32_flat,
  1325. c_bf16_flat,
  1326. scales_i32_flat,
  1327. global_scale,
  1328. packed_route_indices,
  1329. topk_weights_flat,
  1330. c_tmp_f32_flat,
  1331. locks_i32_flat,
  1332. smem_base,
  1333. tid,
  1334. route_block_idx,
  1335. expert_idx,
  1336. output_n_tile,
  1337. reduce_k_tile,
  1338. reduce_tile_count,
  1339. reduce_slice_count,
  1340. reduce_slice_idx,
  1341. lock_slot,
  1342. active_size_m,
  1343. )
  1344. @cute.jit
  1345. def _tile_common_prologue(
  1346. self,
  1347. global_scale: cute.Tensor,
  1348. packed_route_indices: cute.Tensor,
  1349. topk_weights_flat: cute.Tensor,
  1350. smem_base: Int32,
  1351. tid: Int32,
  1352. route_block_idx: Int32,
  1353. expert_idx: Int32,
  1354. output_n_tile: Int32,
  1355. active_size_m: Int32,
  1356. ):
  1357. global_scale_f32 = global_scale[expert_idx].to(cutlass.Float32)
  1358. if cutlass.const_expr(self.scale_format_e8m0_k32):
  1359. if cutlass.const_expr(self.is_fp16):
  1360. global_scale_f32 *= cutlass.Float32(_E8M0_K32_FP16_GLOBAL_COMPENSATION)
  1361. else:
  1362. global_scale_f32 *= cutlass.Float32(_E8M0_K32_BF16_GLOBAL_COMPENSATION)
  1363. block_valid_rows = self._read_moe_block_data(
  1364. packed_route_indices,
  1365. topk_weights_flat,
  1366. smem_base,
  1367. tid,
  1368. route_block_idx,
  1369. global_scale_f32,
  1370. active_size_m,
  1371. )
  1372. (
  1373. a_gl_stride,
  1374. b_gl_stride,
  1375. s_gl_stride,
  1376. scales_expert_off,
  1377. b_gl_rd_base,
  1378. a_gl_rd_row,
  1379. a_gl_rd_col0,
  1380. a_sh_wr,
  1381. a_rows_per_iter,
  1382. b_sh_rd,
  1383. s_sh_rd,
  1384. ) = self._tile_stream_offsets(tid, expert_idx, output_n_tile)
  1385. return (
  1386. global_scale_f32,
  1387. block_valid_rows,
  1388. a_gl_stride,
  1389. b_gl_stride,
  1390. s_gl_stride,
  1391. scales_expert_off,
  1392. b_gl_rd_base,
  1393. a_gl_rd_row,
  1394. a_gl_rd_col0,
  1395. a_sh_wr,
  1396. a_rows_per_iter,
  1397. b_sh_rd,
  1398. s_sh_rd,
  1399. )
  1400. @cute.jit
  1401. def _tile_stream_offsets(self, tid: Int32, expert_idx: Int32, output_n_tile: Int32):
  1402. a_gl_stride = Int32(self.size_k // 8)
  1403. b_gl_stride = Int32(16 * self.size_n // (_PACK_FACTOR * 4))
  1404. s_gl_stride = Int32(self.scale_n_groups)
  1405. if cutlass.const_expr(self.scale_k32):
  1406. if cutlass.const_expr(
  1407. self.scale_format_e8m0_k32 and self.has_logical_tail
  1408. ):
  1409. scales_expert_stride = Int32(self.scale_k_groups * self.scale_n_groups)
  1410. else:
  1411. scales_expert_stride = Int32((self.size_n * self.size_k) // (32 * 16))
  1412. else:
  1413. scales_expert_stride = Int32((self.size_n * self.size_k) // (16 * 16))
  1414. b_expert_off = (
  1415. Int32((self.size_n * self.size_k) // (_PACK_FACTOR * 4)) * expert_idx
  1416. )
  1417. scales_expert_off = scales_expert_stride * expert_idx
  1418. a_gl_rd_row = tid // Int32(self.a_gl_rd_delta_o)
  1419. a_gl_rd_col0 = tid - a_gl_rd_row * Int32(self.a_gl_rd_delta_o)
  1420. a_sh_wr = Int32(self.a_sh_stride) * (tid // Int32(self.a_gl_rd_delta_o)) + (
  1421. tid - (tid // Int32(self.a_gl_rd_delta_o)) * Int32(self.a_gl_rd_delta_o)
  1422. )
  1423. a_rows_per_iter = Int32(self.cta_threads // self.a_gl_rd_delta_o)
  1424. if cutlass.const_expr(self.cta_threads <= self.b_sh_stride):
  1425. b_gl_rd_base = tid
  1426. else:
  1427. b_gl_rd_base = b_gl_stride * (tid // Int32(self.b_sh_stride)) + (
  1428. tid % Int32(self.b_sh_stride)
  1429. )
  1430. b_gl_rd_base += b_expert_off + Int32(self.b_sh_stride) * output_n_tile
  1431. b_sh_rd = tid
  1432. b_sh_rd += (b_sh_rd // Int32(self.b_sh_stride)) * Int32(
  1433. self.b_sh_stride * (self.b_sh_wr_iters - 1)
  1434. )
  1435. s_sh_rd = Int32(8) * ((tid // Int32(32)) % Int32(self.tb_n_warps)) + (
  1436. tid & Int32(31)
  1437. ) // Int32(4)
  1438. return (
  1439. a_gl_stride,
  1440. b_gl_stride,
  1441. s_gl_stride,
  1442. scales_expert_off,
  1443. b_gl_rd_base,
  1444. a_gl_rd_row,
  1445. a_gl_rd_col0,
  1446. a_sh_wr,
  1447. a_rows_per_iter,
  1448. b_sh_rd,
  1449. s_sh_rd,
  1450. )
  1451. @cute.jit
  1452. def _a_shared_read_offset(self, tid: Int32, lanes_per_row: cutlass.Constexpr[int]):
  1453. a_sh_rd = Int32(self.a_sh_stride) * (
  1454. (tid & Int32(31)) % Int32(lanes_per_row)
  1455. ) + (tid & Int32(31)) // Int32(lanes_per_row)
  1456. a_sh_rd += (
  1457. Int32(2)
  1458. * ((tid // Int32(32)) // Int32(self.tb_n_warps))
  1459. * Int32(self.b_sh_wr_iters)
  1460. )
  1461. return a_sh_rd
  1462. @cute.jit
  1463. def _run_tile_m8(
  1464. self,
  1465. a_bf16_flat: cute.Tensor,
  1466. b_i32_flat: cute.Tensor,
  1467. c_bf16_flat: cute.Tensor,
  1468. scales_i32_flat: cute.Tensor,
  1469. global_scale: cute.Tensor,
  1470. packed_route_indices: cute.Tensor,
  1471. topk_weights_flat: cute.Tensor,
  1472. c_tmp_f32_flat: cute.Tensor,
  1473. locks_i32_flat: cute.Tensor,
  1474. smem_base: Int32,
  1475. tid: Int32,
  1476. route_block_idx: Int32,
  1477. expert_idx: Int32,
  1478. output_n_tile: Int32,
  1479. reduce_k_tile: Int32,
  1480. reduce_tile_count: Int32,
  1481. reduce_slice_count: Int32,
  1482. reduce_slice_idx: Int32,
  1483. lock_slot: Int32,
  1484. active_size_m: Int32,
  1485. ):
  1486. (
  1487. global_scale_f32,
  1488. block_valid_rows,
  1489. a_gl_stride,
  1490. b_gl_stride,
  1491. s_gl_stride,
  1492. scales_expert_off,
  1493. b_gl_rd_base,
  1494. a_gl_rd_row,
  1495. a_gl_rd_col0,
  1496. a_sh_wr,
  1497. a_rows_per_iter,
  1498. b_sh_rd,
  1499. s_sh_rd,
  1500. ) = self._tile_common_prologue(
  1501. global_scale,
  1502. packed_route_indices,
  1503. topk_weights_flat,
  1504. smem_base,
  1505. tid,
  1506. route_block_idx,
  1507. expert_idx,
  1508. output_n_tile,
  1509. active_size_m,
  1510. )
  1511. a_sh_rd = self._a_shared_read_offset(tid, 8)
  1512. acc = cute.make_rmem_tensor((4, 4), cutlass.Float32)
  1513. acc.fill(0.0)
  1514. k_tiles = reduce_tile_count
  1515. self._prefetch_initial_tiles(
  1516. a_bf16_flat,
  1517. b_i32_flat,
  1518. scales_i32_flat,
  1519. smem_base,
  1520. tid,
  1521. k_tiles,
  1522. reduce_k_tile,
  1523. block_valid_rows,
  1524. a_gl_stride,
  1525. b_gl_stride,
  1526. s_gl_stride,
  1527. scales_expert_off,
  1528. b_gl_rd_base,
  1529. a_gl_rd_row,
  1530. a_gl_rd_col0,
  1531. a_sh_wr,
  1532. a_rows_per_iter,
  1533. output_n_tile,
  1534. expert_idx,
  1535. )
  1536. b_scale_cur = cute.make_rmem_tensor((2, 4), Uint32)
  1537. b_scale_next = cute.make_rmem_tensor((2, 4), Uint32)
  1538. self._load_b_scale_register_bundle(
  1539. b_scale_cur,
  1540. smem_base,
  1541. tid,
  1542. b_sh_rd,
  1543. s_sh_rd,
  1544. Int32(0),
  1545. Int32(0),
  1546. )
  1547. a_regs_cur = cute.make_rmem_tensor((2,), Uint32)
  1548. a_regs_next = cute.make_rmem_tensor((2,), Uint32)
  1549. self._load_a_register_bundle(
  1550. a_regs_cur,
  1551. smem_base,
  1552. a_sh_rd,
  1553. Int32(0),
  1554. Int32(0),
  1555. True,
  1556. )
  1557. self._run_mma_pipeline(
  1558. a_bf16_flat,
  1559. b_i32_flat,
  1560. scales_i32_flat,
  1561. smem_base,
  1562. tid,
  1563. acc,
  1564. b_scale_cur,
  1565. b_scale_next,
  1566. a_regs_cur,
  1567. a_regs_next,
  1568. b_sh_rd,
  1569. s_sh_rd,
  1570. a_sh_rd,
  1571. k_tiles,
  1572. reduce_k_tile,
  1573. block_valid_rows,
  1574. a_gl_stride,
  1575. b_gl_stride,
  1576. s_gl_stride,
  1577. scales_expert_off,
  1578. b_gl_rd_base,
  1579. a_gl_rd_row,
  1580. a_gl_rd_col0,
  1581. a_sh_wr,
  1582. a_rows_per_iter,
  1583. output_n_tile,
  1584. expert_idx,
  1585. True,
  1586. )
  1587. self._finish_tile(
  1588. acc,
  1589. c_bf16_flat,
  1590. c_tmp_f32_flat,
  1591. locks_i32_flat,
  1592. smem_base,
  1593. tid,
  1594. output_n_tile,
  1595. block_valid_rows,
  1596. global_scale_f32,
  1597. reduce_slice_count,
  1598. reduce_slice_idx,
  1599. lock_slot,
  1600. True,
  1601. )
  1602. @cute.jit
  1603. def _run_tile_large_m(
  1604. self,
  1605. a_bf16_flat: cute.Tensor,
  1606. b_i32_flat: cute.Tensor,
  1607. c_bf16_flat: cute.Tensor,
  1608. scales_i32_flat: cute.Tensor,
  1609. global_scale: cute.Tensor,
  1610. packed_route_indices: cute.Tensor,
  1611. topk_weights_flat: cute.Tensor,
  1612. c_tmp_f32_flat: cute.Tensor,
  1613. locks_i32_flat: cute.Tensor,
  1614. smem_base: Int32,
  1615. tid: Int32,
  1616. route_block_idx: Int32,
  1617. expert_idx: Int32,
  1618. output_n_tile: Int32,
  1619. reduce_k_tile: Int32,
  1620. reduce_tile_count: Int32,
  1621. reduce_slice_count: Int32,
  1622. reduce_slice_idx: Int32,
  1623. lock_slot: Int32,
  1624. active_size_m: Int32,
  1625. ):
  1626. (
  1627. global_scale_f32,
  1628. block_valid_rows,
  1629. a_gl_stride,
  1630. b_gl_stride,
  1631. s_gl_stride,
  1632. scales_expert_off,
  1633. b_gl_rd_base,
  1634. a_gl_rd_row,
  1635. a_gl_rd_col0,
  1636. a_sh_wr,
  1637. a_rows_per_iter,
  1638. b_sh_rd,
  1639. s_sh_rd,
  1640. ) = self._tile_common_prologue(
  1641. global_scale,
  1642. packed_route_indices,
  1643. topk_weights_flat,
  1644. smem_base,
  1645. tid,
  1646. route_block_idx,
  1647. expert_idx,
  1648. output_n_tile,
  1649. active_size_m,
  1650. )
  1651. a_sh_rd = self._a_shared_read_offset(tid, 16)
  1652. acc = cute.make_rmem_tensor((self.cta_m_blocks, 4, 2, 4), cutlass.Float32)
  1653. acc.fill(0.0)
  1654. k_tiles = reduce_tile_count
  1655. self._prefetch_initial_tiles(
  1656. a_bf16_flat,
  1657. b_i32_flat,
  1658. scales_i32_flat,
  1659. smem_base,
  1660. tid,
  1661. k_tiles,
  1662. reduce_k_tile,
  1663. block_valid_rows,
  1664. a_gl_stride,
  1665. b_gl_stride,
  1666. s_gl_stride,
  1667. scales_expert_off,
  1668. b_gl_rd_base,
  1669. a_gl_rd_row,
  1670. a_gl_rd_col0,
  1671. a_sh_wr,
  1672. a_rows_per_iter,
  1673. output_n_tile,
  1674. expert_idx,
  1675. )
  1676. b_scale_cur = cute.make_rmem_tensor((2, 4), Uint32)
  1677. b_scale_next = cute.make_rmem_tensor((2, 4), Uint32)
  1678. self._load_b_scale_register_bundle(
  1679. b_scale_cur,
  1680. smem_base,
  1681. tid,
  1682. b_sh_rd,
  1683. s_sh_rd,
  1684. Int32(0),
  1685. Int32(0),
  1686. )
  1687. a_regs = cute.make_rmem_tensor((self.cta_m_blocks, 4), Uint32)
  1688. a_regs_next = cute.make_rmem_tensor((self.cta_m_blocks, 4), Uint32)
  1689. self._load_a_register_bundle(
  1690. a_regs,
  1691. smem_base,
  1692. a_sh_rd,
  1693. Int32(0),
  1694. Int32(0),
  1695. False,
  1696. )
  1697. self._run_mma_pipeline(
  1698. a_bf16_flat,
  1699. b_i32_flat,
  1700. scales_i32_flat,
  1701. smem_base,
  1702. tid,
  1703. acc,
  1704. b_scale_cur,
  1705. b_scale_next,
  1706. a_regs,
  1707. a_regs_next,
  1708. b_sh_rd,
  1709. s_sh_rd,
  1710. a_sh_rd,
  1711. k_tiles,
  1712. reduce_k_tile,
  1713. block_valid_rows,
  1714. a_gl_stride,
  1715. b_gl_stride,
  1716. s_gl_stride,
  1717. scales_expert_off,
  1718. b_gl_rd_base,
  1719. a_gl_rd_row,
  1720. a_gl_rd_col0,
  1721. a_sh_wr,
  1722. a_rows_per_iter,
  1723. output_n_tile,
  1724. expert_idx,
  1725. False,
  1726. )
  1727. self._finish_tile(
  1728. acc,
  1729. c_bf16_flat,
  1730. c_tmp_f32_flat,
  1731. locks_i32_flat,
  1732. smem_base,
  1733. tid,
  1734. output_n_tile,
  1735. block_valid_rows,
  1736. global_scale_f32,
  1737. reduce_slice_count,
  1738. reduce_slice_idx,
  1739. lock_slot,
  1740. False,
  1741. )
  1742. @cute.jit
  1743. def _run_mma_pipeline(
  1744. self,
  1745. a_bf16_flat: cute.Tensor,
  1746. b_i32_flat: cute.Tensor,
  1747. scales_i32_flat: cute.Tensor,
  1748. smem_base: Int32,
  1749. tid: Int32,
  1750. acc: cute.Tensor,
  1751. b_scale_cur: cute.Tensor,
  1752. b_scale_next: cute.Tensor,
  1753. a_regs_cur: cute.Tensor,
  1754. a_regs_next: cute.Tensor,
  1755. b_sh_rd: Int32,
  1756. s_sh_rd: Int32,
  1757. a_sh_rd: Int32,
  1758. k_tiles: Int32,
  1759. reduce_k_tile: Int32,
  1760. block_valid_rows: Int32,
  1761. a_gl_stride: Int32,
  1762. b_gl_stride: Int32,
  1763. s_gl_stride: Int32,
  1764. scales_expert_off: Int32,
  1765. b_gl_rd_base: Int32,
  1766. a_gl_rd_row: Int32,
  1767. a_gl_rd_col0: Int32,
  1768. a_sh_wr: Int32,
  1769. a_rows_per_iter: Int32,
  1770. output_n_tile: Int32,
  1771. expert_idx: Int32,
  1772. uses_m_block_8: cutlass.Constexpr[bool],
  1773. ):
  1774. b_frag = cute.make_rmem_tensor((2, 2), Uint32)
  1775. tile_idx = Int32(0)
  1776. while tile_idx < k_tiles:
  1777. for pipe in cutlass.range_constexpr(_STAGES):
  1778. if tile_idx < k_tiles:
  1779. for kk in cutlass.range_constexpr(self.b_sh_wr_iters):
  1780. self._load_next_fragment_bundle(
  1781. b_scale_next,
  1782. a_regs_next,
  1783. smem_base,
  1784. tid,
  1785. b_sh_rd,
  1786. s_sh_rd,
  1787. a_sh_rd,
  1788. pipe,
  1789. kk,
  1790. tile_idx,
  1791. k_tiles,
  1792. uses_m_block_8,
  1793. )
  1794. self._prefetch_pipeline_step(
  1795. a_bf16_flat,
  1796. b_i32_flat,
  1797. scales_i32_flat,
  1798. smem_base,
  1799. tid,
  1800. pipe,
  1801. kk,
  1802. tile_idx,
  1803. k_tiles,
  1804. reduce_k_tile,
  1805. block_valid_rows,
  1806. a_gl_stride,
  1807. b_gl_stride,
  1808. s_gl_stride,
  1809. scales_expert_off,
  1810. b_gl_rd_base,
  1811. a_gl_rd_row,
  1812. a_gl_rd_col0,
  1813. a_sh_wr,
  1814. a_rows_per_iter,
  1815. output_n_tile,
  1816. expert_idx,
  1817. )
  1818. for jj in cutlass.range_constexpr(4):
  1819. if cutlass.const_expr(self.weight_layout_nf3):
  1820. # NF3 triple: lo16 = half (jj % 2) of word
  1821. # (jj // 2); hi8 = byte jj of the hi word; scale
  1822. # register is the same per-jj lane as packed.
  1823. lo_w = b_scale_cur[0, jj // 2]
  1824. lo16 = (lo_w >> Uint32(16 * (jj % 2))) & Uint32(0xFFFF)
  1825. hi8 = (b_scale_cur[0, 2] >> Uint32(8 * jj)) & Uint32(
  1826. 0xFF
  1827. )
  1828. s = b_scale_cur[1, jj]
  1829. self._scaled_dequant_b_fragment_nf3(
  1830. b_frag, lo16, hi8, s
  1831. )
  1832. else:
  1833. q, s = self._select_b_scale_register(jj, b_scale_cur)
  1834. self._scaled_dequant_b_fragment(b_frag, q, s)
  1835. if cutlass.const_expr(uses_m_block_8):
  1836. self._mma_accumulate_m8(
  1837. acc,
  1838. jj,
  1839. a_regs_cur,
  1840. b_frag,
  1841. )
  1842. else:
  1843. for mb in cutlass.range_constexpr(self.cta_m_blocks):
  1844. self._mma_accumulate_large_m(
  1845. acc,
  1846. a_regs_cur,
  1847. mb,
  1848. jj,
  1849. b_frag,
  1850. )
  1851. if cutlass.const_expr(uses_m_block_8):
  1852. self._copy_a_register_bundle(
  1853. a_regs_cur,
  1854. a_regs_next,
  1855. uses_m_block_8,
  1856. )
  1857. self._copy_b_scale_register_bundle(
  1858. b_scale_cur, b_scale_next
  1859. )
  1860. else:
  1861. self._copy_b_scale_register_bundle(
  1862. b_scale_cur, b_scale_next
  1863. )
  1864. self._copy_a_register_bundle(
  1865. a_regs_cur,
  1866. a_regs_next,
  1867. uses_m_block_8,
  1868. )
  1869. tile_idx += Int32(1)
  1870. cute.arch.sync_threads()
  1871. if tile_idx < k_tiles:
  1872. self._load_b_scale_register_bundle(
  1873. b_scale_cur,
  1874. smem_base,
  1875. tid,
  1876. b_sh_rd,
  1877. s_sh_rd,
  1878. Int32(0),
  1879. Int32(0),
  1880. )
  1881. self._load_a_register_bundle(
  1882. a_regs_cur,
  1883. smem_base,
  1884. a_sh_rd,
  1885. Int32(0),
  1886. Int32(0),
  1887. uses_m_block_8,
  1888. )
  1889. @cute.jit
  1890. def _finish_tile(
  1891. self,
  1892. acc: cute.Tensor,
  1893. c_bf16_flat: cute.Tensor,
  1894. c_tmp_f32_flat: cute.Tensor,
  1895. locks_i32_flat: cute.Tensor,
  1896. smem_base: Int32,
  1897. tid: Int32,
  1898. output_n_tile: Int32,
  1899. block_valid_rows: Int32,
  1900. global_scale_f32: cutlass.Float32,
  1901. reduce_slice_count: Int32,
  1902. reduce_slice_idx: Int32,
  1903. lock_slot: Int32,
  1904. uses_m_block_8: cutlass.Constexpr[bool],
  1905. ):
  1906. if cutlass.const_expr(uses_m_block_8):
  1907. self._fold_cta_partials_m8(acc, smem_base, tid)
  1908. else:
  1909. self._fold_cta_partials_large_m(acc, smem_base, tid)
  1910. if reduce_slice_count > Int32(1):
  1911. self._wait_for_reduction_turn(
  1912. locks_i32_flat, lock_slot, reduce_slice_idx, tid
  1913. )
  1914. self._combine_splitk_accumulators(
  1915. acc,
  1916. c_tmp_f32_flat,
  1917. block_valid_rows,
  1918. lock_slot,
  1919. reduce_slice_idx,
  1920. reduce_slice_count,
  1921. tid,
  1922. uses_m_block_8,
  1923. )
  1924. self._publish_reduction_turn(
  1925. locks_i32_flat,
  1926. lock_slot,
  1927. reduce_slice_idx == reduce_slice_count - Int32(1),
  1928. tid,
  1929. )
  1930. if reduce_slice_idx == reduce_slice_count - Int32(1):
  1931. if cutlass.const_expr(uses_m_block_8):
  1932. self._store_tile_m8(
  1933. acc,
  1934. c_bf16_flat,
  1935. smem_base,
  1936. tid,
  1937. output_n_tile,
  1938. block_valid_rows,
  1939. global_scale_f32,
  1940. )
  1941. else:
  1942. self._store_tile_large_m(
  1943. acc,
  1944. c_bf16_flat,
  1945. smem_base,
  1946. tid,
  1947. output_n_tile,
  1948. block_valid_rows,
  1949. global_scale_f32,
  1950. )
  1951. @cute.jit
  1952. def _wait_for_reduction_turn(
  1953. self,
  1954. locks_i32_flat: cute.Tensor,
  1955. lock_slot: Int32,
  1956. count: Int32,
  1957. tid: Int32,
  1958. ):
  1959. lock_addr = get_ptr_as_int64(locks_i32_flat, lock_slot)
  1960. if tid == Int32(0):
  1961. state = Int32(-1)
  1962. while state != count:
  1963. state = ld_global_acquire_i32(lock_addr)
  1964. cute.arch.sync_threads()
  1965. @cute.jit
  1966. def _publish_reduction_turn(
  1967. self,
  1968. locks_i32_flat: cute.Tensor,
  1969. lock_slot: Int32,
  1970. reset,
  1971. tid: Int32,
  1972. ):
  1973. lock_addr = get_ptr_as_int64(locks_i32_flat, lock_slot)
  1974. cute.arch.sync_threads()
  1975. if tid == Int32(0):
  1976. if reset:
  1977. st_global_i32(lock_addr, Int32(0))
  1978. else:
  1979. red_add_global_release_i32(lock_addr, Int32(1))
  1980. @cute.jit
  1981. def _merge_splitk_vec4(
  1982. self,
  1983. c_tmp_f32_flat: cute.Tensor,
  1984. f32_off: Int32,
  1985. reduce_slice_idx: Int32,
  1986. reduce_slice_count: Int32,
  1987. c0: cutlass.Float32,
  1988. c1: cutlass.Float32,
  1989. c2: cutlass.Float32,
  1990. c3: cutlass.Float32,
  1991. ):
  1992. if reduce_slice_idx != Int32(0):
  1993. r0, r1, r2, r3 = ld_global_v4_f32(get_ptr_as_int64(c_tmp_f32_flat, f32_off))
  1994. c0 = c0 + r0
  1995. c1 = c1 + r1
  1996. c2 = c2 + r2
  1997. c3 = c3 + r3
  1998. if reduce_slice_idx != reduce_slice_count - Int32(1):
  1999. st_global_v4_f32(
  2000. get_ptr_as_int64(c_tmp_f32_flat, f32_off),
  2001. c0,
  2002. c1,
  2003. c2,
  2004. c3,
  2005. )
  2006. return c0, c1, c2, c3
  2007. @cute.jit
  2008. def _combine_splitk_accumulators(
  2009. self,
  2010. acc: cute.Tensor,
  2011. c_tmp_f32_flat: cute.Tensor,
  2012. block_valid_rows: Int32,
  2013. lock_slot: Int32,
  2014. reduce_slice_idx: Int32,
  2015. reduce_slice_count: Int32,
  2016. tid: Int32,
  2017. uses_m_block_8: cutlass.Constexpr[bool],
  2018. ):
  2019. active_threads = Int32(32 * self.tb_n_warps)
  2020. c_size_int4 = Int32((self.cta_m_blocks * 16 * self.cta_n_blocks * 16) // 4)
  2021. c_cur_offset = lock_slot * c_size_int4
  2022. if cutlass.const_expr(uses_m_block_8):
  2023. if tid < active_threads:
  2024. for jj in cutlass.range_constexpr(4):
  2025. k = jj * 2
  2026. acc[jj, 0], acc[jj, 1], acc[jj, 2], acc[jj, 3] = (
  2027. self._merge_splitk_slot(
  2028. c_tmp_f32_flat,
  2029. c_cur_offset,
  2030. active_threads,
  2031. Int32(k),
  2032. tid,
  2033. reduce_slice_idx,
  2034. reduce_slice_count,
  2035. acc[jj, 0],
  2036. acc[jj, 1],
  2037. acc[jj, 2],
  2038. acc[jj, 3],
  2039. )
  2040. )
  2041. else:
  2042. lane_row = (tid & Int32(31)) // Int32(4)
  2043. if tid < active_threads:
  2044. for k in cutlass.range_constexpr(self.cta_m_blocks * 8):
  2045. mb = k // 8
  2046. flat_j = k % 8
  2047. jj = flat_j // 2
  2048. half = flat_j % 2
  2049. row_valid = Int32(mb * 16) + lane_row < block_valid_rows
  2050. if row_valid:
  2051. (
  2052. acc[mb, jj, half, 0],
  2053. acc[mb, jj, half, 1],
  2054. acc[mb, jj, half, 2],
  2055. acc[mb, jj, half, 3],
  2056. ) = self._merge_splitk_slot(
  2057. c_tmp_f32_flat,
  2058. c_cur_offset,
  2059. active_threads,
  2060. Int32(k),
  2061. tid,
  2062. reduce_slice_idx,
  2063. reduce_slice_count,
  2064. acc[mb, jj, half, 0],
  2065. acc[mb, jj, half, 1],
  2066. acc[mb, jj, half, 2],
  2067. acc[mb, jj, half, 3],
  2068. )
  2069. @cute.jit
  2070. def _merge_splitk_slot(
  2071. self,
  2072. c_tmp_f32_flat: cute.Tensor,
  2073. c_cur_offset: Int32,
  2074. active_threads: Int32,
  2075. slot: Int32,
  2076. tid: Int32,
  2077. reduce_slice_idx: Int32,
  2078. reduce_slice_count: Int32,
  2079. c0: cutlass.Float32,
  2080. c1: cutlass.Float32,
  2081. c2: cutlass.Float32,
  2082. c3: cutlass.Float32,
  2083. ):
  2084. int4_off = c_cur_offset + active_threads * slot + tid
  2085. return self._merge_splitk_vec4(
  2086. c_tmp_f32_flat,
  2087. int4_off * Int32(4),
  2088. reduce_slice_idx,
  2089. reduce_slice_count,
  2090. c0,
  2091. c1,
  2092. c2,
  2093. c3,
  2094. )
  2095. @cute.jit
  2096. def _load_a_registers_large_m(
  2097. self,
  2098. smem_base: Int32,
  2099. a_sh_rd: Int32,
  2100. pipe: Int32,
  2101. kk: Int32,
  2102. m_block: Int32,
  2103. ):
  2104. a_addr = self._int4_addr(
  2105. smem_base,
  2106. Int32(self.sh_a_off)
  2107. + pipe * Int32(self.a_sh_stage)
  2108. + self._activation_smem_permuted_offset(
  2109. Int32(2) * kk + m_block * Int32(self.a_sh_rd_delta_i) + a_sh_rd
  2110. ),
  2111. )
  2112. return ldmatrix_m8n8x4_b16(a_addr)
  2113. @cute.jit
  2114. def _load_a_registers_m8(
  2115. self,
  2116. smem_base: Int32,
  2117. a_sh_rd: Int32,
  2118. pipe: Int32,
  2119. kk: Int32,
  2120. ):
  2121. a_addr = self._int4_addr(
  2122. smem_base,
  2123. Int32(self.sh_a_off)
  2124. + pipe * Int32(self.a_sh_stage)
  2125. + self._activation_smem_permuted_offset(Int32(2) * kk + a_sh_rd),
  2126. )
  2127. return ldmatrix_m8n8x2_b16(a_addr)
  2128. @cute.jit
  2129. def _load_a_registers_large_m_bundle(
  2130. self,
  2131. regs: cute.Tensor,
  2132. smem_base: Int32,
  2133. a_sh_rd: Int32,
  2134. pipe: Int32,
  2135. kk: Int32,
  2136. ):
  2137. for mb in cutlass.range_constexpr(self.cta_m_blocks):
  2138. a0, a1, a2, a3 = self._load_a_registers_large_m(
  2139. smem_base,
  2140. a_sh_rd,
  2141. pipe,
  2142. kk,
  2143. Int32(mb),
  2144. )
  2145. regs[mb, 0] = a0
  2146. regs[mb, 1] = a1
  2147. regs[mb, 2] = a2
  2148. regs[mb, 3] = a3
  2149. @cute.jit
  2150. def _load_a_registers_m8_bundle(
  2151. self,
  2152. regs: cute.Tensor,
  2153. smem_base: Int32,
  2154. a_sh_rd: Int32,
  2155. pipe: Int32,
  2156. kk: Int32,
  2157. ):
  2158. a0, a1 = self._load_a_registers_m8(smem_base, a_sh_rd, pipe, kk)
  2159. regs[0] = a0
  2160. regs[1] = a1
  2161. @cute.jit
  2162. def _clear_a_register_bundle_large_m(self, regs: cute.Tensor):
  2163. for mb in cutlass.range_constexpr(self.cta_m_blocks):
  2164. for reg in cutlass.range_constexpr(4):
  2165. regs[mb, reg] = Uint32(0)
  2166. @cute.jit
  2167. def _clear_a_register_bundle_m8(self, regs: cute.Tensor):
  2168. for reg in cutlass.range_constexpr(2):
  2169. regs[reg] = Uint32(0)
  2170. @cute.jit
  2171. def _copy_a_register_bundle_large_m(self, dst: cute.Tensor, src: cute.Tensor):
  2172. for mb in cutlass.range_constexpr(self.cta_m_blocks):
  2173. for reg in cutlass.range_constexpr(4):
  2174. dst[mb, reg] = src[mb, reg]
  2175. @cute.jit
  2176. def _copy_a_register_bundle_m8(self, dst: cute.Tensor, src: cute.Tensor):
  2177. for reg in cutlass.range_constexpr(2):
  2178. dst[reg] = src[reg]
  2179. @cute.jit
  2180. def _load_a_register_bundle(
  2181. self,
  2182. regs: cute.Tensor,
  2183. smem_base: Int32,
  2184. a_sh_rd: Int32,
  2185. pipe: Int32,
  2186. kk: Int32,
  2187. uses_m_block_8: cutlass.Constexpr[bool],
  2188. ):
  2189. if cutlass.const_expr(uses_m_block_8):
  2190. self._load_a_registers_m8_bundle(regs, smem_base, a_sh_rd, pipe, kk)
  2191. else:
  2192. self._load_a_registers_large_m_bundle(regs, smem_base, a_sh_rd, pipe, kk)
  2193. @cute.jit
  2194. def _clear_a_register_bundle(
  2195. self,
  2196. regs: cute.Tensor,
  2197. uses_m_block_8: cutlass.Constexpr[bool],
  2198. ):
  2199. if cutlass.const_expr(uses_m_block_8):
  2200. self._clear_a_register_bundle_m8(regs)
  2201. else:
  2202. self._clear_a_register_bundle_large_m(regs)
  2203. @cute.jit
  2204. def _copy_a_register_bundle(
  2205. self,
  2206. dst: cute.Tensor,
  2207. src: cute.Tensor,
  2208. uses_m_block_8: cutlass.Constexpr[bool],
  2209. ):
  2210. if cutlass.const_expr(uses_m_block_8):
  2211. self._copy_a_register_bundle_m8(dst, src)
  2212. else:
  2213. self._copy_a_register_bundle_large_m(dst, src)
  2214. @cute.jit
  2215. def _load_b_scale_registers(
  2216. self,
  2217. smem_base: Int32,
  2218. tid: Int32,
  2219. b_sh_rd: Int32,
  2220. s_sh_rd: Int32,
  2221. pipe: Int32,
  2222. kk: Int32,
  2223. ):
  2224. if cutlass.const_expr(self.weight_layout == "modelopt"):
  2225. q0, q1, q2, q3 = self._load_b_registers_modelopt_shared(
  2226. smem_base,
  2227. b_sh_rd,
  2228. pipe,
  2229. kk,
  2230. )
  2231. elif cutlass.const_expr(self.weight_layout_nf3):
  2232. # NF3-MAPPING-V1: the thread's 32-code unit index within the pipe's
  2233. # B region equals the stock staged-unit index (algebraically
  2234. # identical to b_sh_stride*kk + b_sh_rd): cur_group_id is the K16 row
  2235. # within the tile, (tid % b_sh_stride) is the N-pair. Each unit is a
  2236. # 12-byte (lo0, lo1, hi) triple; three scalar u32 loads over stride-3
  2237. # words are bank-conflict free (gcd(3, 32) == 1). q3 is unused.
  2238. nf3_warp_id = tid // Int32(32)
  2239. nf3_warp_row = nf3_warp_id // Int32(self.tb_n_warps)
  2240. nf3_group = Int32(self.b_sh_wr_iters) * nf3_warp_row + kk
  2241. nf3_unit = nf3_group * Int32(self.b_sh_stride) + (
  2242. tid % Int32(self.b_sh_stride)
  2243. )
  2244. b_addr = (
  2245. smem_base
  2246. + Int32(self.sh_b_off * 16)
  2247. + pipe * Int32(self.b_sh_stage_bytes)
  2248. + nf3_unit * Int32(12)
  2249. )
  2250. q0 = ld_shared_u32(b_addr)
  2251. q1 = ld_shared_u32(b_addr + Int32(4))
  2252. q2 = ld_shared_u32(b_addr + Int32(8))
  2253. q3 = Uint32(0)
  2254. else:
  2255. b_addr = self._int4_addr(
  2256. smem_base,
  2257. Int32(self.sh_b_off)
  2258. + pipe * Int32(self.b_sh_stage)
  2259. + Int32(self.b_sh_stride) * kk
  2260. + b_sh_rd,
  2261. )
  2262. q0, q1, q2, q3 = ld_shared_v4_u32(b_addr)
  2263. warp_id = tid // Int32(32)
  2264. warp_row = warp_id // Int32(self.tb_n_warps)
  2265. cur_group_id = Int32(self.b_sh_wr_iters) * warp_row + kk
  2266. if cutlass.const_expr(self.scale_k32):
  2267. scale_group_id = cur_group_id // Int32(2)
  2268. else:
  2269. scale_group_id = cur_group_id
  2270. s_addr = (
  2271. smem_base
  2272. + Int32(self.sh_s_off * 16)
  2273. + pipe * Int32(self.s_sh_stage * 16)
  2274. + (s_sh_rd + scale_group_id * Int32(2 * self.s_sh_stride)) * Int32(8)
  2275. )
  2276. s_pack0, s_pack1 = ld_shared_v2_u32(s_addr)
  2277. s0, s1 = self._dequant_scale_x4_to_elem2x2(s_pack0)
  2278. s2, s3 = self._dequant_scale_x4_to_elem2x2(s_pack1)
  2279. return q0, q1, q2, q3, s0, s1, s2, s3
  2280. @cute.jit
  2281. def _load_b_scale_register_bundle(
  2282. self,
  2283. regs: cute.Tensor,
  2284. smem_base: Int32,
  2285. tid: Int32,
  2286. b_sh_rd: Int32,
  2287. s_sh_rd: Int32,
  2288. pipe: Int32,
  2289. kk: Int32,
  2290. ):
  2291. q0, q1, q2, q3, s0, s1, s2, s3 = self._load_b_scale_registers(
  2292. smem_base,
  2293. tid,
  2294. b_sh_rd,
  2295. s_sh_rd,
  2296. pipe,
  2297. kk,
  2298. )
  2299. regs[0, 0] = q0
  2300. regs[0, 1] = q1
  2301. regs[0, 2] = q2
  2302. regs[0, 3] = q3
  2303. regs[1, 0] = s0
  2304. regs[1, 1] = s1
  2305. regs[1, 2] = s2
  2306. regs[1, 3] = s3
  2307. @cute.jit
  2308. def _clear_b_scale_register_bundle(self, regs: cute.Tensor):
  2309. for row in cutlass.range_constexpr(2):
  2310. for col in cutlass.range_constexpr(4):
  2311. regs[row, col] = Uint32(0)
  2312. @cute.jit
  2313. def _copy_b_scale_register_bundle(self, dst: cute.Tensor, src: cute.Tensor):
  2314. for row in cutlass.range_constexpr(2):
  2315. for col in cutlass.range_constexpr(4):
  2316. dst[row, col] = src[row, col]
  2317. @cute.jit
  2318. def _select_b_scale_register(self, jj: cutlass.Constexpr[int], regs: cute.Tensor):
  2319. return regs[0, jj], regs[1, jj]
  2320. @cute.jit
  2321. def _load_next_fragment_bundle(
  2322. self,
  2323. b_scale_next: cute.Tensor,
  2324. a_regs_next: cute.Tensor,
  2325. smem_base: Int32,
  2326. tid: Int32,
  2327. b_sh_rd: Int32,
  2328. s_sh_rd: Int32,
  2329. a_sh_rd: Int32,
  2330. pipe: cutlass.Constexpr[int],
  2331. kk: cutlass.Constexpr[int],
  2332. tile_idx: Int32,
  2333. k_tiles: Int32,
  2334. uses_m_block_8: cutlass.Constexpr[bool],
  2335. ):
  2336. self._clear_b_scale_register_bundle(b_scale_next)
  2337. self._clear_a_register_bundle(a_regs_next, uses_m_block_8)
  2338. if cutlass.const_expr(kk + 1 < self.b_sh_wr_iters):
  2339. if tile_idx < k_tiles:
  2340. self._load_b_scale_register_bundle(
  2341. b_scale_next,
  2342. smem_base,
  2343. tid,
  2344. b_sh_rd,
  2345. s_sh_rd,
  2346. Int32(pipe),
  2347. Int32(kk + 1),
  2348. )
  2349. self._load_a_register_bundle(
  2350. a_regs_next,
  2351. smem_base,
  2352. a_sh_rd,
  2353. Int32(pipe),
  2354. Int32(kk + 1),
  2355. uses_m_block_8,
  2356. )
  2357. else:
  2358. next_tile = tile_idx + Int32(1)
  2359. if next_tile < k_tiles:
  2360. self._load_b_scale_register_bundle(
  2361. b_scale_next,
  2362. smem_base,
  2363. tid,
  2364. b_sh_rd,
  2365. s_sh_rd,
  2366. Int32((pipe + 1) % _STAGES),
  2367. Int32(0),
  2368. )
  2369. self._load_a_register_bundle(
  2370. a_regs_next,
  2371. smem_base,
  2372. a_sh_rd,
  2373. Int32((pipe + 1) % _STAGES),
  2374. Int32(0),
  2375. uses_m_block_8,
  2376. )
  2377. @cute.jit
  2378. def _scaled_dequant_b_fragment(self, frag: cute.Tensor, q: Uint32, s: Uint32):
  2379. bq1 = q
  2380. bq0 = bq1 << Uint32(8)
  2381. b0_0, b0_1 = self._dequant_e2m1x4_to_elem2x2(bq0)
  2382. b1_0, b1_1 = self._dequant_e2m1x4_to_elem2x2(bq1)
  2383. s_lane0 = bfloat2_broadcast_lane(s, Int32(0))
  2384. s_lane1 = bfloat2_broadcast_lane(s, Int32(1))
  2385. b0_0 = self._elem2_mul(b0_0, s_lane0)
  2386. b0_1 = self._elem2_mul(b0_1, s_lane0)
  2387. b1_0 = self._elem2_mul(b1_0, s_lane1)
  2388. b1_1 = self._elem2_mul(b1_1, s_lane1)
  2389. frag[0, 0] = b0_0
  2390. frag[0, 1] = b0_1
  2391. frag[1, 0] = b1_0
  2392. frag[1, 1] = b1_1
  2393. @cute.jit
  2394. def _scaled_dequant_b_fragment_nf3(
  2395. self, frag: cute.Tensor, lo16: Uint32, hi8: Uint32, s: Uint32
  2396. ):
  2397. # NF3: 8 3-bit codes (2 lo-bits in lo16, 1 hi-bit in hi8) -> 4 bf16x2
  2398. # codebook fragments, in the SAME element order the packed path uses:
  2399. # o0=(cb[c0],cb[c1]) -> frag[0,0], o1 -> frag[0,1], o2 -> frag[1,0],
  2400. # o3 -> frag[1,1]. frag[0,*] takes the N-col-0 scale lane, frag[1,*] the
  2401. # N-col-1 lane (identical broadcast to _scaled_dequant_b_fragment).
  2402. o0, o1, o2, o3 = packed_dequant_nf3x8_to_bfloat2x4(
  2403. lo16,
  2404. hi8,
  2405. Uint32(self._nf3_cb0),
  2406. Uint32(self._nf3_cb1),
  2407. Uint32(self._nf3_cb2),
  2408. Uint32(self._nf3_cb3),
  2409. )
  2410. s_lane0 = bfloat2_broadcast_lane(s, Int32(0))
  2411. s_lane1 = bfloat2_broadcast_lane(s, Int32(1))
  2412. frag[0, 0] = self._elem2_mul(o0, s_lane0)
  2413. frag[0, 1] = self._elem2_mul(o1, s_lane0)
  2414. frag[1, 0] = self._elem2_mul(o2, s_lane1)
  2415. frag[1, 1] = self._elem2_mul(o3, s_lane1)
  2416. @cute.jit
  2417. def _mma_accumulate_m8(
  2418. self,
  2419. acc: cute.Tensor,
  2420. jj: cutlass.Constexpr[int],
  2421. a_regs: cute.Tensor,
  2422. b_frag: cute.Tensor,
  2423. ):
  2424. d0, d1, d2, d3 = self._mma_rhs_fragments_as_mma_a_m16n8k16_f32(
  2425. acc[jj, 0],
  2426. acc[jj, 1],
  2427. acc[jj, 2],
  2428. acc[jj, 3],
  2429. b_frag[0, 0],
  2430. b_frag[1, 0],
  2431. b_frag[0, 1],
  2432. b_frag[1, 1],
  2433. a_regs[0],
  2434. a_regs[1],
  2435. )
  2436. acc[jj, 0] = d0
  2437. acc[jj, 1] = d1
  2438. acc[jj, 2] = d2
  2439. acc[jj, 3] = d3
  2440. @cute.jit
  2441. def _mma_accumulate_large_m(
  2442. self,
  2443. acc: cute.Tensor,
  2444. a_regs: cute.Tensor,
  2445. mb: cutlass.Constexpr[int],
  2446. jj: cutlass.Constexpr[int],
  2447. b_frag: cute.Tensor,
  2448. ):
  2449. d0, d1, d2, d3 = self._mma_m16n8k16_f32(
  2450. acc[mb, jj, 0, 0],
  2451. acc[mb, jj, 0, 1],
  2452. acc[mb, jj, 0, 2],
  2453. acc[mb, jj, 0, 3],
  2454. a_regs[mb, 0],
  2455. a_regs[mb, 1],
  2456. a_regs[mb, 2],
  2457. a_regs[mb, 3],
  2458. b_frag[0, 0],
  2459. b_frag[0, 1],
  2460. )
  2461. acc[mb, jj, 0, 0] = d0
  2462. acc[mb, jj, 0, 1] = d1
  2463. acc[mb, jj, 0, 2] = d2
  2464. acc[mb, jj, 0, 3] = d3
  2465. d0, d1, d2, d3 = self._mma_m16n8k16_f32(
  2466. acc[mb, jj, 1, 0],
  2467. acc[mb, jj, 1, 1],
  2468. acc[mb, jj, 1, 2],
  2469. acc[mb, jj, 1, 3],
  2470. a_regs[mb, 0],
  2471. a_regs[mb, 1],
  2472. a_regs[mb, 2],
  2473. a_regs[mb, 3],
  2474. b_frag[1, 0],
  2475. b_frag[1, 1],
  2476. )
  2477. acc[mb, jj, 1, 0] = d0
  2478. acc[mb, jj, 1, 1] = d1
  2479. acc[mb, jj, 1, 2] = d2
  2480. acc[mb, jj, 1, 3] = d3
  2481. @cute.jit
  2482. def _source_n_from_logical(self, logical_n: Int32) -> Int32:
  2483. source_n = logical_n
  2484. if cutlass.const_expr(self.source_n_rotation != 0):
  2485. source_n += Int32(self.source_n_rotation)
  2486. if source_n >= Int32(self.size_n):
  2487. source_n -= Int32(self.size_n)
  2488. return source_n
  2489. @cute.jit
  2490. def _stage_b_tile_modelopt_native(
  2491. self,
  2492. b_u8_flat: cute.Tensor,
  2493. smem_addr: Int32,
  2494. expert_idx: Int32,
  2495. output_n_tile: Int32,
  2496. tile_idx: Int32,
  2497. local_int4: Int32,
  2498. ):
  2499. chunks_per_row = Int32(self.tile_k // 32)
  2500. local_n = local_int4 // chunks_per_row
  2501. local_k_vec = local_int4 - local_n * chunks_per_row
  2502. logical_n = output_n_tile * Int32(self.tile_n) + local_n
  2503. source_n = self._source_n_from_logical(logical_n)
  2504. packed_cols = Int32(self.size_k // 2)
  2505. tile_byte = tile_idx * Int32(self.tile_k // 2) + local_k_vec * Int32(16)
  2506. byte_offset = (
  2507. Int64(expert_idx) * Int64(self.size_n * (self.size_k // 2))
  2508. + Int64(source_n) * Int64(packed_cols)
  2509. + Int64(tile_byte)
  2510. )
  2511. if cutlass.const_expr(self.has_logical_tail):
  2512. valid_n = logical_n < Int32(self.size_n)
  2513. valid_bytes = packed_cols - tile_byte
  2514. if valid_bytes > Int32(16):
  2515. valid_bytes = Int32(16)
  2516. if valid_n and valid_bytes >= Int32(16) and cutlass.const_expr(
  2517. not self.has_scale_k_tail
  2518. ):
  2519. cp_async4_shared_global(
  2520. smem_addr,
  2521. get_ptr_as_int64(b_u8_flat, byte_offset),
  2522. )
  2523. else:
  2524. v0 = Uint32(0)
  2525. v1 = Uint32(0)
  2526. v2 = Uint32(0)
  2527. v3 = Uint32(0)
  2528. if valid_n and valid_bytes > Int32(0):
  2529. src = get_ptr_as_int64(b_u8_flat, byte_offset)
  2530. if valid_bytes >= Int32(4):
  2531. v0 = ld_global_nc_u32(src)
  2532. if valid_bytes >= Int32(8):
  2533. v1 = ld_global_nc_u32(src + Int64(4))
  2534. if valid_bytes >= Int32(12):
  2535. v2 = ld_global_nc_u32(src + Int64(8))
  2536. if valid_bytes >= Int32(16):
  2537. v3 = ld_global_nc_u32(src + Int64(12))
  2538. st_shared_v4_u32(smem_addr, v0, v1, v2, v3)
  2539. else:
  2540. cp_async4_shared_global(
  2541. smem_addr,
  2542. get_ptr_as_int64(b_u8_flat, byte_offset),
  2543. )
  2544. @cute.jit
  2545. def _load_modelopt_shared_byte(
  2546. self,
  2547. smem_base: Int32,
  2548. pipe: Int32,
  2549. n_tile: Int32,
  2550. k_tile: Int32,
  2551. warp_id: Int32,
  2552. tc_col: Int32,
  2553. tc_row: Int32,
  2554. n_delta: cutlass.Constexpr[int],
  2555. k_delta: cutlass.Constexpr[int],
  2556. ) -> Uint32:
  2557. local_n = (
  2558. n_tile * Int32(64)
  2559. + warp_id * Int32(16)
  2560. + tc_col
  2561. + Int32(n_delta)
  2562. )
  2563. local_k = k_tile * Int32(16) + tc_row + Int32(k_delta)
  2564. byte_offset = local_n * Int32(self.tile_k // 2) + local_k // Int32(2)
  2565. word_byte_offset = byte_offset - (byte_offset & Int32(3))
  2566. word = ld_shared_u32(
  2567. smem_base
  2568. + Int32(self.sh_b_off * 16)
  2569. + pipe * Int32(self.b_sh_stage * 16)
  2570. + word_byte_offset
  2571. )
  2572. shift = Uint32((byte_offset - word_byte_offset) * Int32(8))
  2573. return (word >> shift) & Uint32(0xFF)
  2574. @cute.jit
  2575. def _pack_modelopt_byte_pair(
  2576. self,
  2577. word: Uint32,
  2578. q: Uint32,
  2579. low_shift: cutlass.Constexpr[int],
  2580. high_shift: cutlass.Constexpr[int],
  2581. ) -> Uint32:
  2582. low = q & Uint32(0xF)
  2583. high = (q >> Uint32(4)) & Uint32(0xF)
  2584. return word | (low << Uint32(low_shift)) | (high << Uint32(high_shift))
  2585. @cute.jit
  2586. def _load_modelopt_shared_packed_word_for_lane(
  2587. self,
  2588. smem_base: Int32,
  2589. pipe: Int32,
  2590. n_tile: Int32,
  2591. k_tile: Int32,
  2592. warp_id: Int32,
  2593. tc_col: Int32,
  2594. tc_row: Int32,
  2595. ) -> Uint32:
  2596. q0 = self._load_modelopt_shared_byte(
  2597. smem_base, pipe, n_tile, k_tile, warp_id, tc_col, tc_row, 0, 0
  2598. )
  2599. q1 = self._load_modelopt_shared_byte(
  2600. smem_base, pipe, n_tile, k_tile, warp_id, tc_col, tc_row, 0, 8
  2601. )
  2602. q2 = self._load_modelopt_shared_byte(
  2603. smem_base, pipe, n_tile, k_tile, warp_id, tc_col, tc_row, 8, 0
  2604. )
  2605. q3 = self._load_modelopt_shared_byte(
  2606. smem_base, pipe, n_tile, k_tile, warp_id, tc_col, tc_row, 8, 8
  2607. )
  2608. word = Uint32(0)
  2609. word = self._pack_modelopt_byte_pair(word, q0, 0, 16)
  2610. word = self._pack_modelopt_byte_pair(word, q1, 4, 20)
  2611. word = self._pack_modelopt_byte_pair(word, q2, 8, 24)
  2612. word = self._pack_modelopt_byte_pair(word, q3, 12, 28)
  2613. return word
  2614. @cute.jit
  2615. def _load_b_registers_modelopt_shared(
  2616. self,
  2617. smem_base: Int32,
  2618. b_sh_rd: Int32,
  2619. pipe: Int32,
  2620. kk: Int32,
  2621. ):
  2622. packed_word_index = (Int32(self.b_sh_stride) * kk + b_sh_rd) * Int32(4)
  2623. words_per_k_tile = Int32((self.tile_n // 64) * 128)
  2624. k_tile = packed_word_index // words_per_k_tile
  2625. pos_in_k_tile = packed_word_index - k_tile * words_per_k_tile
  2626. n_tile = pos_in_k_tile // Int32(128)
  2627. pos = pos_in_k_tile - n_tile * Int32(128)
  2628. th_id = pos // Int32(4)
  2629. tc_col = th_id // Int32(4)
  2630. tc_row = (th_id - tc_col * Int32(4)) * Int32(2)
  2631. q0 = self._load_modelopt_shared_packed_word_for_lane(
  2632. smem_base, pipe, n_tile, k_tile, Int32(0), tc_col, tc_row
  2633. )
  2634. q1 = self._load_modelopt_shared_packed_word_for_lane(
  2635. smem_base, pipe, n_tile, k_tile, Int32(1), tc_col, tc_row
  2636. )
  2637. q2 = self._load_modelopt_shared_packed_word_for_lane(
  2638. smem_base, pipe, n_tile, k_tile, Int32(2), tc_col, tc_row
  2639. )
  2640. q3 = self._load_modelopt_shared_packed_word_for_lane(
  2641. smem_base, pipe, n_tile, k_tile, Int32(3), tc_col, tc_row
  2642. )
  2643. return q0, q1, q2, q3
  2644. @cute.jit
  2645. def _stage_k_tile_async(
  2646. self,
  2647. a_bf16_flat: cute.Tensor,
  2648. b_i32_flat: cute.Tensor,
  2649. scales_i32_flat: cute.Tensor,
  2650. smem_base: Int32,
  2651. tid: Int32,
  2652. pipe: Int32,
  2653. tile_idx: Int32,
  2654. block_valid_rows: Int32,
  2655. a_gl_stride: Int32,
  2656. b_gl_stride: Int32,
  2657. s_gl_stride: Int32,
  2658. scales_expert_off: Int32,
  2659. b_gl_rd_base: Int32,
  2660. a_gl_rd_row: Int32,
  2661. a_gl_rd_col0: Int32,
  2662. a_sh_wr: Int32,
  2663. a_rows_per_iter: Int32,
  2664. output_n_tile: Int32,
  2665. expert_idx: Int32,
  2666. ):
  2667. for i in cutlass.range_constexpr(self.a_sh_wr_iters):
  2668. row = a_rows_per_iter * Int32(i) + a_gl_rd_row
  2669. route_index = Int32(0)
  2670. if row < Int32(self.moe_block_size):
  2671. route_index = ld_shared_i32_relaxed(
  2672. smem_base + Int32(self.sh_rd_route_off * 16) + row * Int32(4)
  2673. )
  2674. a_int4 = (
  2675. route_index * a_gl_stride
  2676. + tile_idx * Int32(self.a_gl_rd_delta_o)
  2677. + a_gl_rd_col0
  2678. )
  2679. a_dst = self._int4_addr(
  2680. smem_base,
  2681. Int32(self.sh_a_off)
  2682. + pipe * Int32(self.a_sh_stage)
  2683. + self._activation_smem_permuted_offset(
  2684. Int32(i * self.a_sh_wr_delta) + a_sh_wr
  2685. ),
  2686. )
  2687. if cutlass.const_expr(self.has_k_tile_tail):
  2688. a_k_int4 = tile_idx * Int32(self.a_gl_rd_delta_o) + a_gl_rd_col0
  2689. if row < block_valid_rows and a_k_int4 < a_gl_stride:
  2690. cp_async4_shared_global(
  2691. a_dst,
  2692. get_ptr_as_int64(a_bf16_flat, a_int4 * Int32(8)),
  2693. )
  2694. else:
  2695. st_shared_v4_u32(
  2696. a_dst, Uint32(0), Uint32(0), Uint32(0), Uint32(0)
  2697. )
  2698. else:
  2699. cp_async4_shared_global_pred(
  2700. a_dst,
  2701. get_ptr_as_int64(a_bf16_flat, a_int4 * Int32(8)),
  2702. (row < block_valid_rows).to(Int32),
  2703. )
  2704. if cutlass.const_expr(self.weight_layout_nf3):
  2705. # NF3 flat-span staging: the packer lays the per-(expert,
  2706. # output_n_tile, k-stage) B block out as ONE contiguous 12-byte-triple
  2707. # span (n_tile-major, then K16-row, then N-pair), so we copy it
  2708. # verbatim as 16-byte cp.async chunks. SMEM byte X of the pipe's B
  2709. # region == global byte X of the span, so the register read (unit*12)
  2710. # indexes it directly. b_gl_rd_base is unused on this path.
  2711. nf3_units_per_expert = (self.size_n * self.size_k) // 32
  2712. nf3_ntile_stride = (self.size_k // 16) * (self.tile_n // 2)
  2713. nf3_span_base_unit = (
  2714. Int32(nf3_units_per_expert) * expert_idx
  2715. + Int32(nf3_ntile_stride) * output_n_tile
  2716. + tile_idx * Int32(self.cta_k_blocks * self.b_sh_stride)
  2717. )
  2718. for i in cutlass.range_constexpr(self.b_sh_wr_iters_nf3):
  2719. nf3_chunk = Int32(i * self.cta_threads) + tid
  2720. b_dst = (
  2721. smem_base
  2722. + Int32(self.sh_b_off * 16)
  2723. + pipe * Int32(self.b_sh_stage_bytes)
  2724. + nf3_chunk * Int32(16)
  2725. )
  2726. # int32-element index of the chunk's first word:
  2727. # (span_base_unit*12 + chunk*16) / 4 = span_base_unit*3 + chunk*4.
  2728. b_src_i32 = nf3_span_base_unit * Int32(3) + nf3_chunk * Int32(4)
  2729. cp_async4_shared_global_pred(
  2730. b_dst,
  2731. get_ptr_as_int64(b_i32_flat, b_src_i32),
  2732. (nf3_chunk < Int32(self.b_sh_chunks)).to(Int32),
  2733. )
  2734. for i in cutlass.range_constexpr(
  2735. 0 if self.weight_layout_nf3 else self.b_sh_wr_iters
  2736. ):
  2737. b_src_int4 = (
  2738. b_gl_rd_base
  2739. + tile_idx * Int32(self.cta_k_blocks) * b_gl_stride
  2740. + Int32(i * (self.cta_threads // self.b_sh_stride)) * b_gl_stride
  2741. )
  2742. b_dst = self._int4_addr(
  2743. smem_base,
  2744. Int32(self.sh_b_off)
  2745. + pipe * Int32(self.b_sh_stage)
  2746. + Int32(i * self.cta_threads)
  2747. + tid,
  2748. )
  2749. if cutlass.const_expr(self.weight_layout == "packed"):
  2750. cp_async4_shared_global(
  2751. b_dst,
  2752. get_ptr_as_int64(b_i32_flat, b_src_int4 * Int32(4)),
  2753. )
  2754. else:
  2755. self._stage_b_tile_modelopt_native(
  2756. b_i32_flat,
  2757. b_dst,
  2758. expert_idx,
  2759. output_n_tile,
  2760. tile_idx,
  2761. Int32(i * self.cta_threads) + tid,
  2762. )
  2763. if tid < Int32(self.s_sh_stage):
  2764. s_k_group = tile_idx * Int32(self.s_tb_groups) + tid // Int32(
  2765. self.s_sh_stride
  2766. )
  2767. s_n_group = Int32(self.s_sh_stride) * output_n_tile + (
  2768. tid % Int32(self.s_sh_stride)
  2769. )
  2770. s_src_int4 = scales_expert_off + s_gl_stride * s_k_group + s_n_group
  2771. s_dst = self._int4_addr(
  2772. smem_base,
  2773. Int32(self.sh_s_off) + pipe * Int32(self.s_sh_stage) + tid,
  2774. )
  2775. if cutlass.const_expr(self.has_logical_tail):
  2776. if s_k_group < Int32(self.scale_k_groups) and s_n_group < Int32(
  2777. self.scale_n_groups
  2778. ):
  2779. cp_async4_shared_global(
  2780. s_dst,
  2781. get_ptr_as_int64(scales_i32_flat, s_src_int4 * Int32(4)),
  2782. )
  2783. else:
  2784. st_shared_v4_u32(
  2785. s_dst, Uint32(0), Uint32(0), Uint32(0), Uint32(0)
  2786. )
  2787. else:
  2788. cp_async4_shared_global(
  2789. s_dst,
  2790. get_ptr_as_int64(scales_i32_flat, s_src_int4 * Int32(4)),
  2791. )
  2792. cute.arch.cp_async_commit_group()
  2793. @cute.jit
  2794. def _prefetch_pipeline_step(
  2795. self,
  2796. a_bf16_flat: cute.Tensor,
  2797. b_i32_flat: cute.Tensor,
  2798. scales_i32_flat: cute.Tensor,
  2799. smem_base: Int32,
  2800. tid: Int32,
  2801. pipe: cutlass.Constexpr[int],
  2802. kk: cutlass.Constexpr[int],
  2803. tile_idx: Int32,
  2804. k_tiles: Int32,
  2805. reduce_k_tile: Int32,
  2806. block_valid_rows: Int32,
  2807. a_gl_stride: Int32,
  2808. b_gl_stride: Int32,
  2809. s_gl_stride: Int32,
  2810. scales_expert_off: Int32,
  2811. b_gl_rd_base: Int32,
  2812. a_gl_rd_row: Int32,
  2813. a_gl_rd_col0: Int32,
  2814. a_sh_wr: Int32,
  2815. a_rows_per_iter: Int32,
  2816. output_n_tile: Int32,
  2817. expert_idx: Int32,
  2818. ):
  2819. if cutlass.const_expr(kk == self.b_sh_wr_iters - 2):
  2820. self._prefetch_lookahead_tile(
  2821. a_bf16_flat,
  2822. b_i32_flat,
  2823. scales_i32_flat,
  2824. smem_base,
  2825. tid,
  2826. pipe,
  2827. tile_idx,
  2828. k_tiles,
  2829. reduce_k_tile,
  2830. block_valid_rows,
  2831. a_gl_stride,
  2832. b_gl_stride,
  2833. s_gl_stride,
  2834. scales_expert_off,
  2835. b_gl_rd_base,
  2836. a_gl_rd_row,
  2837. a_gl_rd_col0,
  2838. a_sh_wr,
  2839. a_rows_per_iter,
  2840. output_n_tile,
  2841. expert_idx,
  2842. )
  2843. @cute.jit
  2844. def _prefetch_initial_tiles(
  2845. self,
  2846. a_bf16_flat: cute.Tensor,
  2847. b_i32_flat: cute.Tensor,
  2848. scales_i32_flat: cute.Tensor,
  2849. smem_base: Int32,
  2850. tid: Int32,
  2851. k_tiles: Int32,
  2852. reduce_k_tile: Int32,
  2853. block_valid_rows: Int32,
  2854. a_gl_stride: Int32,
  2855. b_gl_stride: Int32,
  2856. s_gl_stride: Int32,
  2857. scales_expert_off: Int32,
  2858. b_gl_rd_base: Int32,
  2859. a_gl_rd_row: Int32,
  2860. a_gl_rd_col0: Int32,
  2861. a_sh_wr: Int32,
  2862. a_rows_per_iter: Int32,
  2863. output_n_tile: Int32,
  2864. expert_idx: Int32,
  2865. ):
  2866. for pipe in cutlass.range_constexpr(_STAGES - 1):
  2867. if Int32(pipe) < k_tiles:
  2868. self._stage_k_tile_async(
  2869. a_bf16_flat,
  2870. b_i32_flat,
  2871. scales_i32_flat,
  2872. smem_base,
  2873. tid,
  2874. Int32(pipe),
  2875. reduce_k_tile + Int32(pipe),
  2876. block_valid_rows,
  2877. a_gl_stride,
  2878. b_gl_stride,
  2879. s_gl_stride,
  2880. scales_expert_off,
  2881. b_gl_rd_base,
  2882. a_gl_rd_row,
  2883. a_gl_rd_col0,
  2884. a_sh_wr,
  2885. a_rows_per_iter,
  2886. output_n_tile,
  2887. expert_idx,
  2888. )
  2889. else:
  2890. cute.arch.cp_async_commit_group()
  2891. cute.arch.cp_async_wait_group(_STAGES - 2)
  2892. cute.arch.sync_threads()
  2893. @cute.jit
  2894. def _prefetch_lookahead_tile(
  2895. self,
  2896. a_bf16_flat: cute.Tensor,
  2897. b_i32_flat: cute.Tensor,
  2898. scales_i32_flat: cute.Tensor,
  2899. smem_base: Int32,
  2900. tid: Int32,
  2901. pipe: cutlass.Constexpr[int],
  2902. tile_idx: Int32,
  2903. k_tiles: Int32,
  2904. reduce_k_tile: Int32,
  2905. block_valid_rows: Int32,
  2906. a_gl_stride: Int32,
  2907. b_gl_stride: Int32,
  2908. s_gl_stride: Int32,
  2909. scales_expert_off: Int32,
  2910. b_gl_rd_base: Int32,
  2911. a_gl_rd_row: Int32,
  2912. a_gl_rd_col0: Int32,
  2913. a_sh_wr: Int32,
  2914. a_rows_per_iter: Int32,
  2915. output_n_tile: Int32,
  2916. expert_idx: Int32,
  2917. ):
  2918. fetch_tile = tile_idx + Int32(_STAGES - 1)
  2919. if fetch_tile < k_tiles:
  2920. self._stage_k_tile_async(
  2921. a_bf16_flat,
  2922. b_i32_flat,
  2923. scales_i32_flat,
  2924. smem_base,
  2925. tid,
  2926. Int32((pipe + _STAGES - 1) % _STAGES),
  2927. reduce_k_tile + fetch_tile,
  2928. block_valid_rows,
  2929. a_gl_stride,
  2930. b_gl_stride,
  2931. s_gl_stride,
  2932. scales_expert_off,
  2933. b_gl_rd_base,
  2934. a_gl_rd_row,
  2935. a_gl_rd_col0,
  2936. a_sh_wr,
  2937. a_rows_per_iter,
  2938. output_n_tile,
  2939. expert_idx,
  2940. )
  2941. else:
  2942. cute.arch.cp_async_commit_group()
  2943. cute.arch.cp_async_wait_group(_STAGES - 2)
  2944. cute.arch.sync_threads()
  2945. @cute.jit
  2946. def _reduction_offsets(self, tid: Int32):
  2947. red_idx = tid // Int32(self.b_sh_stride_threads)
  2948. red_sh_stride = Int32(self.b_sh_stride_threads * 4 * 2)
  2949. red_sh_delta = Int32(self.b_sh_stride_threads)
  2950. red_sh_rd = red_sh_stride * (tid // Int32(self.b_sh_stride_threads)) + (
  2951. tid % Int32(self.b_sh_stride_threads)
  2952. )
  2953. return red_idx, red_sh_stride, red_sh_delta, red_sh_rd
  2954. @cute.jit
  2955. def _fold_cta_partials_m8(self, acc: cute.Tensor, smem_base: Int32, tid: Int32):
  2956. red_off = self.cta_threads // self.b_sh_stride_threads // 2
  2957. if cutlass.const_expr(red_off >= 1):
  2958. red_idx, red_sh_stride, red_sh_delta, red_sh_rd = self._reduction_offsets(
  2959. tid
  2960. )
  2961. if cutlass.const_expr(red_off == 2):
  2962. if Int32(2) <= red_idx and red_idx < Int32(4):
  2963. for jj in cutlass.range_constexpr(4):
  2964. red_sh_wr = red_sh_delta * Int32(jj * 2) + (
  2965. red_sh_rd - red_sh_stride * Int32(2)
  2966. )
  2967. st_shared_v4_f32(
  2968. self._int4_addr(
  2969. smem_base, Int32(self.sh_red_off) + red_sh_wr
  2970. ),
  2971. acc[jj, 0],
  2972. acc[jj, 1],
  2973. acc[jj, 2],
  2974. acc[jj, 3],
  2975. )
  2976. cute.arch.sync_threads()
  2977. if Int32(1) <= red_idx and red_idx < Int32(2):
  2978. for jj in cutlass.range_constexpr(4):
  2979. red_sh_wr = red_sh_delta * Int32(jj * 2) + (
  2980. red_sh_rd - red_sh_stride
  2981. )
  2982. if cutlass.const_expr(red_off > 1):
  2983. rd_addr = self._int4_addr(
  2984. smem_base,
  2985. Int32(self.sh_red_off)
  2986. + red_sh_delta * Int32(jj * 2)
  2987. + red_sh_rd,
  2988. )
  2989. wr_addr = self._int4_addr(
  2990. smem_base, Int32(self.sh_red_off) + red_sh_wr
  2991. )
  2992. r0, r1, r2, r3 = ld_shared_v4_f32(rd_addr)
  2993. w0, w1, w2, w3 = ld_shared_v4_f32(wr_addr)
  2994. acc[jj, 0] = acc[jj, 0] + r0 + w0
  2995. acc[jj, 1] = acc[jj, 1] + r1 + w1
  2996. acc[jj, 2] = acc[jj, 2] + r2 + w2
  2997. acc[jj, 3] = acc[jj, 3] + r3 + w3
  2998. st_shared_v4_f32(
  2999. self._int4_addr(smem_base, Int32(self.sh_red_off) + red_sh_wr),
  3000. acc[jj, 0],
  3001. acc[jj, 1],
  3002. acc[jj, 2],
  3003. acc[jj, 3],
  3004. )
  3005. cute.arch.sync_threads()
  3006. if red_idx == Int32(0):
  3007. for jj in cutlass.range_constexpr(4):
  3008. rd_addr = self._int4_addr(
  3009. smem_base,
  3010. Int32(self.sh_red_off)
  3011. + red_sh_delta * Int32(jj * 2)
  3012. + red_sh_rd,
  3013. )
  3014. r0, r1, r2, r3 = ld_shared_v4_f32(rd_addr)
  3015. acc[jj, 0] = acc[jj, 0] + r0
  3016. acc[jj, 1] = acc[jj, 1] + r1
  3017. acc[jj, 2] = acc[jj, 2] + r2
  3018. acc[jj, 3] = acc[jj, 3] + r3
  3019. cute.arch.sync_threads()
  3020. @cute.jit
  3021. def _output_store_cursor(self, tid: Int32, output_n_tile: Int32):
  3022. c_gl_stride = Int32(self.size_n // 8)
  3023. c_sh_stride = Int32(2 * self.cta_n_blocks + 1)
  3024. c_gl_wr_delta = c_gl_stride * Int32(self.cta_threads // (2 * self.cta_n_blocks))
  3025. c_sh_rd_delta = c_sh_stride * Int32(
  3026. self.cta_threads // (2 * self.cta_n_blocks)
  3027. )
  3028. c_gl_wr = (
  3029. c_gl_stride * (tid // Int32(2 * self.cta_n_blocks))
  3030. + (tid % Int32(2 * self.cta_n_blocks))
  3031. + Int32(2 * self.cta_n_blocks) * output_n_tile
  3032. )
  3033. c_sh_rd = c_sh_stride * (tid // Int32(2 * self.cta_n_blocks)) + (
  3034. tid % Int32(2 * self.cta_n_blocks)
  3035. )
  3036. return c_gl_stride, c_sh_stride, c_gl_wr_delta, c_sh_rd_delta, c_gl_wr, c_sh_rd
  3037. @cute.jit
  3038. def _output_store_cursor_tail(self, tid: Int32, output_n_tile: Int32):
  3039. c_gl_stride = Int32(self.size_n // 8)
  3040. c_gl_stride_covered = Int32(self.covered_size_n // 8)
  3041. c_sh_stride = Int32(2 * self.cta_n_blocks + 1)
  3042. c_gl_wr_delta = c_gl_stride_covered * Int32(
  3043. self.cta_threads // (2 * self.cta_n_blocks)
  3044. )
  3045. c_sh_rd_delta = c_sh_stride * Int32(self.cta_threads // (2 * self.cta_n_blocks))
  3046. c_gl_wr = (
  3047. c_gl_stride_covered * (tid // Int32(2 * self.cta_n_blocks))
  3048. + (tid % Int32(2 * self.cta_n_blocks))
  3049. + Int32(2 * self.cta_n_blocks) * output_n_tile
  3050. )
  3051. c_sh_rd = c_sh_stride * (tid // Int32(2 * self.cta_n_blocks)) + (
  3052. tid % Int32(2 * self.cta_n_blocks)
  3053. )
  3054. return (
  3055. c_gl_stride,
  3056. c_gl_stride_covered,
  3057. c_sh_stride,
  3058. c_gl_wr_delta,
  3059. c_sh_rd_delta,
  3060. c_gl_wr,
  3061. c_sh_rd,
  3062. )
  3063. @cute.jit
  3064. def _drain_output_smem(
  3065. self,
  3066. c_bf16_flat: cute.Tensor,
  3067. smem_base: Int32,
  3068. c_gl_stride: Int32,
  3069. c_gl_wr: Int32,
  3070. c_gl_wr_delta: Int32,
  3071. c_sh_rd: Int32,
  3072. c_sh_rd_delta: Int32,
  3073. block_valid_rows: Int32,
  3074. store_iters: cutlass.Constexpr[int],
  3075. ):
  3076. for _ in cutlass.range_constexpr(store_iters):
  3077. row = c_gl_wr // c_gl_stride
  3078. if row < block_valid_rows:
  3079. route_index = ld_shared_i32_relaxed(
  3080. smem_base + Int32(self.sh_route_off * 16) + row * Int32(4)
  3081. )
  3082. true_idx = route_index * c_gl_stride + (c_gl_wr % c_gl_stride)
  3083. q0, q1, q2, q3 = ld_shared_v4_u32(
  3084. self._int4_addr(smem_base, Int32(self.sh_red_off) + c_sh_rd)
  3085. )
  3086. if cutlass.const_expr(self.mul_topk_weights):
  3087. scale_bf2 = ld_shared_u32(
  3088. smem_base + Int32(self.sh_topk_off * 16) + row * Int32(4)
  3089. )
  3090. q0 = self._elem2_mul(q0, scale_bf2)
  3091. q1 = self._elem2_mul(q1, scale_bf2)
  3092. q2 = self._elem2_mul(q2, scale_bf2)
  3093. q3 = self._elem2_mul(q3, scale_bf2)
  3094. if cutlass.const_expr(self.epilogue_relu2):
  3095. q0 = self._relu2_elem2(q0)
  3096. q1 = self._relu2_elem2(q1)
  3097. q2 = self._relu2_elem2(q2)
  3098. q3 = self._relu2_elem2(q3)
  3099. if cutlass.const_expr(self.fused_topk_sum):
  3100. # Fold the per-route partials into the per-token output in
  3101. # the epilogue (drops the separate top-k-sum launch). The
  3102. # output must be zeroed before launch; each route slot maps
  3103. # to token = route_index // top_k. bf16x2 add lands two
  3104. # consecutive hidden lanes per word.
  3105. token_idx = route_index // Int32(self.fused_sum_topk)
  3106. out_idx = token_idx * c_gl_stride + (c_gl_wr % c_gl_stride)
  3107. out_addr = get_ptr_as_int64(c_bf16_flat, out_idx * Int32(8))
  3108. red_add_global_bf16x2(out_addr, q0)
  3109. red_add_global_bf16x2(out_addr + Int64(4), q1)
  3110. red_add_global_bf16x2(out_addr + Int64(8), q2)
  3111. red_add_global_bf16x2(out_addr + Int64(12), q3)
  3112. else:
  3113. st_global_v4_u32(
  3114. get_ptr_as_int64(c_bf16_flat, true_idx * Int32(8)),
  3115. q0,
  3116. q1,
  3117. q2,
  3118. q3,
  3119. )
  3120. c_gl_wr += c_gl_wr_delta
  3121. c_sh_rd += c_sh_rd_delta
  3122. cute.arch.sync_threads()
  3123. @cute.jit
  3124. def _drain_output_smem_tail(
  3125. self,
  3126. c_bf16_flat: cute.Tensor,
  3127. smem_base: Int32,
  3128. c_gl_stride: Int32,
  3129. c_gl_stride_covered: Int32,
  3130. c_gl_wr: Int32,
  3131. c_gl_wr_delta: Int32,
  3132. c_sh_rd: Int32,
  3133. c_sh_rd_delta: Int32,
  3134. block_valid_rows: Int32,
  3135. store_iters: cutlass.Constexpr[int],
  3136. ):
  3137. for _ in cutlass.range_constexpr(store_iters):
  3138. row = c_gl_wr // c_gl_stride_covered
  3139. col_word = c_gl_wr - row * c_gl_stride_covered
  3140. if row < block_valid_rows and col_word < c_gl_stride:
  3141. route_index = ld_shared_i32_relaxed(
  3142. smem_base + Int32(self.sh_route_off * 16) + row * Int32(4)
  3143. )
  3144. true_idx = route_index * c_gl_stride + col_word
  3145. q0, q1, q2, q3 = ld_shared_v4_u32(
  3146. self._int4_addr(smem_base, Int32(self.sh_red_off) + c_sh_rd)
  3147. )
  3148. if cutlass.const_expr(self.mul_topk_weights):
  3149. scale_bf2 = ld_shared_u32(
  3150. smem_base + Int32(self.sh_topk_off * 16) + row * Int32(4)
  3151. )
  3152. q0 = self._elem2_mul(q0, scale_bf2)
  3153. q1 = self._elem2_mul(q1, scale_bf2)
  3154. q2 = self._elem2_mul(q2, scale_bf2)
  3155. q3 = self._elem2_mul(q3, scale_bf2)
  3156. if cutlass.const_expr(self.epilogue_relu2):
  3157. q0 = self._relu2_elem2(q0)
  3158. q1 = self._relu2_elem2(q1)
  3159. q2 = self._relu2_elem2(q2)
  3160. q3 = self._relu2_elem2(q3)
  3161. if cutlass.const_expr(self.fused_topk_sum):
  3162. token_idx = route_index // Int32(self.fused_sum_topk)
  3163. out_idx = token_idx * c_gl_stride + col_word
  3164. out_addr = get_ptr_as_int64(c_bf16_flat, out_idx * Int32(8))
  3165. red_add_global_bf16x2(out_addr, q0)
  3166. red_add_global_bf16x2(out_addr + Int64(4), q1)
  3167. red_add_global_bf16x2(out_addr + Int64(8), q2)
  3168. red_add_global_bf16x2(out_addr + Int64(12), q3)
  3169. else:
  3170. st_global_v4_u32(
  3171. get_ptr_as_int64(c_bf16_flat, true_idx * Int32(8)),
  3172. q0,
  3173. q1,
  3174. q2,
  3175. q3,
  3176. )
  3177. c_gl_wr += c_gl_wr_delta
  3178. c_sh_rd += c_sh_rd_delta
  3179. cute.arch.sync_threads()
  3180. @cute.jit
  3181. def _store_tile_m8(
  3182. self,
  3183. acc: cute.Tensor,
  3184. c_bf16_flat: cute.Tensor,
  3185. smem_base: Int32,
  3186. tid: Int32,
  3187. output_n_tile: Int32,
  3188. block_valid_rows: Int32,
  3189. global_scale_f32: cutlass.Float32,
  3190. ):
  3191. if cutlass.const_expr(self.has_n_tile_tail):
  3192. (
  3193. c_gl_stride,
  3194. c_gl_stride_covered,
  3195. c_sh_stride,
  3196. c_gl_wr_delta,
  3197. c_sh_rd_delta,
  3198. c_gl_wr,
  3199. c_sh_rd,
  3200. ) = self._output_store_cursor_tail(tid, output_n_tile)
  3201. else:
  3202. (
  3203. c_gl_stride,
  3204. c_sh_stride,
  3205. c_gl_wr_delta,
  3206. c_sh_rd_delta,
  3207. c_gl_wr,
  3208. c_sh_rd,
  3209. ) = self._output_store_cursor(tid, output_n_tile)
  3210. c_gl_stride_covered = c_gl_stride
  3211. c_sh_wr = (
  3212. Int32(8) * c_sh_stride * (((tid & Int32(31)) % Int32(4)) * Int32(2))
  3213. + (tid & Int32(31)) // Int32(4)
  3214. + Int32(64) * (tid // Int32(32))
  3215. )
  3216. if tid // Int32(32) < Int32(self.tb_n_warps):
  3217. write_scale = cutlass.Float32(1.0)
  3218. if cutlass.const_expr(not self.mul_topk_weights):
  3219. write_scale = global_scale_f32
  3220. for jj in cutlass.range_constexpr(4):
  3221. wr = c_sh_wr + Int32(16 * jj)
  3222. self._st_shared_elem_from_f32(
  3223. smem_base + Int32(self.sh_red_off * 16) + (wr * Int32(2)),
  3224. acc[jj, 0] * write_scale,
  3225. )
  3226. self._st_shared_elem_from_f32(
  3227. smem_base
  3228. + Int32(self.sh_red_off * 16)
  3229. + ((wr + Int32(8) * c_sh_stride) * Int32(2)),
  3230. acc[jj, 1] * write_scale,
  3231. )
  3232. self._st_shared_elem_from_f32(
  3233. smem_base
  3234. + Int32(self.sh_red_off * 16)
  3235. + ((wr + Int32(8)) * Int32(2)),
  3236. acc[jj, 2] * write_scale,
  3237. )
  3238. self._st_shared_elem_from_f32(
  3239. smem_base
  3240. + Int32(self.sh_red_off * 16)
  3241. + ((wr + Int32(8) + Int32(8) * c_sh_stride) * Int32(2)),
  3242. acc[jj, 3] * write_scale,
  3243. )
  3244. cute.arch.sync_threads()
  3245. store_iters = _covering_count(
  3246. 16, self.cta_threads // (2 * self.cta_n_blocks)
  3247. )
  3248. if cutlass.const_expr(self.has_n_tile_tail):
  3249. self._drain_output_smem_tail(
  3250. c_bf16_flat,
  3251. smem_base,
  3252. c_gl_stride,
  3253. c_gl_stride_covered,
  3254. c_gl_wr,
  3255. c_gl_wr_delta,
  3256. c_sh_rd,
  3257. c_sh_rd_delta,
  3258. block_valid_rows,
  3259. store_iters,
  3260. )
  3261. else:
  3262. self._drain_output_smem(
  3263. c_bf16_flat,
  3264. smem_base,
  3265. c_gl_stride,
  3266. c_gl_wr,
  3267. c_gl_wr_delta,
  3268. c_sh_rd,
  3269. c_sh_rd_delta,
  3270. block_valid_rows,
  3271. store_iters,
  3272. )
  3273. @cute.jit
  3274. def _fold_cta_partials_large_m(
  3275. self, acc: cute.Tensor, smem_base: Int32, tid: Int32
  3276. ):
  3277. red_off = self.cta_threads // self.b_sh_stride_threads // 2
  3278. if cutlass.const_expr(red_off >= 1):
  3279. red_idx, red_sh_stride, red_sh_delta, red_sh_rd = self._reduction_offsets(
  3280. tid
  3281. )
  3282. for mb in cutlass.range_constexpr(self.cta_m_blocks):
  3283. if cutlass.const_expr(red_off == 2):
  3284. if Int32(2) <= red_idx and red_idx < Int32(4):
  3285. for flat_j in cutlass.range_constexpr(8):
  3286. jj = flat_j // 2
  3287. half = flat_j % 2
  3288. red_sh_wr = red_sh_delta * Int32(flat_j) + (
  3289. red_sh_rd - red_sh_stride * Int32(2)
  3290. )
  3291. st_shared_v4_f32(
  3292. self._int4_addr(
  3293. smem_base, Int32(self.sh_red_off) + red_sh_wr
  3294. ),
  3295. acc[mb, jj, half, 0],
  3296. acc[mb, jj, half, 1],
  3297. acc[mb, jj, half, 2],
  3298. acc[mb, jj, half, 3],
  3299. )
  3300. cute.arch.sync_threads()
  3301. if Int32(1) <= red_idx and red_idx < Int32(2):
  3302. for flat_j in cutlass.range_constexpr(8):
  3303. jj = flat_j // 2
  3304. half = flat_j % 2
  3305. red_sh_wr = red_sh_delta * Int32(flat_j) + (
  3306. red_sh_rd - red_sh_stride
  3307. )
  3308. if cutlass.const_expr(red_off > 1):
  3309. rd_addr = self._int4_addr(
  3310. smem_base,
  3311. Int32(self.sh_red_off)
  3312. + red_sh_delta * Int32(flat_j)
  3313. + red_sh_rd,
  3314. )
  3315. wr_addr = self._int4_addr(
  3316. smem_base,
  3317. Int32(self.sh_red_off) + red_sh_wr,
  3318. )
  3319. r0, r1, r2, r3 = ld_shared_v4_f32(rd_addr)
  3320. w0, w1, w2, w3 = ld_shared_v4_f32(wr_addr)
  3321. acc[mb, jj, half, 0] = acc[mb, jj, half, 0] + r0 + w0
  3322. acc[mb, jj, half, 1] = acc[mb, jj, half, 1] + r1 + w1
  3323. acc[mb, jj, half, 2] = acc[mb, jj, half, 2] + r2 + w2
  3324. acc[mb, jj, half, 3] = acc[mb, jj, half, 3] + r3 + w3
  3325. st_shared_v4_f32(
  3326. self._int4_addr(
  3327. smem_base, Int32(self.sh_red_off) + red_sh_wr
  3328. ),
  3329. acc[mb, jj, half, 0],
  3330. acc[mb, jj, half, 1],
  3331. acc[mb, jj, half, 2],
  3332. acc[mb, jj, half, 3],
  3333. )
  3334. cute.arch.sync_threads()
  3335. if red_idx == Int32(0):
  3336. for flat_j in cutlass.range_constexpr(8):
  3337. jj = flat_j // 2
  3338. half = flat_j % 2
  3339. rd_addr = self._int4_addr(
  3340. smem_base,
  3341. Int32(self.sh_red_off)
  3342. + red_sh_delta * Int32(flat_j)
  3343. + red_sh_rd,
  3344. )
  3345. r0, r1, r2, r3 = ld_shared_v4_f32(rd_addr)
  3346. acc[mb, jj, half, 0] = acc[mb, jj, half, 0] + r0
  3347. acc[mb, jj, half, 1] = acc[mb, jj, half, 1] + r1
  3348. acc[mb, jj, half, 2] = acc[mb, jj, half, 2] + r2
  3349. acc[mb, jj, half, 3] = acc[mb, jj, half, 3] + r3
  3350. cute.arch.sync_threads()
  3351. @cute.jit
  3352. def _write_bf16x2_shared(
  3353. self,
  3354. smem_base: Int32,
  3355. half2_idx: Int32,
  3356. c0: cutlass.Float32,
  3357. c1: cutlass.Float32,
  3358. write_scale: cutlass.Float32,
  3359. ):
  3360. packed = self._pack_f32x2_to_elem2(c0 * write_scale, c1 * write_scale)
  3361. st_shared_u32(
  3362. smem_base + Int32(self.sh_red_off * 16) + half2_idx * Int32(4),
  3363. packed,
  3364. )
  3365. @cute.jit
  3366. def _store_tile_large_m(
  3367. self,
  3368. acc: cute.Tensor,
  3369. c_bf16_flat: cute.Tensor,
  3370. smem_base: Int32,
  3371. tid: Int32,
  3372. output_n_tile: Int32,
  3373. block_valid_rows: Int32,
  3374. global_scale_f32: cutlass.Float32,
  3375. ):
  3376. if cutlass.const_expr(self.has_n_tile_tail):
  3377. (
  3378. c_gl_stride,
  3379. c_gl_stride_covered,
  3380. c_sh_stride,
  3381. c_gl_wr_delta,
  3382. c_sh_rd_delta,
  3383. c_gl_wr,
  3384. c_sh_rd,
  3385. ) = self._output_store_cursor_tail(tid, output_n_tile)
  3386. else:
  3387. (
  3388. c_gl_stride,
  3389. c_sh_stride,
  3390. c_gl_wr_delta,
  3391. c_sh_rd_delta,
  3392. c_gl_wr,
  3393. c_sh_rd,
  3394. ) = self._output_store_cursor(tid, output_n_tile)
  3395. c_gl_stride_covered = c_gl_stride
  3396. c_sh_wr = (
  3397. Int32(4) * c_sh_stride * ((tid & Int32(31)) // Int32(4))
  3398. + (tid & Int32(31)) % Int32(4)
  3399. + Int32(32) * (tid // Int32(32))
  3400. )
  3401. if tid // Int32(32) < Int32(self.tb_n_warps):
  3402. write_scale = cutlass.Float32(1.0)
  3403. if cutlass.const_expr(not self.mul_topk_weights):
  3404. write_scale = global_scale_f32
  3405. for mb in cutlass.range_constexpr(self.cta_m_blocks):
  3406. for jj in cutlass.range_constexpr(4):
  3407. wr = c_sh_wr + Int32(8 * jj)
  3408. self._write_bf16x2_shared(
  3409. smem_base,
  3410. wr,
  3411. acc[mb, jj, 0, 0],
  3412. acc[mb, jj, 0, 1],
  3413. write_scale,
  3414. )
  3415. self._write_bf16x2_shared(
  3416. smem_base,
  3417. wr + (Int32(4) * c_sh_stride) * Int32(8) + Int32(0),
  3418. acc[mb, jj, 0, 2],
  3419. acc[mb, jj, 0, 3],
  3420. write_scale,
  3421. )
  3422. self._write_bf16x2_shared(
  3423. smem_base,
  3424. wr + Int32(4),
  3425. acc[mb, jj, 1, 0],
  3426. acc[mb, jj, 1, 1],
  3427. write_scale,
  3428. )
  3429. self._write_bf16x2_shared(
  3430. smem_base,
  3431. wr + (Int32(4) * c_sh_stride) * Int32(8) + Int32(4),
  3432. acc[mb, jj, 1, 2],
  3433. acc[mb, jj, 1, 3],
  3434. write_scale,
  3435. )
  3436. c_sh_wr += Int32(16 * (4 * (2 * self.cta_n_blocks + 1)))
  3437. cute.arch.sync_threads()
  3438. store_iters = _covering_count(
  3439. 16 * self.cta_m_blocks,
  3440. self.cta_threads // (2 * self.cta_n_blocks),
  3441. )
  3442. if cutlass.const_expr(self.has_n_tile_tail):
  3443. self._drain_output_smem_tail(
  3444. c_bf16_flat,
  3445. smem_base,
  3446. c_gl_stride,
  3447. c_gl_stride_covered,
  3448. c_gl_wr,
  3449. c_gl_wr_delta,
  3450. c_sh_rd,
  3451. c_sh_rd_delta,
  3452. block_valid_rows,
  3453. store_iters,
  3454. )
  3455. else:
  3456. self._drain_output_smem(
  3457. c_bf16_flat,
  3458. smem_base,
  3459. c_gl_stride,
  3460. c_gl_wr,
  3461. c_gl_wr_delta,
  3462. c_sh_rd,
  3463. c_sh_rd_delta,
  3464. block_valid_rows,
  3465. store_iters,
  3466. )
  3467. class W4A16FusedMoeKernel:
  3468. def __init__(
  3469. self,
  3470. *,
  3471. size_m: int,
  3472. hidden_size: int,
  3473. intermediate_size: int,
  3474. num_experts: int,
  3475. top_k: int,
  3476. activation: str,
  3477. apply_router_weight_on_input: bool,
  3478. zero_fc2_output: bool,
  3479. fc1_tile_n: int,
  3480. fc1_tile_k: int,
  3481. fc2_tile_n: int,
  3482. fc2_tile_k: int,
  3483. moe_block_size: int,
  3484. max_m_blocks: int,
  3485. element_dtype: str = "bf16",
  3486. fast_math: bool = True,
  3487. swiglu_limit: float | None = None,
  3488. swiglu_alpha: float | None = None,
  3489. swiglu_beta: float | None = None,
  3490. weight_layout: str = "packed",
  3491. scale_format: str = "e4m3_k16",
  3492. w13_layout: str = "w13",
  3493. direct_topk_routes: bool = False,
  3494. tc_decode_fused_sum: bool = False,
  3495. tc_zero_output: bool = True,
  3496. collect_activation_amax: bool = False,
  3497. ):
  3498. activation = normalize_moe_activation(activation)
  3499. is_gated = validate_activation(activation)
  3500. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  3501. activation,
  3502. swiglu_limit,
  3503. swiglu_alpha,
  3504. swiglu_beta,
  3505. )
  3506. if weight_layout not in _WEIGHT_LAYOUTS:
  3507. raise ValueError(f"unsupported W4A16 weight_layout {weight_layout!r}")
  3508. scale_format = _normalize_scale_format(scale_format)
  3509. if weight_layout == "modelopt":
  3510. if w13_layout not in _MODEL_OPT_W13_LAYOUTS:
  3511. raise ValueError(f"unsupported W4A16 w13_layout {w13_layout!r}")
  3512. else:
  3513. w13_layout = "packed"
  3514. self.tc_decode_fused_sum = bool(tc_decode_fused_sum)
  3515. # When two TC-decode launches share one pre-zeroed output (the NF3 hybrid
  3516. # runs an NVFP4 launch then an NF3 launch into the same tensor), only the
  3517. # first must zero it. Default True preserves single-launch behavior.
  3518. self.tc_zero_output = bool(tc_zero_output)
  3519. self.collect_activation_amax = bool(collect_activation_amax)
  3520. if self.collect_activation_amax and bool(direct_topk_routes):
  3521. raise ValueError("activation amax collection requires route-packed W4A16")
  3522. if self.collect_activation_amax and self.tc_decode_fused_sum:
  3523. raise ValueError("activation amax collection is incompatible with TC-decode")
  3524. if self.tc_decode_fused_sum and not bool(direct_topk_routes):
  3525. raise ValueError("tc_decode_fused_sum requires direct_topk_routes")
  3526. if self.tc_decode_fused_sum and element_dtype != "bf16":
  3527. raise ValueError("tc_decode_fused_sum currently requires bf16 activations")
  3528. fc1_cols = int(intermediate_size) * (2 if is_gated else 1)
  3529. routed_rows = int(size_m) * int(top_k)
  3530. self.size_m = int(size_m)
  3531. self.hidden_size = int(hidden_size)
  3532. self.intermediate_size = int(intermediate_size)
  3533. self.fc1_cols = int(fc1_cols)
  3534. self.num_experts = int(num_experts)
  3535. self.top_k = int(top_k)
  3536. self.moe_block_size = int(moe_block_size)
  3537. self.activation = activation
  3538. self.activation_is_gated = is_gated
  3539. self.activation_is_swigluoai = activation == SWIGLUOAI_UNINTERLEAVE
  3540. self.has_swiglu_limit = swiglu_limit is not None
  3541. self.swiglu_limit = 0.0 if swiglu_limit is None else swiglu_limit
  3542. self.swiglu_alpha = float(swiglu_alpha)
  3543. self.swiglu_beta = float(swiglu_beta)
  3544. self.weight_layout = weight_layout
  3545. self.scale_format = scale_format
  3546. self.w13_layout = w13_layout
  3547. self.apply_router_weight_on_input = bool(apply_router_weight_on_input)
  3548. self.zero_fc2_output = bool(zero_fc2_output)
  3549. self.element_dtype = element_dtype
  3550. self.is_fp16 = element_dtype == "fp16"
  3551. self.fast_math = bool(fast_math)
  3552. self.direct_topk_routes = bool(direct_topk_routes)
  3553. fc1_source_n_rotation = (
  3554. int(intermediate_size)
  3555. if (
  3556. weight_layout == "modelopt"
  3557. and w13_layout == "w13"
  3558. and is_gated
  3559. )
  3560. else 0
  3561. )
  3562. self.fc1 = W4A16GemmKernel(
  3563. size_m=size_m,
  3564. size_n=fc1_cols,
  3565. size_k=hidden_size,
  3566. num_experts=num_experts,
  3567. top_k=top_k,
  3568. mul_topk_weights=bool(apply_router_weight_on_input),
  3569. tile_n=fc1_tile_n,
  3570. tile_k=fc1_tile_k,
  3571. moe_block_size=moe_block_size,
  3572. max_m_blocks=max_m_blocks,
  3573. element_dtype=element_dtype,
  3574. epilogue_activation=None if is_gated else "relu2",
  3575. weight_layout=weight_layout,
  3576. scale_format=scale_format,
  3577. w13_layout=w13_layout,
  3578. source_n_rotation=fc1_source_n_rotation,
  3579. single_token_route_fast_path=size_m == 1
  3580. and not self.direct_topk_routes,
  3581. direct_topk_routes=self.direct_topk_routes,
  3582. )
  3583. self.fc2 = W4A16GemmKernel(
  3584. size_m=routed_rows,
  3585. size_n=hidden_size,
  3586. size_k=intermediate_size,
  3587. num_experts=num_experts,
  3588. top_k=1,
  3589. mul_topk_weights=not bool(apply_router_weight_on_input),
  3590. tile_n=fc2_tile_n,
  3591. tile_k=fc2_tile_k,
  3592. moe_block_size=moe_block_size,
  3593. max_m_blocks=max_m_blocks,
  3594. element_dtype=element_dtype,
  3595. weight_layout=weight_layout,
  3596. scale_format=scale_format,
  3597. w13_layout=w13_layout,
  3598. single_token_route_fast_path=size_m == 1
  3599. and not self.direct_topk_routes,
  3600. direct_topk_routes=self.direct_topk_routes,
  3601. fused_topk_sum=self.tc_decode_fused_sum,
  3602. fused_sum_topk=int(top_k),
  3603. )
  3604. self.cta_threads = max(self.fc1.cta_threads, self.fc2.cta_threads)
  3605. if self.fc1.cta_threads != self.fc2.cta_threads:
  3606. raise ValueError(
  3607. "fused W4A16 kernel expects matching FC1/FC2 thread counts"
  3608. )
  3609. self.sms = self.fc1.sms
  3610. self.blocks_per_sm = min(self.fc1.blocks_per_sm, self.fc2.blocks_per_sm)
  3611. self.shared_words = max(self.fc1.shared_words, self.fc2.shared_words)
  3612. self.barrier_count_off = self.sms * 4
  3613. self.barrier_sense_off = self.sms * 4 + 1
  3614. @property
  3615. def __cache_key__(self) -> tuple[object, ...]:
  3616. return (
  3617. self.hidden_size,
  3618. self.intermediate_size,
  3619. self.fc1_cols,
  3620. self.num_experts,
  3621. self.top_k,
  3622. self.activation,
  3623. self.activation_is_gated,
  3624. self.activation_is_swigluoai,
  3625. self.has_swiglu_limit,
  3626. self.swiglu_limit,
  3627. self.swiglu_alpha,
  3628. self.swiglu_beta,
  3629. self.weight_layout,
  3630. self.scale_format,
  3631. self.apply_router_weight_on_input,
  3632. self.zero_fc2_output,
  3633. self.element_dtype,
  3634. self.fast_math,
  3635. self.direct_topk_routes,
  3636. self.tc_zero_output,
  3637. self.collect_activation_amax,
  3638. self.fc1.__cache_key__,
  3639. self.fc2.__cache_key__,
  3640. self.cta_threads,
  3641. self.sms,
  3642. self.shared_words,
  3643. self.blocks_per_sm,
  3644. )
  3645. @cute.jit
  3646. def _cast_elem(self, x: cutlass.Float32):
  3647. if cutlass.const_expr(self.is_fp16):
  3648. return cutlass.Float16(x)
  3649. return cutlass.BFloat16(x)
  3650. @cute.jit
  3651. def _clamp_swiglu_inputs(
  3652. self,
  3653. gate: cutlass.Float32,
  3654. up: cutlass.Float32,
  3655. ):
  3656. if cutlass.const_expr(self.has_swiglu_limit):
  3657. limit = cutlass.Float32(self.swiglu_limit)
  3658. neg_limit = cutlass.Float32(-self.swiglu_limit)
  3659. if gate > limit:
  3660. gate = limit
  3661. if up > limit:
  3662. up = limit
  3663. if up < neg_limit:
  3664. up = neg_limit
  3665. return gate, up
  3666. @cute.jit
  3667. def __call__(
  3668. self,
  3669. a_bf16_ptr: cute.Pointer,
  3670. w13_i32_flat: cute.Tensor,
  3671. w2_i32_flat: cute.Tensor,
  3672. fc1_bf16_flat: cute.Tensor,
  3673. activated_bf16_flat: cute.Tensor,
  3674. fc2_bf16_flat: cute.Tensor,
  3675. w13_scales_i32_flat: cute.Tensor,
  3676. w2_scales_i32_flat: cute.Tensor,
  3677. w13_global_scale: cute.Tensor,
  3678. w2_global_scale: cute.Tensor,
  3679. packed_route_indices: cute.Tensor,
  3680. block_expert_ids: cute.Tensor,
  3681. packed_route_count: cute.Tensor,
  3682. activation_amax_flat: cute.Tensor,
  3683. layer_idx: cutlass.Int32,
  3684. topk_weights_ptr: cute.Pointer,
  3685. fc1_c_tmp_f32_flat: cute.Tensor,
  3686. fc2_c_tmp_f32_flat: cute.Tensor,
  3687. locks_i32_flat: cute.Tensor,
  3688. active_m: cutlass.Int32,
  3689. grid_x: cutlass.Int32,
  3690. stream: cuda.CUstream,
  3691. ):
  3692. a_bf16_flat = cute.make_tensor(
  3693. a_bf16_ptr,
  3694. layout=cute.make_layout(
  3695. (active_m * Int32(self.hidden_size),), stride=(1,)
  3696. ),
  3697. )
  3698. topk_weights_flat = cute.make_tensor(
  3699. topk_weights_ptr,
  3700. layout=cute.make_layout((active_m * Int32(self.top_k),), stride=(1,)),
  3701. )
  3702. grid = (grid_x, 1, 1)
  3703. self.kernel(
  3704. a_bf16_flat,
  3705. w13_i32_flat,
  3706. w2_i32_flat,
  3707. fc1_bf16_flat,
  3708. activated_bf16_flat,
  3709. fc2_bf16_flat,
  3710. w13_scales_i32_flat,
  3711. w2_scales_i32_flat,
  3712. w13_global_scale,
  3713. w2_global_scale,
  3714. packed_route_indices,
  3715. block_expert_ids,
  3716. packed_route_count,
  3717. activation_amax_flat,
  3718. layer_idx,
  3719. topk_weights_flat,
  3720. fc1_c_tmp_f32_flat,
  3721. fc2_c_tmp_f32_flat,
  3722. locks_i32_flat,
  3723. active_m,
  3724. ).launch(
  3725. grid=grid,
  3726. block=[self.cta_threads, 1, 1],
  3727. min_blocks_per_mp=self.blocks_per_sm,
  3728. stream=stream,
  3729. )
  3730. @cute.kernel
  3731. def kernel(
  3732. self,
  3733. a_bf16_flat: cute.Tensor,
  3734. w13_i32_flat: cute.Tensor,
  3735. w2_i32_flat: cute.Tensor,
  3736. fc1_bf16_flat: cute.Tensor,
  3737. activated_bf16_flat: cute.Tensor,
  3738. fc2_bf16_flat: cute.Tensor,
  3739. w13_scales_i32_flat: cute.Tensor,
  3740. w2_scales_i32_flat: cute.Tensor,
  3741. w13_global_scale: cute.Tensor,
  3742. w2_global_scale: cute.Tensor,
  3743. packed_route_indices: cute.Tensor,
  3744. block_expert_ids: cute.Tensor,
  3745. packed_route_count: cute.Tensor,
  3746. activation_amax_flat: cute.Tensor,
  3747. layer_idx: cutlass.Int32,
  3748. topk_weights_flat: cute.Tensor,
  3749. fc1_c_tmp_f32_flat: cute.Tensor,
  3750. fc2_c_tmp_f32_flat: cute.Tensor,
  3751. locks_i32_flat: cute.Tensor,
  3752. active_m: cutlass.Int32,
  3753. ):
  3754. tidx, _, _ = cute.arch.thread_idx()
  3755. bidx, _, _ = cute.arch.block_idx()
  3756. grid_x_raw, _, _ = cute.arch.grid_dim()
  3757. tid = Int32(tidx)
  3758. cta = Int32(bidx)
  3759. grid_x = Int32(grid_x_raw)
  3760. smem = cutlass.utils.SmemAllocator()
  3761. @cute.struct
  3762. class Storage:
  3763. words: cute.struct.Align[
  3764. cute.struct.MemRange[cutlass.Uint32, self.shared_words],
  3765. 1024,
  3766. ]
  3767. storage = smem.allocate(Storage)
  3768. smem_base = shared_ptr_to_u32(storage.words.data_ptr())
  3769. if cutlass.const_expr(self.tc_decode_fused_sum):
  3770. # The TC-decode FC2 epilogue atomically accumulates per-route
  3771. # partials directly into the per-token output, so the output must be
  3772. # pre-zeroed. Previously this was a SEPARATE host-side output.zero_()
  3773. # kernel launch on the latency-bound decode critical path (an extra
  3774. # launch + its grid-fill memset before the fused kernel even starts).
  3775. # Fold it into the fused kernel prologue here: every CTA zeroes a
  3776. # grid-strided slice of the output BEFORE FC1, and the EXISTING
  3777. # post-FC1 grid barrier (already required to order FC1 writes before
  3778. # the activation/FC2 read) makes all zero stores globally visible
  3779. # before the first FC2 atomic -- so no extra barrier is added. The
  3780. # tiny m*hidden bf16 memset (decode: <=4*4096 elems) is dwarfed by
  3781. # FC1's whole-K FP4-weight stream, but we delete one whole kernel
  3782. # launch from the per-decode chain. The TC-decode output is per-token
  3783. # (top_k routes atomically summed into the SAME token row), so the
  3784. # zero span is active_m*hidden_size -- NOT the per-route
  3785. # active_m*top_k*hidden_size of _zero_fc2_output.
  3786. # tc_zero_output=False skips the zero (a paired earlier launch has
  3787. # already zeroed the shared output); the grid barrier below is
  3788. # unconditional so ordering is preserved either way.
  3789. if cutlass.const_expr(self.tc_zero_output):
  3790. zidx = cta * Int32(self.cta_threads) + tid
  3791. zstride = grid_x * Int32(self.cta_threads)
  3792. ztotal = active_m * Int32(self.hidden_size)
  3793. zzero = self._cast_elem(cutlass.Float32(0.0))
  3794. while zidx < ztotal:
  3795. fc2_bf16_flat[zidx] = zzero
  3796. zidx += zstride
  3797. if cutlass.const_expr(self.activation_is_gated):
  3798. self.fc1._run_persistent_gemm(
  3799. a_bf16_flat,
  3800. w13_i32_flat,
  3801. fc1_bf16_flat,
  3802. w13_scales_i32_flat,
  3803. w13_global_scale,
  3804. packed_route_indices,
  3805. block_expert_ids,
  3806. packed_route_count,
  3807. topk_weights_flat,
  3808. fc1_c_tmp_f32_flat,
  3809. locks_i32_flat,
  3810. smem_base,
  3811. tid,
  3812. cta,
  3813. grid_x,
  3814. active_m,
  3815. )
  3816. self._grid_barrier(locks_i32_flat, tid, grid_x)
  3817. self._run_activation(
  3818. fc1_bf16_flat,
  3819. activated_bf16_flat,
  3820. tid,
  3821. cta,
  3822. grid_x,
  3823. active_m,
  3824. )
  3825. else:
  3826. self.fc1._run_persistent_gemm(
  3827. a_bf16_flat,
  3828. w13_i32_flat,
  3829. activated_bf16_flat,
  3830. w13_scales_i32_flat,
  3831. w13_global_scale,
  3832. packed_route_indices,
  3833. block_expert_ids,
  3834. packed_route_count,
  3835. topk_weights_flat,
  3836. fc1_c_tmp_f32_flat,
  3837. locks_i32_flat,
  3838. smem_base,
  3839. tid,
  3840. cta,
  3841. grid_x,
  3842. active_m,
  3843. )
  3844. self._grid_barrier(locks_i32_flat, tid, grid_x)
  3845. if cutlass.const_expr(self.collect_activation_amax):
  3846. self._collect_activation_amax_epilogue(
  3847. a_bf16_flat,
  3848. activated_bf16_flat,
  3849. packed_route_indices,
  3850. block_expert_ids,
  3851. packed_route_count,
  3852. activation_amax_flat,
  3853. layer_idx,
  3854. smem_base,
  3855. tid,
  3856. cta,
  3857. grid_x,
  3858. active_m,
  3859. )
  3860. if cutlass.const_expr(self.zero_fc2_output):
  3861. self._zero_fc2_output(fc2_bf16_flat, tid, cta, grid_x, active_m)
  3862. self._grid_barrier(locks_i32_flat, tid, grid_x)
  3863. self.fc2._run_persistent_gemm(
  3864. activated_bf16_flat,
  3865. w2_i32_flat,
  3866. fc2_bf16_flat,
  3867. w2_scales_i32_flat,
  3868. w2_global_scale,
  3869. packed_route_indices,
  3870. block_expert_ids,
  3871. packed_route_count,
  3872. topk_weights_flat,
  3873. fc2_c_tmp_f32_flat,
  3874. locks_i32_flat,
  3875. smem_base,
  3876. tid,
  3877. cta,
  3878. grid_x,
  3879. active_m * Int32(self.top_k),
  3880. )
  3881. @cute.jit
  3882. def _grid_barrier(
  3883. self,
  3884. locks_i32_flat: cute.Tensor,
  3885. tid: Int32,
  3886. grid_x: Int32,
  3887. ):
  3888. cute.arch.sync_threads()
  3889. if tid == Int32(0):
  3890. count_addr = get_ptr_as_int64(locks_i32_flat, Int32(self.barrier_count_off))
  3891. sense_addr = get_ptr_as_int64(locks_i32_flat, Int32(self.barrier_sense_off))
  3892. old_sense = ld_global_acquire_i32(sense_addr)
  3893. old_count = atomic_add_global_i32(count_addr, Int32(1))
  3894. if old_count == grid_x - Int32(1):
  3895. st_global_i32(count_addr, Int32(0))
  3896. threadfence()
  3897. red_add_global_release_i32(sense_addr, Int32(1))
  3898. else:
  3899. sense = old_sense
  3900. while sense == old_sense:
  3901. sense = ld_global_acquire_i32(sense_addr)
  3902. cute.arch.sync_threads()
  3903. @cute.jit
  3904. def _zero_fc2_output(
  3905. self,
  3906. fc2_bf16_flat: cute.Tensor,
  3907. tid: Int32,
  3908. cta: Int32,
  3909. grid_x: Int32,
  3910. active_m: cutlass.Int32,
  3911. ):
  3912. idx = cta * Int32(self.cta_threads) + tid
  3913. stride = grid_x * Int32(self.cta_threads)
  3914. total = active_m * Int32(self.top_k * self.hidden_size)
  3915. zero = self._cast_elem(cutlass.Float32(0.0))
  3916. while idx < total:
  3917. fc2_bf16_flat[idx] = zero
  3918. idx += stride
  3919. @cute.jit
  3920. def _reduce_and_red_activation_amax(
  3921. self,
  3922. local_max: cutlass.Float32,
  3923. activation_amax_flat: cute.Tensor,
  3924. layer_idx: cutlass.Int32,
  3925. expert_idx: Int32,
  3926. slot: Int32,
  3927. smem_base: Int32,
  3928. tid: Int32,
  3929. ):
  3930. lane = tid & Int32(31)
  3931. warp_idx = tid // Int32(32)
  3932. warp_amax = warp_reduce(local_max, fmax_f32)
  3933. if lane == Int32(0):
  3934. st_shared_f32(smem_base + warp_idx * Int32(4), warp_amax)
  3935. cute.arch.sync_threads()
  3936. if warp_idx == Int32(0):
  3937. block_amax = cutlass.Float32(0.0)
  3938. if lane < Int32(self.cta_threads // 32):
  3939. block_amax = ld_shared_f32(smem_base + lane * Int32(4))
  3940. block_amax = warp_reduce(block_amax, fmax_f32)
  3941. if lane == Int32(0) and block_amax > cutlass.Float32(0.0):
  3942. out_idx = (
  3943. (layer_idx * Int32(self.num_experts) + expert_idx) * Int32(2)
  3944. + slot
  3945. )
  3946. red_max_global_f32_nonnegative(
  3947. get_ptr_as_int64(activation_amax_flat, out_idx),
  3948. block_amax,
  3949. )
  3950. cute.arch.sync_threads()
  3951. @cute.jit
  3952. def _collect_activation_amax_epilogue(
  3953. self,
  3954. a_bf16_flat: cute.Tensor,
  3955. activated_bf16_flat: cute.Tensor,
  3956. packed_route_indices: cute.Tensor,
  3957. block_expert_ids: cute.Tensor,
  3958. packed_route_count: cute.Tensor,
  3959. activation_amax_flat: cute.Tensor,
  3960. layer_idx: cutlass.Int32,
  3961. smem_base: Int32,
  3962. tid: Int32,
  3963. cta: Int32,
  3964. grid_x: Int32,
  3965. active_m: cutlass.Int32,
  3966. ):
  3967. live_routes = active_m * Int32(self.top_k)
  3968. route_count = packed_route_count[Int32(0)].to(Int32)
  3969. route_blocks = (
  3970. route_count + Int32(self.moe_block_size) - Int32(1)
  3971. ) // Int32(self.moe_block_size)
  3972. expert_idx = cta
  3973. while expert_idx < Int32(self.num_experts):
  3974. local_fc1 = cutlass.Float32(0.0)
  3975. local_fc2 = cutlass.Float32(0.0)
  3976. has_route_block = Int32(0)
  3977. block_idx = Int32(0)
  3978. while block_idx < route_blocks:
  3979. block_expert = block_expert_ids[block_idx].to(Int32)
  3980. if block_expert == expert_idx:
  3981. has_route_block = Int32(1)
  3982. route_pos = block_idx * Int32(self.moe_block_size)
  3983. route_stop = route_pos + Int32(self.moe_block_size)
  3984. if route_stop > route_count:
  3985. route_stop = route_count
  3986. while route_pos < route_stop:
  3987. route_idx = packed_route_indices[route_pos].to(Int32)
  3988. if route_idx >= Int32(0) and route_idx < live_routes:
  3989. token_idx = route_idx // Int32(self.top_k)
  3990. fc1_col = tid
  3991. while fc1_col < Int32(self.hidden_size):
  3992. v1 = a_bf16_flat[
  3993. token_idx * Int32(self.hidden_size) + fc1_col
  3994. ].to(cutlass.Float32)
  3995. local_fc1 = fmax_f32(local_fc1, fabs_f32(v1))
  3996. fc1_col += Int32(self.cta_threads)
  3997. fc2_col = tid
  3998. while fc2_col < Int32(self.intermediate_size):
  3999. v2 = activated_bf16_flat[
  4000. route_idx * Int32(self.intermediate_size) + fc2_col
  4001. ].to(cutlass.Float32)
  4002. local_fc2 = fmax_f32(local_fc2, fabs_f32(v2))
  4003. fc2_col += Int32(self.cta_threads)
  4004. route_pos += Int32(1)
  4005. block_idx += Int32(1)
  4006. if has_route_block != Int32(0):
  4007. self._reduce_and_red_activation_amax(
  4008. local_fc1,
  4009. activation_amax_flat,
  4010. layer_idx,
  4011. expert_idx,
  4012. Int32(0),
  4013. smem_base,
  4014. tid,
  4015. )
  4016. self._reduce_and_red_activation_amax(
  4017. local_fc2,
  4018. activation_amax_flat,
  4019. layer_idx,
  4020. expert_idx,
  4021. Int32(1),
  4022. smem_base,
  4023. tid,
  4024. )
  4025. expert_idx += grid_x
  4026. @cute.jit
  4027. def _run_activation(
  4028. self,
  4029. fc1_bf16_flat: cute.Tensor,
  4030. activated_bf16_flat: cute.Tensor,
  4031. tid: Int32,
  4032. cta: Int32,
  4033. grid_x: Int32,
  4034. active_m: cutlass.Int32,
  4035. ):
  4036. idx = cta * Int32(self.cta_threads) + tid
  4037. stride = grid_x * Int32(self.cta_threads)
  4038. total = active_m * Int32(self.top_k * self.intermediate_size)
  4039. while idx < total:
  4040. if cutlass.const_expr(self.activation_is_gated):
  4041. row = idx // Int32(self.intermediate_size)
  4042. col = idx - row * Int32(self.intermediate_size)
  4043. base = row * Int32(self.fc1_cols)
  4044. gate = fc1_bf16_flat[base + col].to(cutlass.Float32)
  4045. up = fc1_bf16_flat[base + Int32(self.intermediate_size) + col].to(
  4046. cutlass.Float32
  4047. )
  4048. gate, up = self._clamp_swiglu_inputs(gate, up)
  4049. sigmoid_arg = gate
  4050. up_term = up
  4051. if cutlass.const_expr(self.activation_is_swigluoai):
  4052. sigmoid_arg = cutlass.Float32(self.swiglu_alpha) * gate
  4053. up_term = up + cutlass.Float32(self.swiglu_beta)
  4054. if cutlass.const_expr(self.fast_math):
  4055. exp_neg_gate = cute.math.exp(-sigmoid_arg, fastmath=True)
  4056. else:
  4057. exp_neg_gate = cute.math.exp(-sigmoid_arg, fastmath=False)
  4058. silu = gate / (cutlass.Float32(1.0) + exp_neg_gate)
  4059. if cutlass.const_expr(self.activation_is_swigluoai):
  4060. activated_bf16_flat[idx] = self._cast_elem(silu * up_term)
  4061. else:
  4062. activated_bf16_flat[idx] = self._cast_elem(
  4063. self._cast_elem(silu) * self._cast_elem(up_term)
  4064. )
  4065. else:
  4066. x = fc1_bf16_flat[idx].to(cutlass.Float32)
  4067. if x < cutlass.Float32(0.0):
  4068. x = cutlass.Float32(0.0)
  4069. activated_bf16_flat[idx] = self._cast_elem(x * x)
  4070. idx += stride
  4071. class W4A16ActivationKernel:
  4072. def __init__(
  4073. self,
  4074. *,
  4075. rows: int,
  4076. intermediate_size: int,
  4077. activation: str,
  4078. element_dtype: str = "bf16",
  4079. fast_math: bool = True,
  4080. swiglu_limit: float | None = None,
  4081. swiglu_alpha: float | None = None,
  4082. swiglu_beta: float | None = None,
  4083. ):
  4084. activation = normalize_moe_activation(activation)
  4085. is_gated = validate_activation(activation)
  4086. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  4087. activation,
  4088. swiglu_limit,
  4089. swiglu_alpha,
  4090. swiglu_beta,
  4091. )
  4092. if element_dtype not in {"bf16", "fp16"}:
  4093. raise ValueError(f"unsupported element_dtype {element_dtype!r}")
  4094. if rows <= 0 or intermediate_size <= 0:
  4095. raise ValueError("rows and intermediate_size must be positive")
  4096. self.rows = int(rows)
  4097. self.intermediate_size = int(intermediate_size)
  4098. self.activation = activation
  4099. self.is_gated = is_gated
  4100. self.is_swigluoai = activation == SWIGLUOAI_UNINTERLEAVE
  4101. self.has_swiglu_limit = swiglu_limit is not None
  4102. self.swiglu_limit = 0.0 if swiglu_limit is None else swiglu_limit
  4103. self.swiglu_alpha = float(swiglu_alpha)
  4104. self.swiglu_beta = float(swiglu_beta)
  4105. self.element_dtype = element_dtype
  4106. self.is_fp16 = element_dtype == "fp16"
  4107. self.fast_math = bool(fast_math)
  4108. self.cta_threads = 256
  4109. @property
  4110. def __cache_key__(self) -> tuple[object, ...]:
  4111. return (
  4112. self.intermediate_size,
  4113. self.activation,
  4114. self.is_gated,
  4115. self.is_swigluoai,
  4116. self.has_swiglu_limit,
  4117. self.swiglu_limit,
  4118. self.swiglu_alpha,
  4119. self.swiglu_beta,
  4120. self.element_dtype,
  4121. self.fast_math,
  4122. self.cta_threads,
  4123. )
  4124. @cute.jit
  4125. def _cast_elem(self, x: cutlass.Float32):
  4126. if cutlass.const_expr(self.is_fp16):
  4127. return cutlass.Float16(x)
  4128. return cutlass.BFloat16(x)
  4129. @cute.jit
  4130. def _clamp_swiglu_inputs(
  4131. self,
  4132. gate: cutlass.Float32,
  4133. up: cutlass.Float32,
  4134. ):
  4135. if cutlass.const_expr(self.has_swiglu_limit):
  4136. limit = cutlass.Float32(self.swiglu_limit)
  4137. neg_limit = cutlass.Float32(-self.swiglu_limit)
  4138. if gate > limit:
  4139. gate = limit
  4140. if up > limit:
  4141. up = limit
  4142. if up < neg_limit:
  4143. up = neg_limit
  4144. return gate, up
  4145. @cute.jit
  4146. def __call__(
  4147. self,
  4148. fc1_flat: cute.Tensor,
  4149. activated_flat: cute.Tensor,
  4150. active_rows: cutlass.Int32,
  4151. stream: cuda.CUstream,
  4152. ):
  4153. total = active_rows * Int32(self.intermediate_size)
  4154. grid = (_covering_count(total, self.cta_threads), 1, 1)
  4155. self.kernel(fc1_flat, activated_flat, active_rows).launch(
  4156. grid=grid,
  4157. block=[self.cta_threads, 1, 1],
  4158. stream=stream,
  4159. )
  4160. @cute.kernel
  4161. def kernel(
  4162. self,
  4163. fc1_flat: cute.Tensor,
  4164. activated_flat: cute.Tensor,
  4165. active_rows: cutlass.Int32,
  4166. ):
  4167. tidx, _, _ = cute.arch.thread_idx()
  4168. bidx, _, _ = cute.arch.block_idx()
  4169. idx = Int32(bidx) * Int32(self.cta_threads) + Int32(tidx)
  4170. total = Int32(active_rows) * Int32(self.intermediate_size)
  4171. if idx < total:
  4172. if cutlass.const_expr(self.is_gated):
  4173. row = idx // Int32(self.intermediate_size)
  4174. col = idx - row * Int32(self.intermediate_size)
  4175. base = row * Int32(2 * self.intermediate_size)
  4176. gate = fc1_flat[base + col].to(cutlass.Float32)
  4177. up = fc1_flat[base + Int32(self.intermediate_size) + col].to(
  4178. cutlass.Float32
  4179. )
  4180. gate, up = self._clamp_swiglu_inputs(gate, up)
  4181. sigmoid_arg = gate
  4182. up_term = up
  4183. if cutlass.const_expr(self.is_swigluoai):
  4184. sigmoid_arg = cutlass.Float32(self.swiglu_alpha) * gate
  4185. up_term = up + cutlass.Float32(self.swiglu_beta)
  4186. if cutlass.const_expr(self.fast_math):
  4187. exp_neg_gate = cute.math.exp(-sigmoid_arg, fastmath=True)
  4188. else:
  4189. exp_neg_gate = cute.math.exp(-sigmoid_arg, fastmath=False)
  4190. silu = gate / (cutlass.Float32(1.0) + exp_neg_gate)
  4191. if cutlass.const_expr(self.is_swigluoai):
  4192. activated_flat[idx] = self._cast_elem(silu * up_term)
  4193. else:
  4194. activated_flat[idx] = self._cast_elem(
  4195. self._cast_elem(silu) * self._cast_elem(up_term)
  4196. )
  4197. else:
  4198. x = fc1_flat[idx].to(cutlass.Float32)
  4199. if x < cutlass.Float32(0.0):
  4200. x = cutlass.Float32(0.0)
  4201. activated_flat[idx] = self._cast_elem(x * x)
  4202. class W4A16TopKSumKernel:
  4203. def __init__(
  4204. self, *, topk: int, hidden_size: int, element_dtype: str = "bf16"
  4205. ):
  4206. if element_dtype not in {"bf16", "fp16"}:
  4207. raise ValueError(f"unsupported element_dtype {element_dtype!r}")
  4208. if topk <= 0 or hidden_size <= 0:
  4209. raise ValueError("topk and hidden_size must be positive")
  4210. self.topk = int(topk)
  4211. self.hidden_size = int(hidden_size)
  4212. self.element_dtype = element_dtype
  4213. self.is_fp16 = element_dtype == "fp16"
  4214. self.cta_threads = 256
  4215. @cute.jit
  4216. def _cast_elem(self, x: cutlass.Float32):
  4217. if cutlass.const_expr(self.is_fp16):
  4218. return cutlass.Float16(x)
  4219. return cutlass.BFloat16(x)
  4220. @cute.jit
  4221. def __call__(
  4222. self,
  4223. fc2_ptr: cute.Pointer,
  4224. output_ptr: cute.Pointer,
  4225. active_m: cutlass.Int32,
  4226. stream: cuda.CUstream,
  4227. ):
  4228. fc2_flat = cute.make_tensor(
  4229. fc2_ptr,
  4230. layout=cute.make_layout(
  4231. (active_m * Int32(self.topk * self.hidden_size),), stride=(1,)
  4232. ),
  4233. )
  4234. output_flat = cute.make_tensor(
  4235. output_ptr,
  4236. layout=cute.make_layout((active_m * Int32(self.hidden_size),), stride=(1,)),
  4237. )
  4238. total = active_m * Int32(self.hidden_size)
  4239. grid = (_covering_count(total, self.cta_threads), 1, 1)
  4240. self.kernel(fc2_flat, output_flat, active_m).launch(
  4241. grid=grid,
  4242. block=[self.cta_threads, 1, 1],
  4243. stream=stream,
  4244. )
  4245. @cute.kernel
  4246. def kernel(
  4247. self,
  4248. fc2_flat: cute.Tensor,
  4249. output_flat: cute.Tensor,
  4250. active_m: cutlass.Int32,
  4251. ):
  4252. tidx, _, _ = cute.arch.thread_idx()
  4253. bidx, _, _ = cute.arch.block_idx()
  4254. idx = Int32(bidx) * Int32(self.cta_threads) + Int32(tidx)
  4255. total = active_m * Int32(self.hidden_size)
  4256. if idx < total:
  4257. token = idx // Int32(self.hidden_size)
  4258. col = idx - token * Int32(self.hidden_size)
  4259. acc = cutlass.Float32(0.0)
  4260. for route in cutlass.range_constexpr(self.topk):
  4261. row = token * Int32(self.topk) + Int32(route)
  4262. acc += fc2_flat[row * Int32(self.hidden_size) + col].to(cutlass.Float32)
  4263. output_flat[idx] = self._cast_elem(acc)
  4264. _CACHE: dict[tuple, W4A16GemmCompileResult] = {}
  4265. _FUSED_CACHE: dict[tuple, W4A16FusedMoeCompileResult] = {}
  4266. _ACTIVATION_CACHE: dict[tuple, W4A16ActivationCompileResult] = {}
  4267. _SUM_CACHE: dict[tuple, W4A16TopKSumCompileResult] = {}
  4268. _SMALL_M_DIRECT_CACHE: dict[tuple, _W4A16SmallMDirectLaunch] = {}
  4269. def _normalize_element_dtype(dtype: torch.dtype) -> str:
  4270. if dtype == torch.bfloat16:
  4271. return "bf16"
  4272. if dtype == torch.float16:
  4273. return "fp16"
  4274. raise TypeError(f"unsupported W4A16 activation dtype {dtype}")
  4275. def _normalize_scale_format(scale_format: str) -> str:
  4276. try:
  4277. return _SCALE_FORMATS[scale_format.lower()]
  4278. except KeyError as exc:
  4279. raise ValueError(
  4280. "scale_format must be one of 'e4m3_k16', 'e8m0_k32', or 'e4m3_k32', "
  4281. f"got {scale_format!r}"
  4282. ) from exc
  4283. def _scale_group_size(scale_format: str) -> int:
  4284. return (
  4285. 32
  4286. if _normalize_scale_format(scale_format) in ("e8m0_k32", "e4m3_k32")
  4287. else 16
  4288. )
  4289. def _scale_fake_int32_elements(
  4290. *,
  4291. num_experts: int,
  4292. size_k: int,
  4293. size_n: int,
  4294. scale_format: str,
  4295. allow_k_tail: bool = False,
  4296. allow_n_tail: bool = False,
  4297. ) -> int:
  4298. group_size = _scale_group_size(scale_format)
  4299. if int(size_n) % 16 != 0:
  4300. raise ValueError(
  4301. f"W4A16 {scale_format} scales require size_n divisible by 16"
  4302. )
  4303. if int(size_k) % group_size != 0 and not allow_k_tail:
  4304. raise ValueError(
  4305. f"W4A16 {scale_format} scales require size_k divisible by {group_size}, "
  4306. f"got {size_k}"
  4307. )
  4308. groups_k = (
  4309. _covering_count(int(size_k), group_size)
  4310. if allow_k_tail
  4311. else int(size_k) // group_size
  4312. )
  4313. scale_size_n = (
  4314. _e8m0_logical_tail_scale_n(int(size_n)) if allow_n_tail else int(size_n)
  4315. )
  4316. return int(num_experts) * groups_k * (scale_size_n // 4)
  4317. def _cutlass_element_dtype(element_dtype: str):
  4318. if element_dtype == "bf16":
  4319. return cutlass.BFloat16
  4320. if element_dtype == "fp16":
  4321. return cutlass.Float16
  4322. raise ValueError(f"unsupported element_dtype {element_dtype!r}")
  4323. def _small_m_direct_supported(
  4324. *,
  4325. m: int,
  4326. hidden_size: int,
  4327. intermediate_size: int,
  4328. num_experts: int,
  4329. topk: int,
  4330. activation: str,
  4331. apply_router_weight_on_input: bool,
  4332. swiglu_limit: float | None,
  4333. swiglu_alpha: float,
  4334. swiglu_beta: float,
  4335. element_dtype: str,
  4336. weight_layout: str,
  4337. w13_layout: str,
  4338. scale_format: str = "e4m3_k16",
  4339. expert_map: torch.Tensor | None = None,
  4340. ) -> bool:
  4341. if os.environ.get("B12X_W4A16_SMALL_M_DIRECT", "1") == "0":
  4342. return False
  4343. activation = normalize_moe_activation(activation)
  4344. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  4345. activation,
  4346. swiglu_limit,
  4347. swiglu_alpha,
  4348. swiglu_beta,
  4349. )
  4350. if activation == SWIGLUOAI_UNINTERLEAVE and swiglu_limit is None:
  4351. return False
  4352. return (
  4353. element_dtype == "bf16"
  4354. # Packed serving weights use the W4A16 TC-decode path for small M.
  4355. # The direct micro kernel is kept limited to native ModelOpt weights.
  4356. and weight_layout == "modelopt"
  4357. and w13_layout in ("w13", "w31")
  4358. and not bool(apply_router_weight_on_input)
  4359. and (swiglu_limit is None or activation in ("silu", SWIGLUOAI_UNINTERLEAVE))
  4360. and expert_map is None
  4361. and _W4A16SmallMDirectKernel.is_supported(
  4362. m=m,
  4363. hidden_size=hidden_size,
  4364. intermediate_size=intermediate_size,
  4365. topk=topk,
  4366. num_experts=num_experts,
  4367. scale_format=scale_format,
  4368. )
  4369. )
  4370. def _compile_w4a16_small_m_direct(
  4371. *,
  4372. m: int,
  4373. hidden_size: int,
  4374. intermediate_size: int,
  4375. num_experts: int,
  4376. topk: int,
  4377. activation: str,
  4378. fast_math: bool,
  4379. topk_ids_dtype: torch.dtype,
  4380. device: torch.device | None,
  4381. scale_format: str = "e4m3_k16",
  4382. swiglu_limit: float | None = None,
  4383. swiglu_alpha: float | None = None,
  4384. swiglu_beta: float | None = None,
  4385. w13_layout: str = "w13",
  4386. ) -> _W4A16SmallMDirectLaunch:
  4387. if topk_ids_dtype not in (torch.int32, torch.int64):
  4388. raise TypeError("small-M W4A16 direct path requires int32/int64 topk_ids")
  4389. activation = normalize_moe_activation(activation)
  4390. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  4391. activation,
  4392. swiglu_limit,
  4393. swiglu_alpha,
  4394. swiglu_beta,
  4395. )
  4396. cache_key = (
  4397. "w4a16_small_m_direct",
  4398. None if device is None else int(device.index or 0),
  4399. int(m),
  4400. int(hidden_size),
  4401. int(intermediate_size),
  4402. int(num_experts),
  4403. int(topk),
  4404. activation,
  4405. bool(fast_math),
  4406. topk_ids_dtype,
  4407. scale_format,
  4408. swiglu_limit,
  4409. swiglu_alpha,
  4410. swiglu_beta,
  4411. w13_layout,
  4412. )
  4413. cached = _SMALL_M_DIRECT_CACHE.get(cache_key)
  4414. if cached is not None:
  4415. return cached
  4416. kernel = _W4A16SmallMDirectKernel(
  4417. activation=activation,
  4418. fast_math=bool(fast_math),
  4419. share_input_across_experts=(int(m) == 1),
  4420. share_expert_scales=True,
  4421. single_token=(int(m) == 1),
  4422. scale_format=scale_format,
  4423. swiglu_limit=swiglu_limit,
  4424. swiglu_alpha=swiglu_alpha,
  4425. swiglu_beta=swiglu_beta,
  4426. w13_layout=w13_layout,
  4427. )
  4428. kernel.configure(
  4429. int(m),
  4430. int(hidden_size),
  4431. int(intermediate_size),
  4432. int(topk),
  4433. int(num_experts),
  4434. device=device,
  4435. )
  4436. def dummy(dt):
  4437. return make_ptr(dt, 16, cute.AddressSpace.gmem, assumed_align=16)
  4438. ids_dtype = cutlass.Int32 if topk_ids_dtype == torch.int32 else cutlass.Int64
  4439. barrier_fake = cute.runtime.make_fake_compact_tensor(
  4440. cutlass.Int32,
  4441. (1,),
  4442. assumed_align=4,
  4443. )
  4444. raise_if_kernel_resolution_frozen(
  4445. "cute.compile", target=kernel, cache_key=cache_key
  4446. )
  4447. compiled = b12x_compile(
  4448. kernel,
  4449. dummy(cutlass.BFloat16),
  4450. dummy(cutlass.Uint8),
  4451. dummy(cutlass.Uint8),
  4452. dummy(cutlass.Float32),
  4453. dummy(cutlass.Float32),
  4454. dummy(cutlass.Float32),
  4455. dummy(cutlass.Uint32),
  4456. dummy(cutlass.Uint8),
  4457. dummy(cutlass.Uint8),
  4458. dummy(cutlass.Float32),
  4459. dummy(ids_dtype),
  4460. dummy(cutlass.Float32),
  4461. dummy(cutlass.BFloat16),
  4462. barrier_fake,
  4463. barrier_fake,
  4464. Int32(m),
  4465. Int32(kernel.grid_x),
  4466. current_cuda_stream(),
  4467. compile_spec=KernelCompileSpec.from_facts(
  4468. "moe.w4a16.small_m_direct",
  4469. 1,
  4470. ("device_index", None if device is None else int(device.index or 0)),
  4471. ("m", int(m)),
  4472. ("hidden_size", int(hidden_size)),
  4473. ("intermediate_size", int(intermediate_size)),
  4474. ("num_experts", int(num_experts)),
  4475. ("topk", int(topk)),
  4476. ("activation", activation),
  4477. ("fast_math", bool(fast_math)),
  4478. ("topk_ids_dtype", str(topk_ids_dtype)),
  4479. ("scale_format", scale_format),
  4480. ("swiglu_limit", swiglu_limit),
  4481. ("swiglu_alpha", swiglu_alpha),
  4482. ("swiglu_beta", swiglu_beta),
  4483. ("w13_layout", w13_layout),
  4484. ("grid_x", int(kernel.grid_x)),
  4485. ),
  4486. )
  4487. launch = _W4A16SmallMDirectLaunch(
  4488. compiled=compiled,
  4489. grid_x=int(kernel.grid_x),
  4490. m=int(m),
  4491. hidden_size=int(hidden_size),
  4492. intermediate_size=int(intermediate_size),
  4493. num_experts=int(num_experts),
  4494. topk=int(topk),
  4495. activation=activation,
  4496. fast_math=bool(fast_math),
  4497. topk_ids_dtype=topk_ids_dtype,
  4498. )
  4499. _SMALL_M_DIRECT_CACHE[cache_key] = launch
  4500. return launch
  4501. def compile_w4a16_gemm(
  4502. *,
  4503. size_m: int,
  4504. size_n: int,
  4505. size_k: int,
  4506. num_experts: int,
  4507. top_k: int,
  4508. mul_topk_weights: bool,
  4509. tile_n: int,
  4510. tile_k: int,
  4511. moe_block_size: int,
  4512. max_m_blocks: int,
  4513. element_dtype: str = "bf16",
  4514. scale_format: str = "e4m3_k16",
  4515. ) -> W4A16GemmCompileResult:
  4516. scale_format = _normalize_scale_format(scale_format)
  4517. cutlass_dtype = _cutlass_element_dtype(element_dtype)
  4518. if torch.cuda.is_available():
  4519. device = int(torch.cuda.current_device())
  4520. else:
  4521. device = None
  4522. kernel = W4A16GemmKernel(
  4523. size_m=size_m,
  4524. size_n=size_n,
  4525. size_k=size_k,
  4526. num_experts=num_experts,
  4527. top_k=top_k,
  4528. mul_topk_weights=mul_topk_weights,
  4529. tile_n=tile_n,
  4530. tile_k=tile_k,
  4531. moe_block_size=moe_block_size,
  4532. max_m_blocks=max_m_blocks,
  4533. element_dtype=element_dtype,
  4534. scale_format=scale_format,
  4535. )
  4536. cache_key = (
  4537. "w4a16_gemm",
  4538. device,
  4539. kernel.__cache_key__,
  4540. )
  4541. cached = _CACHE.get(cache_key)
  4542. if cached is not None:
  4543. return replace(
  4544. cached,
  4545. max_m_blocks=max_m_blocks,
  4546. blocks_per_sm=kernel.blocks_per_sm,
  4547. )
  4548. compile_size_m = _fake_m_for_specialization(size_m)
  4549. compile_route_blocks = 1
  4550. compile_route_slots = compile_route_blocks * int(moe_block_size)
  4551. a_fake = cute.runtime.make_fake_compact_tensor(
  4552. cutlass_dtype,
  4553. (compile_size_m * size_k,),
  4554. assumed_align=16,
  4555. )
  4556. b_fake = cute.runtime.make_fake_compact_tensor(
  4557. cutlass.Int32,
  4558. (num_experts * (size_k // 16) * (size_n // 16 * 32),),
  4559. assumed_align=16,
  4560. )
  4561. c_fake = cute.runtime.make_fake_compact_tensor(
  4562. cutlass_dtype,
  4563. (compile_size_m * top_k * size_n,),
  4564. assumed_align=16,
  4565. )
  4566. scales_fake = cute.runtime.make_fake_compact_tensor(
  4567. cutlass.Int32,
  4568. (
  4569. _scale_fake_int32_elements(
  4570. num_experts=num_experts,
  4571. size_k=size_k,
  4572. size_n=size_n,
  4573. scale_format=scale_format,
  4574. ),
  4575. ),
  4576. assumed_align=16,
  4577. )
  4578. global_scale_fake = cute.runtime.make_fake_compact_tensor(
  4579. cutlass.Float32,
  4580. (num_experts,),
  4581. assumed_align=16,
  4582. )
  4583. packed_routes_fake = cute.runtime.make_fake_compact_tensor(
  4584. cutlass.Int32,
  4585. (compile_route_slots,),
  4586. assumed_align=16,
  4587. )
  4588. block_experts_fake = cute.runtime.make_fake_compact_tensor(
  4589. cutlass.Int32,
  4590. (compile_route_blocks,),
  4591. assumed_align=16,
  4592. )
  4593. route_count_fake = cute.runtime.make_fake_compact_tensor(
  4594. cutlass.Int32,
  4595. (1,),
  4596. assumed_align=4,
  4597. )
  4598. topk_fake = cute.runtime.make_fake_compact_tensor(
  4599. cutlass.Float32,
  4600. (compile_size_m * top_k,),
  4601. assumed_align=4,
  4602. )
  4603. c_tmp_fake = cute.runtime.make_fake_compact_tensor(
  4604. cutlass.Float32,
  4605. (
  4606. max(
  4607. size_n * compile_route_slots,
  4608. 4 * 256 * moe_block_size * 256,
  4609. ),
  4610. ),
  4611. assumed_align=16,
  4612. )
  4613. locks_fake = cute.runtime.make_fake_compact_tensor(
  4614. cutlass.Int32,
  4615. (4 * 256,),
  4616. assumed_align=16,
  4617. )
  4618. raise_if_kernel_resolution_frozen(
  4619. "cute.compile", target=kernel, cache_key=cache_key
  4620. )
  4621. compiled = b12x_compile(
  4622. kernel,
  4623. a_fake,
  4624. b_fake,
  4625. c_fake,
  4626. scales_fake,
  4627. global_scale_fake,
  4628. packed_routes_fake,
  4629. block_experts_fake,
  4630. route_count_fake,
  4631. topk_fake,
  4632. c_tmp_fake,
  4633. locks_fake,
  4634. Int32(compile_size_m),
  4635. Int32(1),
  4636. current_cuda_stream(),
  4637. compile_spec=KernelCompileSpec.from_key(
  4638. "moe.w4a16.gemm",
  4639. 1,
  4640. cache_key,
  4641. ),
  4642. )
  4643. result = W4A16GemmCompileResult(
  4644. compiled=compiled,
  4645. tile_n=tile_n,
  4646. tile_k=tile_k,
  4647. moe_block_size=moe_block_size,
  4648. max_m_blocks=max_m_blocks,
  4649. blocks_per_sm=kernel.blocks_per_sm,
  4650. scale_format=scale_format,
  4651. )
  4652. _CACHE[cache_key] = result
  4653. return result
  4654. def compile_w4a16_fused_moe(
  4655. *,
  4656. size_m: int,
  4657. hidden_size: int,
  4658. intermediate_size: int,
  4659. num_experts: int,
  4660. top_k: int,
  4661. activation: str,
  4662. apply_router_weight_on_input: bool,
  4663. zero_fc2_output: bool,
  4664. moe_block_size: int,
  4665. max_m_blocks: int,
  4666. element_dtype: str = "bf16",
  4667. fast_math: bool = True,
  4668. sms: int,
  4669. max_shared_mem: int,
  4670. swiglu_limit: float | None = None,
  4671. swiglu_alpha: float | None = None,
  4672. swiglu_beta: float | None = None,
  4673. weight_layout: str = "packed",
  4674. scale_format: str = "e4m3_k16",
  4675. w13_layout: str = "w13",
  4676. direct_topk_routes: bool = False,
  4677. tc_decode_fused_sum: bool = False,
  4678. collect_activation_amax: bool = False,
  4679. force_tile_config: tuple[int, int, int, int] | None = None,
  4680. ) -> W4A16FusedMoeCompileResult:
  4681. scale_format = _normalize_scale_format(scale_format)
  4682. cutlass_dtype = _cutlass_element_dtype(element_dtype)
  4683. device = int(torch.cuda.current_device()) if torch.cuda.is_available() else None
  4684. activation = normalize_moe_activation(activation)
  4685. is_gated = validate_activation(activation)
  4686. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  4687. activation,
  4688. swiglu_limit,
  4689. swiglu_alpha,
  4690. swiglu_beta,
  4691. )
  4692. if weight_layout not in _WEIGHT_LAYOUTS:
  4693. raise ValueError(f"unsupported W4A16 weight_layout {weight_layout!r}")
  4694. if weight_layout == "modelopt":
  4695. if w13_layout not in _MODEL_OPT_W13_LAYOUTS:
  4696. raise ValueError(f"unsupported W4A16 w13_layout {w13_layout!r}")
  4697. else:
  4698. w13_layout = "packed"
  4699. direct_topk_routes = bool(direct_topk_routes)
  4700. tc_decode_fused_sum = bool(tc_decode_fused_sum)
  4701. collect_activation_amax = bool(collect_activation_amax)
  4702. if collect_activation_amax and (direct_topk_routes or tc_decode_fused_sum):
  4703. raise ValueError(
  4704. "W4A16 activation amax collection requires the route-packed fused path"
  4705. )
  4706. # The TC-decode path validates M in {1,2,4,8} itself and uses direct-topk
  4707. # routing for the whole {1,2,4,8} range, so it lifts the default decode cap.
  4708. direct_topk_m_cap = (
  4709. _W4A16_SMALL_M_DIRECT_MAX_M if tc_decode_fused_sum else _MAX_DIRECT_TOPK_ROUTE_M
  4710. )
  4711. if direct_topk_routes and (
  4712. int(size_m) > direct_topk_m_cap
  4713. or weight_layout not in ("packed", "nf3_2p1")
  4714. or bool(zero_fc2_output)
  4715. ):
  4716. raise ValueError(
  4717. "direct_topk_routes is only valid for small-M packed W4A16 without expert_map"
  4718. )
  4719. fc1_cols = int(intermediate_size) * (2 if is_gated else 1)
  4720. routed_rows = int(size_m) * int(top_k)
  4721. allow_native_logical_tail = (
  4722. weight_layout == "modelopt"
  4723. and scale_format == "e8m0_k32"
  4724. and int(intermediate_size) % 32 != 0
  4725. )
  4726. fc1_tile_k, fc1_tile_n, fc1_cta_threads, _ = _select_tile_config(
  4727. problem_m=size_m,
  4728. problem_n=fc1_cols,
  4729. problem_k=hidden_size,
  4730. top_k=top_k,
  4731. moe_block_size=moe_block_size,
  4732. sms=sms,
  4733. max_shared_mem=max_shared_mem,
  4734. scale_format=scale_format,
  4735. allow_logical_tail=allow_native_logical_tail,
  4736. )
  4737. fc2_tile_k, fc2_tile_n, fc2_cta_threads, _ = _select_tile_config(
  4738. problem_m=routed_rows,
  4739. problem_n=hidden_size,
  4740. problem_k=intermediate_size,
  4741. top_k=1,
  4742. moe_block_size=moe_block_size,
  4743. sms=sms,
  4744. max_shared_mem=max_shared_mem,
  4745. scale_format=scale_format,
  4746. allow_logical_tail=allow_native_logical_tail,
  4747. )
  4748. if fc1_cta_threads != fc2_cta_threads:
  4749. common_cta_threads = min(fc1_cta_threads, fc2_cta_threads)
  4750. fc1_tile_k, fc1_tile_n, fc1_cta_threads, _ = _select_tile_config(
  4751. problem_m=size_m,
  4752. problem_n=fc1_cols,
  4753. problem_k=hidden_size,
  4754. top_k=top_k,
  4755. moe_block_size=moe_block_size,
  4756. sms=sms,
  4757. max_shared_mem=max_shared_mem,
  4758. required_cta_threads=common_cta_threads,
  4759. scale_format=scale_format,
  4760. allow_logical_tail=allow_native_logical_tail,
  4761. )
  4762. fc2_tile_k, fc2_tile_n, fc2_cta_threads, _ = _select_tile_config(
  4763. problem_m=routed_rows,
  4764. problem_n=hidden_size,
  4765. problem_k=intermediate_size,
  4766. top_k=1,
  4767. moe_block_size=moe_block_size,
  4768. sms=sms,
  4769. max_shared_mem=max_shared_mem,
  4770. required_cta_threads=common_cta_threads,
  4771. scale_format=scale_format,
  4772. allow_logical_tail=allow_native_logical_tail,
  4773. )
  4774. if fc1_cta_threads != fc2_cta_threads:
  4775. raise ValueError(
  4776. "fused W4A16 FC1/FC2 selected different thread counts: "
  4777. f"{fc1_cta_threads} vs {fc2_cta_threads}"
  4778. )
  4779. # TC-decode FC1 wide-N override (single-wave-collapse sizes only): in the m8
  4780. # fused path the host right-sizes grid_x to the FC1 mn-tile count and forces
  4781. # one whole mn-tile per CTA over the full K. With the default fc1_tile_n=128,
  4782. # FC1 (N=fc1_cols=2*intermediate_size) produces size_m*top_k*(fc1_cols/128)
  4783. # mn-tiles. For TP=2 I_tp=1024 (fc1_cols=2048 => 16 n-tiles/route) bs=1 is
  4784. # 96 tiles (<= 188 SMs, one wave); bs=2 is 192 -- JUST over the single-wave
  4785. # SM cap -- forcing a 2-wave launch of grid_x=96 (half the machine idle per
  4786. # wave, a serialized second FC1 wave of pure tail latency on the bandwidth-
  4787. # bound decode). Widening FC1 to tile_n=256 (256-wide N slab per CTA over
  4788. # full K; tile_k=64 keeps cta_threads=256) HALVES FC1's mn-tile count, so
  4789. # bs=2 collapses to 96 tiles = exactly ONE wave, removing that whole second
  4790. # FC1 wave. The narrower tile_k=64 is slower per-tile, so we apply this ONLY
  4791. # where it turns a 2-wave launch into a 1-wave launch: the default 128-wide
  4792. # FC1 spans 2 waves (sms < default_mn_tiles <= 2*sms) AND the wide tile fits
  4793. # in one (default/2 <= sms). bs=1 (one wave already) and bs>=4 (still multi-
  4794. # wave after halving) keep the faster default 128x128 tile.
  4795. # Guarded by fc1_cols%256==0, smem-fit, and 256-thread geometry so the fused
  4796. # FC1/FC2 single-thread-geometry contract is preserved.
  4797. default_fc1_mn_tiles = (
  4798. int(size_m) * int(top_k) * (int(fc1_cols) // int(fc1_tile_n))
  4799. if fc1_tile_n > 0
  4800. else 0
  4801. )
  4802. if (
  4803. bool(tc_decode_fused_sum)
  4804. and int(fc1_cols) % 256 == 0
  4805. and fc1_tile_n == 128
  4806. and (fc1_tile_n * fc1_tile_k) // 64 == 256
  4807. and int(sms) < default_fc1_mn_tiles <= 2 * int(sms)
  4808. and (default_fc1_mn_tiles // 2) <= int(sms)
  4809. ):
  4810. wide_fc1_tile_k = 64
  4811. if _candidate_tile_fits(
  4812. problem_n=fc1_cols,
  4813. problem_k=hidden_size,
  4814. cta_m_blocks=_covering_count(moe_block_size, 16),
  4815. tile_n=256,
  4816. tile_k=wide_fc1_tile_k,
  4817. cta_threads=256,
  4818. max_shared_mem=int(max_shared_mem) - 512,
  4819. scale_format=scale_format,
  4820. ):
  4821. fc1_tile_n = 256
  4822. fc1_tile_k = wide_fc1_tile_k
  4823. fc1_cta_threads = 256
  4824. # TC-decode FC2 wide-N override: in the m8 fused path the host right-sizes
  4825. # grid_x to the FC1 mn-tile count (one persistent wave, no split-K). FC2
  4826. # (N=hidden_size, K=intermediate_size) with the default tile_n=128 produces
  4827. # route_blocks*(hidden_size/128) mn-tiles -- roughly double FC1's count --
  4828. # so FC2 would need ~2 persistent waves while FC1 fits in 1; that second
  4829. # FC2 wave is pure serialized tail latency on the bandwidth-bound decode.
  4830. # Selecting tile_n=256 for FC2 (a 256-wide N slab per CTA over full K)
  4831. # halves FC2's mn-tile count so it also fits one wave. Guarded by smem-fit,
  4832. # hidden_size%256==0, and matching cta_threads so the fused single
  4833. # thread-geometry contract is preserved.
  4834. if (
  4835. bool(tc_decode_fused_sum)
  4836. and int(hidden_size) % 256 == 0
  4837. and fc2_tile_n == 128
  4838. and fc1_cta_threads == 256
  4839. ):
  4840. wide_fc2_tile_k = 64
  4841. if _candidate_tile_fits(
  4842. problem_n=hidden_size,
  4843. problem_k=intermediate_size,
  4844. cta_m_blocks=_covering_count(moe_block_size, 16),
  4845. tile_n=256,
  4846. tile_k=wide_fc2_tile_k,
  4847. cta_threads=256,
  4848. max_shared_mem=int(max_shared_mem) - 512,
  4849. scale_format=scale_format,
  4850. ):
  4851. fc2_tile_n = 256
  4852. fc2_tile_k = wide_fc2_tile_k
  4853. fc2_cta_threads = 256
  4854. # TC-decode FC2 ultra-wide override (perfect wave-balance with FC1): the
  4855. # persistent grid_x is right-sized to FC1's mn-tile count. After the FC1/FC2
  4856. # wide-N (tile_n=256) overrides, bs=2 still has FC1=route_blocks*8 tiles vs
  4857. # FC2=route_blocks*16 tiles -- FC2 is DOUBLE FC1 and thus needs a second
  4858. # persistent wave at grid_x sized to FC1. That FC2 second wave is pure
  4859. # serialized tail latency on the bandwidth-bound decode. Widening FC2 to
  4860. # tile_n=512 (a 512-wide N slab per CTA over full K) halves FC2's mn-tile
  4861. # count again so FC2 == FC1's tile count and fits the SAME single wave. A
  4862. # 512-wide N tile needs tile_k=32 to keep cta_threads=256 (512*32/64); that
  4863. # is below the generic tile_k>=64 fits-floor, so we validate the footprint
  4864. # directly here. tile_k=32 == the e8m0_k32 scale group, so cta_k_blocks=2
  4865. # with one e8m0 scale group per k-tile -- the existing scale layout is a
  4866. # clean covering. Fire ONLY when it drops FC2 from >1 wave to FC1's wave
  4867. # count (bs=2). Numerically identical: only the FC2 output-tile width changes.
  4868. fc1_mn_after = (
  4869. int(size_m) * int(top_k) * (int(fc1_cols) // int(fc1_tile_n))
  4870. if fc1_tile_n > 0
  4871. else 0
  4872. )
  4873. fc2_mn_after = (
  4874. int(size_m) * int(top_k) * (int(hidden_size) // int(fc2_tile_n))
  4875. if fc2_tile_n > 0
  4876. else 0
  4877. )
  4878. if (
  4879. bool(tc_decode_fused_sum)
  4880. and int(hidden_size) % 512 == 0
  4881. and fc2_tile_n == 256
  4882. and fc1_cta_threads == 256
  4883. and fc1_mn_after > 0
  4884. and fc2_mn_after > fc1_mn_after
  4885. and (fc2_mn_after // 2) <= fc1_mn_after
  4886. and fc1_mn_after <= int(sms)
  4887. ):
  4888. ultra_fc2_tile_k = 32
  4889. ultra_smem = _shared_memory_footprint(
  4890. cta_m_blocks=_covering_count(moe_block_size, 16),
  4891. tile_n=512,
  4892. tile_k=ultra_fc2_tile_k,
  4893. scale_format=scale_format,
  4894. )
  4895. if (
  4896. int(intermediate_size) % ultra_fc2_tile_k == 0
  4897. and ultra_smem <= int(max_shared_mem) - 512
  4898. ):
  4899. fc2_tile_n = 512
  4900. fc2_tile_k = ultra_fc2_tile_k
  4901. fc2_cta_threads = 256
  4902. if force_tile_config is not None:
  4903. # Explicit (fc1_tile_k, fc1_tile_n, fc2_tile_k, fc2_tile_n) pin. The
  4904. # NF3 ("nf3_2p1") flat-span weight layout is packed for a specific CTA
  4905. # N-tile, so hybrid deployments pin ONE tile config across every m
  4906. # regime instead of the m-dependent auto selection (and TC-decode
  4907. # wide-N overrides) above. Overrides whatever was selected; the tiles
  4908. # land in the GEMM cache keys, so no cache collision is possible.
  4909. fc1_tile_k, fc1_tile_n, fc2_tile_k, fc2_tile_n = (
  4910. int(v) for v in force_tile_config
  4911. )
  4912. fc1_cta_threads = (fc1_tile_n * fc1_tile_k) // 64
  4913. fc2_cta_threads = (fc2_tile_n * fc2_tile_k) // 64
  4914. if fc1_cta_threads != fc2_cta_threads:
  4915. raise ValueError(
  4916. "force_tile_config FC1/FC2 thread counts must match, got "
  4917. f"{fc1_cta_threads} vs {fc2_cta_threads}"
  4918. )
  4919. for name, forced_pn, forced_pk, forced_tn, forced_tk in (
  4920. ("fc1", fc1_cols, hidden_size, fc1_tile_n, fc1_tile_k),
  4921. ("fc2", hidden_size, intermediate_size, fc2_tile_n, fc2_tile_k),
  4922. ):
  4923. if not _candidate_tile_fits(
  4924. problem_n=forced_pn,
  4925. problem_k=forced_pk,
  4926. cta_m_blocks=_covering_count(moe_block_size, 16),
  4927. tile_n=forced_tn,
  4928. tile_k=forced_tk,
  4929. cta_threads=fc1_cta_threads,
  4930. max_shared_mem=int(max_shared_mem) - 512,
  4931. scale_format=scale_format,
  4932. allow_logical_tail=allow_native_logical_tail,
  4933. ):
  4934. raise ValueError(
  4935. f"force_tile_config {name} tile "
  4936. f"(tile_k={forced_tk}, tile_n={forced_tn}) does not fit "
  4937. f"problem N/K={forced_pn}/{forced_pk} at "
  4938. f"moe_block_size={moe_block_size}"
  4939. )
  4940. kernel = W4A16FusedMoeKernel(
  4941. size_m=size_m,
  4942. hidden_size=hidden_size,
  4943. intermediate_size=intermediate_size,
  4944. num_experts=num_experts,
  4945. top_k=top_k,
  4946. activation=activation,
  4947. apply_router_weight_on_input=apply_router_weight_on_input,
  4948. zero_fc2_output=zero_fc2_output,
  4949. fc1_tile_n=fc1_tile_n,
  4950. fc1_tile_k=fc1_tile_k,
  4951. fc2_tile_n=fc2_tile_n,
  4952. fc2_tile_k=fc2_tile_k,
  4953. moe_block_size=moe_block_size,
  4954. max_m_blocks=max_m_blocks,
  4955. element_dtype=element_dtype,
  4956. fast_math=fast_math,
  4957. swiglu_limit=swiglu_limit,
  4958. swiglu_alpha=swiglu_alpha,
  4959. swiglu_beta=swiglu_beta,
  4960. weight_layout=weight_layout,
  4961. scale_format=scale_format,
  4962. w13_layout=w13_layout,
  4963. direct_topk_routes=direct_topk_routes,
  4964. tc_decode_fused_sum=tc_decode_fused_sum,
  4965. collect_activation_amax=collect_activation_amax,
  4966. )
  4967. cache_key = (
  4968. "w4a16_fused_moe",
  4969. device,
  4970. kernel.__cache_key__,
  4971. )
  4972. cached = _FUSED_CACHE.get(cache_key)
  4973. if cached is not None:
  4974. return replace(
  4975. cached,
  4976. size_m=size_m,
  4977. max_m_blocks=max_m_blocks,
  4978. blocks_per_sm=kernel.blocks_per_sm,
  4979. )
  4980. if (not collect_activation_amax) and _small_m_direct_supported(
  4981. m=size_m,
  4982. hidden_size=hidden_size,
  4983. intermediate_size=intermediate_size,
  4984. num_experts=num_experts,
  4985. topk=top_k,
  4986. activation=activation,
  4987. apply_router_weight_on_input=bool(apply_router_weight_on_input),
  4988. swiglu_limit=swiglu_limit,
  4989. swiglu_alpha=swiglu_alpha,
  4990. swiglu_beta=swiglu_beta,
  4991. element_dtype=element_dtype,
  4992. weight_layout=weight_layout,
  4993. w13_layout=w13_layout,
  4994. scale_format=scale_format,
  4995. ):
  4996. for ids_dtype in (torch.int32, torch.int64):
  4997. _compile_w4a16_small_m_direct(
  4998. m=size_m,
  4999. hidden_size=hidden_size,
  5000. intermediate_size=intermediate_size,
  5001. num_experts=num_experts,
  5002. topk=top_k,
  5003. activation=activation,
  5004. fast_math=fast_math,
  5005. topk_ids_dtype=ids_dtype,
  5006. scale_format=scale_format,
  5007. swiglu_limit=swiglu_limit,
  5008. swiglu_alpha=swiglu_alpha,
  5009. swiglu_beta=swiglu_beta,
  5010. w13_layout=w13_layout,
  5011. device=torch.device("cuda", device) if device is not None else None,
  5012. )
  5013. compile_size_m = _fake_m_for_specialization(size_m)
  5014. compile_routed_rows = int(compile_size_m) * int(top_k)
  5015. compile_route_blocks = compile_routed_rows if direct_topk_routes else 1
  5016. compile_route_slots = compile_route_blocks * int(moe_block_size)
  5017. packed_route_fake_elements = (
  5018. compile_routed_rows
  5019. if direct_topk_routes
  5020. else compile_route_slots
  5021. )
  5022. a_fake = make_ptr(cutlass_dtype, 16, cute.AddressSpace.gmem, assumed_align=16)
  5023. if weight_layout == "modelopt":
  5024. w13_fake = cute.runtime.make_fake_compact_tensor(
  5025. cutlass.Uint8,
  5026. (num_experts * fc1_cols * (hidden_size // 2),),
  5027. assumed_align=16,
  5028. )
  5029. w2_fake = cute.runtime.make_fake_compact_tensor(
  5030. cutlass.Uint8,
  5031. (num_experts * hidden_size * (intermediate_size // 2),),
  5032. assumed_align=16,
  5033. )
  5034. elif weight_layout == "nf3_2p1":
  5035. # NF3: int32, 3 words per 32-code unit; (size_n // 2) units per K16 row.
  5036. w13_fake = cute.runtime.make_fake_compact_tensor(
  5037. cutlass.Int32,
  5038. (num_experts * (hidden_size // 16) * (fc1_cols // 2) * 3,),
  5039. assumed_align=16,
  5040. )
  5041. w2_fake = cute.runtime.make_fake_compact_tensor(
  5042. cutlass.Int32,
  5043. (num_experts * (intermediate_size // 16) * (hidden_size // 2) * 3,),
  5044. assumed_align=16,
  5045. )
  5046. else:
  5047. w13_fake = cute.runtime.make_fake_compact_tensor(
  5048. cutlass.Int32,
  5049. (num_experts * (hidden_size // 16) * (fc1_cols // 16 * 32),),
  5050. assumed_align=16,
  5051. )
  5052. w2_fake = cute.runtime.make_fake_compact_tensor(
  5053. cutlass.Int32,
  5054. (num_experts * (intermediate_size // 16) * (hidden_size // 16 * 32),),
  5055. assumed_align=16,
  5056. )
  5057. fc1_fake = cute.runtime.make_fake_compact_tensor(
  5058. cutlass_dtype,
  5059. (compile_routed_rows * fc1_cols,),
  5060. assumed_align=16,
  5061. )
  5062. activated_fake = cute.runtime.make_fake_compact_tensor(
  5063. cutlass_dtype,
  5064. (compile_routed_rows * intermediate_size,),
  5065. assumed_align=16,
  5066. )
  5067. fc2_fake = cute.runtime.make_fake_compact_tensor(
  5068. cutlass_dtype,
  5069. (compile_routed_rows * hidden_size,),
  5070. assumed_align=16,
  5071. )
  5072. w13_scales_fake = cute.runtime.make_fake_compact_tensor(
  5073. cutlass.Int32,
  5074. (
  5075. _scale_fake_int32_elements(
  5076. num_experts=num_experts,
  5077. size_k=hidden_size,
  5078. size_n=fc1_cols,
  5079. scale_format=scale_format,
  5080. allow_n_tail=allow_native_logical_tail,
  5081. ),
  5082. ),
  5083. assumed_align=16,
  5084. )
  5085. w2_scales_fake = cute.runtime.make_fake_compact_tensor(
  5086. cutlass.Int32,
  5087. (
  5088. _scale_fake_int32_elements(
  5089. num_experts=num_experts,
  5090. size_k=intermediate_size,
  5091. size_n=hidden_size,
  5092. scale_format=scale_format,
  5093. allow_k_tail=allow_native_logical_tail,
  5094. ),
  5095. ),
  5096. assumed_align=16,
  5097. )
  5098. w13_global_fake = cute.runtime.make_fake_compact_tensor(
  5099. cutlass.Float32,
  5100. (num_experts,),
  5101. assumed_align=16,
  5102. )
  5103. w2_global_fake = cute.runtime.make_fake_compact_tensor(
  5104. cutlass.Float32,
  5105. (num_experts,),
  5106. assumed_align=16,
  5107. )
  5108. packed_routes_fake = cute.runtime.make_fake_compact_tensor(
  5109. cutlass.Int32,
  5110. (packed_route_fake_elements,),
  5111. assumed_align=16,
  5112. )
  5113. block_experts_fake = cute.runtime.make_fake_compact_tensor(
  5114. cutlass.Int32,
  5115. (compile_route_blocks,),
  5116. assumed_align=16,
  5117. )
  5118. route_count_fake = cute.runtime.make_fake_compact_tensor(
  5119. cutlass.Int32,
  5120. (1,),
  5121. assumed_align=4,
  5122. )
  5123. activation_amax_fake = cute.runtime.make_fake_compact_tensor(
  5124. cutlass.Float32,
  5125. (num_experts * 2,),
  5126. assumed_align=4,
  5127. )
  5128. topk_fake = make_ptr(cutlass.Float32, 4, cute.AddressSpace.gmem, assumed_align=4)
  5129. fc1_c_tmp_fake = cute.runtime.make_fake_compact_tensor(
  5130. cutlass.Float32,
  5131. (
  5132. max(
  5133. fc1_cols * compile_route_slots,
  5134. 4 * 256 * moe_block_size * 256,
  5135. ),
  5136. ),
  5137. assumed_align=16,
  5138. )
  5139. fc2_c_tmp_fake = cute.runtime.make_fake_compact_tensor(
  5140. cutlass.Float32,
  5141. (
  5142. max(
  5143. hidden_size * compile_route_slots,
  5144. 4 * 256 * moe_block_size * 256,
  5145. ),
  5146. ),
  5147. assumed_align=16,
  5148. )
  5149. locks_fake = cute.runtime.make_fake_compact_tensor(
  5150. cutlass.Int32,
  5151. (4 * 256 + 2,),
  5152. assumed_align=16,
  5153. )
  5154. raise_if_kernel_resolution_frozen(
  5155. "cute.compile", target=kernel, cache_key=cache_key
  5156. )
  5157. compiled = b12x_compile(
  5158. kernel,
  5159. a_fake,
  5160. w13_fake,
  5161. w2_fake,
  5162. fc1_fake,
  5163. activated_fake,
  5164. fc2_fake,
  5165. w13_scales_fake,
  5166. w2_scales_fake,
  5167. w13_global_fake,
  5168. w2_global_fake,
  5169. packed_routes_fake,
  5170. block_experts_fake,
  5171. route_count_fake,
  5172. activation_amax_fake,
  5173. 0,
  5174. topk_fake,
  5175. fc1_c_tmp_fake,
  5176. fc2_c_tmp_fake,
  5177. locks_fake,
  5178. 1,
  5179. 1,
  5180. current_cuda_stream(),
  5181. compile_spec=KernelCompileSpec.from_key(
  5182. "moe.w4a16.fused_moe",
  5183. 1,
  5184. cache_key,
  5185. ),
  5186. )
  5187. result = W4A16FusedMoeCompileResult(
  5188. compiled=compiled,
  5189. size_m=size_m,
  5190. hidden_size=hidden_size,
  5191. intermediate_size=intermediate_size,
  5192. num_experts=num_experts,
  5193. top_k=top_k,
  5194. activation=activation,
  5195. apply_router_weight_on_input=bool(apply_router_weight_on_input),
  5196. zero_fc2_output=bool(zero_fc2_output),
  5197. element_dtype=element_dtype,
  5198. fast_math=bool(fast_math),
  5199. swiglu_limit=swiglu_limit,
  5200. swiglu_alpha=swiglu_alpha,
  5201. swiglu_beta=swiglu_beta,
  5202. fc1_tile_n=fc1_tile_n,
  5203. fc1_tile_k=fc1_tile_k,
  5204. fc2_tile_n=fc2_tile_n,
  5205. fc2_tile_k=fc2_tile_k,
  5206. moe_block_size=moe_block_size,
  5207. max_m_blocks=max_m_blocks,
  5208. blocks_per_sm=kernel.blocks_per_sm,
  5209. weight_layout=weight_layout,
  5210. w13_layout=w13_layout,
  5211. direct_topk_routes=kernel.direct_topk_routes,
  5212. scale_format=scale_format,
  5213. tc_decode_fused_sum=bool(tc_decode_fused_sum),
  5214. collect_activation_amax=collect_activation_amax,
  5215. )
  5216. _FUSED_CACHE[cache_key] = result
  5217. return result
  5218. def clear_w4a16_kernel_cache() -> None:
  5219. _CACHE.clear()
  5220. _FUSED_CACHE.clear()
  5221. _ACTIVATION_CACHE.clear()
  5222. _SUM_CACHE.clear()
  5223. _SMALL_M_DIRECT_CACHE.clear()
  5224. def compile_w4a16_activation(
  5225. *,
  5226. rows: int,
  5227. intermediate_size: int,
  5228. activation: str,
  5229. element_dtype: str = "bf16",
  5230. fast_math: bool = True,
  5231. swiglu_limit: float | None = None,
  5232. swiglu_alpha: float | None = None,
  5233. swiglu_beta: float | None = None,
  5234. ) -> W4A16ActivationCompileResult:
  5235. cutlass_dtype = _cutlass_element_dtype(element_dtype)
  5236. activation = normalize_moe_activation(activation)
  5237. is_gated = validate_activation(activation)
  5238. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  5239. activation,
  5240. swiglu_limit,
  5241. swiglu_alpha,
  5242. swiglu_beta,
  5243. )
  5244. kernel = W4A16ActivationKernel(
  5245. rows=rows,
  5246. intermediate_size=intermediate_size,
  5247. activation=activation,
  5248. element_dtype=element_dtype,
  5249. fast_math=fast_math,
  5250. swiglu_limit=swiglu_limit,
  5251. swiglu_alpha=swiglu_alpha,
  5252. swiglu_beta=swiglu_beta,
  5253. )
  5254. cache_key = (
  5255. "w4a16_activation",
  5256. kernel.__cache_key__,
  5257. )
  5258. cached = _ACTIVATION_CACHE.get(cache_key)
  5259. if cached is not None:
  5260. return replace(cached, rows=rows)
  5261. w13_shards = 2 if is_gated else 1
  5262. compile_rows = _fake_m_for_specialization(rows)
  5263. fc1_fake = cute.runtime.make_fake_compact_tensor(
  5264. cutlass_dtype,
  5265. (compile_rows * w13_shards * intermediate_size,),
  5266. assumed_align=16,
  5267. )
  5268. activated_fake = cute.runtime.make_fake_compact_tensor(
  5269. cutlass_dtype,
  5270. (compile_rows * intermediate_size,),
  5271. assumed_align=16,
  5272. )
  5273. raise_if_kernel_resolution_frozen(
  5274. "cute.compile", target=kernel, cache_key=cache_key
  5275. )
  5276. compiled = b12x_compile(
  5277. kernel,
  5278. fc1_fake,
  5279. activated_fake,
  5280. Int32(compile_rows),
  5281. current_cuda_stream(),
  5282. compile_spec=KernelCompileSpec.from_key(
  5283. "moe.w4a16.activation",
  5284. 1,
  5285. cache_key,
  5286. ),
  5287. )
  5288. result = W4A16ActivationCompileResult(
  5289. compiled=compiled,
  5290. rows=rows,
  5291. intermediate_size=intermediate_size,
  5292. activation=activation,
  5293. swiglu_limit=swiglu_limit,
  5294. swiglu_alpha=swiglu_alpha,
  5295. swiglu_beta=swiglu_beta,
  5296. )
  5297. _ACTIVATION_CACHE[cache_key] = result
  5298. return result
  5299. def compile_w4a16_topk_sum(
  5300. *,
  5301. m: int,
  5302. topk: int,
  5303. hidden_size: int,
  5304. element_dtype: str = "bf16",
  5305. ) -> W4A16TopKSumCompileResult:
  5306. cutlass_dtype = _cutlass_element_dtype(element_dtype)
  5307. cache_key = ("w4a16_topk_sum", element_dtype, topk, hidden_size)
  5308. cached = _SUM_CACHE.get(cache_key)
  5309. if cached is not None:
  5310. return cached
  5311. fc2_fake = make_ptr(cutlass_dtype, 16, cute.AddressSpace.gmem, assumed_align=16)
  5312. output_fake = make_ptr(cutlass_dtype, 16, cute.AddressSpace.gmem, assumed_align=16)
  5313. kernel = W4A16TopKSumKernel(
  5314. topk=topk,
  5315. hidden_size=hidden_size,
  5316. element_dtype=element_dtype,
  5317. )
  5318. raise_if_kernel_resolution_frozen(
  5319. "cute.compile", target=kernel, cache_key=cache_key
  5320. )
  5321. compiled = b12x_compile(
  5322. kernel,
  5323. fc2_fake,
  5324. output_fake,
  5325. 1,
  5326. current_cuda_stream(),
  5327. compile_spec=KernelCompileSpec.from_key(
  5328. "moe.w4a16.topk_sum",
  5329. 1,
  5330. cache_key,
  5331. ),
  5332. )
  5333. result = W4A16TopKSumCompileResult(
  5334. compiled=compiled,
  5335. m=0,
  5336. topk=topk,
  5337. hidden_size=hidden_size,
  5338. )
  5339. _SUM_CACHE[cache_key] = result
  5340. return result
  5341. def _w4a16_small_m_direct_launch_flat(
  5342. a_input: torch.Tensor,
  5343. w13_u8: torch.Tensor,
  5344. w13_scale_u8: torch.Tensor,
  5345. w13_global_scale: torch.Tensor,
  5346. w2_global_scale: torch.Tensor,
  5347. inter_u32: torch.Tensor,
  5348. w2_u8: torch.Tensor,
  5349. w2_scale_u8: torch.Tensor,
  5350. topk_ids: torch.Tensor,
  5351. topk_weights: torch.Tensor,
  5352. output: torch.Tensor,
  5353. barrier_count: torch.Tensor,
  5354. barrier_epoch: torch.Tensor,
  5355. m: int,
  5356. hidden_size: int,
  5357. intermediate_size: int,
  5358. num_experts: int,
  5359. topk: int,
  5360. activation: str,
  5361. fast_math: bool,
  5362. scale_format: str,
  5363. has_swiglu_limit: bool,
  5364. swiglu_limit_value: float,
  5365. swiglu_alpha: float,
  5366. swiglu_beta: float,
  5367. w13_layout: str,
  5368. stream_int: int,
  5369. ) -> None:
  5370. swiglu_limit = float(swiglu_limit_value) if has_swiglu_limit else None
  5371. direct_launch = _compile_w4a16_small_m_direct(
  5372. m=m,
  5373. hidden_size=hidden_size,
  5374. intermediate_size=intermediate_size,
  5375. num_experts=num_experts,
  5376. topk=topk,
  5377. activation=activation,
  5378. fast_math=bool(fast_math),
  5379. topk_ids_dtype=topk_ids.dtype,
  5380. scale_format=scale_format,
  5381. swiglu_limit=swiglu_limit,
  5382. swiglu_alpha=swiglu_alpha,
  5383. swiglu_beta=swiglu_beta,
  5384. w13_layout=w13_layout,
  5385. device=a_input.device,
  5386. )
  5387. def ptr(dt, tensor: torch.Tensor):
  5388. return make_ptr(dt, tensor.data_ptr(), cute.AddressSpace.gmem, assumed_align=16)
  5389. ids_dtype = cutlass.Int64 if topk_ids.dtype == torch.int64 else cutlass.Int32
  5390. direct_launch.compiled(
  5391. ptr(cutlass.BFloat16, a_input),
  5392. ptr(cutlass.Uint8, w13_u8),
  5393. ptr(cutlass.Uint8, w13_scale_u8.view(torch.uint8)),
  5394. ptr(cutlass.Float32, w13_global_scale),
  5395. ptr(cutlass.Float32, w13_global_scale),
  5396. ptr(cutlass.Float32, w2_global_scale),
  5397. ptr(cutlass.Uint32, inter_u32.view(torch.uint32)),
  5398. ptr(cutlass.Uint8, w2_u8),
  5399. ptr(cutlass.Uint8, w2_scale_u8.view(torch.uint8)),
  5400. ptr(cutlass.Float32, w2_global_scale),
  5401. ptr(ids_dtype, topk_ids),
  5402. ptr(cutlass.Float32, topk_weights),
  5403. ptr(cutlass.BFloat16, output),
  5404. barrier_count,
  5405. barrier_epoch,
  5406. Int32(m),
  5407. Int32(direct_launch.grid_x),
  5408. cuda.CUstream(stream_int),
  5409. )
  5410. @torch.library.custom_op(
  5411. "b12x::w4a16_small_m_direct_launch",
  5412. mutates_args="unknown",
  5413. )
  5414. def _w4a16_small_m_direct_launch_op(
  5415. a_input: torch.Tensor,
  5416. w13_u8: torch.Tensor,
  5417. w13_scale_u8: torch.Tensor,
  5418. w13_global_scale: torch.Tensor,
  5419. w2_global_scale: torch.Tensor,
  5420. inter_u32: torch.Tensor,
  5421. w2_u8: torch.Tensor,
  5422. w2_scale_u8: torch.Tensor,
  5423. topk_ids: torch.Tensor,
  5424. topk_weights: torch.Tensor,
  5425. output: torch.Tensor,
  5426. barrier_count: torch.Tensor,
  5427. barrier_epoch: torch.Tensor,
  5428. m: int,
  5429. hidden_size: int,
  5430. intermediate_size: int,
  5431. num_experts: int,
  5432. topk: int,
  5433. activation: str,
  5434. fast_math: bool,
  5435. scale_format: str,
  5436. has_swiglu_limit: bool,
  5437. swiglu_limit_value: float,
  5438. swiglu_alpha: float,
  5439. swiglu_beta: float,
  5440. w13_layout: str,
  5441. stream_int: int,
  5442. ) -> None:
  5443. _w4a16_small_m_direct_launch_flat(
  5444. a_input=a_input,
  5445. w13_u8=w13_u8,
  5446. w13_scale_u8=w13_scale_u8,
  5447. w13_global_scale=w13_global_scale,
  5448. w2_global_scale=w2_global_scale,
  5449. inter_u32=inter_u32,
  5450. w2_u8=w2_u8,
  5451. w2_scale_u8=w2_scale_u8,
  5452. topk_ids=topk_ids,
  5453. topk_weights=topk_weights,
  5454. output=output,
  5455. barrier_count=barrier_count,
  5456. barrier_epoch=barrier_epoch,
  5457. m=m,
  5458. hidden_size=hidden_size,
  5459. intermediate_size=intermediate_size,
  5460. num_experts=num_experts,
  5461. topk=topk,
  5462. activation=activation,
  5463. fast_math=fast_math,
  5464. scale_format=scale_format,
  5465. has_swiglu_limit=has_swiglu_limit,
  5466. swiglu_limit_value=swiglu_limit_value,
  5467. swiglu_alpha=swiglu_alpha,
  5468. swiglu_beta=swiglu_beta,
  5469. w13_layout=w13_layout,
  5470. stream_int=stream_int,
  5471. )
  5472. @_w4a16_small_m_direct_launch_op.register_fake
  5473. def _w4a16_small_m_direct_launch_fake(
  5474. a_input: torch.Tensor,
  5475. w13_u8: torch.Tensor,
  5476. w13_scale_u8: torch.Tensor,
  5477. w13_global_scale: torch.Tensor,
  5478. w2_global_scale: torch.Tensor,
  5479. inter_u32: torch.Tensor,
  5480. w2_u8: torch.Tensor,
  5481. w2_scale_u8: torch.Tensor,
  5482. topk_ids: torch.Tensor,
  5483. topk_weights: torch.Tensor,
  5484. output: torch.Tensor,
  5485. barrier_count: torch.Tensor,
  5486. barrier_epoch: torch.Tensor,
  5487. m: int,
  5488. hidden_size: int,
  5489. intermediate_size: int,
  5490. num_experts: int,
  5491. topk: int,
  5492. activation: str,
  5493. fast_math: bool,
  5494. scale_format: str,
  5495. has_swiglu_limit: bool,
  5496. swiglu_limit_value: float,
  5497. swiglu_alpha: float,
  5498. swiglu_beta: float,
  5499. w13_layout: str,
  5500. stream_int: int,
  5501. ) -> None:
  5502. return None
  5503. def _w4a16_fused_moe_launch_flat(
  5504. a_input: torch.Tensor,
  5505. w13_arg: torch.Tensor,
  5506. w2_arg: torch.Tensor,
  5507. fc1_out: torch.Tensor,
  5508. activated: torch.Tensor,
  5509. fc2_out: torch.Tensor,
  5510. w13_scale_i32: torch.Tensor,
  5511. w2_scale_i32: torch.Tensor,
  5512. w13_global_scale: torch.Tensor,
  5513. w2_global_scale: torch.Tensor,
  5514. packed_route_indices: torch.Tensor,
  5515. block_expert_ids: torch.Tensor,
  5516. packed_route_count: torch.Tensor,
  5517. activation_amax: torch.Tensor | None,
  5518. layer_idx: int,
  5519. topk_weights: torch.Tensor,
  5520. fc1_scratch: torch.Tensor,
  5521. fc2_scratch: torch.Tensor,
  5522. workspace: torch.Tensor,
  5523. m: int,
  5524. size_m: int,
  5525. hidden_size: int,
  5526. intermediate_size: int,
  5527. num_experts: int,
  5528. topk: int,
  5529. activation: str,
  5530. apply_router_weight_on_input: bool,
  5531. zero_fc2_output: bool,
  5532. moe_block_size: int,
  5533. max_m_blocks: int,
  5534. element_dtype: str,
  5535. fast_math: bool,
  5536. sms: int,
  5537. max_shared_mem: int,
  5538. has_swiglu_limit: bool,
  5539. swiglu_limit_value: float,
  5540. swiglu_alpha: float,
  5541. swiglu_beta: float,
  5542. weight_layout: str,
  5543. scale_format: str,
  5544. w13_layout: str,
  5545. direct_topk_routes: bool,
  5546. tc_decode_fused_sum: bool,
  5547. collect_activation_amax: bool,
  5548. stream_int: int,
  5549. ) -> None:
  5550. swiglu_limit = float(swiglu_limit_value) if has_swiglu_limit else None
  5551. collect_activation_amax = bool(collect_activation_amax)
  5552. if collect_activation_amax and activation_amax is None:
  5553. raise ValueError("activation_amax is required for calibrated W4A16 launch")
  5554. activation_amax_arg = (
  5555. activation_amax.view(-1) if activation_amax is not None else w13_global_scale
  5556. )
  5557. fused = compile_w4a16_fused_moe(
  5558. size_m=size_m,
  5559. hidden_size=hidden_size,
  5560. intermediate_size=intermediate_size,
  5561. num_experts=num_experts,
  5562. top_k=topk,
  5563. activation=activation,
  5564. apply_router_weight_on_input=bool(apply_router_weight_on_input),
  5565. zero_fc2_output=bool(zero_fc2_output),
  5566. moe_block_size=moe_block_size,
  5567. max_m_blocks=max_m_blocks,
  5568. element_dtype=element_dtype,
  5569. fast_math=bool(fast_math),
  5570. sms=sms,
  5571. max_shared_mem=max_shared_mem,
  5572. swiglu_limit=swiglu_limit,
  5573. swiglu_alpha=swiglu_alpha,
  5574. swiglu_beta=swiglu_beta,
  5575. weight_layout=weight_layout,
  5576. scale_format=scale_format,
  5577. w13_layout=w13_layout,
  5578. direct_topk_routes=bool(direct_topk_routes),
  5579. tc_decode_fused_sum=bool(tc_decode_fused_sum),
  5580. collect_activation_amax=collect_activation_amax,
  5581. )
  5582. fused.compiled(
  5583. make_ptr(
  5584. _cutlass_element_dtype(element_dtype),
  5585. a_input.data_ptr(),
  5586. cute.AddressSpace.gmem,
  5587. assumed_align=16,
  5588. ),
  5589. w13_arg,
  5590. w2_arg,
  5591. fc1_out,
  5592. activated,
  5593. fc2_out,
  5594. w13_scale_i32,
  5595. w2_scale_i32,
  5596. w13_global_scale,
  5597. w2_global_scale,
  5598. packed_route_indices,
  5599. block_expert_ids,
  5600. packed_route_count,
  5601. activation_amax_arg,
  5602. int(layer_idx),
  5603. make_ptr(
  5604. cutlass.Float32,
  5605. topk_weights.data_ptr(),
  5606. cute.AddressSpace.gmem,
  5607. assumed_align=4,
  5608. ),
  5609. fc1_scratch,
  5610. fc2_scratch,
  5611. workspace,
  5612. m,
  5613. _w4a16_fused_persistent_grid_x(
  5614. fused=fused,
  5615. m=m,
  5616. topk=topk,
  5617. intermediate_size=intermediate_size,
  5618. activation=activation,
  5619. direct_topk_routes=bool(direct_topk_routes),
  5620. sms=sms,
  5621. ),
  5622. cuda.CUstream(stream_int),
  5623. )
  5624. def _w4a16_fused_persistent_grid_x(
  5625. *,
  5626. fused: W4A16FusedMoeCompileResult,
  5627. m: int,
  5628. topk: int,
  5629. intermediate_size: int,
  5630. activation: str,
  5631. direct_topk_routes: bool,
  5632. sms: int,
  5633. ) -> int:
  5634. """Right-size the persistent grid for the fused FC1+FC2 launch.
  5635. The default over-subscribes the cooperative grid (sms*blocks_per_sm CTAs)
  5636. and forces the larger GEMM into the cross-CTA split-K tail (lock-serialized
  5637. finalize + c_tmp gmem round-trip) whenever its mn-tile count exceeds the
  5638. grid. For the small-M direct-topk decode the host knows the exact FC1
  5639. mn-tile count, so pick the fewest full persistent waves that fit the
  5640. co-residency cap and set grid_x to that wave's tile count. Then every CTA
  5641. owns whole FC1 (and FC2, whose tile count is an integer multiple) mn-tiles
  5642. over the full K -- no split-K reduction, no lock traffic -- with fewer
  5643. grid-barrier participants, while staying <= the cap so the cooperative
  5644. barrier never deadlocks. Falls back to the default for the route-pack path
  5645. where the host cannot know route_blocks ahead of the launch.
  5646. """
  5647. cap = int(sms) * int(fused.blocks_per_sm)
  5648. if not direct_topk_routes or m <= 0:
  5649. return max(cap, 1)
  5650. is_gated = is_gated_moe_activation(activation)
  5651. fc1_cols = int(intermediate_size) * (2 if is_gated else 1)
  5652. fc1_tile_n = int(getattr(fused, "fc1_tile_n", 0))
  5653. if fc1_tile_n <= 0 or fc1_cols % fc1_tile_n != 0:
  5654. return max(cap, 1)
  5655. n_tiles = fc1_cols // fc1_tile_n
  5656. route_blocks = int(m) * int(topk)
  5657. fc1_mn_tiles = route_blocks * n_tiles
  5658. if fc1_mn_tiles <= 0 or cap <= 0:
  5659. return max(cap, 1)
  5660. waves = (fc1_mn_tiles + cap - 1) // cap
  5661. if waves <= 0:
  5662. return max(cap, 1)
  5663. # Only commit to the right-sized grid when every wave is a whole tile cover
  5664. # (no remainder => no split-K tail); otherwise keep the safe default.
  5665. if fc1_mn_tiles % waves != 0:
  5666. return max(cap, 1)
  5667. grid_x = fc1_mn_tiles // waves
  5668. if grid_x < 1 or grid_x > cap:
  5669. return max(cap, 1)
  5670. return grid_x
  5671. @torch.library.custom_op(
  5672. "b12x::w4a16_fused_moe_launch",
  5673. mutates_args="unknown",
  5674. )
  5675. def _w4a16_fused_moe_launch_op(
  5676. a_input: torch.Tensor,
  5677. w13_arg: torch.Tensor,
  5678. w2_arg: torch.Tensor,
  5679. fc1_out: torch.Tensor,
  5680. activated: torch.Tensor,
  5681. fc2_out: torch.Tensor,
  5682. w13_scale_i32: torch.Tensor,
  5683. w2_scale_i32: torch.Tensor,
  5684. w13_global_scale: torch.Tensor,
  5685. w2_global_scale: torch.Tensor,
  5686. packed_route_indices: torch.Tensor,
  5687. block_expert_ids: torch.Tensor,
  5688. packed_route_count: torch.Tensor,
  5689. topk_weights: torch.Tensor,
  5690. fc1_scratch: torch.Tensor,
  5691. fc2_scratch: torch.Tensor,
  5692. workspace: torch.Tensor,
  5693. m: int,
  5694. size_m: int,
  5695. hidden_size: int,
  5696. intermediate_size: int,
  5697. num_experts: int,
  5698. topk: int,
  5699. activation: str,
  5700. apply_router_weight_on_input: bool,
  5701. zero_fc2_output: bool,
  5702. moe_block_size: int,
  5703. max_m_blocks: int,
  5704. element_dtype: str,
  5705. fast_math: bool,
  5706. sms: int,
  5707. max_shared_mem: int,
  5708. has_swiglu_limit: bool,
  5709. swiglu_limit_value: float,
  5710. swiglu_alpha: float,
  5711. swiglu_beta: float,
  5712. weight_layout: str,
  5713. scale_format: str,
  5714. w13_layout: str,
  5715. direct_topk_routes: bool,
  5716. tc_decode_fused_sum: bool,
  5717. stream_int: int,
  5718. ) -> None:
  5719. _w4a16_fused_moe_launch_flat(
  5720. a_input=a_input,
  5721. w13_arg=w13_arg,
  5722. w2_arg=w2_arg,
  5723. fc1_out=fc1_out,
  5724. activated=activated,
  5725. fc2_out=fc2_out,
  5726. w13_scale_i32=w13_scale_i32,
  5727. w2_scale_i32=w2_scale_i32,
  5728. w13_global_scale=w13_global_scale,
  5729. w2_global_scale=w2_global_scale,
  5730. packed_route_indices=packed_route_indices,
  5731. block_expert_ids=block_expert_ids,
  5732. packed_route_count=packed_route_count,
  5733. activation_amax=None,
  5734. layer_idx=0,
  5735. topk_weights=topk_weights,
  5736. fc1_scratch=fc1_scratch,
  5737. fc2_scratch=fc2_scratch,
  5738. workspace=workspace,
  5739. m=m,
  5740. size_m=size_m,
  5741. hidden_size=hidden_size,
  5742. intermediate_size=intermediate_size,
  5743. num_experts=num_experts,
  5744. topk=topk,
  5745. activation=activation,
  5746. apply_router_weight_on_input=apply_router_weight_on_input,
  5747. zero_fc2_output=zero_fc2_output,
  5748. moe_block_size=moe_block_size,
  5749. max_m_blocks=max_m_blocks,
  5750. element_dtype=element_dtype,
  5751. fast_math=fast_math,
  5752. sms=sms,
  5753. max_shared_mem=max_shared_mem,
  5754. has_swiglu_limit=has_swiglu_limit,
  5755. swiglu_limit_value=swiglu_limit_value,
  5756. swiglu_alpha=swiglu_alpha,
  5757. swiglu_beta=swiglu_beta,
  5758. weight_layout=weight_layout,
  5759. scale_format=scale_format,
  5760. w13_layout=w13_layout,
  5761. direct_topk_routes=direct_topk_routes,
  5762. tc_decode_fused_sum=tc_decode_fused_sum,
  5763. collect_activation_amax=False,
  5764. stream_int=stream_int,
  5765. )
  5766. @_w4a16_fused_moe_launch_op.register_fake
  5767. def _w4a16_fused_moe_launch_fake(
  5768. a_input: torch.Tensor,
  5769. w13_arg: torch.Tensor,
  5770. w2_arg: torch.Tensor,
  5771. fc1_out: torch.Tensor,
  5772. activated: torch.Tensor,
  5773. fc2_out: torch.Tensor,
  5774. w13_scale_i32: torch.Tensor,
  5775. w2_scale_i32: torch.Tensor,
  5776. w13_global_scale: torch.Tensor,
  5777. w2_global_scale: torch.Tensor,
  5778. packed_route_indices: torch.Tensor,
  5779. block_expert_ids: torch.Tensor,
  5780. packed_route_count: torch.Tensor,
  5781. topk_weights: torch.Tensor,
  5782. fc1_scratch: torch.Tensor,
  5783. fc2_scratch: torch.Tensor,
  5784. workspace: torch.Tensor,
  5785. m: int,
  5786. size_m: int,
  5787. hidden_size: int,
  5788. intermediate_size: int,
  5789. num_experts: int,
  5790. topk: int,
  5791. activation: str,
  5792. apply_router_weight_on_input: bool,
  5793. zero_fc2_output: bool,
  5794. moe_block_size: int,
  5795. max_m_blocks: int,
  5796. element_dtype: str,
  5797. fast_math: bool,
  5798. sms: int,
  5799. max_shared_mem: int,
  5800. has_swiglu_limit: bool,
  5801. swiglu_limit_value: float,
  5802. swiglu_alpha: float,
  5803. swiglu_beta: float,
  5804. weight_layout: str,
  5805. scale_format: str,
  5806. w13_layout: str,
  5807. direct_topk_routes: bool,
  5808. tc_decode_fused_sum: bool,
  5809. stream_int: int,
  5810. ) -> None:
  5811. return None
  5812. @torch.library.custom_op(
  5813. "b12x::w4a16_fused_moe_calibrated_launch",
  5814. mutates_args="unknown",
  5815. )
  5816. def _w4a16_fused_moe_calibrated_launch_op(
  5817. a_input: torch.Tensor,
  5818. w13_arg: torch.Tensor,
  5819. w2_arg: torch.Tensor,
  5820. fc1_out: torch.Tensor,
  5821. activated: torch.Tensor,
  5822. fc2_out: torch.Tensor,
  5823. w13_scale_i32: torch.Tensor,
  5824. w2_scale_i32: torch.Tensor,
  5825. w13_global_scale: torch.Tensor,
  5826. w2_global_scale: torch.Tensor,
  5827. packed_route_indices: torch.Tensor,
  5828. block_expert_ids: torch.Tensor,
  5829. packed_route_count: torch.Tensor,
  5830. activation_amax: torch.Tensor,
  5831. layer_idx: int,
  5832. topk_weights: torch.Tensor,
  5833. fc1_scratch: torch.Tensor,
  5834. fc2_scratch: torch.Tensor,
  5835. workspace: torch.Tensor,
  5836. m: int,
  5837. size_m: int,
  5838. hidden_size: int,
  5839. intermediate_size: int,
  5840. num_experts: int,
  5841. topk: int,
  5842. activation: str,
  5843. apply_router_weight_on_input: bool,
  5844. zero_fc2_output: bool,
  5845. moe_block_size: int,
  5846. max_m_blocks: int,
  5847. element_dtype: str,
  5848. fast_math: bool,
  5849. sms: int,
  5850. max_shared_mem: int,
  5851. has_swiglu_limit: bool,
  5852. swiglu_limit_value: float,
  5853. swiglu_alpha: float,
  5854. swiglu_beta: float,
  5855. weight_layout: str,
  5856. scale_format: str,
  5857. w13_layout: str,
  5858. stream_int: int,
  5859. ) -> None:
  5860. _w4a16_fused_moe_launch_flat(
  5861. a_input=a_input,
  5862. w13_arg=w13_arg,
  5863. w2_arg=w2_arg,
  5864. fc1_out=fc1_out,
  5865. activated=activated,
  5866. fc2_out=fc2_out,
  5867. w13_scale_i32=w13_scale_i32,
  5868. w2_scale_i32=w2_scale_i32,
  5869. w13_global_scale=w13_global_scale,
  5870. w2_global_scale=w2_global_scale,
  5871. packed_route_indices=packed_route_indices,
  5872. block_expert_ids=block_expert_ids,
  5873. packed_route_count=packed_route_count,
  5874. activation_amax=activation_amax,
  5875. layer_idx=layer_idx,
  5876. topk_weights=topk_weights,
  5877. fc1_scratch=fc1_scratch,
  5878. fc2_scratch=fc2_scratch,
  5879. workspace=workspace,
  5880. m=m,
  5881. size_m=size_m,
  5882. hidden_size=hidden_size,
  5883. intermediate_size=intermediate_size,
  5884. num_experts=num_experts,
  5885. topk=topk,
  5886. activation=activation,
  5887. apply_router_weight_on_input=apply_router_weight_on_input,
  5888. zero_fc2_output=zero_fc2_output,
  5889. moe_block_size=moe_block_size,
  5890. max_m_blocks=max_m_blocks,
  5891. element_dtype=element_dtype,
  5892. fast_math=fast_math,
  5893. sms=sms,
  5894. max_shared_mem=max_shared_mem,
  5895. has_swiglu_limit=has_swiglu_limit,
  5896. swiglu_limit_value=swiglu_limit_value,
  5897. swiglu_alpha=swiglu_alpha,
  5898. swiglu_beta=swiglu_beta,
  5899. weight_layout=weight_layout,
  5900. scale_format=scale_format,
  5901. w13_layout=w13_layout,
  5902. direct_topk_routes=False,
  5903. tc_decode_fused_sum=False,
  5904. collect_activation_amax=True,
  5905. stream_int=stream_int,
  5906. )
  5907. @_w4a16_fused_moe_calibrated_launch_op.register_fake
  5908. def _w4a16_fused_moe_calibrated_launch_fake(
  5909. a_input: torch.Tensor,
  5910. w13_arg: torch.Tensor,
  5911. w2_arg: torch.Tensor,
  5912. fc1_out: torch.Tensor,
  5913. activated: torch.Tensor,
  5914. fc2_out: torch.Tensor,
  5915. w13_scale_i32: torch.Tensor,
  5916. w2_scale_i32: torch.Tensor,
  5917. w13_global_scale: torch.Tensor,
  5918. w2_global_scale: torch.Tensor,
  5919. packed_route_indices: torch.Tensor,
  5920. block_expert_ids: torch.Tensor,
  5921. packed_route_count: torch.Tensor,
  5922. activation_amax: torch.Tensor,
  5923. layer_idx: int,
  5924. topk_weights: torch.Tensor,
  5925. fc1_scratch: torch.Tensor,
  5926. fc2_scratch: torch.Tensor,
  5927. workspace: torch.Tensor,
  5928. m: int,
  5929. size_m: int,
  5930. hidden_size: int,
  5931. intermediate_size: int,
  5932. num_experts: int,
  5933. topk: int,
  5934. activation: str,
  5935. apply_router_weight_on_input: bool,
  5936. zero_fc2_output: bool,
  5937. moe_block_size: int,
  5938. max_m_blocks: int,
  5939. element_dtype: str,
  5940. fast_math: bool,
  5941. sms: int,
  5942. max_shared_mem: int,
  5943. has_swiglu_limit: bool,
  5944. swiglu_limit_value: float,
  5945. swiglu_alpha: float,
  5946. swiglu_beta: float,
  5947. weight_layout: str,
  5948. scale_format: str,
  5949. w13_layout: str,
  5950. stream_int: int,
  5951. ) -> None:
  5952. return None
  5953. def _w4a16_topk_sum_launch_flat(
  5954. fc2_out: torch.Tensor,
  5955. output: torch.Tensor,
  5956. m: int,
  5957. topk: int,
  5958. hidden_size: int,
  5959. element_dtype: str,
  5960. stream_int: int,
  5961. ) -> None:
  5962. sum_kernel = compile_w4a16_topk_sum(
  5963. m=m,
  5964. topk=topk,
  5965. hidden_size=hidden_size,
  5966. element_dtype=element_dtype,
  5967. )
  5968. sum_kernel.compiled(
  5969. make_ptr(
  5970. _cutlass_element_dtype(element_dtype),
  5971. fc2_out.data_ptr(),
  5972. cute.AddressSpace.gmem,
  5973. assumed_align=16,
  5974. ),
  5975. make_ptr(
  5976. _cutlass_element_dtype(element_dtype),
  5977. output.data_ptr(),
  5978. cute.AddressSpace.gmem,
  5979. assumed_align=16,
  5980. ),
  5981. m,
  5982. cuda.CUstream(stream_int),
  5983. )
  5984. @torch.library.custom_op(
  5985. "b12x::w4a16_topk_sum_launch",
  5986. mutates_args=("output",),
  5987. )
  5988. def _w4a16_topk_sum_launch_op(
  5989. fc2_out: torch.Tensor,
  5990. output: torch.Tensor,
  5991. m: int,
  5992. topk: int,
  5993. hidden_size: int,
  5994. element_dtype: str,
  5995. stream_int: int,
  5996. ) -> None:
  5997. _w4a16_topk_sum_launch_flat(
  5998. fc2_out=fc2_out,
  5999. output=output,
  6000. m=m,
  6001. topk=topk,
  6002. hidden_size=hidden_size,
  6003. element_dtype=element_dtype,
  6004. stream_int=stream_int,
  6005. )
  6006. @_w4a16_topk_sum_launch_op.register_fake
  6007. def _w4a16_topk_sum_launch_fake(
  6008. fc2_out: torch.Tensor,
  6009. output: torch.Tensor,
  6010. m: int,
  6011. topk: int,
  6012. hidden_size: int,
  6013. element_dtype: str,
  6014. stream_int: int,
  6015. ) -> None:
  6016. return None
  6017. def _get_c_tmp(
  6018. elements: int,
  6019. *,
  6020. device: torch.device,
  6021. scratch: torch.Tensor | None = None,
  6022. ) -> torch.Tensor:
  6023. if scratch is not None:
  6024. if scratch.dtype != torch.float32:
  6025. raise TypeError("W4A16 c_tmp scratch buffers must be torch.float32")
  6026. if scratch.device != device:
  6027. raise ValueError(f"W4A16 c_tmp scratch buffers must be on {device}")
  6028. if not scratch.is_contiguous():
  6029. raise ValueError("W4A16 c_tmp scratch buffers must be contiguous")
  6030. if int(scratch.numel()) >= int(elements):
  6031. return scratch[: int(elements)]
  6032. if torch.cuda.is_current_stream_capturing():
  6033. raise RuntimeError(
  6034. "W4A16 GEMM scratch is not initialized for CUDA graph capture; "
  6035. "provide a preallocated fc*_c_tmp workspace with sufficient capacity"
  6036. )
  6037. return torch.empty((elements,), dtype=torch.float32, device=device)
  6038. def _validate_topk_ids(
  6039. topk_ids: torch.Tensor,
  6040. *,
  6041. require_cuda: bool,
  6042. require_contiguous: bool = True,
  6043. ) -> None:
  6044. if topk_ids.dtype not in (torch.int32, torch.int64):
  6045. raise TypeError("topk_ids must be torch.int32 or torch.int64")
  6046. if require_cuda and not topk_ids.is_cuda:
  6047. raise ValueError("topk_ids must be a CUDA tensor")
  6048. if require_contiguous and not topk_ids.is_contiguous():
  6049. raise ValueError("topk_ids must be contiguous")
  6050. def _validate_expert_map(
  6051. expert_map: torch.Tensor | None,
  6052. *,
  6053. device: torch.device | None = None,
  6054. exact_num_experts: int | None = None,
  6055. ) -> None:
  6056. if expert_map is None:
  6057. return
  6058. if expert_map.dtype != torch.int32:
  6059. raise TypeError("expert_map must be torch.int32")
  6060. if device is None:
  6061. if not expert_map.is_cuda:
  6062. raise ValueError("expert_map must be a CUDA tensor")
  6063. elif expert_map.device != device:
  6064. raise ValueError("expert_map must be on the same device as a_input")
  6065. if exact_num_experts is None:
  6066. if expert_map.ndim != 1 or not expert_map.is_contiguous():
  6067. raise ValueError("expert_map must be a contiguous rank-1 tensor")
  6068. return
  6069. if not expert_map.is_contiguous():
  6070. raise ValueError("expert_map must be contiguous")
  6071. if expert_map.ndim != 1 or int(expert_map.numel()) != int(exact_num_experts):
  6072. raise ValueError(
  6073. f"expert_map must have shape {(int(exact_num_experts),)}, got {tuple(expert_map.shape)}"
  6074. )
  6075. def _validate_activation_amax(
  6076. activation_amax: torch.Tensor | None,
  6077. *,
  6078. layer_idx: int | None,
  6079. num_experts: int,
  6080. device: torch.device,
  6081. ) -> int | None:
  6082. if activation_amax is None:
  6083. if layer_idx is not None:
  6084. raise ValueError("layer_idx requires activation_amax")
  6085. return None
  6086. if layer_idx is None:
  6087. raise ValueError("layer_idx is required when activation_amax is provided")
  6088. if activation_amax.dtype != torch.float32:
  6089. raise TypeError("activation_amax must be torch.float32")
  6090. if activation_amax.device != device:
  6091. raise ValueError("activation_amax must be on the same device as a_input")
  6092. if not activation_amax.is_cuda:
  6093. raise ValueError("activation_amax must be a CUDA tensor")
  6094. if not activation_amax.is_contiguous():
  6095. raise ValueError("activation_amax must be contiguous")
  6096. if activation_amax.ndim != 3 or int(activation_amax.shape[2]) != 2:
  6097. raise ValueError(
  6098. "activation_amax must have shape [num_layers, num_experts, 2]"
  6099. )
  6100. if int(activation_amax.shape[1]) < int(num_experts):
  6101. raise ValueError(
  6102. "activation_amax expert dimension is smaller than the local expert count"
  6103. )
  6104. layer = int(layer_idx)
  6105. if layer < 0 or layer >= int(activation_amax.shape[0]):
  6106. raise ValueError(
  6107. f"layer_idx {layer} is out of bounds for activation_amax with "
  6108. f"{int(activation_amax.shape[0])} layers"
  6109. )
  6110. return layer
  6111. def _compile_w4a16_gemm_launch(
  6112. *,
  6113. size_m: int,
  6114. size_n: int,
  6115. size_k: int,
  6116. num_experts: int,
  6117. top_k: int,
  6118. mul_topk_weights: bool,
  6119. moe_block_size: int,
  6120. max_m_blocks: int,
  6121. element_dtype: str,
  6122. packed_route_indices: torch.Tensor,
  6123. sms: int,
  6124. max_shared_mem: int,
  6125. device: torch.device,
  6126. c_tmp: torch.Tensor | None = None,
  6127. scale_format: str = "e4m3_k16",
  6128. ) -> _W4A16GemmLaunch:
  6129. tile_k, tile_n, _, _ = _select_tile_config(
  6130. problem_m=size_m,
  6131. problem_n=size_n,
  6132. problem_k=size_k,
  6133. top_k=top_k,
  6134. moe_block_size=moe_block_size,
  6135. sms=sms,
  6136. max_shared_mem=max_shared_mem,
  6137. scale_format=scale_format,
  6138. )
  6139. kernel = compile_w4a16_gemm(
  6140. size_m=size_m,
  6141. size_n=size_n,
  6142. size_k=size_k,
  6143. num_experts=num_experts,
  6144. top_k=top_k,
  6145. mul_topk_weights=mul_topk_weights,
  6146. tile_n=tile_n,
  6147. tile_k=tile_k,
  6148. moe_block_size=moe_block_size,
  6149. max_m_blocks=max_m_blocks,
  6150. element_dtype=element_dtype,
  6151. scale_format=scale_format,
  6152. )
  6153. c_tmp = _get_c_tmp(
  6154. packed_gemm_scratch_elements(
  6155. size_n=size_n,
  6156. route_slots=int(packed_route_indices.numel()),
  6157. moe_block_size=moe_block_size,
  6158. sms=sms,
  6159. ),
  6160. device=device,
  6161. scratch=c_tmp,
  6162. )
  6163. return _W4A16GemmLaunch(kernel=kernel, c_tmp=c_tmp)
  6164. def pack_topk_routes_by_expert(
  6165. topk_ids: torch.Tensor,
  6166. block_size: int,
  6167. num_experts: int,
  6168. *,
  6169. expert_map: torch.Tensor | None = None,
  6170. packed_route_indices: torch.Tensor | None = None,
  6171. block_expert_ids: torch.Tensor | None = None,
  6172. packed_route_count: torch.Tensor | None = None,
  6173. expert_offsets: torch.Tensor | None = None,
  6174. stream: cuda.CUstream | None = None,
  6175. ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
  6176. """Group top-k routes by expert and pad each group to the GEMM M-block size."""
  6177. _validate_topk_ids(topk_ids, require_cuda=True)
  6178. if block_size <= 0:
  6179. raise ValueError(f"block_size must be positive, got {block_size}")
  6180. if num_experts <= 0:
  6181. raise ValueError(f"num_experts must be positive, got {num_experts}")
  6182. _validate_expert_map(expert_map, exact_num_experts=int(num_experts))
  6183. del stream
  6184. return _pack_topk_routes_by_expert(
  6185. topk_ids,
  6186. int(block_size),
  6187. int(num_experts),
  6188. expert_map=expert_map,
  6189. packed_route_indices=packed_route_indices,
  6190. block_expert_ids=block_expert_ids,
  6191. packed_route_count=packed_route_count,
  6192. expert_offsets=expert_offsets,
  6193. )
  6194. def run_w4a16_moe(
  6195. a_input: torch.Tensor,
  6196. prepared,
  6197. topk_weights: torch.Tensor,
  6198. topk_ids: torch.Tensor,
  6199. *,
  6200. activation: str,
  6201. intermediate_cache13: torch.Tensor,
  6202. intermediate_cache2: torch.Tensor,
  6203. output: torch.Tensor,
  6204. fc1_c_tmp: torch.Tensor | None = None,
  6205. fc2_c_tmp: torch.Tensor | None = None,
  6206. packed_route_indices: torch.Tensor | None = None,
  6207. block_expert_ids: torch.Tensor | None = None,
  6208. packed_route_count: torch.Tensor | None = None,
  6209. expert_offsets: torch.Tensor | None = None,
  6210. expert_map: torch.Tensor | None = None,
  6211. activation_amax: torch.Tensor | None = None,
  6212. layer_idx: int | None = None,
  6213. apply_router_weight_on_input: bool = False,
  6214. fast_math: bool = True,
  6215. swiglu_limit: float | None = None,
  6216. swiglu_alpha: float | None = None,
  6217. swiglu_beta: float | None = None,
  6218. fused_launch: W4A16FusedMoeCompileResult | None = None,
  6219. topk_sum_launch: W4A16TopKSumCompileResult | None = None,
  6220. stream: cuda.CUstream | None = None,
  6221. ) -> torch.Tensor:
  6222. activation = normalize_moe_activation(activation)
  6223. is_gated = validate_activation(activation)
  6224. swiglu_limit, swiglu_alpha, swiglu_beta = _normalize_activation_swiglu_params(
  6225. activation,
  6226. swiglu_limit,
  6227. swiglu_alpha,
  6228. swiglu_beta,
  6229. )
  6230. element_dtype = _normalize_element_dtype(a_input.dtype)
  6231. if output.dtype != a_input.dtype:
  6232. raise TypeError(f"output must have dtype {a_input.dtype}, got {output.dtype}")
  6233. prepared_dtype = getattr(prepared, "params_dtype", a_input.dtype)
  6234. if prepared_dtype != a_input.dtype:
  6235. raise TypeError(
  6236. f"prepared weights were built for {prepared_dtype}, but a_input has dtype {a_input.dtype}"
  6237. )
  6238. weight_layout = getattr(prepared, "weight_layout", "packed")
  6239. if weight_layout not in _WEIGHT_LAYOUTS:
  6240. raise ValueError(f"unsupported W4A16 weight_layout {weight_layout!r}")
  6241. scale_format = _normalize_scale_format(
  6242. getattr(prepared, "scale_format", None)
  6243. or (
  6244. "e8m0_k32"
  6245. if getattr(prepared, "source_format", "") == "fp4_e8m0_k32"
  6246. else "e4m3_k16"
  6247. )
  6248. )
  6249. w13_layout = getattr(
  6250. prepared,
  6251. "w13_layout",
  6252. "w13" if weight_layout == "modelopt" else "packed",
  6253. )
  6254. if weight_layout == "modelopt":
  6255. if w13_layout not in _MODEL_OPT_W13_LAYOUTS:
  6256. raise ValueError(f"unsupported W4A16 w13_layout {w13_layout!r}")
  6257. else:
  6258. w13_layout = "packed"
  6259. if topk_weights.dtype != torch.float32:
  6260. raise TypeError("topk_weights must be torch.float32")
  6261. _validate_topk_ids(topk_ids, require_cuda=False, require_contiguous=False)
  6262. if (
  6263. not a_input.is_contiguous()
  6264. or not topk_weights.is_contiguous()
  6265. or not topk_ids.is_contiguous()
  6266. ):
  6267. raise ValueError("a_input, topk_weights, and topk_ids must be contiguous")
  6268. _validate_expert_map(expert_map, device=a_input.device)
  6269. m, hidden_size = a_input.shape
  6270. topk = int(topk_ids.shape[1])
  6271. if tuple(topk_weights.shape) != (m, topk):
  6272. raise ValueError(f"topk_weights must have shape {(m, topk)}")
  6273. if int(prepared.hidden_size) != hidden_size:
  6274. raise ValueError("prepared hidden_size does not match a_input")
  6275. if bool(prepared.is_gated) != is_gated:
  6276. raise ValueError("prepared weights do not match activation")
  6277. if tuple(output.shape) != (m, hidden_size):
  6278. raise ValueError(f"output must have shape {(m, hidden_size)}")
  6279. if expert_map is not None and int(expert_map.numel()) < int(prepared.num_experts):
  6280. raise ValueError("expert_map cannot be shorter than the local expert count")
  6281. layer_idx_int = _validate_activation_amax(
  6282. activation_amax,
  6283. layer_idx=layer_idx,
  6284. num_experts=int(prepared.num_experts),
  6285. device=a_input.device,
  6286. )
  6287. collect_activation_amax = activation_amax is not None
  6288. route_num_experts = (
  6289. int(expert_map.numel()) if expert_map is not None else int(prepared.num_experts)
  6290. )
  6291. if fused_launch is None:
  6292. block_size_m = select_route_block_size_m(m, topk, route_num_experts)
  6293. else:
  6294. block_size_m = int(fused_launch.moe_block_size)
  6295. if block_size_m not in _ALLOWED_ROUTED_SIZES:
  6296. raise ValueError(f"unsupported W4A16 moe_block_size={block_size_m}")
  6297. stream = current_cuda_stream() if stream is None else stream
  6298. if (not collect_activation_amax) and _small_m_direct_supported(
  6299. m=m,
  6300. hidden_size=hidden_size,
  6301. intermediate_size=int(prepared.intermediate_size),
  6302. num_experts=int(prepared.num_experts),
  6303. topk=topk,
  6304. activation=activation,
  6305. apply_router_weight_on_input=bool(apply_router_weight_on_input),
  6306. swiglu_limit=swiglu_limit,
  6307. swiglu_alpha=swiglu_alpha,
  6308. swiglu_beta=swiglu_beta,
  6309. element_dtype=element_dtype,
  6310. weight_layout=weight_layout,
  6311. w13_layout=w13_layout,
  6312. scale_format=scale_format,
  6313. expert_map=expert_map,
  6314. ):
  6315. if topk_ids.dtype not in (torch.int32, torch.int64):
  6316. raise TypeError("W4A16 small-M direct path requires int32/int64 topk_ids")
  6317. if not topk_ids.is_cuda:
  6318. raise ValueError("W4A16 small-M direct path requires CUDA topk_ids")
  6319. if not intermediate_cache2.is_contiguous() or not output.is_contiguous():
  6320. raise ValueError(
  6321. "W4A16 small-M direct path requires contiguous intermediate_cache2 and output"
  6322. )
  6323. if intermediate_cache2.dtype != a_input.dtype:
  6324. raise TypeError(f"intermediate_cache2 must be {a_input.dtype}")
  6325. if int(prepared.workspace.numel()) < 2:
  6326. raise ValueError("prepared W4A16 workspace is too small for small-M direct")
  6327. intermediate_size = int(prepared.intermediate_size)
  6328. fc2_n_chunks = ((intermediate_size // 2) + 127) // 128
  6329. inter_u32_per_m = fc2_n_chunks * 128 * topk
  6330. inter_u32 = intermediate_cache2.view(-1).view(torch.uint32)
  6331. if int(inter_u32.numel()) < m * inter_u32_per_m:
  6332. raise ValueError(
  6333. "intermediate_cache2 is smaller than the W4A16 small-M direct scratch "
  6334. f"requirement: have_u32={int(inter_u32.numel())}, "
  6335. f"need_u32={m * inter_u32_per_m}"
  6336. )
  6337. micro_w13_scale = getattr(prepared, "micro_w13_scale", None)
  6338. micro_w2_scale = getattr(prepared, "micro_w2_scale", None)
  6339. micro_w13_global = getattr(prepared, "micro_w13_global_scale", None)
  6340. micro_w2_global = getattr(prepared, "micro_w2_global_scale", None)
  6341. if (
  6342. micro_w13_scale is None
  6343. or micro_w2_scale is None
  6344. or micro_w13_global is None
  6345. or micro_w2_global is None
  6346. ):
  6347. raise RuntimeError(
  6348. "W4A16 small-M direct path requires prepared micro scale metadata"
  6349. )
  6350. barrier_count = prepared.workspace[-2:-1]
  6351. barrier_epoch = prepared.workspace[-1:]
  6352. barrier_count.zero_()
  6353. barrier_epoch.zero_()
  6354. torch.ops.b12x.w4a16_small_m_direct_launch(
  6355. a_input,
  6356. prepared.w13.view(torch.uint8),
  6357. micro_w13_scale,
  6358. micro_w13_global,
  6359. micro_w2_global,
  6360. inter_u32[: m * inter_u32_per_m],
  6361. prepared.w2.view(torch.uint8),
  6362. micro_w2_scale,
  6363. topk_ids,
  6364. topk_weights,
  6365. output,
  6366. barrier_count,
  6367. barrier_epoch,
  6368. m,
  6369. hidden_size,
  6370. intermediate_size,
  6371. int(prepared.num_experts),
  6372. topk,
  6373. activation,
  6374. bool(fast_math),
  6375. scale_format,
  6376. swiglu_limit is not None,
  6377. float(swiglu_limit or 0.0),
  6378. float(swiglu_alpha),
  6379. float(swiglu_beta),
  6380. w13_layout,
  6381. int(stream),
  6382. )
  6383. return output
  6384. # TC-decode: small-M packed decode that folds the top-k sum into the FC2
  6385. # store epilogue. Reuses the packed tensor-core MMA path; only the launch
  6386. # scheduling/epilogue changes. Requires the packed object, bf16 gated
  6387. # activation, int32 routes, no expert_map, and a runtime-compiled launch.
  6388. # A preplanned launch built with the TC-decode fused-sum epilogue carries
  6389. # ``tc_decode_fused_sum``; accept it through the binding path. A runtime
  6390. # ``fused_launch is None`` (e.g. the standalone benchmark) compiles its own.
  6391. preplanned_tc_decode = bool(getattr(fused_launch, "tc_decode_fused_sum", False))
  6392. use_tc_decode = bool(
  6393. (not collect_activation_amax)
  6394. and (fused_launch is None or preplanned_tc_decode)
  6395. and weight_layout in ("packed", "nf3_2p1")
  6396. and expert_map is None
  6397. and is_gated
  6398. and element_dtype == "bf16"
  6399. and topk_ids.dtype in (torch.int32, torch.int64)
  6400. and topk_ids.is_cuda
  6401. and int(m) <= _TC_DECODE_MAX_M
  6402. )
  6403. if use_tc_decode and topk_ids.dtype != torch.int32:
  6404. # The inline direct-topk route path needs int32 route indices.
  6405. topk_ids = topk_ids.to(torch.int32)
  6406. direct_topk_eligible = (
  6407. (not collect_activation_amax)
  6408. and (m <= _MAX_DIRECT_TOPK_ROUTE_M or use_tc_decode)
  6409. and weight_layout in ("packed", "nf3_2p1")
  6410. and expert_map is None
  6411. )
  6412. use_direct_topk_routes = bool(
  6413. direct_topk_eligible
  6414. and topk_ids.dtype == torch.int32
  6415. and (
  6416. fused_launch is None
  6417. or bool(getattr(fused_launch, "direct_topk_routes", False))
  6418. )
  6419. )
  6420. if (
  6421. bool(getattr(fused_launch, "direct_topk_routes", False))
  6422. and not use_direct_topk_routes
  6423. ):
  6424. raise RuntimeError(
  6425. "preplanned W4A16 direct top-k routing requires small-M packed "
  6426. "int32 topk_ids without expert_map"
  6427. )
  6428. # TC-decode requires the inline direct-topk route path (no route-pack).
  6429. use_tc_decode = bool(use_tc_decode and use_direct_topk_routes)
  6430. # A preplanned TC-decode launch atomically accumulates FC2 partials into the
  6431. # (pre-zeroed) output and emits no separate top-k sum. If it was selected but
  6432. # the decode preconditions don't hold, running it would corrupt the output,
  6433. # so fail loudly instead.
  6434. if preplanned_tc_decode and not use_tc_decode:
  6435. raise RuntimeError(
  6436. "preplanned TC-decode W4A16 launch requires small-M packed bf16 "
  6437. f"decode (m <= {_TC_DECODE_MAX_M}, cuda int32/int64 topk_ids, "
  6438. "no expert_map)"
  6439. )
  6440. route_slots_for_scratch = int(m) * int(topk) * int(block_size_m)
  6441. required_m_blocks = int(m) * int(topk) if use_direct_topk_routes else 0
  6442. if fused_launch is not None and not use_direct_topk_routes:
  6443. route_slots_capacity = max_packed_route_slots(
  6444. int(fused_launch.size_m) * int(topk),
  6445. int(block_size_m),
  6446. route_num_experts,
  6447. )
  6448. route_blocks_capacity = (route_slots_capacity + int(block_size_m) - 1) // int(
  6449. block_size_m
  6450. )
  6451. if packed_route_indices is not None:
  6452. if int(packed_route_indices.numel()) < route_slots_capacity:
  6453. raise ValueError(
  6454. "packed_route_indices is smaller than the selected W4A16 launch capacity"
  6455. )
  6456. packed_route_indices = packed_route_indices[:route_slots_capacity]
  6457. if block_expert_ids is not None:
  6458. if int(block_expert_ids.numel()) < route_blocks_capacity:
  6459. raise ValueError(
  6460. "block_expert_ids is smaller than the selected W4A16 launch capacity"
  6461. )
  6462. block_expert_ids = block_expert_ids[:route_blocks_capacity]
  6463. if use_direct_topk_routes:
  6464. packed_route_indices = topk_ids.view(-1)
  6465. if block_expert_ids is None:
  6466. block_expert_ids = packed_route_indices
  6467. if packed_route_count is None:
  6468. packed_route_count = packed_route_indices
  6469. else:
  6470. packed_route_indices, block_expert_ids, packed_route_count = (
  6471. pack_topk_routes_by_expert(
  6472. topk_ids,
  6473. block_size_m,
  6474. route_num_experts,
  6475. expert_map=expert_map,
  6476. packed_route_indices=packed_route_indices,
  6477. block_expert_ids=block_expert_ids,
  6478. packed_route_count=packed_route_count,
  6479. expert_offsets=expert_offsets,
  6480. stream=stream,
  6481. )
  6482. )
  6483. route_slots_for_scratch = int(packed_route_indices.numel())
  6484. required_m_blocks = int(block_expert_ids.numel())
  6485. props = torch.cuda.get_device_properties(a_input.device)
  6486. sms = int(props.multi_processor_count)
  6487. max_shared_mem = int(
  6488. getattr(props, "shared_memory_per_block_optin", _DEFAULT_MAX_SHARED_MEM)
  6489. )
  6490. buffer_plan = plan_w4a16_buffers(
  6491. prepared,
  6492. m=m,
  6493. topk=topk,
  6494. route_num_experts=route_num_experts,
  6495. sms=sms,
  6496. )
  6497. intermediate_size = int(prepared.intermediate_size)
  6498. fc1_cols = buffer_plan.fc1_cols
  6499. if intermediate_cache13.numel() < buffer_plan.intermediate_cache13_elements:
  6500. raise ValueError(
  6501. f"intermediate_cache13 has {intermediate_cache13.numel()} elements; "
  6502. f"need at least {buffer_plan.intermediate_cache13_elements}"
  6503. )
  6504. if intermediate_cache2.numel() < buffer_plan.intermediate_cache2_elements:
  6505. raise ValueError(
  6506. f"intermediate_cache2 has {intermediate_cache2.numel()} elements; "
  6507. f"need at least {buffer_plan.intermediate_cache2_elements}"
  6508. )
  6509. if (
  6510. intermediate_cache13.dtype != a_input.dtype
  6511. or intermediate_cache2.dtype != a_input.dtype
  6512. ):
  6513. raise TypeError(f"intermediate caches must be {a_input.dtype}")
  6514. if (
  6515. not intermediate_cache13.is_contiguous()
  6516. or not intermediate_cache2.is_contiguous()
  6517. or not output.is_contiguous()
  6518. ):
  6519. raise ValueError("intermediate caches and output must be contiguous")
  6520. intermediate_cache13_flat = intermediate_cache13.view(-1)
  6521. intermediate_cache2_flat = intermediate_cache2.view(-1)
  6522. if int(prepared.workspace.numel()) < sms * 4 + 2:
  6523. raise ValueError("prepared W4A16 workspace is too small for fused FC1+FC2")
  6524. if fused_launch is None:
  6525. fused = compile_w4a16_fused_moe(
  6526. size_m=m,
  6527. hidden_size=hidden_size,
  6528. intermediate_size=intermediate_size,
  6529. num_experts=int(prepared.num_experts),
  6530. top_k=topk,
  6531. activation=activation,
  6532. apply_router_weight_on_input=bool(apply_router_weight_on_input),
  6533. zero_fc2_output=expert_map is not None,
  6534. moe_block_size=block_size_m,
  6535. max_m_blocks=int(required_m_blocks),
  6536. element_dtype=element_dtype,
  6537. fast_math=bool(fast_math),
  6538. sms=sms,
  6539. max_shared_mem=max_shared_mem,
  6540. swiglu_limit=swiglu_limit,
  6541. swiglu_alpha=swiglu_alpha,
  6542. swiglu_beta=swiglu_beta,
  6543. weight_layout=weight_layout,
  6544. scale_format=scale_format,
  6545. w13_layout=w13_layout,
  6546. direct_topk_routes=use_direct_topk_routes,
  6547. tc_decode_fused_sum=use_tc_decode,
  6548. collect_activation_amax=collect_activation_amax,
  6549. )
  6550. else:
  6551. if int(fused_launch.size_m) < m:
  6552. raise RuntimeError(
  6553. "preplanned W4A16 fused MoE launch capacity is smaller than requested rows: "
  6554. f"requested={m}, planned={int(fused_launch.size_m)}"
  6555. )
  6556. expected_fused = (
  6557. hidden_size,
  6558. intermediate_size,
  6559. int(prepared.num_experts),
  6560. topk,
  6561. activation,
  6562. bool(apply_router_weight_on_input),
  6563. expert_map is not None,
  6564. element_dtype,
  6565. bool(fast_math),
  6566. swiglu_limit,
  6567. float(swiglu_alpha),
  6568. float(swiglu_beta),
  6569. weight_layout,
  6570. scale_format,
  6571. w13_layout,
  6572. bool(use_direct_topk_routes),
  6573. bool(collect_activation_amax),
  6574. block_size_m,
  6575. )
  6576. actual_fused = (
  6577. int(fused_launch.hidden_size),
  6578. int(fused_launch.intermediate_size),
  6579. int(fused_launch.num_experts),
  6580. int(fused_launch.top_k),
  6581. fused_launch.activation,
  6582. bool(fused_launch.apply_router_weight_on_input),
  6583. bool(fused_launch.zero_fc2_output),
  6584. fused_launch.element_dtype,
  6585. bool(fused_launch.fast_math),
  6586. fused_launch.swiglu_limit,
  6587. float(fused_launch.swiglu_alpha),
  6588. float(fused_launch.swiglu_beta),
  6589. getattr(fused_launch, "weight_layout", "packed"),
  6590. getattr(fused_launch, "scale_format", "e4m3_k16"),
  6591. getattr(
  6592. fused_launch,
  6593. "w13_layout",
  6594. "w13"
  6595. if getattr(fused_launch, "weight_layout", "packed") == "modelopt"
  6596. else "packed",
  6597. ),
  6598. bool(getattr(fused_launch, "direct_topk_routes", False)),
  6599. bool(getattr(fused_launch, "collect_activation_amax", False)),
  6600. int(fused_launch.moe_block_size),
  6601. )
  6602. if actual_fused != expected_fused or int(fused_launch.max_m_blocks) < int(
  6603. required_m_blocks
  6604. ):
  6605. raise RuntimeError(
  6606. "preplanned W4A16 fused MoE launch does not match requested contract: "
  6607. f"requested={expected_fused + (int(required_m_blocks),)}, "
  6608. f"planned={actual_fused + (int(fused_launch.max_m_blocks),)}"
  6609. )
  6610. fused = fused_launch
  6611. capacity_m = int(fused.size_m)
  6612. capacity_routed_rows = capacity_m * topk
  6613. if intermediate_cache13_flat.numel() < capacity_routed_rows * max(fc1_cols, hidden_size):
  6614. raise ValueError(
  6615. "intermediate_cache13 is smaller than the selected W4A16 launch capacity: "
  6616. f"capacity_rows={capacity_m}, topk={topk}"
  6617. )
  6618. if intermediate_cache2_flat.numel() < capacity_routed_rows * intermediate_size:
  6619. raise ValueError(
  6620. "intermediate_cache2 is smaller than the selected W4A16 launch capacity: "
  6621. f"capacity_rows={capacity_m}, topk={topk}"
  6622. )
  6623. fc1_out = intermediate_cache13_flat[: capacity_routed_rows * fc1_cols]
  6624. activated = intermediate_cache2_flat[: capacity_routed_rows * intermediate_size]
  6625. if use_tc_decode:
  6626. # FC2 atomically accumulates per-route partials directly into the
  6627. # per-token output, so the output is the FC2 store target and must be
  6628. # pre-zeroed. The fused tc_decode kernel now zeroes the output in its
  6629. # own prologue (before FC1, made visible by the existing post-FC1 grid
  6630. # barrier), so the separate host-side output.zero_() launch is removed
  6631. # from the decode critical path here. This drops the separate top-k-sum
  6632. # launch as well.
  6633. fc2_out = output.view(-1)
  6634. else:
  6635. fc2_out = intermediate_cache13_flat[: capacity_routed_rows * hidden_size]
  6636. fc1_scratch = _get_c_tmp(
  6637. packed_gemm_scratch_elements(
  6638. size_n=fc1_cols,
  6639. route_slots=int(route_slots_for_scratch),
  6640. moe_block_size=block_size_m,
  6641. sms=sms,
  6642. ),
  6643. device=a_input.device,
  6644. scratch=fc1_c_tmp,
  6645. )
  6646. fc2_scratch = _get_c_tmp(
  6647. packed_gemm_scratch_elements(
  6648. size_n=hidden_size,
  6649. route_slots=int(route_slots_for_scratch),
  6650. moe_block_size=block_size_m,
  6651. sms=sms,
  6652. ),
  6653. device=a_input.device,
  6654. scratch=fc2_c_tmp,
  6655. )
  6656. if weight_layout == "modelopt":
  6657. w13_arg = prepared.w13.view(torch.uint8).view(-1)
  6658. w2_arg = prepared.w2.view(torch.uint8).view(-1)
  6659. else:
  6660. w13_arg = prepared.w13.view(torch.int32).view(-1)
  6661. w2_arg = prepared.w2.view(torch.int32).view(-1)
  6662. launch_common = (
  6663. a_input,
  6664. w13_arg,
  6665. w2_arg,
  6666. fc1_out,
  6667. activated,
  6668. fc2_out,
  6669. prepared.w13_scale.view(torch.uint8).view(torch.int32).view(-1),
  6670. prepared.w2_scale.view(torch.uint8).view(torch.int32).view(-1),
  6671. prepared.w13_global_scale,
  6672. prepared.w2_global_scale,
  6673. packed_route_indices,
  6674. block_expert_ids,
  6675. packed_route_count,
  6676. )
  6677. launch_tail = (
  6678. topk_weights,
  6679. fc1_scratch,
  6680. fc2_scratch,
  6681. prepared.workspace,
  6682. m,
  6683. capacity_m,
  6684. hidden_size,
  6685. intermediate_size,
  6686. int(prepared.num_experts),
  6687. topk,
  6688. activation,
  6689. bool(apply_router_weight_on_input),
  6690. expert_map is not None,
  6691. block_size_m,
  6692. int(fused.max_m_blocks),
  6693. element_dtype,
  6694. bool(fast_math),
  6695. sms,
  6696. max_shared_mem,
  6697. swiglu_limit is not None,
  6698. float(swiglu_limit or 0.0),
  6699. float(swiglu_alpha),
  6700. float(swiglu_beta),
  6701. weight_layout,
  6702. scale_format,
  6703. w13_layout,
  6704. )
  6705. if collect_activation_amax:
  6706. assert activation_amax is not None
  6707. assert layer_idx_int is not None
  6708. torch.ops.b12x.w4a16_fused_moe_calibrated_launch(
  6709. *launch_common,
  6710. activation_amax,
  6711. int(layer_idx_int),
  6712. *launch_tail,
  6713. int(stream),
  6714. )
  6715. else:
  6716. torch.ops.b12x.w4a16_fused_moe_launch(
  6717. *launch_common,
  6718. *launch_tail,
  6719. bool(use_direct_topk_routes),
  6720. bool(use_tc_decode),
  6721. int(stream),
  6722. )
  6723. if use_tc_decode:
  6724. # FC2 already wrote the top-k-summed result into `output`.
  6725. return output
  6726. if topk_sum_launch is not None:
  6727. expected_sum = (topk, hidden_size)
  6728. actual_sum = (
  6729. int(topk_sum_launch.topk),
  6730. int(topk_sum_launch.hidden_size),
  6731. )
  6732. if actual_sum != expected_sum:
  6733. raise RuntimeError(
  6734. "preplanned W4A16 top-k sum launch does not match requested contract: "
  6735. f"requested={expected_sum}, planned={actual_sum}"
  6736. )
  6737. torch.ops.b12x.w4a16_topk_sum_launch(
  6738. fc2_out,
  6739. output,
  6740. m,
  6741. topk,
  6742. hidden_size,
  6743. element_dtype,
  6744. int(stream),
  6745. )
  6746. return output
  6747. __all__ = [
  6748. "W4A16ActivationCompileResult",
  6749. "W4A16FusedMoeCompileResult",
  6750. "W4A16GemmCompileResult",
  6751. "W4A16TopKSumCompileResult",
  6752. "W4A16FusedMoeKernel",
  6753. "W4A16ActivationKernel",
  6754. "W4A16GemmKernel",
  6755. "W4A16TopKSumKernel",
  6756. "clear_w4a16_kernel_cache",
  6757. "compile_w4a16_activation",
  6758. "compile_w4a16_fused_moe",
  6759. "compile_w4a16_gemm",
  6760. "compile_w4a16_topk_sum",
  6761. "pack_topk_routes_by_expert",
  6762. "run_w4a16_moe",
  6763. ]