dense_gemm.py 317 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797
  1. # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  2. # SPDX-License-Identifier: BSD-3-Clause
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions are met:
  5. # 1. Redistributions of source code must retain the above copyright notice, this
  6. # list of conditions and the following disclaimer.
  7. # 2. Redistributions in binary form must reproduce the above copyright notice,
  8. # this list of conditions and the following disclaimer in the documentation
  9. # and/or other materials provided with the distribution.
  10. # 3. Neither the name of the copyright holder nor the names of its
  11. # contributors may be used to endorse or promote products derived from
  12. # this software without specific prior written permission.
  13. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  14. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  17. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19. # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  20. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  21. # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  22. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. # This file is ported from the CUTLASS dense block-scaled GEMM example
  24. # and adapted for the current Blackwell GeForce target.
  25. from dataclasses import dataclass
  26. from typing import Callable, List, Literal, Optional, Tuple, Type
  27. import cuda.bindings.driver as cuda
  28. import cutlass
  29. import cutlass.cute as cute
  30. import cutlass.pipeline as pipeline
  31. import cutlass.utils as utils
  32. import cutlass.utils.blackwell_helpers as sm120_utils
  33. import cutlass.utils.blockscaled_layout as blockscaled_utils
  34. import cutlass.utils.hopper_helpers as sm90_utils
  35. import functools
  36. import logging
  37. import os
  38. import time
  39. import torch
  40. import triton
  41. import triton.language as tl
  42. from cutlass import Int32, Int64, Uint8, Uint32, Uint64
  43. from cutlass.cute.nvgpu import cpasync
  44. from cutlass.cute.nvgpu.warp.mma import Field as WarpField
  45. from cutlass.utils.static_persistent_tile_scheduler import WorkTileInfo
  46. from b12x._lib.compiler import (
  47. KernelCompileSpec,
  48. compile as b12x_compile,
  49. )
  50. from b12x._lib.utils import (
  51. cuda_stream_from_int_or_current,
  52. cuda_stream_to_int,
  53. current_cuda_stream,
  54. cutlass_to_torch_dtype,
  55. get_cutlass_dtype,
  56. get_max_active_clusters,
  57. get_num_sm,
  58. is_mxfp6_ab_dtype,
  59. make_ptr,
  60. mxfp6_logical_k_from_packed_bytes,
  61. mxfp6_tile_k,
  62. sm120_make_smem_layout_sfa,
  63. sm120_make_smem_layout_sfb,
  64. )
  65. from b12x._lib.intrinsics import (
  66. FLOAT8_E4M3_MAX,
  67. bfloat2_to_float2_scaled,
  68. cp_async_bulk_g2s_mbar,
  69. cvt_f32x4_to_e4m3x4,
  70. elem_pointer,
  71. fabs_f32,
  72. fmax_f32,
  73. get_ptr_as_int64,
  74. ld_global_b16,
  75. ld_global_v4_u32,
  76. ld_shared_v4_u32,
  77. mma_m16n8k32_f32_e4m3,
  78. pow2_ceil_ue8m0,
  79. quantize_block_fp8_mx,
  80. scatter_add_bf16,
  81. scatter_add_bf16x2,
  82. shared_ptr_to_u32,
  83. st_global_u32,
  84. st_global_u64,
  85. st_shared_u16,
  86. st_shared_u8,
  87. st_shared_v4_u32,
  88. u32_as_f32,
  89. ue8m0_to_output_scale,
  90. warp_reduce,
  91. )
  92. from b12x._lib.dense_gemm_mxfp6 import emit_mxfp6_dense_mma_k_block
  93. from b12x._lib.fp6 import (
  94. FLOAT6_E2M3_MAX,
  95. FLOAT6_E3M2_MAX,
  96. cvt_f32_to_e2m3x2,
  97. cvt_f32_to_e3m2x2,
  98. cvt_f32_to_e4m3x2,
  99. expand_mxfp6_packed_to_bytes,
  100. fp6_block_ue8m0_exact,
  101. mx_gs_numerator,
  102. ue8m0_output_scale_exact,
  103. )
  104. from b12x._lib.runtime_control import (
  105. raise_if_kernel_resolution_frozen,
  106. )
  107. logger = logging.getLogger(__name__)
  108. _WO_SPARK_MAX_SMS = 64
  109. _DENSE_SPARK_MAX_SMS = 64
  110. def _dense_spark_policy_for_sm_count(sm_count: int) -> bool:
  111. """Select dense-GEMM tactics measured on the low-SM DGX Spark class."""
  112. return int(sm_count) <= _DENSE_SPARK_MAX_SMS
  113. _B12X_TIMING = (
  114. os.getenv("B12X_TIMING", "0") == "1"
  115. or os.getenv("VLLM_B12X_TIMING", "0") == "1"
  116. )
  117. _B12X_TIMING_THRESHOLD_MS = float(
  118. os.getenv(
  119. "B12X_TIMING_THRESHOLD_MS",
  120. os.getenv("VLLM_B12X_TIMING_THRESHOLD_MS", "0"),
  121. )
  122. )
  123. _B12X_DENSE_SPLITK_TURBO = (
  124. os.getenv("B12X_DENSE_SPLITK_TURBO", "1") == "1"
  125. )
  126. # MX-FP6 decode uses at most three mainloop stages when two CTAs share an SM.
  127. _FP6_DECODE_TILE = (16, 64)
  128. _FP6_PREFILL_TILE = (128, 128)
  129. _B12X_DENSE_ATOM_24 = (
  130. os.getenv("B12X_DENSE_ATOM_24", "0") == "1"
  131. )
  132. _DENSE_LOAD_PATHS = ("tma", "cpasync")
  133. # Expand-ahead for packed-B: at k_block 0 the MMA warps wait for stage s+1 and
  134. # expand it in place, overlapping the expansion with ALL of stage s's MMA work
  135. # instead of putting it on the critical path at the stage boundary. It requires
  136. # at least four pipeline stages of producer slack.
  137. _PACKED_B_EXPAND_AHEAD = os.environ.get(
  138. "B12X_PACKED_B_EXPAND_AHEAD", "1"
  139. ).lower() not in ("0", "false")
  140. # MX-FP6 fused activation quantization: fuse BF16 activation quantization into
  141. # the GEMM's DMA producer prologue, eliminating the separate quant kernel and
  142. # the HBM round-trip for activation codes+scales. Currently m=1 only (decode
  143. # hot path). The GEMM's producer warp does a full-row amax scan, derives
  144. # gs/alpha, then quantizes each K-tile's 32-element blocks directly into
  145. # sA/sSFA smem. Distinct from the MXFP8 ``fused_quant_a`` machinery.
  146. _DENSE_FUSED_QUANT = os.environ.get(
  147. "B12X_DENSE_FUSED_QUANT", "0"
  148. ).lower() not in ("0", "false")
  149. @dataclass(frozen=True)
  150. class _DenseGemmPlan:
  151. mma_tiler_mn: Tuple[int, int]
  152. load_path: Literal["tma", "cpasync"]
  153. swap_ab: bool
  154. @triton.jit
  155. def _reduce_split_k2_bf16_kernel(
  156. partials, out, total: tl.constexpr, BLOCK: tl.constexpr
  157. ) -> None:
  158. pid = tl.program_id(0)
  159. offs = pid * BLOCK + tl.arange(0, BLOCK)
  160. mask = offs < total
  161. accum = tl.load(partials + offs, mask=mask).to(tl.float32)
  162. accum += tl.load(partials + total + offs, mask=mask).to(tl.float32)
  163. tl.store(out + offs, accum, mask=mask)
  164. def _reduce_split_k2_bf16(
  165. partials: torch.Tensor, out: torch.Tensor, *, m: int, n: int
  166. ) -> None:
  167. """Fused 2-way split-K FP32-partials reduction (exact); faster than torch.add.
  168. Falls back to torch.add when the scratch/output layout is not the expected
  169. [m, n, 2] / [m, n, 1] contiguous-row form.
  170. """
  171. total = int(m) * int(n)
  172. if (
  173. partials.shape == (m, n, 2)
  174. and partials.stride() == (n, 1, total)
  175. and out.shape == (m, n, 1)
  176. and out.stride()[0] == n
  177. and out.stride()[1] == 1
  178. ):
  179. block = 1024
  180. grid = (triton.cdiv(total, block),)
  181. _reduce_split_k2_bf16_kernel[grid](partials, out, total, BLOCK=block)
  182. else:
  183. torch.add(partials[:, :, 0], partials[:, :, 1], out=out[:, :, 0])
  184. # @dsl_user_op on PersistentTileSchedulerParams.__init__ can rename attributes
  185. # (e.g. raster_along_m -> _raster_along_m, cluster_shape_major_fdd ->
  186. # cluster_shape_m_fdd) but __extract_mlir_values__ (used by TVM-FFI)
  187. # still references the original names.
  188. _orig_extract = utils.PersistentTileSchedulerParams.__extract_mlir_values__
  189. # Map of source-code attr name -> runtime attr name set by @dsl_user_op
  190. _ATTR_RENAMES = {
  191. "raster_along_m": "_raster_along_m",
  192. "cluster_shape_major_fdd": "cluster_shape_m_fdd",
  193. "cluster_shape_minor_fdd": "cluster_shape_n_fdd",
  194. }
  195. def _patched_extract(self):
  196. for src_name, dst_name in _ATTR_RENAMES.items():
  197. if not hasattr(self, src_name) and hasattr(self, dst_name):
  198. setattr(self, src_name, getattr(self, dst_name))
  199. return _orig_extract(self)
  200. utils.PersistentTileSchedulerParams.__extract_mlir_values__ = _patched_extract
  201. def _convert_layout_acc_mn(
  202. acc_layout: cute.Layout, transpose: bool = False
  203. ) -> cute.Layout:
  204. acc_layout_col_major = cute.make_layout(acc_layout.shape)
  205. shape = (
  206. (acc_layout_col_major.shape[0][1], acc_layout_col_major.shape[1]),
  207. (
  208. acc_layout_col_major.shape[0][0],
  209. *acc_layout_col_major.shape[0][2:],
  210. acc_layout_col_major.shape[2],
  211. ),
  212. *acc_layout_col_major.shape[3:],
  213. )
  214. stride = (
  215. (acc_layout_col_major.stride[0][1], acc_layout_col_major.stride[1]),
  216. (
  217. acc_layout_col_major.stride[0][0],
  218. *acc_layout_col_major.stride[0][2:],
  219. acc_layout_col_major.stride[2],
  220. ),
  221. *acc_layout_col_major.stride[3:],
  222. )
  223. if cutlass.const_expr(transpose):
  224. shape = (shape[1], shape[0], *shape[2:])
  225. stride = (stride[1], stride[0], *stride[2:])
  226. return cute.composition(acc_layout, cute.make_layout(shape, stride=stride))
  227. def _reshape_acc_to_mn(acc: cute.Tensor, transpose: bool = False) -> cute.Tensor:
  228. return cute.make_tensor(
  229. acc.iterator, _convert_layout_acc_mn(acc.layout, transpose=transpose)
  230. )
  231. @cute.jit
  232. def _emit_plain_fp8_dense_mma_k_block(
  233. accumulators: cute.Tensor,
  234. tCrA: cute.Tensor,
  235. tCrB: cute.Tensor,
  236. mt: int,
  237. nt: int,
  238. k_block_idx: int,
  239. ) -> None:
  240. acc = accumulators[None, mt, nt]
  241. a_frag = cute.flatten(
  242. cute.recast_tensor(tCrA[None, mt, k_block_idx], cutlass.Uint32)
  243. )
  244. b_frag = cute.flatten(
  245. cute.recast_tensor(tCrB[None, nt, k_block_idx], cutlass.Uint32)
  246. )
  247. d0, d1, d2, d3 = mma_m16n8k32_f32_e4m3(
  248. acc[0],
  249. acc[1],
  250. acc[2],
  251. acc[3],
  252. a_frag[0],
  253. a_frag[1],
  254. a_frag[2],
  255. a_frag[3],
  256. b_frag[0],
  257. b_frag[1],
  258. )
  259. acc[0] = d0
  260. acc[1] = d1
  261. acc[2] = d2
  262. acc[3] = d3
  263. @dataclass(frozen=True)
  264. class _DenseGemmPolicy:
  265. single_work_tile_per_cta: bool
  266. direct_one_m_tile_scheduler: bool
  267. use_m1_non_tma: bool
  268. split_k_slices: int
  269. split_k_atomic_bf16: bool
  270. large_m_unroll: bool
  271. def _max_active_clusters_for(
  272. cluster_shape_mn: Tuple[int, int],
  273. sm_count: int,
  274. ) -> int:
  275. cluster_size = cluster_shape_mn[0] * cluster_shape_mn[1]
  276. # For the default single-cluster launch, occupancy is bounded only by
  277. # the SM count. Avoid the CUTLASS hardware-info probe here because it
  278. # can fail on some driver/runtime combinations with INVALID_HANDLE
  279. # while providing no additional information for cluster_size == 1.
  280. return (
  281. sm_count
  282. if cluster_size == 1
  283. else min(get_max_active_clusters(cluster_size), sm_count)
  284. )
  285. def _use_direct_sfa_live16(
  286. *,
  287. m: int,
  288. n: int,
  289. k: int,
  290. l: int,
  291. sf_vec_size: int,
  292. tile_k: int,
  293. mma_tiler_mn: Tuple[int, int],
  294. load_path: str,
  295. swap_ab: bool,
  296. b_tile_major: bool,
  297. sfb_k_reuse: bool,
  298. alpha_is_one: bool,
  299. is_mxfp8: bool,
  300. ) -> bool:
  301. return (
  302. m == 16
  303. and is_mxfp8
  304. and sf_vec_size == 32
  305. and tile_k == 128
  306. and mma_tiler_mn == (32, 64)
  307. and load_path == "tma"
  308. and not swap_ab
  309. and b_tile_major
  310. and sfb_k_reuse
  311. and alpha_is_one
  312. and (n, k, l) in ((1024, 4096, 4), (4096, 4096, 1))
  313. )
  314. def _use_direct_m1_wo_a_inputs(
  315. *,
  316. m: int,
  317. n: int,
  318. k: int,
  319. l: int,
  320. sf_vec_size: int,
  321. tile_k: int,
  322. mma_tiler_mn: Tuple[int, int],
  323. load_path: str,
  324. swap_ab: bool,
  325. b_tile_major: bool,
  326. sfb_k_reuse: bool,
  327. is_mxfp8: bool,
  328. ) -> bool:
  329. return (
  330. m == 1
  331. and (n, k, l) == (1024, 4096, 4)
  332. and is_mxfp8
  333. and sf_vec_size == 32
  334. and tile_k == 128
  335. and mma_tiler_mn == (16, 64)
  336. and load_path == "tma"
  337. and not swap_ab
  338. and b_tile_major
  339. and sfb_k_reuse
  340. )
  341. def _dense_gemm_policy_for(
  342. *,
  343. m: int,
  344. n: int,
  345. k: int,
  346. l: int,
  347. ab_dtype: Type[cutlass.Numeric],
  348. c_dtype: Type[cutlass.Numeric],
  349. mma_tiler_mn: Tuple[int, int],
  350. cluster_shape_mn: Tuple[int, int],
  351. sm_count: int,
  352. expected_m: Optional[int] = None,
  353. ) -> _DenseGemmPolicy:
  354. max_active_clusters = _max_active_clusters_for(cluster_shape_mn, sm_count)
  355. tile_m, tile_n = mma_tiler_mn
  356. one_work_tile_per_cta = ((m + tile_m - 1) // tile_m) * (
  357. (n + tile_n - 1) // tile_n
  358. ) * l <= max_active_clusters
  359. single_work_tile_per_cta = (
  360. one_work_tile_per_cta and m < 16 and m <= tile_m and l == 1
  361. )
  362. direct_one_m_tile_scheduler = (
  363. one_work_tile_per_cta and m == 1 and m <= tile_m and l == 1
  364. )
  365. use_m1_non_tma = ab_dtype == cutlass.Float8E4M3FN and m == 1
  366. split_k_candidate = (
  367. single_work_tile_per_cta
  368. and ab_dtype == cutlass.Float8E4M3FN
  369. and c_dtype == cutlass.BFloat16
  370. and m <= 8
  371. and n >= 4096
  372. and k >= 4096
  373. and k % 256 == 0
  374. and l == 1
  375. )
  376. split_k_slices = 1
  377. if split_k_candidate:
  378. split_k_slices = (
  379. 4 if m == 8 and (n, k) == (4096, 4096) and mma_tiler_mn == (16, 128) else 2
  380. )
  381. # A declared expected_m owns compile-time tuning for its regime. Without a
  382. # hint, keep the unroll choice stable throughout the existing persistent
  383. # scheduler regime (m >= 16); otherwise warming a large prefill and serving
  384. # a smaller live prefill resolves a second kernel under frozen resolution.
  385. # Tiny M already has distinct scheduler/load policies and is warmed
  386. # separately by contract. M=4096 unrolling is a Spark win, but the RTX
  387. # audit measured regressions on q_b and wo_b, so RTX keeps the M=8192
  388. # threshold.
  389. large_m_unroll_threshold = (
  390. 4096 if _dense_spark_policy_for_sm_count(sm_count) else 8192
  391. )
  392. use_large_m_unroll = (
  393. expected_m >= large_m_unroll_threshold
  394. if expected_m is not None
  395. else not single_work_tile_per_cta
  396. and not direct_one_m_tile_scheduler
  397. and not use_m1_non_tma
  398. )
  399. return _DenseGemmPolicy(
  400. single_work_tile_per_cta=single_work_tile_per_cta,
  401. direct_one_m_tile_scheduler=direct_one_m_tile_scheduler,
  402. use_m1_non_tma=use_m1_non_tma,
  403. split_k_slices=split_k_slices,
  404. split_k_atomic_bf16=_B12X_DENSE_SPLITK_TURBO,
  405. large_m_unroll=(
  406. ab_dtype == cutlass.Float8E4M3FN and use_large_m_unroll and l == 1
  407. ),
  408. )
  409. @cute.jit
  410. def _spread_fp6_group_u32(g: Uint32) -> Uint32:
  411. """Spread 4 packed 6-bit codes (24 bits) into 4 byte lanes (bits[5:0] each).
  412. Output byte ``i`` holds ``(g >> 6*i) & 0x3F`` — identical per-group math to
  413. :func:`b12x._lib.fp6.expand_mxfp6_packed_to_bytes`. Two-step binary
  414. spread (12-bit halves to 16-bit lanes, then 6-bit fields to byte lanes): 2
  415. shifts + 2 LOP3-fusable mask/or pairs, ~30% fewer ops than masking each
  416. field out of ``g`` individually.
  417. """
  418. a = (g & Uint32(0x00000FFF)) | ((g << Uint32(4)) & Uint32(0x0FFF0000))
  419. return (a & Uint32(0x003F003F)) | ((a << Uint32(2)) & Uint32(0x3F003F00))
  420. @cute.jit
  421. def _expand_packed_b_triplet(
  422. a: Uint32, b: Uint32, c: Uint32
  423. ) -> Tuple[Uint32, Uint32, Uint32, Uint32]:
  424. """Expand 12 packed bytes (3 LE u32 words = 16 FP6 codes) to 4 output words.
  425. Little-endian regroup into four 24-bit groups of 4 codes, then byte-lane
  426. spread per group.
  427. """
  428. g0 = a & Uint32(0x00FFFFFF)
  429. g1 = (a >> Uint32(24)) | ((b & Uint32(0xFFFF)) << Uint32(8))
  430. g2 = (b >> Uint32(16)) | ((c & Uint32(0xFF)) << Uint32(16))
  431. g3 = c >> Uint32(8)
  432. return (
  433. _spread_fp6_group_u32(g0),
  434. _spread_fp6_group_u32(g1),
  435. _spread_fp6_group_u32(g2),
  436. _spread_fp6_group_u32(g3),
  437. )
  438. @cute.jit
  439. def _expand_packed_b_stage_smem(
  440. sb_base_addr: Int32,
  441. stage: Int32,
  442. tidx: Int32,
  443. tile_n: cutlass.Constexpr,
  444. tile_k: cutlass.Constexpr,
  445. num_threads: cutlass.Constexpr,
  446. sync_barrier,
  447. ) -> None:
  448. """Expand one 3:4-packed B stage IN PLACE into the byte-container sB stage.
  449. TMA stages the packed tile (96 B/row, plain k-major, no swizzle) into the
  450. BOTTOM ``tile_n * 3*tile_k/4`` bytes of the sB stage itself — no separate
  451. staging buffer, which buys an extra pipeline stage (3 -> 4 for the decode
  452. tile). The expanded output (128 B/row, swizzled) overlaps the packed input
  453. region, so expansion is two-phase: every thread loads ALL of its packed
  454. rows into registers, one MMA-group named-barrier, then writes the expanded
  455. swizzled rows. Each thread owns whole rows (``tile_n / num_threads`` of
  456. them), so within a row reads always precede writes; the barrier orders
  457. them across threads.
  458. Raw addressing is deliberate: ``cute.recast_tensor(sB, Uint8)`` STRIPS the
  459. smem swizzle on this build (confirmed in the fused-MoE FC2 requant path).
  460. The 8-bit K-major SW128 atom (Sw<3,4,3>, 8x128 = 1024 B) gives
  461. ``physical = flat ^ ((row & 7) << 4)`` — the XOR touches bits 4..6 only,
  462. so every 16-byte unit stays contiguous and the traffic is fully 128-bit
  463. (6 x ld.shared.v4 in, 8 x st.shared.v4 out per row). Per-group bit math
  464. matches :func:`b12x._lib.fp6.expand_mxfp6_packed_to_bytes` exactly,
  465. so downstream ldmatrix/MMA sees bytes identical to the pre-expanded path.
  466. Runs on the MMA warp group only; the caller must still follow with the
  467. MMA-group named barrier before any ldmatrix reads of this stage.
  468. """
  469. # Whole-row ownership; ceil-divide so configs with more threads than rows
  470. # (e.g. the 256-thread (128,128) tile) stay correct — guarded threads skip
  471. # the row I/O but ALL threads reach the phase barrier.
  472. rows_per_thread = (tile_n + num_threads - 1) // num_threads
  473. packed_row_bytes = tile_k * 3 // 4
  474. words_per_row = packed_row_bytes // 4
  475. sb_stage = sb_base_addr + stage * Int32(tile_n * tile_k)
  476. # Phase 1: read all owned packed rows into registers (static rmem layout).
  477. w = cute.make_rmem_tensor((rows_per_thread * words_per_row,), Uint32)
  478. for r_i in cutlass.range_constexpr(rows_per_thread):
  479. row = Int32(tidx) + Int32(r_i * num_threads)
  480. if row < Int32(tile_n):
  481. src = sb_stage + row * Int32(packed_row_bytes)
  482. for c in cutlass.range_constexpr(words_per_row // 4):
  483. w0, w1, w2, w3 = ld_shared_v4_u32(src + Int32(c * 16))
  484. w[r_i * words_per_row + c * 4 + 0] = w0
  485. w[r_i * words_per_row + c * 4 + 1] = w1
  486. w[r_i * words_per_row + c * 4 + 2] = w2
  487. w[r_i * words_per_row + c * 4 + 3] = w3
  488. # All packed reads must complete before any expanded write lands.
  489. sync_barrier.arrive_and_wait()
  490. # Phase 2: expand and write the swizzled byte-container rows.
  491. for r_i in cutlass.range_constexpr(rows_per_thread):
  492. row = Int32(tidx) + Int32(r_i * num_threads)
  493. if row < Int32(tile_n):
  494. sw = (row & Int32(7)) << Int32(4)
  495. flat = row * Int32(tile_k)
  496. for o in cutlass.range_constexpr(tile_k // 16):
  497. base = r_i * words_per_row + o * 3
  498. o0, o1, o2, o3 = _expand_packed_b_triplet(
  499. w[base + 0], w[base + 1], w[base + 2]
  500. )
  501. st_shared_v4_u32(
  502. sb_stage + ((flat + Int32(o * 16)) ^ sw), o0, o1, o2, o3
  503. )
  504. class DenseGemmKernel:
  505. """Implements batched matrix multiplication (C = A x SFA x B x SFB) for
  506. Blackwell GeForce architecture using warp-level MMA.
  507. Key architectural differences from the tcgen05 donor path:
  508. - No TMEM, no tcgen05, no 2-CTA instructions, no multi-cluster
  509. - Warp-level MMA: MmaMXF4NVF4Op atom m16n8k64, atom_layout=(4,2,1)
  510. - 256 MMA threads + 32 DMA = 288 total threads
  511. - PipelineTmaAsync (not PipelineTmaUmma)
  512. - Manual atom unroll workaround for CuTe DSL compiler SF address space bug
  513. - Cluster shape always (1,1,1)
  514. Notes:
  515. - Supported combinations:
  516. * NVF4: A/B: Float4E2M1FN, SF: Float8E4M3FN, sf_vec_size: 16
  517. * MXF4: A/B: Float4E2M1FN, SF: Float8E8M0FNU, sf_vec_size: 32
  518. * MXFP8: A/B: Float8E4M3FN, SF: Float8E8M0FNU, sf_vec_size: 32
  519. * MX-FP6: A/B: Float6E3M2FN or Float6E2M3FN codes carried in
  520. Float8E4M3FN byte-containers, SF: Float8E8M0FNU, sf_vec_size: 32
  521. (inline ``mxf8f6f4`` MMA, m16n8k32; see ``mxfp6_fmt_a/b``)
  522. - Tile shape constraints:
  523. * tile_m must be divisible by 128
  524. * tile_n must be divisible by 128
  525. * tile_k must be divisible by 64
  526. """
  527. def __init__(
  528. self,
  529. sf_vec_size: int,
  530. mma_tiler_mn: Tuple[int, int],
  531. cluster_shape_mn: Tuple[int, int],
  532. mma_k: int = 64,
  533. tile_k: Optional[int] = None,
  534. single_work_tile_per_cta: bool = False,
  535. use_prefetch: bool = False,
  536. direct_one_m_tile_scheduler: bool = False,
  537. split_k_slices: int = 1,
  538. split_k_atomic_bf16: bool = False,
  539. large_m_unroll: bool = False,
  540. use_m1_non_tma_a: bool = False,
  541. use_m1_non_tma_c: bool = False,
  542. use_m1_non_tma_sfa: bool = False,
  543. load_path: Literal["tma", "cpasync"] = "tma",
  544. swap_ab: bool = False,
  545. sfb_k_reuse: bool = False,
  546. fused_quant_a: bool = False,
  547. fused_quant_a_inner_span: int = 0,
  548. fused_quant_a_row_stride: int = 0,
  549. fused_quant_a_l_stride: int = 0,
  550. fused_quant_a_inv_rope: bool = False,
  551. fused_quant_a_head_dim: int = 0,
  552. fused_quant_a_nope_dim: int = 0,
  553. fused_quant_a_rope_dim: int = 0,
  554. fused_quant_a_wide: bool = False,
  555. atom_shape_24: bool = False,
  556. b_tile_major: bool = False,
  557. quantize_c: bool = False,
  558. alpha_is_one: bool = False,
  559. row_scale: bool = False,
  560. direct_sfa_live16: bool = False,
  561. direct_m1_wo_a_inputs: bool = False,
  562. target_occupancy: int = 1,
  563. mxfp6_fmt: Optional[str] = None,
  564. mxfp6_fmt_a: Optional[str] = None,
  565. mxfp6_fmt_b: Optional[str] = None,
  566. b_packed: bool = False,
  567. plain_fp8: bool = False,
  568. fused_quant_bf16: Optional[bool] = None,
  569. block_fp8: bool = False,
  570. ):
  571. # When set, A/B operands are MX codes carried in Float8E4M3FN
  572. # byte-containers: the whole kernel runs the MXFP8 smem/TMA/ldmatrix
  573. # machinery, and only the mainloop MMA is emitted as the inline
  574. # ``mxf8f6f4`` instruction (cutlass has no working 6-bit smem layout).
  575. # ``mxfp6_fmt_a`` / ``mxfp6_fmt_b`` may differ (W6A8: e4m3 acts, e2m3
  576. # weights). A single ``mxfp6_fmt`` still means both operands match.
  577. if mxfp6_fmt_a is None and mxfp6_fmt_b is None:
  578. mxfp6_fmt_a = mxfp6_fmt
  579. mxfp6_fmt_b = mxfp6_fmt
  580. elif mxfp6_fmt_a is None or mxfp6_fmt_b is None:
  581. raise ValueError("mxfp6_fmt_a and mxfp6_fmt_b must both be set or both None")
  582. self.mxfp6_fmt_a = mxfp6_fmt_a
  583. self.mxfp6_fmt_b = mxfp6_fmt_b
  584. self.block_fp8 = bool(block_fp8)
  585. self.plain_fp8 = bool(plain_fp8 or block_fp8)
  586. if self.plain_fp8:
  587. assert mxfp6_fmt_a is None
  588. assert not b_packed
  589. assert not fused_quant_a
  590. assert not quantize_c
  591. if self.block_fp8:
  592. assert sf_vec_size == 128
  593. assert (tile_k or 128) == 128
  594. assert load_path == "tma"
  595. assert not swap_ab
  596. assert not sfb_k_reuse
  597. assert not b_tile_major
  598. if mxfp6_fmt_a is not None:
  599. # Upstream mainloop variants not wired for the FP6 byte-container
  600. # path; fail loudly instead of silently miscomputing.
  601. assert load_path == "tma", (
  602. "MX-FP6 is only wired for the TMA load path, not "
  603. f"load_path={load_path!r}"
  604. )
  605. assert not swap_ab, "MX-FP6 is not wired for swap_ab"
  606. assert split_k_slices == 1, "MX-FP6 is not wired for split-K"
  607. assert not fused_quant_a, (
  608. "MX-FP6 uses its own fused activation-quant prologue, not the "
  609. "MXFP8 fused_quant_a machinery"
  610. )
  611. assert not b_tile_major, "MX-FP6 is not wired for tile-major B"
  612. assert not quantize_c, "MX-FP6 is not wired for quantize_c"
  613. assert not sfb_k_reuse, "MX-FP6 is not wired for sfb_k_reuse"
  614. # Native packed-FP6 streaming: B arrives 3:4-packed ``(N, 3K/4, L)`` in
  615. # gmem, TMA stages the packed tile into a plain smem buffer, and the MMA
  616. # warps expand it into the swizzled byte-container sB right after
  617. # consumer_wait. Cuts B HBM traffic by 25% vs the byte-container layout;
  618. # the ldmatrix/MMA path is unchanged.
  619. assert not b_packed or mxfp6_fmt_b is not None, (
  620. "b_packed requires the MX-FP6 path (mxfp6_fmt_b set)"
  621. )
  622. # The in-kernel expansion computes swizzled sB addresses assuming the
  623. # 8-bit K-major SW128 atom (8x128 = 1024 B): physical =
  624. # flat ^ ((row & 7) << 4). That atom is selected iff the smem major
  625. # size is 128, i.e. tile_k == 128 (always true for MX-FP6).
  626. assert not b_packed or (tile_k or sf_vec_size * 8) == 128, (
  627. "b_packed expansion assumes tile_k == 128 (SW128 smem atom)"
  628. )
  629. self.b_packed = b_packed
  630. # The fused flag changes code generation and must be explicit in every
  631. # memoized launch key. The environment is only a direct-construction
  632. # fallback.
  633. if fused_quant_bf16 is None:
  634. fused_quant_bf16 = _DENSE_FUSED_QUANT
  635. self.a_bf16_fused = bool(fused_quant_bf16) and use_m1_non_tma_a
  636. if self.a_bf16_fused:
  637. _fused_fmt = mxfp6_fmt_a or "e4m3"
  638. self._fused_gs_num = mx_gs_numerator(_fused_fmt)
  639. self._fused_act_fmt = _fused_fmt
  640. self._fused_fmt_max = {
  641. "e4m3": FLOAT8_E4M3_MAX,
  642. "e3m2": FLOAT6_E3M2_MAX,
  643. "e2m3": FLOAT6_E2M3_MAX,
  644. }[_fused_fmt]
  645. else:
  646. self._fused_gs_num = 0.0
  647. self._fused_act_fmt = ""
  648. self._fused_fmt_max = 0.0
  649. self.acc_dtype = cutlass.Float32
  650. self.sf_vec_size = sf_vec_size
  651. self.mma_k = mma_k
  652. if tile_k is None:
  653. tile_k = sf_vec_size * 8
  654. self.tile_shape_mnk = (mma_tiler_mn[0], mma_tiler_mn[1], tile_k)
  655. self.manual_bk64_sf = sf_vec_size == 32 and tile_k == 64
  656. self.mma_tile_shape_mnk = (
  657. (mma_tiler_mn[1], mma_tiler_mn[0], tile_k)
  658. if swap_ab
  659. else self.tile_shape_mnk
  660. )
  661. self.sfa_tile_shape_mk = (max(128, mma_tiler_mn[0]), tile_k)
  662. self.sfa_tiles_per_block = self.sfa_tile_shape_mk[0] // mma_tiler_mn[0]
  663. self.sfb_tile_shape_nk = (max(128, mma_tiler_mn[1]), tile_k)
  664. self.sfb_tiles_per_block = self.sfb_tile_shape_nk[0] // mma_tiler_mn[1]
  665. self.cluster_shape_mnk = (1, 1, 1) # Always (1,1,1) on the current target
  666. self.epi_tile = mma_tiler_mn
  667. self.single_work_tile_per_cta = single_work_tile_per_cta
  668. self.use_prefetch = use_prefetch
  669. self.direct_one_m_tile_scheduler = direct_one_m_tile_scheduler
  670. self.split_k_slices = split_k_slices
  671. self.split_k_atomic_bf16 = split_k_atomic_bf16
  672. self.large_m_unroll = large_m_unroll
  673. self.use_m1_non_tma_a = use_m1_non_tma_a
  674. self.use_m1_non_tma_c = use_m1_non_tma_c
  675. self.use_m1_non_tma_sfa = use_m1_non_tma_sfa
  676. self.load_path = load_path
  677. self.swap_ab = swap_ab
  678. # SFB bytes are k-replicated within a 128-wide k tile (128x128 block
  679. # weight scales expanded to per-32): load one byte per stage and feed
  680. # every k block from it.
  681. self.sfb_k_reuse = sfb_k_reuse
  682. self.fused_quant_a = fused_quant_a
  683. # When >0, the BF16 A source is stored L-blocked along K (physical
  684. # [K/span, M, span], e.g. the WO tmp group-major view over [groups, M,
  685. # rank]): flat k = outer * span + inner reads element
  686. # outer * (M * span) + row * span + inner. 0 keeps contiguous [M, K].
  687. self.fused_quant_a_inner_span = fused_quant_a_inner_span
  688. # Grouped (L>1) BF16 A source, e.g. WO-A reading attention output
  689. # [M, groups, group_width] flat rows: element offset is
  690. # row * row_stride + l * l_stride + k (both strides in elements;
  691. # row_stride 0 keeps the contiguous shape[1] row pitch).
  692. self.fused_quant_a_row_stride = fused_quant_a_row_stride
  693. self.fused_quant_a_l_stride = fused_quant_a_l_stride
  694. # Inverse-RoPE applied in the quantizing A load: the trailing rope_dim
  695. # of every head_dim block is de-rotated with cos/sin at positions[row]
  696. # before MXFP8 quantization (head_dim/nope_dim aligned to 32-value
  697. # scale blocks; adjacent-pair rotation stays inside one load).
  698. self.fused_quant_a_inv_rope = fused_quant_a_inv_rope
  699. self.fused_quant_a_head_dim = fused_quant_a_head_dim
  700. self.fused_quant_a_nope_dim = fused_quant_a_nope_dim
  701. self.fused_quant_a_rope_dim = fused_quant_a_rope_dim
  702. # M=1 layout: 4 lanes per 32-value scale block (16 active lanes per
  703. # 128-wide k tile) instead of one, cutting the DMA-warp quantization
  704. # latency that serializes deep-K small-N pipelines.
  705. self.fused_quant_a_wide = fused_quant_a_wide
  706. self.b_tile_major = b_tile_major
  707. self.quantize_c = quantize_c
  708. self.alpha_is_one = alpha_is_one
  709. # Per-row output scale applied in the epilogue, replacing a separate
  710. # ``result.mul_(inv_gs)`` launch. It is NOT foldable into ``alpha``:
  711. # the eager multiply rounds to c_dtype twice (once after alpha, once
  712. # after the row scale), and the epilogue must reproduce both roundings
  713. # to stay bit-identical. See the epilogue application site.
  714. self.row_scale = row_scale
  715. if row_scale:
  716. # Each of these takes a store path that bypasses the r2s register
  717. # stage where the row scale is applied.
  718. assert not quantize_c, "row_scale is not wired for quantize_c"
  719. assert split_k_slices == 1, "row_scale is not wired for split-K"
  720. assert not swap_ab, "row_scale is not wired for swap_ab"
  721. self.direct_sfb_representative = (
  722. sfb_k_reuse
  723. and b_tile_major
  724. and (
  725. (
  726. not fused_quant_a
  727. and self.tile_shape_mnk in ((16, 64, 128), (32, 64, 128))
  728. )
  729. or (fused_quant_a and self.tile_shape_mnk == (16, 128, 128))
  730. )
  731. )
  732. self.direct_m1_wo_a_inputs = direct_m1_wo_a_inputs
  733. # Exact B16 consumes only rows 0-15 of each 128-row SFA atom. Those
  734. # rows are the contiguous first 256 bytes in both the packed global
  735. # and shared-memory layouts.
  736. self.direct_sfa_prefix = direct_sfa_live16 and self.direct_sfb_representative
  737. mma_atom_mn = (self.mma_tile_shape_mnk[0], self.mma_tile_shape_mnk[1])
  738. if mma_atom_mn in ((16, 64), (16, 128)):
  739. # This table sets the MMA atom tiling only. The warp count is a
  740. # SEPARATE table below and both must list the same tiles: an atom
  741. # shape covering two warps of work under a launch geometry sized
  742. # for eight leaves warps 2-7 with no valid tile, reading shared
  743. # memory past the end of the staged operands.
  744. self.atom_shape = (1, 2, 1)
  745. elif mma_atom_mn in ((32, 64), (32, 128)):
  746. self.atom_shape = (2, 2, 1)
  747. elif atom_shape_24:
  748. self.atom_shape = (2, 4, 1)
  749. else:
  750. self.atom_shape = (4, 2, 1)
  751. self.tiled_mma = None
  752. self.occupancy = target_occupancy
  753. if mma_atom_mn in ((16, 64), (16, 128)):
  754. self.num_mma_warps = 2
  755. elif mma_atom_mn in ((32, 64), (32, 128)):
  756. self.num_mma_warps = 4
  757. else:
  758. self.num_mma_warps = 8
  759. self.tma_load_warp_id = self.num_mma_warps
  760. self.num_threads_per_warp = 32
  761. self.threads_per_cta = (
  762. self.num_mma_warps + 1 # 1 warp for DMA
  763. ) * self.num_threads_per_warp
  764. self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_120")
  765. self.ab_stage = None
  766. self.epi_stage = None
  767. self.a_smem_layout_staged = None
  768. self.b_smem_layout_staged = None
  769. self.epi_smem_layout_staged = None
  770. self.buffer_align_bytes = 1024
  771. self.mma_sync_barrier = pipeline.NamedBarrier(
  772. barrier_id=1,
  773. num_threads=self.num_mma_warps * self.num_threads_per_warp,
  774. )
  775. self.epilog_sync_barrier = pipeline.NamedBarrier(
  776. barrier_id=2,
  777. num_threads=self.num_mma_warps * self.num_threads_per_warp,
  778. )
  779. self.load_register_requirement = 40
  780. self.mma_register_requirement = 232
  781. def _setup_attributes(self):
  782. mma_sf_dtype = (
  783. cutlass.Float8E8M0FNU if self.block_fp8 else self.sf_dtype
  784. )
  785. if cutlass.const_expr(self.a_dtype == cutlass.Float8E4M3FN):
  786. mma_op = cute.nvgpu.warp.MmaMXF8Op(
  787. self.a_dtype,
  788. self.acc_dtype,
  789. mma_sf_dtype,
  790. )
  791. elif cutlass.const_expr(
  792. self.a_dtype == cutlass.Float6E3M2FN
  793. or self.a_dtype == cutlass.Float6E2M3FN
  794. ):
  795. # MX-FP6 uses inline ``mxf8f6f4`` MMA in the mainloop. Build tiled_mma
  796. # with the MXFP8 op so smem/SF layouts match m16n8k32 geometry.
  797. mma_op = cute.nvgpu.warp.MmaMXF8Op(
  798. cutlass.Float8E4M3FN,
  799. self.acc_dtype,
  800. self.sf_dtype,
  801. )
  802. else:
  803. mma_op = cute.nvgpu.warp.MmaMXF4NVF4Op(
  804. self.a_dtype,
  805. self.acc_dtype,
  806. self.sf_dtype,
  807. )
  808. atom_shape = self.atom_shape
  809. atom_layout = cute.make_layout(atom_shape)
  810. permutation_mnk = sm120_utils.get_permutation_mnk(
  811. self.mma_tile_shape_mnk,
  812. 32 if self.block_fp8 else self.sf_vec_size,
  813. cutlass.const_expr(
  814. self.a_dtype == cutlass.Float8E4M3FN
  815. or self.a_dtype == cutlass.Float6E3M2FN
  816. or self.a_dtype == cutlass.Float6E2M3FN
  817. ),
  818. )
  819. self.tiled_mma = cute.make_tiled_mma(
  820. mma_op,
  821. atom_layout,
  822. permutation_mnk=permutation_mnk,
  823. )
  824. # Bare atom for manual unroll workaround (avoids hasAuxTensor address space bug)
  825. self.mma_atom = cute.make_mma_atom(mma_op)
  826. # Compute atom loop bounds from tile shape and atom/layout shape
  827. # MMA atom: m16n8k64 for FP4, m16n8k32 for MXFP8.
  828. mma_m, mma_n, mma_k = 16, 8, self.mma_k
  829. self.num_m_tiles = self.mma_tile_shape_mnk[0] // (mma_m * atom_shape[0])
  830. self.num_n_tiles = self.mma_tile_shape_mnk[1] // (mma_n * atom_shape[1])
  831. self.num_k_blocks = self.mma_tile_shape_mnk[2] // mma_k
  832. self.cta_layout_mnk = cute.make_layout(self.cluster_shape_mnk)
  833. # Compute the smem size of SFA/SFB
  834. if self.block_fp8:
  835. sfa_smem_layout_per_stage = cute.make_layout((1, 1))
  836. sfb_smem_layout_per_stage = cute.make_layout((1, 1))
  837. else:
  838. sfa_smem_layout_per_stage = sm120_make_smem_layout_sfa(
  839. self.tiled_mma,
  840. self.tile_shape_mnk,
  841. self.sf_vec_size,
  842. 1,
  843. )
  844. sfb_smem_layout_per_stage = sm120_make_smem_layout_sfb(
  845. self.tiled_mma,
  846. self.tile_shape_mnk,
  847. self.sf_vec_size,
  848. 1,
  849. )
  850. # MX-FP6 operands use Float8E4M3FN byte containers in global and
  851. # shared memory. The explicit format is therefore the reliable policy
  852. # discriminator; ``a_dtype`` alone also matches ordinary MXFP8.
  853. if self.mxfp6_fmt_a is not None:
  854. def _probe_stages(epi_tile: tuple, epi_stage_cap: int) -> tuple:
  855. return self._compute_stages(
  856. self.tile_shape_mnk,
  857. self.a_dtype,
  858. self.b_dtype,
  859. self.sf_dtype,
  860. sfa_smem_layout_per_stage,
  861. sfb_smem_layout_per_stage,
  862. epi_tile,
  863. self.c_dtype,
  864. self.smem_capacity,
  865. self.occupancy,
  866. self.b_packed,
  867. epi_stage_cap,
  868. decode_stage3=True,
  869. )
  870. self.epi_tile, epi_stage_cap = self._choose_epilogue(
  871. (self.tile_shape_mnk[0], self.tile_shape_mnk[1]),
  872. (16 * self.atom_shape[0], 8 * self.atom_shape[1]),
  873. _probe_stages,
  874. stages_through_smem=not self.use_m1_non_tma_c,
  875. )
  876. self.ab_stage, self.epi_stage = _probe_stages(
  877. self.epi_tile, epi_stage_cap
  878. )
  879. else:
  880. # Non-FP6 families use the generic stage policy.
  881. self.ab_stage, self.epi_stage = self._compute_stages(
  882. self.tile_shape_mnk,
  883. self.a_dtype,
  884. self.b_dtype,
  885. self.sf_dtype,
  886. sfa_smem_layout_per_stage,
  887. sfb_smem_layout_per_stage,
  888. self.epi_tile,
  889. self.c_dtype,
  890. self.smem_capacity,
  891. self.occupancy,
  892. self.b_packed,
  893. )
  894. assert self.epi_stage > 0, (
  895. "epi_stage <= 0, not enough shared memory. This configuration will be skipped."
  896. )
  897. # Decided here because it depends on the computed stage depth; see the
  898. # _PACKED_B_EXPAND_AHEAD module comment for the >= 4 stage rationale.
  899. self.packed_expand_ahead = (
  900. self.b_packed and _PACKED_B_EXPAND_AHEAD and self.ab_stage >= 4
  901. )
  902. (
  903. self.a_smem_layout_staged,
  904. self.b_smem_layout_staged,
  905. self.sfa_smem_layout_staged,
  906. self.sfb_smem_layout_staged,
  907. self.epi_smem_layout_staged,
  908. ) = self._make_smem_layouts(
  909. self.tile_shape_mnk,
  910. self.epi_tile,
  911. self.a_dtype,
  912. self.a_layout,
  913. self.b_dtype,
  914. self.b_layout,
  915. self.ab_stage,
  916. self.c_dtype,
  917. self.c_layout,
  918. self.epi_stage,
  919. self.sf_vec_size,
  920. self.tiled_mma,
  921. self.block_fp8,
  922. )
  923. # Plain (non-swizzled) k-major staging layout for the 3:4-packed B
  924. # tile, ALIASED into the bottom of each sB stage: TMA writes the 96
  925. # packed bytes/row there (16-byte aligned box, within TMA's
  926. # non-swizzled constraints) and the MMA warps expand IN PLACE into the
  927. # full swizzled 128 B/row stage (two-phase, see
  928. # _expand_packed_b_stage_smem). No separate staging buffer means the
  929. # packed mode pays zero extra smem -> one more pipeline stage. The
  930. # stage stride is sB's full stage size, NOT the packed tile size.
  931. if self.b_packed:
  932. self.b_packed_smem_layout_staged = cute.make_layout(
  933. (
  934. self.tile_shape_mnk[1],
  935. self.tile_shape_mnk[2] * 3 // 4,
  936. self.ab_stage,
  937. ),
  938. stride=(
  939. self.tile_shape_mnk[2] * 3 // 4,
  940. 1,
  941. self.tile_shape_mnk[1] * self.tile_shape_mnk[2],
  942. ),
  943. )
  944. else:
  945. self.b_packed_smem_layout_staged = None
  946. @cute.jit
  947. def __call__(
  948. self,
  949. a: cute.Tensor,
  950. quant_a_source: cute.Tensor,
  951. quant_a_positions: cute.Tensor,
  952. quant_a_cos_sin: cute.Tensor,
  953. b: cute.Tensor,
  954. sfa: cute.Tensor,
  955. sfb: cute.Tensor,
  956. c: cute.Tensor,
  957. quant_c_values: cute.Tensor,
  958. quant_c_scale_rows: cute.Tensor,
  959. quant_c_scale_mma: cute.Tensor,
  960. alpha: cute.Tensor,
  961. max_active_clusters: cutlass.Constexpr,
  962. stream: cuda.CUstream,
  963. epilogue_op: cutlass.Constexpr = lambda x: x,
  964. x_bf16: cute.Tensor = None,
  965. w_gscale: cute.Tensor = None,
  966. ):
  967. """Execute the GEMM operation.
  968. Args:
  969. a: Input tensor A (byte-containers, or dummy when a_bf16_fused)
  970. b: Input tensor B
  971. sfa: Scale factor tensor for A (or dummy when a_bf16_fused)
  972. sfb: Scale factor tensor for B
  973. c: Output tensor C
  974. alpha: Alpha scaling factor tensor, shape (1,), float32
  975. max_active_clusters: Max active clusters
  976. stream: CUDA stream
  977. epilogue_op: Elementwise epilogue function
  978. x_bf16: BF16 activation input (MX-FP6 fused quant mode only)
  979. w_gscale: Weight global scale, shape (1,), f32 (fused mode only)
  980. """
  981. # Dead kernel arguments on every non-fused path; substitute a
  982. # type-compatible live tensor so the traced signature stays uniform.
  983. # const_expr is required: DSL >= 4.6 otherwise traces this as a
  984. # runtime if-region and rejects the rebind (CONTAINER_STRUCTURE_CHANGED).
  985. if cutlass.const_expr(x_bf16 is None):
  986. x_bf16 = alpha
  987. if cutlass.const_expr(w_gscale is None):
  988. w_gscale = alpha
  989. # Setup static attributes
  990. self.a_dtype = a.element_type
  991. self.b_dtype = b.element_type
  992. self.c_dtype = c.element_type
  993. self.sf_dtype = sfa.element_type
  994. self.a_layout = utils.LayoutEnum.from_tensor(a)
  995. self.b_layout = (
  996. utils.LayoutEnum.ROW_MAJOR
  997. if self.b_tile_major
  998. else utils.LayoutEnum.from_tensor(b)
  999. )
  1000. self.c_layout = utils.LayoutEnum.from_tensor(c)
  1001. if cutlass.const_expr(self.a_dtype != self.b_dtype):
  1002. raise TypeError(f"Type mismatch: {self.a_dtype} != {self.b_dtype}")
  1003. self._setup_attributes()
  1004. # Regular block-FP8 carries compact FP32 scales directly. Native MX
  1005. # paths retain the MMA scale-factor atom layout.
  1006. if cutlass.const_expr(self.block_fp8):
  1007. sfa_tensor = sfa
  1008. sfb_tensor = sfb
  1009. else:
  1010. self.sfa_layout = blockscaled_utils.tile_atom_to_shape_SF(
  1011. a.shape, self.sf_vec_size
  1012. )
  1013. sfa_tensor = cute.make_tensor(sfa.iterator, self.sfa_layout)
  1014. # With packed B the gmem extent is 3K/4 bytes, but scale-factor
  1015. # geometry follows the LOGICAL K.
  1016. if cutlass.const_expr(self.b_packed):
  1017. b_logical_shape = (
  1018. cute.size(b.shape[0]),
  1019. cute.size(b.shape[1]) * 4 // 3,
  1020. cute.size(b.shape[2]),
  1021. )
  1022. else:
  1023. b_logical_shape = (
  1024. cute.size(b.shape[0]),
  1025. cute.size(b.shape[1]),
  1026. cute.size(b.shape[2]),
  1027. )
  1028. self.sfb_layout = blockscaled_utils.tile_atom_to_shape_SF(
  1029. b_logical_shape, self.sf_vec_size
  1030. )
  1031. sfb_tensor = cute.make_tensor(sfb.iterator, self.sfb_layout)
  1032. if cutlass.const_expr(self.b_packed):
  1033. # TMA loads the packed tile (tile_n, 3*tile_k/4 bytes) into the
  1034. # plain staging layout; the swizzled sB is filled in-kernel.
  1035. tma_atom_b, tma_tensor_b = self._make_tma_atoms_and_tensors(
  1036. b,
  1037. self.b_packed_smem_layout_staged,
  1038. (self.tile_shape_mnk[1], self.tile_shape_mnk[2] * 3 // 4),
  1039. 1,
  1040. )
  1041. else:
  1042. tma_atom_b, tma_tensor_b = self._make_tma_atoms_and_tensors(
  1043. b,
  1044. self.b_smem_layout_staged,
  1045. (self.tile_shape_mnk[1], self.tile_shape_mnk[2]),
  1046. 1,
  1047. )
  1048. if cutlass.const_expr(self.fused_quant_a or self.direct_m1_wo_a_inputs):
  1049. # A does not use a TMA descriptor on these paths. Reuse B's as a
  1050. # type-compatible placeholder for the dead kernel argument.
  1051. tma_atom_a = tma_atom_b
  1052. tma_tensor_a = a
  1053. else:
  1054. tma_atom_a, tma_tensor_a = self._make_tma_atoms_and_tensors(
  1055. a,
  1056. self.a_smem_layout_staged,
  1057. (self.tile_shape_mnk[0], self.tile_shape_mnk[2]),
  1058. 1,
  1059. )
  1060. if cutlass.const_expr(
  1061. self.block_fp8
  1062. or self.fused_quant_a
  1063. or self.use_m1_non_tma_sfa
  1064. or self.manual_bk64_sf
  1065. or self.direct_sfa_prefix
  1066. ):
  1067. tma_atom_sfa = tma_atom_b
  1068. tma_tensor_sfa = sfa_tensor
  1069. else:
  1070. tma_atom_sfa, tma_tensor_sfa = self._make_tma_atoms_and_tensors(
  1071. sfa_tensor,
  1072. self.sfa_smem_layout_staged,
  1073. self.sfa_tile_shape_mk,
  1074. 1,
  1075. internal_type=cutlass.Int16,
  1076. )
  1077. if cutlass.const_expr(
  1078. self.block_fp8 or self.manual_bk64_sf or self.direct_sfb_representative
  1079. ):
  1080. tma_atom_sfb = tma_atom_b
  1081. tma_tensor_sfb = sfb_tensor
  1082. else:
  1083. tma_atom_sfb, tma_tensor_sfb = self._make_tma_atoms_and_tensors(
  1084. sfb_tensor,
  1085. self.sfb_smem_layout_staged,
  1086. self.sfb_tile_shape_nk,
  1087. 1,
  1088. internal_type=cutlass.Int16,
  1089. )
  1090. tma_atom_c, tma_tensor_c = self._make_tma_store_atoms_and_tensors(
  1091. c,
  1092. self.epi_smem_layout_staged,
  1093. self.epi_tile,
  1094. )
  1095. tile_sched_params, grid = self._compute_grid(
  1096. c,
  1097. self.tile_shape_mnk,
  1098. max_active_clusters,
  1099. self.direct_one_m_tile_scheduler,
  1100. self.split_k_slices,
  1101. self.large_m_unroll,
  1102. )
  1103. @cute.struct
  1104. class SharedStorage:
  1105. mainloop_pipeline_array_ptr: cute.struct.MemRange[
  1106. cutlass.Int64, self.ab_stage * 2
  1107. ]
  1108. sA: cute.struct.Align[
  1109. cute.struct.MemRange[
  1110. self.a_dtype, cute.cosize(self.a_smem_layout_staged)
  1111. ],
  1112. self.buffer_align_bytes,
  1113. ]
  1114. sB: cute.struct.Align[
  1115. cute.struct.MemRange[
  1116. self.b_dtype, cute.cosize(self.b_smem_layout_staged)
  1117. ],
  1118. self.buffer_align_bytes,
  1119. ]
  1120. sSFA: cute.struct.Align[
  1121. cute.struct.MemRange[
  1122. self.sf_dtype, cute.cosize(self.sfa_smem_layout_staged)
  1123. ],
  1124. self.buffer_align_bytes,
  1125. ]
  1126. sSFB: cute.struct.Align[
  1127. cute.struct.MemRange[
  1128. self.sf_dtype, cute.cosize(self.sfb_smem_layout_staged)
  1129. ],
  1130. self.buffer_align_bytes,
  1131. ]
  1132. sC: cute.struct.Align[
  1133. cute.struct.MemRange[
  1134. self.c_dtype, cute.cosize(self.epi_smem_layout_staged)
  1135. ],
  1136. self.buffer_align_bytes,
  1137. ]
  1138. self.shared_storage = SharedStorage
  1139. # Unused (never traced) when b_packed is off; pass the expanded layout
  1140. # as a stand-in so the kernel signature stays uniform.
  1141. if cutlass.const_expr(self.b_packed):
  1142. b_packed_smem_layout_arg = self.b_packed_smem_layout_staged
  1143. else:
  1144. b_packed_smem_layout_arg = self.b_smem_layout_staged
  1145. self.kernel(
  1146. tma_atom_a,
  1147. tma_tensor_a,
  1148. a,
  1149. quant_a_source,
  1150. quant_a_positions,
  1151. quant_a_cos_sin,
  1152. tma_atom_b,
  1153. tma_tensor_b,
  1154. b,
  1155. tma_atom_sfa,
  1156. tma_tensor_sfa,
  1157. sfa if self.manual_bk64_sf else sfa_tensor,
  1158. tma_atom_sfb,
  1159. tma_tensor_sfb,
  1160. sfb if self.manual_bk64_sf else sfb_tensor,
  1161. tma_atom_c,
  1162. tma_tensor_c,
  1163. c,
  1164. quant_c_values,
  1165. quant_c_scale_rows,
  1166. quant_c_scale_mma,
  1167. self.tiled_mma,
  1168. self.mma_atom,
  1169. self.cta_layout_mnk,
  1170. self.a_smem_layout_staged,
  1171. self.b_smem_layout_staged,
  1172. b_packed_smem_layout_arg,
  1173. self.sfa_smem_layout_staged,
  1174. self.sfb_smem_layout_staged,
  1175. self.epi_smem_layout_staged,
  1176. tile_sched_params,
  1177. epilogue_op,
  1178. alpha,
  1179. x_bf16,
  1180. w_gscale,
  1181. ).launch(
  1182. grid=grid,
  1183. block=[self.threads_per_cta, 1, 1],
  1184. cluster=[1, 1, 1],
  1185. stream=stream,
  1186. )
  1187. return
  1188. def _partition_fragment_SFA(
  1189. self,
  1190. sfa_tensor: cute.Tensor,
  1191. thr_mma: cute.ThrMma,
  1192. tidx: int,
  1193. ):
  1194. return sm120_utils.partition_fragment_SFA(sfa_tensor, thr_mma, tidx)
  1195. def _partition_fragment_SFB(
  1196. self,
  1197. sfb_tensor: cute.Tensor,
  1198. thr_mma: cute.ThrMma,
  1199. tidx: int,
  1200. ):
  1201. return sm120_utils.partition_fragment_SFB(sfb_tensor, thr_mma, tidx)
  1202. def _thrfrg_SFA(self, sfa_tensor, tiled_mma: cute.TiledMma):
  1203. return sm120_utils.thrfrg_SFA(sfa_tensor, tiled_mma)
  1204. def _thrfrg_SFB(self, sfb_tensor, tiled_mma: cute.TiledMma):
  1205. return sm120_utils.thrfrg_SFB(sfb_tensor, tiled_mma)
  1206. def _get_layoutSFA_TV(self, tiled_mma: cute.TiledMma):
  1207. return sm120_utils.get_layoutSFA_TV(tiled_mma)
  1208. def _get_layoutSFB_TV(self, tiled_mma: cute.TiledMma):
  1209. return sm120_utils.get_layoutSFB_TV(tiled_mma)
  1210. @cute.jit
  1211. def _fill_replicated_sfb_fragment(self, fragment: cute.Tensor, scale) -> None:
  1212. flat = cute.group_modes(cute.flatten(fragment), 0, cute.rank(fragment))
  1213. for idx in cutlass.range_constexpr(cute.size(flat)):
  1214. flat[idx] = scale
  1215. @cute.jit
  1216. def _accumulate_block_fp8_stage(
  1217. self,
  1218. accumulators: cute.Tensor,
  1219. stage_accumulators: cute.Tensor,
  1220. coord_mn: cute.Tensor,
  1221. sfa: cute.Tensor,
  1222. sfb: cute.Tensor,
  1223. tile_coord_mnl,
  1224. k_tile_global: Int32,
  1225. ) -> None:
  1226. accum_mn = _reshape_acc_to_mn(accumulators)
  1227. stage_accum_mn = _reshape_acc_to_mn(stage_accumulators)
  1228. scale_n = (
  1229. tile_coord_mnl[1] * Int32(self.tile_shape_mnk[1])
  1230. ) // Int32(128)
  1231. scale_b = cutlass.Float32(
  1232. sfb[(scale_n, k_tile_global, tile_coord_mnl[2])]
  1233. )
  1234. for acc_m in cutlass.range_constexpr(cute.size(accum_mn.shape[0])):
  1235. coord = coord_mn[acc_m, 0]
  1236. m_coord = (
  1237. tile_coord_mnl[0] * Int32(self.tile_shape_mnk[0]) + coord[0]
  1238. )
  1239. scale_a = cutlass.Float32(0.0)
  1240. if m_coord < Int32(sfa.shape[0]):
  1241. scale_a = cutlass.Float32(
  1242. sfa[(m_coord, k_tile_global, tile_coord_mnl[2])]
  1243. )
  1244. scale_ab = scale_a * scale_b
  1245. for acc_n in cutlass.range_constexpr(cute.size(accum_mn.shape[1])):
  1246. accum_mn[acc_m, acc_n] += (
  1247. stage_accum_mn[acc_m, acc_n] * scale_ab
  1248. )
  1249. stage_accum_mn[acc_m, acc_n] = 0.0
  1250. @cute.jit
  1251. def _make_cpasync_tiled_copy(
  1252. self,
  1253. dtype: cutlass.Constexpr,
  1254. tile_cols: cutlass.Constexpr[int],
  1255. ) -> cute.TiledCopy:
  1256. copy_bits = 128
  1257. atom_async_copy = cute.make_copy_atom(
  1258. cpasync.CopyG2SOp(cache_mode=cpasync.LoadCacheMode.GLOBAL),
  1259. dtype,
  1260. num_bits_per_copy=copy_bits,
  1261. )
  1262. async_copy_elems = copy_bits // dtype.width
  1263. t_shape_dim_1 = tile_cols // async_copy_elems
  1264. assert self.num_threads_per_warp % t_shape_dim_1 == 0
  1265. t_layout = cute.make_ordered_layout(
  1266. (self.num_threads_per_warp // t_shape_dim_1, t_shape_dim_1),
  1267. order=(1, 0),
  1268. )
  1269. v_layout = cute.make_layout((1, async_copy_elems))
  1270. return cute.make_tiled_copy_tv(atom_async_copy, t_layout, v_layout)
  1271. @cute.jit
  1272. def _make_scale_tiled_copy(
  1273. self,
  1274. dtype: cutlass.Constexpr,
  1275. ) -> cute.TiledCopy:
  1276. copy_bits = dtype.width
  1277. atom_async_copy = cute.make_copy_atom(
  1278. cute.nvgpu.CopyUniversalOp(),
  1279. dtype,
  1280. num_bits_per_copy=copy_bits,
  1281. )
  1282. return cute.make_tiled_copy_tv(
  1283. atom_async_copy,
  1284. cute.make_layout((self.num_threads_per_warp,)),
  1285. cute.make_layout((copy_bits // dtype.width,)),
  1286. )
  1287. @cute.jit
  1288. def _predicate_cpasync_rows(
  1289. self,
  1290. tCc: cute.Tensor,
  1291. row_limit: Int32,
  1292. ) -> cute.Tensor:
  1293. tPred = cute.make_rmem_tensor(
  1294. cute.make_layout(
  1295. (
  1296. cute.size(tCc, mode=[0, 1]),
  1297. cute.size(tCc, mode=[1]),
  1298. cute.size(tCc, mode=[2]),
  1299. ),
  1300. stride=(cute.size(tCc, mode=[2]), 0, 1),
  1301. ),
  1302. cutlass.Boolean,
  1303. )
  1304. for rest_v in cutlass.range_constexpr(tPred.shape[0]):
  1305. for rest_k in cutlass.range_constexpr(tPred.shape[2]):
  1306. tPred[rest_v, 0, rest_k] = tCc[(0, rest_v), 0, rest_k][0] < row_limit
  1307. return tPred
  1308. @cute.jit
  1309. def _cpasync_copy_2d(
  1310. self,
  1311. tiled_copy: cute.TiledCopy,
  1312. tG: cute.Tensor,
  1313. tS: cute.Tensor,
  1314. tC: cute.Tensor,
  1315. row_limit: Int32,
  1316. predicate_rows: cutlass.Constexpr[bool],
  1317. ) -> None:
  1318. if cutlass.const_expr(predicate_rows):
  1319. tP = self._predicate_cpasync_rows(tC, row_limit)
  1320. for rest_m in cutlass.range_constexpr(cute.size(tS.shape[1])):
  1321. if cutlass.const_expr(predicate_rows):
  1322. cute.copy(
  1323. tiled_copy,
  1324. tG[None, rest_m, None],
  1325. tS[None, rest_m, None],
  1326. pred=tP[None, rest_m, None],
  1327. )
  1328. else:
  1329. cute.copy(
  1330. tiled_copy,
  1331. tG[None, rest_m, None],
  1332. tS[None, rest_m, None],
  1333. )
  1334. @cute.jit
  1335. def _scale_copy_2d(
  1336. self,
  1337. tiled_copy: cute.TiledCopy,
  1338. tG: cute.Tensor,
  1339. tS: cute.Tensor,
  1340. tC: cute.Tensor,
  1341. row_limit: Int32,
  1342. ) -> None:
  1343. tP = cute.make_rmem_tensor(cute.make_layout(tS.shape), cutlass.Boolean)
  1344. for i in cutlass.range_constexpr(cute.size(tP)):
  1345. tP[i] = cute.elem_less(tC[i][0][0][0], row_limit)
  1346. for rest_m in cutlass.range_constexpr(cute.size(tS.shape[1])):
  1347. cute.copy(
  1348. tiled_copy,
  1349. tG[None, rest_m, None],
  1350. tS[None, rest_m, None],
  1351. pred=tP[None, rest_m, None],
  1352. )
  1353. # GPU device kernel
  1354. @cute.kernel
  1355. def kernel(
  1356. self,
  1357. tma_atom_a: cute.CopyAtom,
  1358. mA_mkl: cute.Tensor,
  1359. directA_mkl: cute.Tensor,
  1360. quantA_mkl: cute.Tensor,
  1361. quantA_positions: cute.Tensor,
  1362. quantA_cos_sin: cute.Tensor,
  1363. tma_atom_b: cute.CopyAtom,
  1364. mB_nkl: cute.Tensor,
  1365. directB_nkl: cute.Tensor,
  1366. tma_atom_sfa: cute.CopyAtom,
  1367. mSFA_mkl: cute.Tensor,
  1368. directSFA_mkl: cute.Tensor,
  1369. tma_atom_sfb: cute.CopyAtom,
  1370. mSFB_nkl: cute.Tensor,
  1371. directSFB_nkl: cute.Tensor,
  1372. tma_atom_c: cute.CopyAtom,
  1373. mC_mnl: cute.Tensor,
  1374. directC_mnl: cute.Tensor,
  1375. quantC_values: cute.Tensor,
  1376. quantC_scale_rows: cute.Tensor,
  1377. quantC_scale_mma: cute.Tensor,
  1378. tiled_mma: cute.TiledMma,
  1379. mma_atom: cute.MmaAtom,
  1380. cta_layout_mnk: cute.Layout,
  1381. a_smem_layout_staged: cute.ComposedLayout,
  1382. b_smem_layout_staged: cute.ComposedLayout,
  1383. b_packed_smem_layout_staged,
  1384. sfa_smem_layout_staged: cute.Layout,
  1385. sfb_smem_layout_staged: cute.Layout,
  1386. epi_smem_layout_staged: cute.ComposedLayout,
  1387. tile_sched_params: utils.PersistentTileSchedulerParams,
  1388. epilogue_op: cutlass.Constexpr,
  1389. alpha: cute.Tensor,
  1390. directX_bf16: cute.Tensor,
  1391. w_gscale: cute.Tensor,
  1392. ):
  1393. # Keep alpha in FP32 for precision
  1394. alpha_value = alpha[0].to(cutlass.Float32)
  1395. tidx, _, _ = cute.arch.thread_idx()
  1396. warp_idx = cute.arch.warp_idx()
  1397. warp_idx = cute.arch.make_warp_uniform(warp_idx)
  1398. # Prefetch TMA descriptors
  1399. if warp_idx == 0:
  1400. if cutlass.const_expr(
  1401. self.load_path == "tma"
  1402. and not self.use_m1_non_tma_a
  1403. and not self.fused_quant_a
  1404. and not self.direct_m1_wo_a_inputs
  1405. ):
  1406. cpasync.prefetch_descriptor(tma_atom_a)
  1407. if cutlass.const_expr(self.load_path == "tma"):
  1408. cpasync.prefetch_descriptor(tma_atom_b)
  1409. if cutlass.const_expr(
  1410. self.load_path == "tma"
  1411. and not self.block_fp8
  1412. and not self.use_m1_non_tma_sfa
  1413. and not self.fused_quant_a
  1414. and not self.manual_bk64_sf
  1415. and not self.direct_sfa_prefix
  1416. ):
  1417. cpasync.prefetch_descriptor(tma_atom_sfa)
  1418. if cutlass.const_expr(
  1419. self.load_path == "tma"
  1420. and not self.block_fp8
  1421. and not self.manual_bk64_sf
  1422. and not self.direct_sfb_representative
  1423. ):
  1424. cpasync.prefetch_descriptor(tma_atom_sfb)
  1425. if cutlass.const_expr(not self.use_m1_non_tma_c):
  1426. cpasync.prefetch_descriptor(tma_atom_c)
  1427. cta_rank_in_cluster = cute.arch.make_warp_uniform(
  1428. cute.arch.block_idx_in_cluster()
  1429. )
  1430. cluster_coord_mnk = cta_layout_mnk.get_flat_coord(cta_rank_in_cluster)
  1431. a_smem_layout = cute.slice_(a_smem_layout_staged, (None, None, 0))
  1432. b_smem_layout = cute.slice_(b_smem_layout_staged, (None, None, 0))
  1433. sfa_smem_layout = cute.slice_(sfa_smem_layout_staged, (None, None, 0))
  1434. sfb_smem_layout = cute.slice_(sfb_smem_layout_staged, (None, None, 0))
  1435. # B's TMA transaction covers the packed staging tile when b_packed (the
  1436. # expanded sB is filled by the MMA warps, not by TMA).
  1437. if cutlass.const_expr(self.b_packed):
  1438. b_tma_smem_layout = cute.slice_(
  1439. b_packed_smem_layout_staged, (None, None, 0)
  1440. )
  1441. else:
  1442. b_tma_smem_layout = b_smem_layout
  1443. if cutlass.const_expr(self.block_fp8):
  1444. tma_copy_bytes = cute.size_in_bytes(
  1445. self.a_dtype, a_smem_layout
  1446. ) + cute.size_in_bytes(self.b_dtype, b_tma_smem_layout)
  1447. elif cutlass.const_expr(self.fused_quant_a):
  1448. tma_copy_bytes = cute.size_in_bytes(
  1449. self.b_dtype, b_tma_smem_layout
  1450. ) + cute.size_in_bytes(self.sf_dtype, sfb_smem_layout)
  1451. elif cutlass.const_expr(self.manual_bk64_sf):
  1452. tma_copy_bytes = cute.size_in_bytes(self.b_dtype, b_tma_smem_layout)
  1453. if cutlass.const_expr(not self.use_m1_non_tma_a):
  1454. tma_copy_bytes += cute.size_in_bytes(self.a_dtype, a_smem_layout)
  1455. elif cutlass.const_expr(self.use_m1_non_tma_sfa):
  1456. tma_copy_bytes = cute.size_in_bytes(
  1457. self.b_dtype, b_tma_smem_layout
  1458. ) + cute.size_in_bytes(self.sf_dtype, sfb_smem_layout)
  1459. if cutlass.const_expr(not self.use_m1_non_tma_a):
  1460. tma_copy_bytes += cute.size_in_bytes(self.a_dtype, a_smem_layout)
  1461. else:
  1462. tma_copy_bytes = (
  1463. cute.size_in_bytes(self.b_dtype, b_tma_smem_layout)
  1464. + cute.size_in_bytes(self.sf_dtype, sfa_smem_layout)
  1465. + cute.size_in_bytes(self.sf_dtype, sfb_smem_layout)
  1466. )
  1467. if cutlass.const_expr(self.direct_m1_wo_a_inputs):
  1468. tma_copy_bytes += 128
  1469. else:
  1470. tma_copy_bytes += cute.size_in_bytes(self.a_dtype, a_smem_layout)
  1471. if cutlass.const_expr(self.direct_sfb_representative):
  1472. tma_copy_bytes -= cute.size_in_bytes(self.sf_dtype, sfb_smem_layout)
  1473. tma_copy_bytes += 16
  1474. if cutlass.const_expr(self.direct_sfa_prefix):
  1475. tma_copy_bytes -= cute.size_in_bytes(self.sf_dtype, sfa_smem_layout)
  1476. tma_copy_bytes += 256
  1477. # Allocate shared memory
  1478. smem = cutlass.utils.SmemAllocator()
  1479. storage = smem.allocate(self.shared_storage)
  1480. # Pipeline setup
  1481. mainloop_pipeline_array_ptr = storage.mainloop_pipeline_array_ptr.data_ptr()
  1482. mainloop_pipeline_producer_group = pipeline.CooperativeGroup(
  1483. pipeline.Agent.Thread
  1484. )
  1485. mainloop_pipeline_consumer_group = pipeline.CooperativeGroup(
  1486. pipeline.Agent.Thread, self.num_mma_warps
  1487. )
  1488. cta_layout_vmnk = cute.make_layout((1, *cta_layout_mnk.shape))
  1489. if cutlass.const_expr(self.load_path == "cpasync"):
  1490. mainloop_pipeline_producer_group = pipeline.CooperativeGroup(
  1491. pipeline.Agent.Thread,
  1492. self.num_threads_per_warp,
  1493. )
  1494. mainloop_pipeline_consumer_group = pipeline.CooperativeGroup(
  1495. pipeline.Agent.Thread,
  1496. self.num_mma_warps * self.num_threads_per_warp,
  1497. )
  1498. mainloop_pipeline = pipeline.PipelineAsync.create(
  1499. num_stages=self.ab_stage,
  1500. producer_group=mainloop_pipeline_producer_group,
  1501. consumer_group=mainloop_pipeline_consumer_group,
  1502. barrier_storage=mainloop_pipeline_array_ptr,
  1503. )
  1504. else:
  1505. mainloop_pipeline = pipeline.PipelineTmaAsync.create(
  1506. num_stages=self.ab_stage,
  1507. producer_group=mainloop_pipeline_producer_group,
  1508. consumer_group=mainloop_pipeline_consumer_group,
  1509. tx_count=tma_copy_bytes,
  1510. barrier_storage=mainloop_pipeline_array_ptr,
  1511. cta_layout_vmnk=cta_layout_vmnk,
  1512. )
  1513. if cute.size(self.cluster_shape_mnk) > 1:
  1514. cute.arch.cluster_arrive_relaxed()
  1515. # Generate smem tensors
  1516. sA = storage.sA.get_tensor(
  1517. a_smem_layout_staged.outer, swizzle=a_smem_layout_staged.inner
  1518. )
  1519. sB = storage.sB.get_tensor(
  1520. b_smem_layout_staged.outer, swizzle=b_smem_layout_staged.inner
  1521. )
  1522. if cutlass.const_expr(self.b_packed):
  1523. # Packed TMA destination ALIASED into sB's storage (bottom 96 B of
  1524. # each row span, stage stride = full sB stage): no separate buffer,
  1525. # expansion happens in place (see _expand_packed_b_stage_smem).
  1526. sBPacked = cute.make_tensor(
  1527. storage.sB.data_ptr(), b_packed_smem_layout_staged
  1528. )
  1529. # Raw u32 smem address for the packed->container expansion. Two
  1530. # constraints force this exact spot: (1) cute.recast_tensor strips
  1531. # sB's swizzle (see _expand_packed_b_stage_smem), so the expansion
  1532. # needs raw addresses; (2) @cute.struct instances cannot be
  1533. # flattened across DYNAMIC ifs (NVIDIA/cutlass#3268) and the if-
  1534. # capture analysis is syntactic, so ``storage`` must never be
  1535. # referenced inside the warp-dispatch branches - only this Int32
  1536. # address may cross into them.
  1537. sb_base_addr = shared_ptr_to_u32(storage.sB.data_ptr())
  1538. if cutlass.const_expr(self.a_bf16_fused):
  1539. sa_base_addr = shared_ptr_to_u32(storage.sA.data_ptr())
  1540. ssfa_base_addr = shared_ptr_to_u32(storage.sSFA.data_ptr())
  1541. sC = storage.sC.get_tensor(
  1542. epi_smem_layout_staged.outer, swizzle=epi_smem_layout_staged.inner
  1543. )
  1544. sSFA = storage.sSFA.get_tensor(sfa_smem_layout_staged)
  1545. sSFB = storage.sSFB.get_tensor(sfb_smem_layout_staged)
  1546. # Local_tile partition global tensors
  1547. gA_mkl = cute.local_tile(
  1548. mA_mkl,
  1549. cute.slice_(self.tile_shape_mnk, (None, 0, None)),
  1550. (None, None, None),
  1551. )
  1552. if cutlass.const_expr(self.b_packed):
  1553. # Packed gmem extent: 96 bytes per 128-wide logical K-tile, same
  1554. # K-tile count as the A side ((3K/4)/96 == K/128).
  1555. gB_nkl = cute.local_tile(
  1556. mB_nkl,
  1557. (self.tile_shape_mnk[1], self.tile_shape_mnk[2] * 3 // 4),
  1558. (None, None, None),
  1559. )
  1560. else:
  1561. gB_nkl = cute.local_tile(
  1562. mB_nkl,
  1563. cute.slice_(self.tile_shape_mnk, (0, None, None)),
  1564. (None, None, None),
  1565. )
  1566. if cutlass.const_expr(
  1567. not self.block_fp8
  1568. and not self.use_m1_non_tma_sfa
  1569. and not self.fused_quant_a
  1570. ):
  1571. gSFA_mkl = cute.local_tile(
  1572. mSFA_mkl,
  1573. self.sfa_tile_shape_mk,
  1574. (None, None, None),
  1575. )
  1576. gSFB_nkl = cute.local_tile(
  1577. mSFB_nkl,
  1578. self.sfb_tile_shape_nk,
  1579. (None, None, None),
  1580. )
  1581. if cutlass.const_expr(self.load_path == "cpasync"):
  1582. gA_cpasync_mkl = cute.local_tile(
  1583. directA_mkl,
  1584. cute.slice_(self.tile_shape_mnk, (None, 0, None)),
  1585. (None, None, None),
  1586. )
  1587. gB_cpasync_nkl = cute.local_tile(
  1588. directB_nkl,
  1589. cute.slice_(self.tile_shape_mnk, (0, None, None)),
  1590. (None, None, None),
  1591. )
  1592. gSFA_cpasync_mkl = cute.local_tile(
  1593. directSFA_mkl,
  1594. self.sfa_tile_shape_mk,
  1595. (None, None, None),
  1596. )
  1597. gSFB_cpasync_nkl = cute.local_tile(
  1598. directSFB_nkl,
  1599. self.sfb_tile_shape_nk,
  1600. (None, None, None),
  1601. )
  1602. gC_mnl = cute.local_tile(
  1603. mC_mnl,
  1604. cute.slice_(self.tile_shape_mnk, (None, None, 0)),
  1605. (None, None, None),
  1606. )
  1607. # Partition for TiledMMA
  1608. thr_mma = tiled_mma.get_slice(tidx)
  1609. # TMA partitions for A
  1610. a_cta_layout = cute.make_layout(cute.slice_(cta_layout_mnk, (0, None, 0)).shape)
  1611. a_cta_crd = cluster_coord_mnk[1]
  1612. if cutlass.const_expr(
  1613. self.load_path == "tma"
  1614. and not self.use_m1_non_tma_a
  1615. and not self.fused_quant_a
  1616. and not self.direct_m1_wo_a_inputs
  1617. ):
  1618. tAsA, tAgA = cpasync.tma_partition(
  1619. tma_atom_a,
  1620. a_cta_crd,
  1621. a_cta_layout,
  1622. cute.group_modes(sA, 0, 2),
  1623. cute.group_modes(gA_mkl, 0, 2),
  1624. )
  1625. # TMA partitions for B (targets the packed staging buffer when b_packed)
  1626. b_cta_layout = cute.make_layout(cute.slice_(cta_layout_mnk, (None, 0, 0)).shape)
  1627. b_cta_crd = cluster_coord_mnk[0]
  1628. if cutlass.const_expr(self.load_path == "tma" and self.b_packed):
  1629. tBsB, tBgB = cpasync.tma_partition(
  1630. tma_atom_b,
  1631. b_cta_crd,
  1632. b_cta_layout,
  1633. cute.group_modes(sBPacked, 0, 2),
  1634. cute.group_modes(gB_nkl, 0, 2),
  1635. )
  1636. elif cutlass.const_expr(self.load_path == "tma"):
  1637. tBsB, tBgB = cpasync.tma_partition(
  1638. tma_atom_b,
  1639. b_cta_crd,
  1640. b_cta_layout,
  1641. cute.group_modes(sB, 0, 2),
  1642. cute.group_modes(gB_nkl, 0, 2),
  1643. )
  1644. # TMA partitions for SFA
  1645. if cutlass.const_expr(
  1646. self.load_path == "tma"
  1647. and not self.block_fp8
  1648. and not self.use_m1_non_tma_sfa
  1649. and not self.fused_quant_a
  1650. and not self.manual_bk64_sf
  1651. and not self.direct_sfa_prefix
  1652. ):
  1653. tAsSFA, tAgSFA = cpasync.tma_partition(
  1654. tma_atom_sfa,
  1655. a_cta_crd,
  1656. a_cta_layout,
  1657. cute.group_modes(sSFA, 0, 2),
  1658. cute.group_modes(gSFA_mkl, 0, 2),
  1659. )
  1660. tAsSFA = cute.filter_zeros(tAsSFA)
  1661. tAgSFA = cute.filter_zeros(tAgSFA)
  1662. # TMA partitions for SFB
  1663. if cutlass.const_expr(
  1664. self.load_path == "tma"
  1665. and not self.block_fp8
  1666. and not self.manual_bk64_sf
  1667. and not self.direct_sfb_representative
  1668. ):
  1669. tBsSFB, tBgSFB = cpasync.tma_partition(
  1670. tma_atom_sfb,
  1671. b_cta_crd,
  1672. b_cta_layout,
  1673. cute.group_modes(sSFB, 0, 2),
  1674. cute.group_modes(gSFB_nkl, 0, 2),
  1675. )
  1676. tBsSFB = cute.filter_zeros(tBsSFB)
  1677. tBgSFB = cute.filter_zeros(tBgSFB)
  1678. if cutlass.const_expr(self.load_path == "cpasync"):
  1679. cpasync_tiled_copy_A = self._make_cpasync_tiled_copy(
  1680. self.a_dtype,
  1681. self.tile_shape_mnk[2],
  1682. )
  1683. cpasync_tiled_copy_B = self._make_cpasync_tiled_copy(
  1684. self.b_dtype,
  1685. self.tile_shape_mnk[2],
  1686. )
  1687. cpasync_tiled_copy_SF = self._make_scale_tiled_copy(self.sf_dtype)
  1688. cA_mkl = cute.make_identity_tensor(cute.shape(directA_mkl))
  1689. cA_cpasync_mkl = cute.local_tile(
  1690. cA_mkl,
  1691. cute.slice_(self.tile_shape_mnk, (None, 0, None)),
  1692. (None, None, None),
  1693. )
  1694. cB_nkl = cute.make_identity_tensor(cute.shape(directB_nkl))
  1695. cB_cpasync_nkl = cute.local_tile(
  1696. cB_nkl,
  1697. cute.slice_(self.tile_shape_mnk, (0, None, None)),
  1698. (None, None, None),
  1699. )
  1700. cSFA_mkl = cute.make_identity_tensor(cute.shape(directSFA_mkl))
  1701. cSFA_cpasync_mkl = cute.local_tile(
  1702. cSFA_mkl,
  1703. self.sfa_tile_shape_mk,
  1704. (None, None, None),
  1705. )
  1706. cSFB_nkl = cute.make_identity_tensor(cute.shape(directSFB_nkl))
  1707. cSFB_cpasync_nkl = cute.local_tile(
  1708. cSFB_nkl,
  1709. self.sfb_tile_shape_nk,
  1710. (None, None, None),
  1711. )
  1712. cpasync_lane = tidx % self.num_threads_per_warp
  1713. thr_cpasync_A = cpasync_tiled_copy_A.get_slice(cpasync_lane)
  1714. thr_cpasync_B = cpasync_tiled_copy_B.get_slice(cpasync_lane)
  1715. thr_cpasync_SF = cpasync_tiled_copy_SF.get_slice(cpasync_lane)
  1716. tAgA_cpasync_mkl = thr_cpasync_A.partition_S(gA_cpasync_mkl)
  1717. tAsA_cpasync = thr_cpasync_A.partition_D(sA)
  1718. tAcA_cpasync_mkl = thr_cpasync_A.partition_S(cA_cpasync_mkl)
  1719. tBgB_cpasync_nkl = thr_cpasync_B.partition_S(gB_cpasync_nkl)
  1720. tBsB_cpasync = thr_cpasync_B.partition_D(sB)
  1721. tBcB_cpasync_nkl = thr_cpasync_B.partition_S(cB_cpasync_nkl)
  1722. tAgSFA_cpasync_mkl = thr_cpasync_SF.partition_S(gSFA_cpasync_mkl)
  1723. tAsSFA_cpasync = thr_cpasync_SF.partition_D(sSFA)
  1724. tAcSFA_cpasync_mkl = thr_cpasync_SF.partition_S(cSFA_cpasync_mkl)
  1725. tBgSFB_cpasync_nkl = thr_cpasync_SF.partition_S(gSFB_cpasync_nkl)
  1726. tBsSFB_cpasync = thr_cpasync_SF.partition_D(sSFB)
  1727. tBcSFB_cpasync_nkl = thr_cpasync_SF.partition_S(cSFB_cpasync_nkl)
  1728. # Make fragments. swap_ab keeps public C[M,N] unchanged but presents
  1729. # B as MMA-A and A as MMA-B.
  1730. if cutlass.const_expr(self.swap_ab):
  1731. tCsA = thr_mma.partition_A(sB)
  1732. tCsB = thr_mma.partition_B(sA)
  1733. else:
  1734. tCsA = thr_mma.partition_A(sA)
  1735. tCsB = thr_mma.partition_B(sB)
  1736. tCrA = tiled_mma.make_fragment_A(tCsA[None, None, None, 0])
  1737. tCrB = tiled_mma.make_fragment_B(tCsB[None, None, None, 0])
  1738. if cutlass.const_expr(self.block_fp8):
  1739. tCgC = thr_mma.partition_C(gC_mnl)
  1740. elif cutlass.const_expr(self.swap_ab):
  1741. tCrSFA_full = self._partition_fragment_SFA(
  1742. sSFB[None, None, 0], thr_mma, tidx
  1743. )
  1744. tCrSFB_full = self._partition_fragment_SFB(
  1745. sSFA[None, None, 0], thr_mma, tidx
  1746. )
  1747. c_mma = cute.make_identity_tensor(
  1748. (self.tile_shape_mnk[1], self.tile_shape_mnk[0])
  1749. )
  1750. tCgC = thr_mma.partition_C(c_mma)
  1751. else:
  1752. tCrSFA_full = self._partition_fragment_SFA(
  1753. sSFA[None, None, 0], thr_mma, tidx
  1754. )
  1755. tCrSFB_full = self._partition_fragment_SFB(
  1756. sSFB[None, None, 0], thr_mma, tidx
  1757. )
  1758. tCgC = thr_mma.partition_C(gC_mnl)
  1759. acc_shape = tCgC.shape[:3]
  1760. accumulators = cute.make_rmem_tensor(acc_shape, self.acc_dtype)
  1761. if cutlass.const_expr(self.block_fp8):
  1762. stage_accumulators = cute.make_rmem_tensor(acc_shape, self.acc_dtype)
  1763. block_fp8_c_identity = cute.make_identity_tensor(
  1764. cute.slice_(self.tile_shape_mnk, (None, None, 0))
  1765. )
  1766. block_fp8_coord_mn = _reshape_acc_to_mn(
  1767. thr_mma.partition_C(block_fp8_c_identity)
  1768. )
  1769. if cutlass.const_expr(self.swap_ab):
  1770. swap_ab_acc_mn = _reshape_acc_to_mn(accumulators, transpose=True)
  1771. swap_ab_c_identity = cute.make_identity_tensor(
  1772. (self.tile_shape_mnk[1], self.tile_shape_mnk[0])
  1773. )
  1774. swap_ab_coord_mn = _reshape_acc_to_mn(
  1775. thr_mma.partition_C(swap_ab_c_identity),
  1776. transpose=True,
  1777. )
  1778. if cutlass.const_expr(self.split_k_slices > 1):
  1779. split_k_acc_mn = _reshape_acc_to_mn(accumulators)
  1780. split_k_c_identity = cute.make_identity_tensor(
  1781. cute.slice_(self.tile_shape_mnk, (None, None, 0))
  1782. )
  1783. split_k_coord_mn = _reshape_acc_to_mn(
  1784. thr_mma.partition_C(split_k_c_identity)
  1785. )
  1786. # Cluster/thread sync
  1787. if cute.size(self.cluster_shape_mnk) > 1:
  1788. cute.arch.cluster_wait()
  1789. else:
  1790. cute.arch.sync_threads()
  1791. k_tile_cnt = cute.size(gA_mkl, mode=[3])
  1792. block_idx = cute.arch.block_idx()
  1793. k_tile_start = Int32(0)
  1794. k_tile_iter_cnt = k_tile_cnt
  1795. if cutlass.const_expr(self.split_k_slices > 1):
  1796. k_tiles_per_split = k_tile_cnt // self.split_k_slices
  1797. k_tile_start = Int32(block_idx[1]) * Int32(k_tiles_per_split)
  1798. k_tile_iter_cnt = k_tiles_per_split
  1799. # Tile scheduler
  1800. if cutlass.const_expr(self.direct_one_m_tile_scheduler):
  1801. direct_tile_valid = Int32(block_idx[2]) < Int32(
  1802. tile_sched_params.problem_shape_ntile_mnl[1]
  1803. )
  1804. work_tile = WorkTileInfo(
  1805. (Int32(0), Int32(block_idx[2]), Int32(0)),
  1806. direct_tile_valid,
  1807. )
  1808. else:
  1809. tile_sched = utils.StaticPersistentTileScheduler.create(
  1810. tile_sched_params, block_idx, cute.arch.grid_dim()
  1811. )
  1812. work_tile = tile_sched.initial_work_tile_info()
  1813. # Pipeline states
  1814. mainloop_producer_state = pipeline.make_pipeline_state(
  1815. pipeline.PipelineUserType.Producer, self.ab_stage
  1816. )
  1817. mainloop_consumer_state = pipeline.make_pipeline_state(
  1818. pipeline.PipelineUserType.Consumer, self.ab_stage
  1819. )
  1820. if cutlass.const_expr(self.packed_expand_ahead):
  1821. # Second consumer-side view of the mainloop pipeline, kept exactly
  1822. # one stage ahead of mainloop_consumer_state: it advances once at
  1823. # each work-tile prologue and once per k_tile at k_block 0, for a
  1824. # total of k_tile_cnt per tile — the same as the main state's
  1825. # (k_tile_cnt - 1) in-loop advances plus 1 in the hoisted tail —
  1826. # so the two stay phase-aligned across persistent work tiles.
  1827. packed_b_lookahead_state = pipeline.make_pipeline_state(
  1828. pipeline.PipelineUserType.Consumer, self.ab_stage
  1829. )
  1830. # MMA warp group
  1831. if warp_idx < self.num_mma_warps:
  1832. cute.arch.setmaxregister_increase(self.mma_register_requirement)
  1833. num_k_blocks = cute.size(tCrA, mode=[2])
  1834. # Copy atoms for SMEM->RMEM
  1835. if cutlass.const_expr(self.swap_ab):
  1836. atom_copy_ldmatrix_A = cute.make_copy_atom(
  1837. cute.nvgpu.warp.LdMatrix8x8x16bOp(self.b_layout.is_n_major_b(), 4),
  1838. self.b_dtype,
  1839. )
  1840. atom_copy_ldmatrix_B = cute.make_copy_atom(
  1841. cute.nvgpu.warp.LdMatrix8x8x16bOp(self.a_layout.is_m_major_a(), 4),
  1842. self.a_dtype,
  1843. )
  1844. else:
  1845. atom_copy_ldmatrix_A = cute.make_copy_atom(
  1846. cute.nvgpu.warp.LdMatrix8x8x16bOp(self.a_layout.is_m_major_a(), 4),
  1847. self.a_dtype,
  1848. )
  1849. atom_copy_ldmatrix_B = cute.make_copy_atom(
  1850. cute.nvgpu.warp.LdMatrix8x8x16bOp(self.b_layout.is_n_major_b(), 4),
  1851. self.b_dtype,
  1852. )
  1853. smem_tiled_copy_A = cute.make_tiled_copy_A(atom_copy_ldmatrix_A, tiled_mma)
  1854. smem_tiled_copy_B = cute.make_tiled_copy_B(atom_copy_ldmatrix_B, tiled_mma)
  1855. if cutlass.const_expr(not self.block_fp8):
  1856. atom_copy_ldmatrix_SF = cute.make_copy_atom(
  1857. cute.nvgpu.CopyUniversalOp(),
  1858. self.sf_dtype,
  1859. )
  1860. smem_tiled_copy_SFA = cute.make_tiled_copy(
  1861. atom_copy_ldmatrix_SF,
  1862. self._get_layoutSFA_TV(tiled_mma),
  1863. (
  1864. cute.size(tiled_mma.permutation_mnk[0]),
  1865. cute.size(tiled_mma.permutation_mnk[2]),
  1866. ),
  1867. )
  1868. smem_tiled_copy_SFB = cute.make_tiled_copy(
  1869. atom_copy_ldmatrix_SF,
  1870. self._get_layoutSFB_TV(tiled_mma),
  1871. (
  1872. cute.size(tiled_mma.permutation_mnk[1]),
  1873. cute.size(tiled_mma.permutation_mnk[2]),
  1874. ),
  1875. )
  1876. thr_copy_ldmatrix_A = smem_tiled_copy_A.get_slice(tidx)
  1877. thr_copy_ldmatrix_B = smem_tiled_copy_B.get_slice(tidx)
  1878. tCsA_copy_view = thr_copy_ldmatrix_A.partition_S(
  1879. sB if cutlass.const_expr(self.swap_ab) else sA
  1880. )
  1881. tCrA_copy_view = thr_copy_ldmatrix_A.retile(tCrA)
  1882. tCsB_copy_view = thr_copy_ldmatrix_B.partition_S(
  1883. sA if cutlass.const_expr(self.swap_ab) else sB
  1884. )
  1885. tCrB_copy_view = thr_copy_ldmatrix_B.retile(tCrB)
  1886. if cutlass.const_expr(not self.block_fp8):
  1887. thr_copy_ldmatrix_SFA = smem_tiled_copy_SFA.get_slice(tidx)
  1888. thr_copy_ldmatrix_SFB = smem_tiled_copy_SFB.get_slice(tidx)
  1889. tCsSFA_copy_view_full = thr_copy_ldmatrix_SFA.partition_S(
  1890. sSFB if cutlass.const_expr(self.swap_ab) else sSFA
  1891. )
  1892. tCrSFA_copy_view_full = thr_copy_ldmatrix_SFA.retile(tCrSFA_full)
  1893. tCsSFB_copy_view_full = thr_copy_ldmatrix_SFB.partition_S(
  1894. sSFA if cutlass.const_expr(self.swap_ab) else sSFB
  1895. )
  1896. tCrSFB_copy_view_full = thr_copy_ldmatrix_SFB.retile(tCrSFB_full)
  1897. while work_tile.is_valid_tile:
  1898. tile_coord_mnl = work_tile.tile_idx
  1899. gC_mnl_slice = gC_mnl[(None, None, *tile_coord_mnl)]
  1900. sfa_tile_offset = tile_coord_mnl[0] % self.sfa_tiles_per_block
  1901. sfb_tile_offset = tile_coord_mnl[1] % self.sfb_tiles_per_block
  1902. if cutlass.const_expr(self.block_fp8):
  1903. pass
  1904. elif cutlass.const_expr(self.swap_ab):
  1905. if cutlass.const_expr(self.sfb_tiles_per_block > 1):
  1906. sSFB_tile = cute.local_tile(
  1907. sSFB,
  1908. cute.slice_(self.tile_shape_mnk, (0, None, None)),
  1909. (sfb_tile_offset, 0, None),
  1910. )
  1911. tCsSFA_tile_copy_view = thr_copy_ldmatrix_SFA.partition_S(
  1912. sSFB_tile
  1913. )
  1914. tCrSFA_tile = self._partition_fragment_SFA(
  1915. sSFB_tile[None, None, 0], thr_mma, tidx
  1916. )
  1917. tCrSFA_tile_copy_view = thr_copy_ldmatrix_SFA.retile(
  1918. tCrSFA_tile
  1919. )
  1920. else:
  1921. tCsSFA_tile_copy_view = tCsSFA_copy_view_full
  1922. tCrSFA_tile = tCrSFA_full
  1923. tCrSFA_tile_copy_view = tCrSFA_copy_view_full
  1924. if cutlass.const_expr(self.sfa_tiles_per_block > 1):
  1925. sSFA_tile = cute.local_tile(
  1926. sSFA,
  1927. cute.slice_(self.tile_shape_mnk, (None, 0, None)),
  1928. (sfa_tile_offset, 0, None),
  1929. )
  1930. tCsSFB_tile_copy_view = thr_copy_ldmatrix_SFB.partition_S(
  1931. sSFA_tile
  1932. )
  1933. tCrSFB_tile = self._partition_fragment_SFB(
  1934. sSFA_tile[None, None, 0], thr_mma, tidx
  1935. )
  1936. tCrSFB_tile_copy_view = thr_copy_ldmatrix_SFB.retile(
  1937. tCrSFB_tile
  1938. )
  1939. else:
  1940. tCsSFB_tile_copy_view = tCsSFB_copy_view_full
  1941. tCrSFB_tile = tCrSFB_full
  1942. tCrSFB_tile_copy_view = tCrSFB_copy_view_full
  1943. else:
  1944. if cutlass.const_expr(self.sfa_tiles_per_block > 1):
  1945. sSFA_tile = cute.local_tile(
  1946. sSFA,
  1947. cute.slice_(self.tile_shape_mnk, (None, 0, None)),
  1948. (sfa_tile_offset, 0, None),
  1949. )
  1950. tCsSFA_tile_copy_view = thr_copy_ldmatrix_SFA.partition_S(
  1951. sSFA_tile
  1952. )
  1953. tCrSFA_tile = self._partition_fragment_SFA(
  1954. sSFA_tile[None, None, 0], thr_mma, tidx
  1955. )
  1956. tCrSFA_tile_copy_view = thr_copy_ldmatrix_SFA.retile(
  1957. tCrSFA_tile
  1958. )
  1959. else:
  1960. tCsSFA_tile_copy_view = tCsSFA_copy_view_full
  1961. tCrSFA_tile = tCrSFA_full
  1962. tCrSFA_tile_copy_view = tCrSFA_copy_view_full
  1963. if cutlass.const_expr(self.sfb_tiles_per_block > 1):
  1964. sSFB_tile = cute.local_tile(
  1965. sSFB,
  1966. cute.slice_(self.tile_shape_mnk, (0, None, None)),
  1967. (sfb_tile_offset, 0, None),
  1968. )
  1969. tCsSFB_tile_copy_view = thr_copy_ldmatrix_SFB.partition_S(
  1970. sSFB_tile
  1971. )
  1972. tCrSFB_tile = self._partition_fragment_SFB(
  1973. sSFB_tile[None, None, 0], thr_mma, tidx
  1974. )
  1975. tCrSFB_tile_copy_view = thr_copy_ldmatrix_SFB.retile(
  1976. tCrSFB_tile
  1977. )
  1978. else:
  1979. tCsSFB_tile_copy_view = tCsSFB_copy_view_full
  1980. tCrSFB_tile = tCrSFB_full
  1981. tCrSFB_tile_copy_view = tCrSFB_copy_view_full
  1982. accumulators.fill(0.0)
  1983. if cutlass.const_expr(self.block_fp8):
  1984. stage_accumulators.fill(0.0)
  1985. # Pipelined MAINLOOP
  1986. mainloop_consumer_state.reset_count()
  1987. peek_ab_full_status = cutlass.Boolean(1)
  1988. if mainloop_consumer_state.count < k_tile_iter_cnt:
  1989. peek_ab_full_status = mainloop_pipeline.consumer_try_wait(
  1990. mainloop_consumer_state
  1991. )
  1992. mainloop_pipeline.consumer_wait(
  1993. mainloop_consumer_state, peek_ab_full_status
  1994. )
  1995. if cutlass.const_expr(self.b_packed):
  1996. # Expand the TMA-staged packed B tile in place into the
  1997. # swizzled byte-container sB BEFORE any ldmatrix touches
  1998. # this stage (two-phase; internal read/write barrier).
  1999. _expand_packed_b_stage_smem(
  2000. sb_base_addr,
  2001. mainloop_consumer_state.index,
  2002. Int32(tidx),
  2003. self.tile_shape_mnk[1],
  2004. self.tile_shape_mnk[2],
  2005. self.num_mma_warps * self.num_threads_per_warp,
  2006. self.mma_sync_barrier,
  2007. )
  2008. self.mma_sync_barrier.arrive_and_wait()
  2009. if cutlass.const_expr(self.packed_expand_ahead):
  2010. # Lookahead expanded this stage too (same index as the
  2011. # consumer state at the prologue); move it one ahead.
  2012. packed_b_lookahead_state.advance()
  2013. tCsA_p = tCsA_copy_view[None, None, None, mainloop_consumer_state.index]
  2014. tCsB_p = tCsB_copy_view[None, None, None, mainloop_consumer_state.index]
  2015. if cutlass.const_expr(not self.block_fp8):
  2016. tCsSFA_p = tCsSFA_tile_copy_view[
  2017. None, None, None, mainloop_consumer_state.index
  2018. ]
  2019. tCsSFB_p = tCsSFB_tile_copy_view[
  2020. None, None, None, mainloop_consumer_state.index
  2021. ]
  2022. cute.copy(
  2023. smem_tiled_copy_A,
  2024. tCsA_p[None, None, 0],
  2025. tCrA_copy_view[None, None, 0],
  2026. )
  2027. cute.copy(
  2028. smem_tiled_copy_B,
  2029. tCsB_p[None, None, 0],
  2030. tCrB_copy_view[None, None, 0],
  2031. )
  2032. if cutlass.const_expr(not self.block_fp8):
  2033. tCsSFA_p_filtered = cute.filter_zeros(tCsSFA_p)
  2034. tCsSFB_p_filtered = cute.filter_zeros(tCsSFB_p)
  2035. tCrSFA_copy_view_filtered = cute.filter_zeros(
  2036. tCrSFA_tile_copy_view
  2037. )
  2038. tCrSFB_copy_view_filtered = cute.filter_zeros(
  2039. tCrSFB_tile_copy_view
  2040. )
  2041. # Whole-stage SF copy: scale bytes for all k blocks of the
  2042. # acquired stage load in one bulk copy.
  2043. cute.copy(
  2044. smem_tiled_copy_SFA,
  2045. tCsSFA_p_filtered,
  2046. tCrSFA_copy_view_filtered,
  2047. )
  2048. if cutlass.const_expr(self.direct_sfb_representative):
  2049. self._fill_replicated_sfb_fragment(
  2050. tCrSFB_tile[None, None, 0],
  2051. sSFB[
  2052. (
  2053. Int32(0),
  2054. Int32(0),
  2055. mainloop_consumer_state.index,
  2056. )
  2057. ],
  2058. )
  2059. elif cutlass.const_expr(self.sfb_k_reuse):
  2060. cute.copy(
  2061. smem_tiled_copy_SFB,
  2062. tCsSFB_p_filtered[None, None, 0],
  2063. tCrSFB_copy_view_filtered[None, None, 0],
  2064. )
  2065. else:
  2066. cute.copy(
  2067. smem_tiled_copy_SFB,
  2068. tCsSFB_p_filtered,
  2069. tCrSFB_copy_view_filtered,
  2070. )
  2071. for k_tile in range(
  2072. 0,
  2073. k_tile_iter_cnt - 1,
  2074. 1,
  2075. unroll=4 if self.large_m_unroll else 2,
  2076. ):
  2077. for k_block_idx in cutlass.range_constexpr(num_k_blocks):
  2078. k_block_next = (
  2079. 0 if k_block_idx + 1 == num_k_blocks else k_block_idx + 1
  2080. )
  2081. if cutlass.const_expr(self.packed_expand_ahead):
  2082. if k_block_idx == 0:
  2083. # Expand-ahead: wait for stage s+1 (producer
  2084. # runs >= 2 stages ahead at this depth, so the
  2085. # wait is usually free) and expand it now, so
  2086. # the whole expansion overlaps stage s's MMA
  2087. # k-blocks instead of sitting at the stage
  2088. # boundary. The matching write->ldmatrix fence
  2089. # is the deferred barrier after the last
  2090. # k-block's MMA below.
  2091. lookahead_peek = (
  2092. mainloop_pipeline.consumer_try_wait(
  2093. packed_b_lookahead_state
  2094. )
  2095. )
  2096. mainloop_pipeline.consumer_wait(
  2097. packed_b_lookahead_state, lookahead_peek
  2098. )
  2099. _expand_packed_b_stage_smem(
  2100. sb_base_addr,
  2101. packed_b_lookahead_state.index,
  2102. Int32(tidx),
  2103. self.tile_shape_mnk[1],
  2104. self.tile_shape_mnk[2],
  2105. self.num_mma_warps
  2106. * self.num_threads_per_warp,
  2107. self.mma_sync_barrier,
  2108. )
  2109. packed_b_lookahead_state.advance()
  2110. if k_block_idx == num_k_blocks - 1:
  2111. mainloop_pipeline.consumer_release(mainloop_consumer_state)
  2112. mainloop_consumer_state.advance()
  2113. peek_ab_full_status = cutlass.Boolean(1)
  2114. peek_ab_full_status = mainloop_pipeline.consumer_try_wait(
  2115. mainloop_consumer_state
  2116. )
  2117. tCsA_p = tCsA_copy_view[
  2118. None, None, None, mainloop_consumer_state.index
  2119. ]
  2120. tCsB_p = tCsB_copy_view[
  2121. None, None, None, mainloop_consumer_state.index
  2122. ]
  2123. if cutlass.const_expr(not self.block_fp8):
  2124. tCsSFA_p = tCsSFA_tile_copy_view[
  2125. None, None, None, mainloop_consumer_state.index
  2126. ]
  2127. tCsSFB_p = tCsSFB_tile_copy_view[
  2128. None, None, None, mainloop_consumer_state.index
  2129. ]
  2130. mainloop_pipeline.consumer_wait(
  2131. mainloop_consumer_state, peek_ab_full_status
  2132. )
  2133. if cutlass.const_expr(
  2134. self.b_packed and not self.packed_expand_ahead
  2135. ):
  2136. # Shallow-pipeline fallback (< 4 stages): the
  2137. # new stage just became full (packed bytes
  2138. # only); expand before the k_block_next=0
  2139. # ldmatrix of this stage later in this
  2140. # iteration. Reads of the PREVIOUS stage
  2141. # finished at the prior iteration's copies, so
  2142. # writing here is safe. The matching barrier
  2143. # sits AFTER the MMA block below: the last
  2144. # k-block's MMA reads only registers, so it
  2145. # overlaps expansion stragglers instead of
  2146. # waiting on the barrier first. (In expand-
  2147. # ahead mode this stage was already expanded
  2148. # at k_block 0; the consumer_wait above then
  2149. # returns immediately.)
  2150. _expand_packed_b_stage_smem(
  2151. sb_base_addr,
  2152. mainloop_consumer_state.index,
  2153. Int32(tidx),
  2154. self.tile_shape_mnk[1],
  2155. self.tile_shape_mnk[2],
  2156. self.num_mma_warps
  2157. * self.num_threads_per_warp,
  2158. self.mma_sync_barrier,
  2159. )
  2160. # Manual atom unroll: avoids hasAuxTensor address space bug
  2161. for _mt in range(self.num_m_tiles):
  2162. for _nt in range(self.num_n_tiles):
  2163. if cutlass.const_expr(self.plain_fp8):
  2164. _emit_plain_fp8_dense_mma_k_block(
  2165. (
  2166. stage_accumulators
  2167. if cutlass.const_expr(self.block_fp8)
  2168. else accumulators
  2169. ),
  2170. tCrA,
  2171. tCrB,
  2172. _mt,
  2173. _nt,
  2174. k_block_idx,
  2175. )
  2176. elif cutlass.const_expr(self.mxfp6_fmt_a is not None):
  2177. emit_mxfp6_dense_mma_k_block(
  2178. accumulators,
  2179. tCrA,
  2180. tCrB,
  2181. tCrSFA_tile,
  2182. tCrSFB_tile,
  2183. _mt,
  2184. _nt,
  2185. k_block_idx,
  2186. self.mxfp6_fmt_a,
  2187. self.mxfp6_fmt_b,
  2188. )
  2189. else:
  2190. mma_atom.set(
  2191. WarpField.SFA,
  2192. tCrSFA_tile[None, _mt, k_block_idx].iterator,
  2193. )
  2194. if cutlass.const_expr(self.sfb_k_reuse):
  2195. mma_atom.set(
  2196. WarpField.SFB,
  2197. tCrSFB_tile[None, _nt, 0].iterator,
  2198. )
  2199. else:
  2200. mma_atom.set(
  2201. WarpField.SFB,
  2202. tCrSFB_tile[None, _nt, k_block_idx].iterator,
  2203. )
  2204. cute.gemm(
  2205. mma_atom,
  2206. accumulators[None, _mt, _nt],
  2207. tCrA[None, _mt, k_block_idx],
  2208. tCrB[None, _nt, k_block_idx],
  2209. accumulators[None, _mt, _nt],
  2210. )
  2211. if cutlass.const_expr(self.block_fp8):
  2212. if k_block_idx == num_k_blocks - 1:
  2213. self._accumulate_block_fp8_stage(
  2214. accumulators,
  2215. stage_accumulators,
  2216. block_fp8_coord_mn,
  2217. directSFA_mkl,
  2218. directSFB_nkl,
  2219. tile_coord_mnl,
  2220. k_tile_start + Int32(k_tile),
  2221. )
  2222. if cutlass.const_expr(self.b_packed):
  2223. # Deferred expansion barrier (see expansion call
  2224. # above): must precede the k_block_next=0 ldmatrix
  2225. # of the just-expanded next stage right below.
  2226. if k_block_idx == num_k_blocks - 1:
  2227. self.mma_sync_barrier.arrive_and_wait()
  2228. cute.copy(
  2229. smem_tiled_copy_A,
  2230. tCsA_p[None, None, k_block_next],
  2231. tCrA_copy_view[None, None, k_block_next],
  2232. )
  2233. cute.copy(
  2234. smem_tiled_copy_B,
  2235. tCsB_p[None, None, k_block_next],
  2236. tCrB_copy_view[None, None, k_block_next],
  2237. )
  2238. if k_block_idx == num_k_blocks - 1:
  2239. if cutlass.const_expr(not self.block_fp8):
  2240. # New stage acquired above: bulk-load its whole
  2241. # SF tile once.
  2242. tCsSFA_p_filtered = cute.filter_zeros(tCsSFA_p)
  2243. tCsSFB_p_filtered = cute.filter_zeros(tCsSFB_p)
  2244. tCrSFA_copy_view_filtered = cute.filter_zeros(
  2245. tCrSFA_tile_copy_view
  2246. )
  2247. tCrSFB_copy_view_filtered = cute.filter_zeros(
  2248. tCrSFB_tile_copy_view
  2249. )
  2250. cute.copy(
  2251. smem_tiled_copy_SFA,
  2252. tCsSFA_p_filtered,
  2253. tCrSFA_copy_view_filtered,
  2254. )
  2255. if cutlass.const_expr(
  2256. self.direct_sfb_representative
  2257. ):
  2258. self._fill_replicated_sfb_fragment(
  2259. tCrSFB_tile[None, None, 0],
  2260. sSFB[
  2261. (
  2262. Int32(0),
  2263. Int32(0),
  2264. mainloop_consumer_state.index,
  2265. )
  2266. ],
  2267. )
  2268. elif cutlass.const_expr(self.sfb_k_reuse):
  2269. cute.copy(
  2270. smem_tiled_copy_SFB,
  2271. tCsSFB_p_filtered[None, None, 0],
  2272. tCrSFB_copy_view_filtered[None, None, 0],
  2273. )
  2274. else:
  2275. cute.copy(
  2276. smem_tiled_copy_SFB,
  2277. tCsSFB_p_filtered,
  2278. tCrSFB_copy_view_filtered,
  2279. )
  2280. # Hoist out last k_tile
  2281. for k_block_idx in cutlass.range_constexpr(num_k_blocks):
  2282. k_block_next = (
  2283. 0 if k_block_idx + 1 == num_k_blocks else k_block_idx + 1
  2284. )
  2285. if k_block_idx == num_k_blocks - 1:
  2286. mainloop_pipeline.consumer_release(mainloop_consumer_state)
  2287. mainloop_consumer_state.advance()
  2288. if k_block_next > 0:
  2289. cute.copy(
  2290. smem_tiled_copy_A,
  2291. tCsA_p[None, None, k_block_next],
  2292. tCrA_copy_view[None, None, k_block_next],
  2293. )
  2294. cute.copy(
  2295. smem_tiled_copy_B,
  2296. tCsB_p[None, None, k_block_next],
  2297. tCrB_copy_view[None, None, k_block_next],
  2298. )
  2299. # SF registers for the whole stage were bulk-loaded at
  2300. # stage acquisition; nothing to reload per k block.
  2301. # Manual atom unroll: avoids hasAuxTensor address space bug
  2302. for _mt in range(self.num_m_tiles):
  2303. for _nt in range(self.num_n_tiles):
  2304. if cutlass.const_expr(self.plain_fp8):
  2305. _emit_plain_fp8_dense_mma_k_block(
  2306. (
  2307. stage_accumulators
  2308. if cutlass.const_expr(self.block_fp8)
  2309. else accumulators
  2310. ),
  2311. tCrA,
  2312. tCrB,
  2313. _mt,
  2314. _nt,
  2315. k_block_idx,
  2316. )
  2317. elif cutlass.const_expr(self.mxfp6_fmt_a is not None):
  2318. emit_mxfp6_dense_mma_k_block(
  2319. accumulators,
  2320. tCrA,
  2321. tCrB,
  2322. tCrSFA_tile,
  2323. tCrSFB_tile,
  2324. _mt,
  2325. _nt,
  2326. k_block_idx,
  2327. self.mxfp6_fmt_a,
  2328. self.mxfp6_fmt_b,
  2329. )
  2330. else:
  2331. mma_atom.set(
  2332. WarpField.SFA,
  2333. tCrSFA_tile[None, _mt, k_block_idx].iterator,
  2334. )
  2335. if cutlass.const_expr(self.sfb_k_reuse):
  2336. mma_atom.set(
  2337. WarpField.SFB,
  2338. tCrSFB_tile[None, _nt, 0].iterator,
  2339. )
  2340. else:
  2341. mma_atom.set(
  2342. WarpField.SFB,
  2343. tCrSFB_tile[None, _nt, k_block_idx].iterator,
  2344. )
  2345. cute.gemm(
  2346. mma_atom,
  2347. accumulators[None, _mt, _nt],
  2348. tCrA[None, _mt, k_block_idx],
  2349. tCrB[None, _nt, k_block_idx],
  2350. accumulators[None, _mt, _nt],
  2351. )
  2352. if cutlass.const_expr(self.block_fp8):
  2353. if k_block_idx == num_k_blocks - 1:
  2354. self._accumulate_block_fp8_stage(
  2355. accumulators,
  2356. stage_accumulators,
  2357. block_fp8_coord_mn,
  2358. directSFA_mkl,
  2359. directSFB_nkl,
  2360. tile_coord_mnl,
  2361. k_tile_start + Int32(k_tile_iter_cnt - 1),
  2362. )
  2363. if cutlass.const_expr(self.swap_ab):
  2364. for acc_m in cutlass.range_constexpr(
  2365. cute.size(swap_ab_acc_mn.shape[0])
  2366. ):
  2367. for acc_n in cutlass.range_constexpr(
  2368. cute.size(swap_ab_acc_mn.shape[1])
  2369. ):
  2370. coord = swap_ab_coord_mn[acc_m, acc_n]
  2371. m_coord = (
  2372. tile_coord_mnl[0] * Int32(self.tile_shape_mnk[0])
  2373. + coord[1]
  2374. )
  2375. n_coord = (
  2376. tile_coord_mnl[1] * Int32(self.tile_shape_mnk[1])
  2377. + coord[0]
  2378. )
  2379. if m_coord < Int32(
  2380. directC_mnl.shape[0]
  2381. ) and n_coord < Int32(directC_mnl.shape[1]):
  2382. directC_mnl[
  2383. (
  2384. m_coord,
  2385. n_coord,
  2386. tile_coord_mnl[2],
  2387. )
  2388. ] = epilogue_op(
  2389. (alpha_value * swap_ab_acc_mn[acc_m, acc_n]).to(
  2390. self.c_dtype
  2391. )
  2392. )
  2393. if cutlass.const_expr(self.single_work_tile_per_cta):
  2394. work_tile = WorkTileInfo(
  2395. work_tile.tile_idx,
  2396. cutlass.Boolean(0),
  2397. )
  2398. else:
  2399. tile_sched.advance_to_next_work()
  2400. work_tile = tile_sched.get_current_work()
  2401. if cutlass.const_expr(not self.swap_ab):
  2402. # EPILOGUE
  2403. _is_m_major = self.c_layout.is_m_major_c()
  2404. if cutlass.const_expr(self.c_dtype.width == 16):
  2405. copy_atom_r2s = cute.make_copy_atom(
  2406. cute.nvgpu.warp.StMatrix8x8x16bOp(_is_m_major, 2),
  2407. self.c_dtype,
  2408. )
  2409. else:
  2410. copy_atom_r2s = cute.make_copy_atom(
  2411. cute.nvgpu.CopyUniversalOp(),
  2412. self.c_dtype,
  2413. )
  2414. if cutlass.const_expr(self.c_dtype.width == 16):
  2415. copy_atom_C = cute.make_copy_atom(
  2416. cute.nvgpu.warp.StMatrix8x8x16bOp(
  2417. self.c_layout.is_m_major_c(),
  2418. 2,
  2419. ),
  2420. self.c_dtype,
  2421. )
  2422. else:
  2423. copy_atom_C = cute.make_copy_atom(
  2424. cute.nvgpu.CopyUniversalOp(), self.c_dtype
  2425. )
  2426. tiled_copy_C_Atom = cute.make_tiled_copy_C_atom(
  2427. copy_atom_C, tiled_mma
  2428. )
  2429. tiled_copy_r2s = cute.make_tiled_copy_S(
  2430. copy_atom_r2s,
  2431. tiled_copy_C_Atom,
  2432. )
  2433. thr_copy_r2s = tiled_copy_r2s.get_slice(tidx)
  2434. tRS_sD = thr_copy_r2s.partition_D(sC)
  2435. tRS_rAcc = tiled_copy_r2s.retile(accumulators)
  2436. rD_shape = cute.shape(thr_copy_r2s.partition_S(sC))
  2437. tRS_rD_layout = cute.make_layout(rD_shape[:3])
  2438. tRS_rD = cute.make_rmem_tensor(tRS_rD_layout.shape, self.acc_dtype)
  2439. if cutlass.const_expr(self.row_scale):
  2440. # Retiled through the SAME copy as tRS_rAcc, so element
  2441. # ``i`` of tRS_cAcc is the (m,n) tile coordinate of
  2442. # element ``i`` of tRS_rAcc, and therefore of tRS_rD.
  2443. # Partitioning an identity tensor against sC directly
  2444. # would not be safe here: sC is a swizzled composed
  2445. # layout and the identity is not.
  2446. tRS_cAcc = tiled_copy_r2s.retile(
  2447. thr_mma.partition_C(
  2448. cute.make_identity_tensor(
  2449. cute.slice_(self.tile_shape_mnk, (None, None, 0))
  2450. )
  2451. )
  2452. )
  2453. tRS_rRowScale = cute.make_rmem_tensor(
  2454. tRS_rD_layout.shape, self.acc_dtype
  2455. )
  2456. sepi_for_tma_partition = cute.group_modes(sC, 0, 2)
  2457. tcgc_for_tma_partition = cute.zipped_divide(
  2458. gC_mnl_slice, self.epi_tile
  2459. )
  2460. bSG_sD, bSG_gD = cpasync.tma_partition(
  2461. tma_atom_c,
  2462. 0,
  2463. cute.make_layout(1),
  2464. sepi_for_tma_partition,
  2465. tcgc_for_tma_partition,
  2466. )
  2467. epi_rest_m = bSG_gD.shape[1][0]
  2468. epi_rest_n = bSG_gD.shape[1][1]
  2469. epi_tile_m = self.epi_tile[0]
  2470. epi_tile_n = self.epi_tile[1]
  2471. mma_tile_m = self.tile_shape_mnk[0] // cute.size(tRS_rAcc, mode=[1])
  2472. mma_tile_n = self.tile_shape_mnk[1] // cute.size(tRS_rAcc, mode=[2])
  2473. has_multi_epi_store = cutlass.const_expr(
  2474. not (
  2475. self.epi_stage == 1 and epi_rest_m == 1 and epi_rest_n == 1
  2476. )
  2477. )
  2478. tma_store_producer_group = pipeline.CooperativeGroup(
  2479. pipeline.Agent.Thread,
  2480. self.num_mma_warps * self.num_threads_per_warp,
  2481. )
  2482. tma_store_pipeline = pipeline.PipelineTmaStore.create(
  2483. num_stages=self.epi_stage,
  2484. producer_group=tma_store_producer_group,
  2485. )
  2486. for epi_m in cutlass.range_constexpr(epi_rest_m):
  2487. for epi_n in cutlass.range_constexpr(epi_rest_n):
  2488. MmaMPerEpiM = epi_tile_m // mma_tile_m
  2489. MmaNPerEpiN = epi_tile_n // mma_tile_n
  2490. for mma_n_in_epi in cutlass.range_constexpr(MmaNPerEpiN):
  2491. for mma_m_in_epi in cutlass.range_constexpr(
  2492. MmaMPerEpiM
  2493. ):
  2494. mma_n = (epi_n * MmaNPerEpiN) + mma_n_in_epi
  2495. mma_m = (epi_m * MmaMPerEpiM) + mma_m_in_epi
  2496. tRS_rD_slice = tRS_rD[
  2497. (None, mma_m_in_epi, mma_n_in_epi)
  2498. ]
  2499. tRS_rAcc_slice = tRS_rAcc[(None, mma_m, mma_n)]
  2500. for elem_idx in cutlass.range_constexpr(
  2501. cute.size(tRS_rD_slice)
  2502. ):
  2503. tRS_rD_slice[elem_idx] = tRS_rAcc_slice[
  2504. elem_idx
  2505. ]
  2506. if cutlass.const_expr(self.row_scale):
  2507. tRS_cAcc_slice = tRS_cAcc[
  2508. (None, mma_m, mma_n)
  2509. ]
  2510. tRS_rRowScale_slice = tRS_rRowScale[
  2511. (None, mma_m_in_epi, mma_n_in_epi)
  2512. ]
  2513. for elem_idx in cutlass.range_constexpr(
  2514. cute.size(tRS_rD_slice)
  2515. ):
  2516. m_coord = (
  2517. tile_coord_mnl[0]
  2518. * Int32(self.tile_shape_mnk[0])
  2519. + tRS_cAcc_slice[elem_idx][0]
  2520. )
  2521. # Out-of-range rows are never
  2522. # stored, so any finite filler is
  2523. # fine; 1.0 keeps the multiply from
  2524. # manufacturing NaN/Inf in a lane
  2525. # whose accumulator is garbage.
  2526. row_scale_value = cutlass.Float32(1.0)
  2527. if m_coord < Int32(quantC_values.shape[0]):
  2528. row_scale_value = quantC_values[
  2529. m_coord
  2530. ].to(cutlass.Float32)
  2531. tRS_rRowScale_slice[elem_idx] = (
  2532. row_scale_value
  2533. )
  2534. gmem_coord = (epi_m, epi_n)
  2535. if cutlass.const_expr(self.split_k_slices > 1):
  2536. if cutlass.const_expr(self.split_k_atomic_bf16):
  2537. for acc_m in cutlass.range_constexpr(
  2538. cute.size(split_k_acc_mn.shape[0])
  2539. ):
  2540. for acc_n_pair in cutlass.range_constexpr(
  2541. cute.size(split_k_acc_mn.shape[1]) // 2
  2542. ):
  2543. acc_n0 = acc_n_pair * 2
  2544. acc_n1 = acc_n0 + 1
  2545. coord0 = split_k_coord_mn[acc_m, acc_n0]
  2546. coord1 = split_k_coord_mn[acc_m, acc_n1]
  2547. m_coord0 = (
  2548. tile_coord_mnl[0]
  2549. * Int32(self.tile_shape_mnk[0])
  2550. + coord0[0]
  2551. )
  2552. n_coord0 = (
  2553. tile_coord_mnl[1]
  2554. * Int32(self.tile_shape_mnk[1])
  2555. + coord0[1]
  2556. )
  2557. m_coord1 = (
  2558. tile_coord_mnl[0]
  2559. * Int32(self.tile_shape_mnk[0])
  2560. + coord1[0]
  2561. )
  2562. n_coord1 = (
  2563. tile_coord_mnl[1]
  2564. * Int32(self.tile_shape_mnk[1])
  2565. + coord1[1]
  2566. )
  2567. if (
  2568. m_coord0 < Int32(directC_mnl.shape[0])
  2569. and m_coord1
  2570. < Int32(directC_mnl.shape[0])
  2571. and n_coord0
  2572. < Int32(directC_mnl.shape[1])
  2573. and n_coord1
  2574. < Int32(directC_mnl.shape[1])
  2575. ):
  2576. c_offset = cute.crd2idx(
  2577. (
  2578. m_coord0,
  2579. n_coord0,
  2580. Int32(0),
  2581. ),
  2582. directC_mnl.layout,
  2583. )
  2584. scatter_add_bf16x2(
  2585. get_ptr_as_int64(
  2586. directC_mnl,
  2587. c_offset,
  2588. ),
  2589. alpha_value
  2590. * split_k_acc_mn[acc_m, acc_n0],
  2591. alpha_value
  2592. * split_k_acc_mn[acc_m, acc_n1],
  2593. )
  2594. if cutlass.const_expr(
  2595. cute.size(split_k_acc_mn.shape[1]) % 2
  2596. == 1
  2597. ):
  2598. acc_n = (
  2599. cute.size(split_k_acc_mn.shape[1]) - 1
  2600. )
  2601. coord = split_k_coord_mn[acc_m, acc_n]
  2602. m_coord = (
  2603. tile_coord_mnl[0]
  2604. * Int32(self.tile_shape_mnk[0])
  2605. + coord[0]
  2606. )
  2607. n_coord = (
  2608. tile_coord_mnl[1]
  2609. * Int32(self.tile_shape_mnk[1])
  2610. + coord[1]
  2611. )
  2612. if m_coord < Int32(
  2613. directC_mnl.shape[0]
  2614. ) and n_coord < Int32(directC_mnl.shape[1]):
  2615. c_offset = cute.crd2idx(
  2616. (
  2617. m_coord,
  2618. n_coord,
  2619. Int32(0),
  2620. ),
  2621. directC_mnl.layout,
  2622. )
  2623. scatter_add_bf16(
  2624. get_ptr_as_int64(
  2625. directC_mnl,
  2626. c_offset,
  2627. ),
  2628. alpha_value
  2629. * split_k_acc_mn[acc_m, acc_n],
  2630. )
  2631. else:
  2632. split_idx = Int32(block_idx[1])
  2633. for acc_m in cutlass.range_constexpr(
  2634. cute.size(split_k_acc_mn.shape[0])
  2635. ):
  2636. for acc_n in cutlass.range_constexpr(
  2637. cute.size(split_k_acc_mn.shape[1])
  2638. ):
  2639. coord = split_k_coord_mn[acc_m, acc_n]
  2640. m_coord = (
  2641. tile_coord_mnl[0]
  2642. * Int32(self.tile_shape_mnk[0])
  2643. + coord[0]
  2644. )
  2645. n_coord = (
  2646. tile_coord_mnl[1]
  2647. * Int32(self.tile_shape_mnk[1])
  2648. + coord[1]
  2649. )
  2650. if m_coord < Int32(
  2651. directC_mnl.shape[0]
  2652. ) and n_coord < Int32(directC_mnl.shape[1]):
  2653. directC_mnl[
  2654. (m_coord, n_coord, split_idx)
  2655. ] = (
  2656. alpha_value
  2657. * split_k_acc_mn[acc_m, acc_n]
  2658. )
  2659. else:
  2660. # Type conversion with alpha scaling
  2661. tRS_rD_out = cute.make_rmem_tensor(
  2662. tRS_rD_layout.shape, self.c_dtype
  2663. )
  2664. acc_vec = tRS_rD.load()
  2665. # Multiply alpha in FP32 before converting to c_dtype
  2666. # to avoid overflow when c_dtype is FP16
  2667. if cutlass.const_expr(self.alpha_is_one):
  2668. acc_vec = epilogue_op(acc_vec.to(self.c_dtype))
  2669. else:
  2670. acc_vec = epilogue_op(
  2671. (alpha_value * acc_vec).to(self.c_dtype)
  2672. )
  2673. if cutlass.const_expr(self.row_scale):
  2674. # Deliberately a SECOND rounding to c_dtype,
  2675. # applied to the already-rounded alpha
  2676. # result. This reproduces the eager
  2677. # ``result.mul_(inv_gs)`` it replaces, which
  2678. # rounds once when the GEMM writes C and
  2679. # again after the multiply. Folding the row
  2680. # scale into alpha, or multiplying the fp32
  2681. # accumulator, would each round only once
  2682. # and would not be bit-identical.
  2683. acc_vec = (
  2684. acc_vec.to(cutlass.Float32)
  2685. * tRS_rRowScale.load()
  2686. ).to(self.c_dtype)
  2687. tRS_rD_out.store(acc_vec)
  2688. # Register to shared memory
  2689. epi_buffer = (epi_m * epi_rest_n + epi_n) % cute.size(
  2690. tRS_sD, mode=[3]
  2691. )
  2692. if has_multi_epi_store:
  2693. self.epilog_sync_barrier.arrive_and_wait()
  2694. cute.copy(
  2695. tiled_copy_r2s,
  2696. tRS_rD_out,
  2697. tRS_sD[(None, None, None, epi_buffer)],
  2698. )
  2699. cute.arch.fence_proxy(
  2700. "async.shared",
  2701. space="cta",
  2702. )
  2703. self.epilog_sync_barrier.arrive_and_wait()
  2704. if cutlass.const_expr(self.quantize_c):
  2705. quant_chunks_per_epi = self.epi_tile[1] // 32
  2706. quant_active_warps = min(self.num_mma_warps, 4)
  2707. quant_subgroups = quant_active_warps * 8
  2708. quant_lane = Int32(tidx % 4)
  2709. quant_subgroup = Int32(tidx // 4)
  2710. quant_iters = (
  2711. 16 * quant_chunks_per_epi + quant_subgroups - 1
  2712. ) // quant_subgroups
  2713. if warp_idx < Int32(quant_active_warps):
  2714. for quant_iter in cutlass.range(
  2715. quant_iters,
  2716. unroll=1,
  2717. at_least_once=True,
  2718. ):
  2719. quant_task = quant_subgroup + Int32(
  2720. quant_iter * quant_subgroups
  2721. )
  2722. quant_row = quant_task // Int32(
  2723. quant_chunks_per_epi
  2724. )
  2725. quant_chunk = (
  2726. quant_task
  2727. - quant_row
  2728. * Int32(quant_chunks_per_epi)
  2729. )
  2730. quant_row_valid = quant_row < Int32(
  2731. quantC_values.shape[0]
  2732. )
  2733. quant_row_safe = quant_row
  2734. if quant_row_safe >= Int32(
  2735. quantC_values.shape[0]
  2736. ):
  2737. quant_row_safe = Int32(0)
  2738. if quant_row_safe < Int32(
  2739. quantC_values.shape[0]
  2740. ):
  2741. quant_elem0 = quant_chunk * Int32(
  2742. 32
  2743. ) + quant_lane * Int32(8)
  2744. quant_v0 = cutlass.Float32(
  2745. sC[
  2746. (
  2747. quant_row_safe,
  2748. quant_elem0,
  2749. epi_buffer,
  2750. )
  2751. ]
  2752. )
  2753. quant_v1 = cutlass.Float32(
  2754. sC[
  2755. (
  2756. quant_row_safe,
  2757. quant_elem0 + Int32(1),
  2758. epi_buffer,
  2759. )
  2760. ]
  2761. )
  2762. quant_v2 = cutlass.Float32(
  2763. sC[
  2764. (
  2765. quant_row_safe,
  2766. quant_elem0 + Int32(2),
  2767. epi_buffer,
  2768. )
  2769. ]
  2770. )
  2771. quant_v3 = cutlass.Float32(
  2772. sC[
  2773. (
  2774. quant_row_safe,
  2775. quant_elem0 + Int32(3),
  2776. epi_buffer,
  2777. )
  2778. ]
  2779. )
  2780. quant_v4 = cutlass.Float32(
  2781. sC[
  2782. (
  2783. quant_row_safe,
  2784. quant_elem0 + Int32(4),
  2785. epi_buffer,
  2786. )
  2787. ]
  2788. )
  2789. quant_v5 = cutlass.Float32(
  2790. sC[
  2791. (
  2792. quant_row_safe,
  2793. quant_elem0 + Int32(5),
  2794. epi_buffer,
  2795. )
  2796. ]
  2797. )
  2798. quant_v6 = cutlass.Float32(
  2799. sC[
  2800. (
  2801. quant_row_safe,
  2802. quant_elem0 + Int32(6),
  2803. epi_buffer,
  2804. )
  2805. ]
  2806. )
  2807. quant_v7 = cutlass.Float32(
  2808. sC[
  2809. (
  2810. quant_row_safe,
  2811. quant_elem0 + Int32(7),
  2812. epi_buffer,
  2813. )
  2814. ]
  2815. )
  2816. quant_max = fmax_f32(
  2817. fmax_f32(
  2818. fmax_f32(
  2819. fabs_f32(quant_v0),
  2820. fabs_f32(quant_v1),
  2821. ),
  2822. fmax_f32(
  2823. fabs_f32(quant_v2),
  2824. fabs_f32(quant_v3),
  2825. ),
  2826. ),
  2827. fmax_f32(
  2828. fmax_f32(
  2829. fabs_f32(quant_v4),
  2830. fabs_f32(quant_v5),
  2831. ),
  2832. fmax_f32(
  2833. fabs_f32(quant_v6),
  2834. fabs_f32(quant_v7),
  2835. ),
  2836. ),
  2837. )
  2838. for (
  2839. quant_shift
  2840. ) in cutlass.range_constexpr(2):
  2841. quant_max = fmax_f32(
  2842. quant_max,
  2843. cute.arch.shuffle_sync_bfly(
  2844. quant_max,
  2845. offset=1 << quant_shift,
  2846. ),
  2847. )
  2848. _, quant_scale_byte = pow2_ceil_ue8m0(
  2849. quant_max
  2850. * cutlass.Float32(
  2851. 1.0 / FLOAT8_E4M3_MAX
  2852. )
  2853. )
  2854. if quant_max == cutlass.Float32(0.0):
  2855. quant_scale_byte = cutlass.Uint32(
  2856. 127
  2857. )
  2858. quant_inv_scale = ue8m0_to_output_scale(
  2859. quant_scale_byte
  2860. )
  2861. quant_payload_lo = cvt_f32x4_to_e4m3x4(
  2862. quant_v0 * quant_inv_scale,
  2863. quant_v1 * quant_inv_scale,
  2864. quant_v2 * quant_inv_scale,
  2865. quant_v3 * quant_inv_scale,
  2866. )
  2867. quant_payload_hi = cvt_f32x4_to_e4m3x4(
  2868. quant_v4 * quant_inv_scale,
  2869. quant_v5 * quant_inv_scale,
  2870. quant_v6 * quant_inv_scale,
  2871. quant_v7 * quant_inv_scale,
  2872. )
  2873. quant_payload = (
  2874. Uint64(quant_payload_hi)
  2875. << Uint64(32)
  2876. ) | Uint64(quant_payload_lo)
  2877. quant_global_chunk = (
  2878. tile_coord_mnl[2]
  2879. * Int32(directC_mnl.shape[1] // 32)
  2880. + tile_coord_mnl[1]
  2881. * Int32(
  2882. self.tile_shape_mnk[1] // 32
  2883. )
  2884. + Int32(
  2885. epi_n * quant_chunks_per_epi
  2886. )
  2887. + quant_chunk
  2888. )
  2889. quant_global_col = (
  2890. quant_global_chunk * Int32(32)
  2891. + quant_lane * Int32(8)
  2892. )
  2893. if quant_row_valid:
  2894. st_global_u64(
  2895. get_ptr_as_int64(
  2896. quantC_values,
  2897. quant_row
  2898. * Int32(
  2899. quantC_values.shape[1]
  2900. )
  2901. + quant_global_col,
  2902. ),
  2903. quant_payload,
  2904. )
  2905. if quant_lane == Int32(0):
  2906. quant_scale = cutlass.Uint8(
  2907. quant_scale_byte
  2908. ).bitcast(cutlass.Float8E8M0FNU)
  2909. quantC_scale_rows[
  2910. (
  2911. quant_row,
  2912. quant_global_chunk,
  2913. )
  2914. ] = quant_scale
  2915. quantC_scale_mma[
  2916. (
  2917. quant_row,
  2918. Int32(0),
  2919. Int32(0),
  2920. quant_global_chunk
  2921. % Int32(4),
  2922. quant_global_chunk
  2923. // Int32(4),
  2924. Int32(0),
  2925. )
  2926. ] = quant_scale
  2927. # Only synchronize before a persistent CTA
  2928. # reuses sC for another work tile. Kernel
  2929. # completion orders terminal-tile stores.
  2930. if cutlass.const_expr(
  2931. self.single_work_tile_per_cta
  2932. ):
  2933. work_tile = WorkTileInfo(
  2934. work_tile.tile_idx,
  2935. cutlass.Boolean(0),
  2936. )
  2937. else:
  2938. tile_sched.advance_to_next_work()
  2939. work_tile = tile_sched.get_current_work()
  2940. if work_tile.is_valid_tile:
  2941. self.epilog_sync_barrier.arrive_and_wait()
  2942. # Copy from shared memory to global memory
  2943. if cutlass.const_expr(
  2944. self.use_m1_non_tma_c and not self.quantize_c
  2945. ):
  2946. for n_iter in cutlass.range_constexpr(
  2947. (
  2948. self.epi_tile[1]
  2949. + self.num_mma_warps
  2950. * self.num_threads_per_warp
  2951. - 1
  2952. )
  2953. // (
  2954. self.num_mma_warps
  2955. * self.num_threads_per_warp
  2956. )
  2957. ):
  2958. n_local = Int32(tidx) + Int32(
  2959. n_iter
  2960. * self.num_mma_warps
  2961. * self.num_threads_per_warp
  2962. )
  2963. n_coord = (
  2964. tile_coord_mnl[1]
  2965. * Int32(self.tile_shape_mnk[1])
  2966. + Int32(epi_n * self.epi_tile[1])
  2967. + n_local
  2968. )
  2969. if n_local < Int32(
  2970. self.epi_tile[1]
  2971. ) and n_coord < Int32(directC_mnl.shape[1]):
  2972. directC_mnl[
  2973. (
  2974. Int32(0),
  2975. n_coord,
  2976. tile_coord_mnl[2],
  2977. )
  2978. ] = sC[(Int32(0), n_local, epi_buffer)]
  2979. elif cutlass.const_expr(not self.quantize_c):
  2980. if warp_idx == 0:
  2981. cute.copy(
  2982. tma_atom_c,
  2983. bSG_sD[(None, epi_buffer)],
  2984. bSG_gD[(None, gmem_coord)],
  2985. )
  2986. if has_multi_epi_store:
  2987. tma_store_pipeline.producer_commit()
  2988. tma_store_pipeline.producer_acquire()
  2989. # Advance to the next work tile
  2990. if cutlass.const_expr(not self.quantize_c):
  2991. if cutlass.const_expr(self.single_work_tile_per_cta):
  2992. work_tile = WorkTileInfo(
  2993. work_tile.tile_idx,
  2994. cutlass.Boolean(0),
  2995. )
  2996. else:
  2997. tile_sched.advance_to_next_work()
  2998. work_tile = tile_sched.get_current_work()
  2999. if has_multi_epi_store and cutlass.const_expr(
  3000. self.split_k_slices == 1
  3001. ):
  3002. tma_store_pipeline.producer_tail()
  3003. elif warp_idx == self.tma_load_warp_id:
  3004. cute.arch.setmaxregister_decrease(self.load_register_requirement)
  3005. while work_tile.is_valid_tile:
  3006. tile_coord_mnl = work_tile.tile_idx
  3007. if cutlass.const_expr(
  3008. self.load_path == "tma"
  3009. and not self.use_m1_non_tma_a
  3010. and not self.fused_quant_a
  3011. and not self.direct_m1_wo_a_inputs
  3012. ):
  3013. tAgA_mkl = tAgA[(None, tile_coord_mnl[0], None, tile_coord_mnl[2])]
  3014. if cutlass.const_expr(self.load_path == "tma"):
  3015. tBgB_nkl = tBgB[(None, tile_coord_mnl[1], None, tile_coord_mnl[2])]
  3016. if cutlass.const_expr(
  3017. self.load_path == "tma"
  3018. and not self.block_fp8
  3019. and not self.use_m1_non_tma_sfa
  3020. and not self.fused_quant_a
  3021. and not self.manual_bk64_sf
  3022. and not self.direct_sfa_prefix
  3023. ):
  3024. sfa_tile_coord_m = tile_coord_mnl[0] // self.sfa_tiles_per_block
  3025. tAgSFA_mkl = tAgSFA[
  3026. (None, sfa_tile_coord_m, None, tile_coord_mnl[2])
  3027. ]
  3028. if cutlass.const_expr(
  3029. self.load_path == "tma"
  3030. and not self.block_fp8
  3031. and not self.manual_bk64_sf
  3032. and not self.direct_sfb_representative
  3033. ):
  3034. sfb_tile_coord_n = tile_coord_mnl[1] // self.sfb_tiles_per_block
  3035. tBgSFB_nkl = tBgSFB[
  3036. (None, sfb_tile_coord_n, None, tile_coord_mnl[2])
  3037. ]
  3038. if cutlass.const_expr(self.load_path == "cpasync"):
  3039. cpasync_sfa_tile_coord_m = (
  3040. tile_coord_mnl[0] // self.sfa_tiles_per_block
  3041. )
  3042. cpasync_sfb_tile_coord_n = (
  3043. tile_coord_mnl[1] // self.sfb_tiles_per_block
  3044. )
  3045. mainloop_producer_state.reset_count()
  3046. if cutlass.const_expr(self.a_bf16_fused):
  3047. fq_lane = Int32(tidx % self.num_threads_per_warp)
  3048. full_k = Int32(directX_bf16.shape[1])
  3049. nvec = full_k // Int32(8)
  3050. bf16_base = get_ptr_as_int64(directX_bf16, Int32(0))
  3051. local_amax = cutlass.Float32(0.0)
  3052. i_vec = fq_lane
  3053. while i_vec < nvec:
  3054. w0, w1, w2, w3 = ld_global_v4_u32(
  3055. bf16_base + cutlass.Int64(i_vec) * cutlass.Int64(16)
  3056. )
  3057. for w in (w0, w1, w2, w3):
  3058. hi = u32_as_f32(w & Uint32(0x7FFF0000))
  3059. lo = u32_as_f32(
  3060. (w << Uint32(16)) & Uint32(0x7FFF0000)
  3061. )
  3062. local_amax = fmax_f32(
  3063. local_amax, fmax_f32(hi, lo)
  3064. )
  3065. i_vec += Int32(self.num_threads_per_warp)
  3066. fused_amax = warp_reduce(local_amax, fmax_f32)
  3067. fused_amax_c = fmax_f32(
  3068. fused_amax, cutlass.Float32(1e-6)
  3069. )
  3070. fused_gs = (
  3071. cutlass.Float32(self._fused_gs_num) / fused_amax_c
  3072. )
  3073. _fq_k_base = Int32(0)
  3074. _fq_sa_stage = Int32(0)
  3075. _fq_packed_scales = Uint32(0)
  3076. _fq_k_abs = Int32(0)
  3077. _fq_val = cutlass.Float32(0.0)
  3078. _fq_bmax = cutlass.Float32(0.0)
  3079. _fq_su32 = Uint32(0)
  3080. _fq_inv = cutlass.Float32(0.0)
  3081. _fq_scaled = cutlass.Float32(0.0)
  3082. _fq_pair = Uint32(0)
  3083. _fq_code = Uint8(0)
  3084. _fq_ssfa_stage = Int32(0)
  3085. _fq_lin = Int32(0)
  3086. _fq_m = Int32(0)
  3087. _fq_sg_idx = Int32(0)
  3088. _fq_sf_off = Int32(0)
  3089. _fq_sb = Uint8(0)
  3090. _fq_sfa_sg = self.tile_shape_mnk[2] // self.sf_vec_size
  3091. _fq_sfa_slots = self.sfa_tile_shape_mk[0] * _fq_sfa_sg
  3092. _fq_sg = 0
  3093. _fq_si = 0
  3094. for k_tile in range(0, k_tile_iter_cnt, 1, unroll=2):
  3095. mainloop_pipeline.producer_acquire(mainloop_producer_state)
  3096. k_tile_global = k_tile_start + mainloop_producer_state.count
  3097. if cutlass.const_expr(self.load_path == "tma"):
  3098. tBgB_k = tBgB_nkl[(None, k_tile_global)]
  3099. tBsB_pipe = tBsB[(None, mainloop_producer_state.index)]
  3100. if cutlass.const_expr(
  3101. not self.use_m1_non_tma_a
  3102. and not self.fused_quant_a
  3103. and not self.direct_m1_wo_a_inputs
  3104. ):
  3105. tAgA_k = tAgA_mkl[(None, k_tile_global)]
  3106. tAsA_pipe = tAsA[(None, mainloop_producer_state.index)]
  3107. if cutlass.const_expr(
  3108. not self.block_fp8
  3109. and not self.use_m1_non_tma_sfa
  3110. and not self.fused_quant_a
  3111. and not self.manual_bk64_sf
  3112. and not self.direct_sfa_prefix
  3113. ):
  3114. tAgSFA_k = tAgSFA_mkl[(None, k_tile_global)]
  3115. tAsSFA_pipe = tAsSFA[(None, mainloop_producer_state.index)]
  3116. if cutlass.const_expr(
  3117. not self.block_fp8
  3118. and not self.manual_bk64_sf
  3119. and not self.direct_sfb_representative
  3120. ):
  3121. tBgSFB_k = tBgSFB_nkl[(None, k_tile_global)]
  3122. tBsSFB_pipe = tBsSFB[(None, mainloop_producer_state.index)]
  3123. if cutlass.const_expr(self.fused_quant_a and self.b_tile_major):
  3124. # Start the large weight transfer before synchronous
  3125. # A quantization. SFB stays below as a post-fence
  3126. # doorbell because TMA producer_commit is a no-op.
  3127. cute.copy(
  3128. tma_atom_b,
  3129. tBgB_k,
  3130. tBsB_pipe,
  3131. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3132. mainloop_producer_state
  3133. ),
  3134. cache_policy=Int64(0x12F0000000000000),
  3135. )
  3136. if cutlass.const_expr(self.load_path == "cpasync"):
  3137. tAgA_cpasync_k = tAgA_cpasync_mkl[
  3138. (
  3139. None,
  3140. None,
  3141. None,
  3142. tile_coord_mnl[0],
  3143. k_tile_global,
  3144. tile_coord_mnl[2],
  3145. )
  3146. ]
  3147. tAsA_cpasync_pipe = tAsA_cpasync[
  3148. (None, None, None, mainloop_producer_state.index)
  3149. ]
  3150. tAcA_cpasync_k = cute.slice_(
  3151. tAcA_cpasync_mkl,
  3152. (
  3153. None,
  3154. None,
  3155. None,
  3156. tile_coord_mnl[0],
  3157. k_tile_global,
  3158. tile_coord_mnl[2],
  3159. ),
  3160. )
  3161. tBgB_cpasync_k = tBgB_cpasync_nkl[
  3162. (
  3163. None,
  3164. None,
  3165. None,
  3166. tile_coord_mnl[1],
  3167. k_tile_global,
  3168. tile_coord_mnl[2],
  3169. )
  3170. ]
  3171. tBsB_cpasync_pipe = tBsB_cpasync[
  3172. (None, None, None, mainloop_producer_state.index)
  3173. ]
  3174. tBcB_cpasync_k = cute.slice_(
  3175. tBcB_cpasync_nkl,
  3176. (
  3177. None,
  3178. None,
  3179. None,
  3180. tile_coord_mnl[1],
  3181. k_tile_global,
  3182. tile_coord_mnl[2],
  3183. ),
  3184. )
  3185. tAgSFA_cpasync_k = cute.filter_zeros(
  3186. tAgSFA_cpasync_mkl[
  3187. (
  3188. None,
  3189. None,
  3190. None,
  3191. cpasync_sfa_tile_coord_m,
  3192. k_tile_global,
  3193. tile_coord_mnl[2],
  3194. )
  3195. ]
  3196. )
  3197. tAsSFA_cpasync_pipe = cute.filter_zeros(
  3198. tAsSFA_cpasync[
  3199. (None, None, None, mainloop_producer_state.index)
  3200. ]
  3201. )
  3202. tAcSFA_cpasync_k = cute.filter_zeros(
  3203. cute.slice_(
  3204. tAcSFA_cpasync_mkl,
  3205. (
  3206. None,
  3207. None,
  3208. None,
  3209. cpasync_sfa_tile_coord_m,
  3210. k_tile_global,
  3211. tile_coord_mnl[2],
  3212. ),
  3213. )
  3214. )
  3215. tBgSFB_cpasync_k = cute.filter_zeros(
  3216. tBgSFB_cpasync_nkl[
  3217. (
  3218. None,
  3219. None,
  3220. None,
  3221. cpasync_sfb_tile_coord_n,
  3222. k_tile_global,
  3223. tile_coord_mnl[2],
  3224. )
  3225. ]
  3226. )
  3227. tBsSFB_cpasync_pipe = cute.filter_zeros(
  3228. tBsSFB_cpasync[
  3229. (None, None, None, mainloop_producer_state.index)
  3230. ]
  3231. )
  3232. tBcSFB_cpasync_k = cute.filter_zeros(
  3233. cute.slice_(
  3234. tBcSFB_cpasync_nkl,
  3235. (
  3236. None,
  3237. None,
  3238. None,
  3239. cpasync_sfb_tile_coord_n,
  3240. k_tile_global,
  3241. tile_coord_mnl[2],
  3242. ),
  3243. )
  3244. )
  3245. self._cpasync_copy_2d(
  3246. cpasync_tiled_copy_A,
  3247. tAgA_cpasync_k,
  3248. tAsA_cpasync_pipe,
  3249. tAcA_cpasync_k,
  3250. Int32(directA_mkl.shape[0]),
  3251. True,
  3252. )
  3253. self._cpasync_copy_2d(
  3254. cpasync_tiled_copy_B,
  3255. tBgB_cpasync_k,
  3256. tBsB_cpasync_pipe,
  3257. tBcB_cpasync_k,
  3258. Int32(directC_mnl.shape[1]),
  3259. True,
  3260. )
  3261. self._scale_copy_2d(
  3262. cpasync_tiled_copy_SF,
  3263. tAgSFA_cpasync_k,
  3264. tAsSFA_cpasync_pipe,
  3265. tAcSFA_cpasync_k,
  3266. Int32(directA_mkl.shape[0]),
  3267. )
  3268. self._scale_copy_2d(
  3269. cpasync_tiled_copy_SF,
  3270. tBgSFB_cpasync_k,
  3271. tBsSFB_cpasync_pipe,
  3272. tBcSFB_cpasync_k,
  3273. Int32(directC_mnl.shape[1]),
  3274. )
  3275. cute.arch.fence_proxy("async.shared", space="cta")
  3276. elif cutlass.const_expr(
  3277. self.fused_quant_a and self.fused_quant_a_wide
  3278. ):
  3279. # M=1 wide layout: 4 lanes cooperate on each 32-value
  3280. # scale block (16B load per lane, butterfly max), so a
  3281. # k-tile quantizes with 16 lanes instead of 4 and stops
  3282. # throttling deep-K producer pipelines. Lanes 16..31
  3283. # mirror blocks 0..3 (clamped index, stores predicated
  3284. # off) so the warp stays converged at the shuffles.
  3285. lane = Int32(tidx % self.num_threads_per_warp)
  3286. scale_group_raw = lane // Int32(4)
  3287. scale_group = scale_group_raw % Int32(4)
  3288. lane4 = lane % Int32(4)
  3289. row_global = tile_coord_mnl[0] * Int32(self.tile_shape_mnk[0])
  3290. # Uniform across the warp: at M=1 there is a single
  3291. # m tile, so this only guards the degenerate case.
  3292. if row_global < Int32(quantA_mkl.shape[0]):
  3293. values = cute.make_rmem_tensor((8,), cutlass.Float32)
  3294. k_local0 = scale_group * Int32(32)
  3295. k_global0 = (
  3296. k_tile_global * Int32(self.tile_shape_mnk[2]) + k_local0
  3297. )
  3298. if cutlass.const_expr(self.fused_quant_a_inner_span > 0):
  3299. span = Int32(self.fused_quant_a_inner_span)
  3300. outer = k_global0 // span
  3301. linear_offset = (
  3302. outer * (Int32(quantA_mkl.shape[0]) * span)
  3303. + row_global * span
  3304. + (k_global0 - outer * span)
  3305. )
  3306. else:
  3307. if cutlass.const_expr(
  3308. self.fused_quant_a_row_stride > 0
  3309. ):
  3310. linear_offset = (
  3311. row_global
  3312. * Int32(self.fused_quant_a_row_stride)
  3313. + k_global0
  3314. )
  3315. else:
  3316. linear_offset = (
  3317. row_global * Int32(quantA_mkl.shape[1])
  3318. + k_global0
  3319. )
  3320. if cutlass.const_expr(self.fused_quant_a_l_stride > 0):
  3321. linear_offset = linear_offset + tile_coord_mnl[
  3322. 2
  3323. ] * Int32(self.fused_quant_a_l_stride)
  3324. elem0 = lane4 * Int32(8)
  3325. source_base = get_ptr_as_int64(
  3326. quantA_mkl, linear_offset + elem0
  3327. )
  3328. max_abs = cutlass.Float32(0.0)
  3329. w0, w1, w2, w3 = ld_global_v4_u32(source_base)
  3330. v0, v1 = bfloat2_to_float2_scaled(w0, cutlass.Float32(1.0))
  3331. v2, v3 = bfloat2_to_float2_scaled(w1, cutlass.Float32(1.0))
  3332. v4, v5 = bfloat2_to_float2_scaled(w2, cutlass.Float32(1.0))
  3333. v6, v7 = bfloat2_to_float2_scaled(w3, cutlass.Float32(1.0))
  3334. values[0] = v0
  3335. values[1] = v1
  3336. values[2] = v2
  3337. values[3] = v3
  3338. values[4] = v4
  3339. values[5] = v5
  3340. values[6] = v6
  3341. values[7] = v7
  3342. if cutlass.const_expr(self.fused_quant_a_inv_rope):
  3343. head_d0 = k_global0 % Int32(self.fused_quant_a_head_dim)
  3344. if head_d0 >= Int32(self.fused_quant_a_nope_dim):
  3345. pos = Int32(quantA_positions[row_global])
  3346. half_rope = Int32(self.fused_quant_a_rope_dim // 2)
  3347. cs_base = pos * Int32(self.fused_quant_a_rope_dim)
  3348. rl_half0 = (
  3349. head_d0
  3350. - Int32(self.fused_quant_a_nope_dim)
  3351. + elem0
  3352. ) // Int32(2)
  3353. for pair in cutlass.range_constexpr(4):
  3354. cs_idx = cs_base + rl_half0 + Int32(pair)
  3355. cos_v = cutlass.Float32(quantA_cos_sin[cs_idx])
  3356. sin_v = cutlass.Float32(
  3357. quantA_cos_sin[cs_idx + half_rope]
  3358. )
  3359. v_even = values[pair * 2]
  3360. v_odd = values[pair * 2 + 1]
  3361. values[pair * 2] = (
  3362. v_even * cos_v + v_odd * sin_v
  3363. )
  3364. values[pair * 2 + 1] = (
  3365. v_odd * cos_v - v_even * sin_v
  3366. )
  3367. for elem in cutlass.range_constexpr(8):
  3368. max_abs = fmax_f32(max_abs, fabs_f32(values[elem]))
  3369. for shift in cutlass.range_constexpr(2):
  3370. max_abs = fmax_f32(
  3371. max_abs,
  3372. cute.arch.shuffle_sync_bfly(
  3373. max_abs, offset=1 << shift
  3374. ),
  3375. )
  3376. _, scale_byte = pow2_ceil_ue8m0(
  3377. max_abs * cutlass.Float32(1.0 / FLOAT8_E4M3_MAX)
  3378. )
  3379. if max_abs == cutlass.Float32(0.0):
  3380. scale_byte = cutlass.Uint32(127)
  3381. inv_scale = ue8m0_to_output_scale(scale_byte)
  3382. payload0 = cvt_f32x4_to_e4m3x4(
  3383. values[0] * inv_scale,
  3384. values[1] * inv_scale,
  3385. values[2] * inv_scale,
  3386. values[3] * inv_scale,
  3387. )
  3388. payload1 = cvt_f32x4_to_e4m3x4(
  3389. values[4] * inv_scale,
  3390. values[5] * inv_scale,
  3391. values[6] * inv_scale,
  3392. values[7] * inv_scale,
  3393. )
  3394. if scale_group_raw < Int32(4):
  3395. for byte in cutlass.range_constexpr(4):
  3396. raw0 = cutlass.Uint8(
  3397. payload0 >> cutlass.Uint32(byte * 8)
  3398. )
  3399. raw1 = cutlass.Uint8(
  3400. payload1 >> cutlass.Uint32(byte * 8)
  3401. )
  3402. sA[
  3403. (
  3404. Int32(0),
  3405. k_local0 + elem0 + Int32(byte),
  3406. mainloop_producer_state.index,
  3407. )
  3408. ] = raw0.bitcast(cutlass.Float8E4M3FN)
  3409. sA[
  3410. (
  3411. Int32(0),
  3412. k_local0 + elem0 + Int32(4 + byte),
  3413. mainloop_producer_state.index,
  3414. )
  3415. ] = raw1.bitcast(cutlass.Float8E4M3FN)
  3416. if lane4 == Int32(0):
  3417. sSFA[
  3418. (
  3419. Int32(0),
  3420. k_local0,
  3421. mainloop_producer_state.index,
  3422. )
  3423. ] = cutlass.Uint8(scale_byte).bitcast(
  3424. cutlass.Float8E8M0FNU
  3425. )
  3426. cute.arch.fence_proxy("async.shared", space="cta")
  3427. elif cutlass.const_expr(self.fused_quant_a):
  3428. lane = Int32(tidx % self.num_threads_per_warp)
  3429. row_local = lane // Int32(4)
  3430. scale_group = lane % Int32(4)
  3431. row_global = (
  3432. tile_coord_mnl[0] * Int32(self.tile_shape_mnk[0])
  3433. + row_local
  3434. )
  3435. if row_global < Int32(quantA_mkl.shape[0]):
  3436. values = cute.make_rmem_tensor((32,), cutlass.Float32)
  3437. k_local0 = scale_group * Int32(32)
  3438. k_global0 = (
  3439. k_tile_global * Int32(self.tile_shape_mnk[2]) + k_local0
  3440. )
  3441. if cutlass.const_expr(self.fused_quant_a_inner_span > 0):
  3442. # L-blocked source: each 32-value scale block
  3443. # lies within one span (span % 32 == 0).
  3444. span = Int32(self.fused_quant_a_inner_span)
  3445. outer = k_global0 // span
  3446. linear_offset = (
  3447. outer * (Int32(quantA_mkl.shape[0]) * span)
  3448. + row_global * span
  3449. + (k_global0 - outer * span)
  3450. )
  3451. else:
  3452. if cutlass.const_expr(
  3453. self.fused_quant_a_row_stride > 0
  3454. ):
  3455. linear_offset = (
  3456. row_global
  3457. * Int32(self.fused_quant_a_row_stride)
  3458. + k_global0
  3459. )
  3460. else:
  3461. linear_offset = (
  3462. row_global * Int32(quantA_mkl.shape[1])
  3463. + k_global0
  3464. )
  3465. if cutlass.const_expr(self.fused_quant_a_l_stride > 0):
  3466. linear_offset = linear_offset + tile_coord_mnl[
  3467. 2
  3468. ] * Int32(self.fused_quant_a_l_stride)
  3469. source_base = get_ptr_as_int64(quantA_mkl, linear_offset)
  3470. max_abs = cutlass.Float32(0.0)
  3471. for vec in cutlass.range_constexpr(4):
  3472. w0, w1, w2, w3 = ld_global_v4_u32(
  3473. source_base + Int64(vec * 16)
  3474. )
  3475. v0, v1 = bfloat2_to_float2_scaled(
  3476. w0, cutlass.Float32(1.0)
  3477. )
  3478. v2, v3 = bfloat2_to_float2_scaled(
  3479. w1, cutlass.Float32(1.0)
  3480. )
  3481. v4, v5 = bfloat2_to_float2_scaled(
  3482. w2, cutlass.Float32(1.0)
  3483. )
  3484. v6, v7 = bfloat2_to_float2_scaled(
  3485. w3, cutlass.Float32(1.0)
  3486. )
  3487. values[vec * 8 + 0] = v0
  3488. values[vec * 8 + 1] = v1
  3489. values[vec * 8 + 2] = v2
  3490. values[vec * 8 + 3] = v3
  3491. values[vec * 8 + 4] = v4
  3492. values[vec * 8 + 5] = v5
  3493. values[vec * 8 + 6] = v6
  3494. values[vec * 8 + 7] = v7
  3495. max_abs = fmax_f32(max_abs, fabs_f32(v0))
  3496. max_abs = fmax_f32(max_abs, fabs_f32(v1))
  3497. max_abs = fmax_f32(max_abs, fabs_f32(v2))
  3498. max_abs = fmax_f32(max_abs, fabs_f32(v3))
  3499. max_abs = fmax_f32(max_abs, fabs_f32(v4))
  3500. max_abs = fmax_f32(max_abs, fabs_f32(v5))
  3501. max_abs = fmax_f32(max_abs, fabs_f32(v6))
  3502. max_abs = fmax_f32(max_abs, fabs_f32(v7))
  3503. if cutlass.const_expr(self.fused_quant_a_inv_rope):
  3504. # nope_dim % 32 == 0, so a scale block is
  3505. # entirely nope (left as loaded) or entirely
  3506. # rope: de-rotate adjacent pairs with cos/sin
  3507. # at positions[row] and recompute the block
  3508. # max over the rotated values.
  3509. head_d0 = k_global0 % Int32(self.fused_quant_a_head_dim)
  3510. if head_d0 >= Int32(self.fused_quant_a_nope_dim):
  3511. pos = Int32(quantA_positions[row_global])
  3512. half_rope = Int32(self.fused_quant_a_rope_dim // 2)
  3513. cs_base = pos * Int32(self.fused_quant_a_rope_dim)
  3514. rl_half0 = (
  3515. head_d0 - Int32(self.fused_quant_a_nope_dim)
  3516. ) // Int32(2)
  3517. max_abs = cutlass.Float32(0.0)
  3518. for pair in cutlass.range_constexpr(16):
  3519. cs_idx = cs_base + rl_half0 + Int32(pair)
  3520. cos_v = cutlass.Float32(quantA_cos_sin[cs_idx])
  3521. sin_v = cutlass.Float32(
  3522. quantA_cos_sin[cs_idx + half_rope]
  3523. )
  3524. v_even = values[pair * 2]
  3525. v_odd = values[pair * 2 + 1]
  3526. values[pair * 2] = (
  3527. v_even * cos_v + v_odd * sin_v
  3528. )
  3529. values[pair * 2 + 1] = (
  3530. v_odd * cos_v - v_even * sin_v
  3531. )
  3532. max_abs = fmax_f32(
  3533. max_abs,
  3534. fabs_f32(values[pair * 2]),
  3535. )
  3536. max_abs = fmax_f32(
  3537. max_abs,
  3538. fabs_f32(values[pair * 2 + 1]),
  3539. )
  3540. payload, scale_byte = quantize_block_fp8_mx(values, max_abs)
  3541. if max_abs == cutlass.Float32(0.0):
  3542. scale_byte = cutlass.Uint32(127)
  3543. for word in cutlass.range_constexpr(8):
  3544. for byte in cutlass.range_constexpr(4):
  3545. raw_byte = cutlass.Uint8(
  3546. payload[word] >> cutlass.Uint32(byte * 8)
  3547. )
  3548. sA[
  3549. (
  3550. row_local,
  3551. k_local0 + Int32(word * 4 + byte),
  3552. mainloop_producer_state.index,
  3553. )
  3554. ] = raw_byte.bitcast(cutlass.Float8E4M3FN)
  3555. sSFA[
  3556. (
  3557. row_local,
  3558. k_local0,
  3559. mainloop_producer_state.index,
  3560. )
  3561. ] = cutlass.Uint8(scale_byte).bitcast(cutlass.Float8E8M0FNU)
  3562. cute.arch.fence_proxy("async.shared", space="cta")
  3563. elif cutlass.const_expr(self.a_bf16_fused):
  3564. # MX-FP6 fused activation quantization (m=1 decode):
  3565. # quantize this K-tile's 32-element blocks from the
  3566. # BF16 row straight into sA/sSFA using the row-wide
  3567. # gs derived in the work-tile prologue above.
  3568. _fq_k_base = (
  3569. k_tile_global
  3570. * Int32(self.tile_shape_mnk[2])
  3571. )
  3572. _fq_sa_stage = (
  3573. mainloop_producer_state.index
  3574. * Int32(
  3575. self.tile_shape_mnk[0]
  3576. * self.tile_shape_mnk[2]
  3577. )
  3578. )
  3579. _fq_packed_scales = Uint32(0)
  3580. for _fq_sg in cutlass.range_constexpr(
  3581. self.tile_shape_mnk[2] // self.sf_vec_size
  3582. ):
  3583. _fq_k_abs = (
  3584. _fq_k_base
  3585. + Int32(_fq_sg * self.sf_vec_size)
  3586. + fq_lane
  3587. )
  3588. _fq_val = cutlass.Float32(
  3589. directX_bf16[(Int32(0), _fq_k_abs)]
  3590. )
  3591. _fq_bmax = warp_reduce(
  3592. fabs_f32(_fq_val), fmax_f32
  3593. )
  3594. _fq_su32 = fp6_block_ue8m0_exact(
  3595. _fq_bmax,
  3596. fused_gs,
  3597. cutlass.Float32(self._fused_fmt_max),
  3598. )
  3599. _fq_inv = ue8m0_output_scale_exact(
  3600. _fq_su32, fused_gs
  3601. )
  3602. _fq_scaled = _fq_val * _fq_inv
  3603. if cutlass.const_expr(
  3604. self._fused_act_fmt == "e4m3"
  3605. ):
  3606. _fq_pair = cvt_f32_to_e4m3x2(
  3607. cutlass.Float32(0.0), _fq_scaled
  3608. )
  3609. elif cutlass.const_expr(
  3610. self._fused_act_fmt == "e3m2"
  3611. ):
  3612. _fq_pair = cvt_f32_to_e3m2x2(
  3613. cutlass.Float32(0.0), _fq_scaled
  3614. )
  3615. else:
  3616. _fq_pair = cvt_f32_to_e2m3x2(
  3617. cutlass.Float32(0.0), _fq_scaled
  3618. )
  3619. _fq_code = Uint8(_fq_pair & Uint32(0xFF))
  3620. # sA row-0 store (SW128 XOR is zero for row 0)
  3621. st_shared_u8(
  3622. sa_base_addr
  3623. + _fq_sa_stage
  3624. + Int32(_fq_sg * self.sf_vec_size)
  3625. + fq_lane,
  3626. _fq_code,
  3627. )
  3628. _fq_packed_scales = _fq_packed_scales | (
  3629. (_fq_su32 & Uint32(0xFF))
  3630. << Uint32(_fq_sg * 8)
  3631. )
  3632. # Broadcast scale bytes to all 128 SFA M-rows.
  3633. # SFA atom layout (Sw<3,4,3> UE8M0 block): flat offset
  3634. # for (m_row, sg) = (m%32)*16 + (m//32)*4 + sg.
  3635. _fq_sfa_sg = self.tile_shape_mnk[2] // self.sf_vec_size
  3636. _fq_sfa_slots = self.sfa_tile_shape_mk[0] * _fq_sfa_sg
  3637. _fq_ssfa_stage = (
  3638. mainloop_producer_state.index
  3639. * Int32(
  3640. (self.sfa_tile_shape_mk[0] // 128) * 128
  3641. * _fq_sfa_sg
  3642. )
  3643. )
  3644. for _fq_si in cutlass.range_constexpr(
  3645. (_fq_sfa_slots + self.num_threads_per_warp - 1)
  3646. // self.num_threads_per_warp
  3647. ):
  3648. _fq_lin = fq_lane + Int32(
  3649. _fq_si * self.num_threads_per_warp
  3650. )
  3651. if _fq_lin < Int32(_fq_sfa_slots):
  3652. _fq_m = _fq_lin // Int32(_fq_sfa_sg)
  3653. _fq_sg_idx = _fq_lin - _fq_m * Int32(
  3654. _fq_sfa_sg
  3655. )
  3656. _fq_sf_off = (
  3657. (_fq_m & Int32(31)) * Int32(16)
  3658. + (_fq_m >> Int32(5)) * Int32(4)
  3659. + _fq_sg_idx
  3660. )
  3661. _fq_sb = Uint8(
  3662. (_fq_packed_scales
  3663. >> (Uint32(_fq_sg_idx) * Uint32(8)))
  3664. & Uint32(0xFF)
  3665. )
  3666. st_shared_u8(
  3667. ssfa_base_addr
  3668. + _fq_ssfa_stage
  3669. + _fq_sf_off,
  3670. _fq_sb,
  3671. )
  3672. cute.arch.fence_proxy("async.shared", space="cta")
  3673. elif cutlass.const_expr(self.use_m1_non_tma_a):
  3674. lane = Int32(tidx % self.num_threads_per_warp)
  3675. for a_iter in cutlass.range_constexpr(
  3676. (self.tile_shape_mnk[2] + self.num_threads_per_warp - 1)
  3677. // self.num_threads_per_warp
  3678. ):
  3679. k_local = lane + Int32(a_iter * self.num_threads_per_warp)
  3680. if k_local < Int32(self.tile_shape_mnk[2]):
  3681. k_coord = (
  3682. k_tile_global * Int32(self.tile_shape_mnk[2])
  3683. + k_local
  3684. )
  3685. sA[
  3686. (
  3687. Int32(0),
  3688. k_local,
  3689. mainloop_producer_state.index,
  3690. )
  3691. ] = directA_mkl[
  3692. (
  3693. Int32(0),
  3694. k_coord,
  3695. tile_coord_mnl[2],
  3696. )
  3697. ]
  3698. elif cutlass.const_expr(self.direct_m1_wo_a_inputs):
  3699. lane = Int32(tidx % self.num_threads_per_warp)
  3700. if lane == Int32(0):
  3701. a_offset = tile_coord_mnl[2] * Int32(
  3702. directA_mkl.shape[0]
  3703. ) * Int32(directA_mkl.shape[1]) + k_tile_global * Int32(
  3704. self.tile_shape_mnk[2]
  3705. )
  3706. cp_async_bulk_g2s_mbar(
  3707. shared_ptr_to_u32(
  3708. elem_pointer(
  3709. sA,
  3710. (
  3711. Int32(0),
  3712. Int32(0),
  3713. mainloop_producer_state.index,
  3714. ),
  3715. )
  3716. ),
  3717. get_ptr_as_int64(directA_mkl, a_offset),
  3718. Int32(128),
  3719. shared_ptr_to_u32(
  3720. mainloop_pipeline.producer_get_barrier(
  3721. mainloop_producer_state
  3722. )
  3723. ),
  3724. )
  3725. else:
  3726. cute.copy(
  3727. tma_atom_a,
  3728. tAgA_k,
  3729. tAsA_pipe,
  3730. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3731. mainloop_producer_state
  3732. ),
  3733. )
  3734. if cutlass.const_expr(
  3735. self.load_path == "cpasync"
  3736. or self.block_fp8
  3737. or self.fused_quant_a
  3738. ):
  3739. pass
  3740. elif cutlass.const_expr(self.a_bf16_fused):
  3741. pass # scales already written above
  3742. elif cutlass.const_expr(self.manual_bk64_sf):
  3743. lane = Int32(tidx % self.num_threads_per_warp)
  3744. sf_k_tiles = Int32(k_tile_cnt // 2)
  3745. sf_k_tile = k_tile_global // Int32(2)
  3746. sf_k_half = k_tile_global - sf_k_tile * Int32(2)
  3747. sfa_tile = tile_coord_mnl[0]
  3748. sfb_tile = tile_coord_mnl[1] // Int32(self.sfb_tiles_per_block)
  3749. # directSFA/directSFB retain the physical packed-scale
  3750. # storage order [L, MN-tile, K128-tile, 32, 4, 4].
  3751. # The original BK64 address arithmetic covered only
  3752. # L=1; without these strides every later grouped GEMM
  3753. # batch silently consumed batch 0's scales.
  3754. sfa_l_stride = (
  3755. Int32(tile_sched_params.problem_shape_ntile_mnl[0])
  3756. * sf_k_tiles
  3757. * Int32(512)
  3758. )
  3759. problem_n_tiles = Int32(
  3760. tile_sched_params.problem_shape_ntile_mnl[1]
  3761. )
  3762. sfb_scale_n_tiles = (
  3763. problem_n_tiles + Int32(self.sfb_tiles_per_block - 1)
  3764. ) // Int32(self.sfb_tiles_per_block)
  3765. sfb_l_stride = sfb_scale_n_tiles * sf_k_tiles * Int32(512)
  3766. l_coord = tile_coord_mnl[2]
  3767. for sf_iter in cutlass.range_constexpr(4):
  3768. mn_local = lane + Int32(sf_iter * self.num_threads_per_warp)
  3769. mn_outer = mn_local // Int32(32)
  3770. mn_inner = mn_local - mn_outer * Int32(32)
  3771. atom_offset = (
  3772. mn_inner * Int32(16)
  3773. + mn_outer * Int32(4)
  3774. + sf_k_half * Int32(2)
  3775. )
  3776. sfa_offset = (
  3777. l_coord * sfa_l_stride
  3778. + (sfa_tile * sf_k_tiles + sf_k_tile) * Int32(512)
  3779. + atom_offset
  3780. )
  3781. sfb_offset = (
  3782. l_coord * sfb_l_stride
  3783. + (sfb_tile * sf_k_tiles + sf_k_tile) * Int32(512)
  3784. + atom_offset
  3785. )
  3786. sfa_pair = ld_global_b16(
  3787. get_ptr_as_int64(directSFA_mkl, sfa_offset)
  3788. )
  3789. sfb_pair = ld_global_b16(
  3790. get_ptr_as_int64(directSFB_nkl, sfb_offset)
  3791. )
  3792. sfa_smem_addr = shared_ptr_to_u32(
  3793. elem_pointer(
  3794. sSFA,
  3795. (
  3796. mn_local,
  3797. Int32(0),
  3798. mainloop_producer_state.index,
  3799. ),
  3800. )
  3801. )
  3802. sfb_smem_addr = shared_ptr_to_u32(
  3803. elem_pointer(
  3804. sSFB,
  3805. (
  3806. mn_local,
  3807. Int32(0),
  3808. mainloop_producer_state.index,
  3809. ),
  3810. )
  3811. )
  3812. st_shared_u16(sfa_smem_addr, sfa_pair)
  3813. st_shared_u16(sfb_smem_addr, sfb_pair)
  3814. cute.arch.fence_proxy("async.shared", space="cta")
  3815. elif cutlass.const_expr(self.direct_sfa_prefix):
  3816. lane = Int32(tidx % self.num_threads_per_warp)
  3817. if lane == Int32(0):
  3818. scale_k_tiles = (
  3819. Int32(directA_mkl.shape[1]) + Int32(127)
  3820. ) // Int32(128)
  3821. scale_offset = (
  3822. tile_coord_mnl[2] * scale_k_tiles + k_tile_global
  3823. ) * Int32(512)
  3824. cp_async_bulk_g2s_mbar(
  3825. shared_ptr_to_u32(
  3826. elem_pointer(
  3827. sSFA,
  3828. (
  3829. Int32(0),
  3830. Int32(0),
  3831. mainloop_producer_state.index,
  3832. ),
  3833. )
  3834. ),
  3835. get_ptr_as_int64(directSFA_mkl, scale_offset),
  3836. Int32(256),
  3837. shared_ptr_to_u32(
  3838. mainloop_pipeline.producer_get_barrier(
  3839. mainloop_producer_state
  3840. )
  3841. ),
  3842. )
  3843. elif cutlass.const_expr(self.use_m1_non_tma_sfa):
  3844. lane = Int32(tidx % self.num_threads_per_warp)
  3845. scale_groups_per_k_tile = (
  3846. self.tile_shape_mnk[2] // self.sf_vec_size
  3847. )
  3848. sfa_slots = self.sfa_tile_shape_mk[0] * scale_groups_per_k_tile
  3849. for sfa_iter in cutlass.range_constexpr(
  3850. (sfa_slots + self.num_threads_per_warp - 1)
  3851. // self.num_threads_per_warp
  3852. ):
  3853. linear = lane + Int32(sfa_iter * self.num_threads_per_warp)
  3854. m_local = linear // Int32(scale_groups_per_k_tile)
  3855. scale_group = linear - m_local * Int32(
  3856. scale_groups_per_k_tile
  3857. )
  3858. k_local_sfa = scale_group * Int32(self.sf_vec_size)
  3859. k_coord_sfa = (
  3860. k_tile_global * Int32(self.tile_shape_mnk[2])
  3861. + k_local_sfa
  3862. )
  3863. if linear < Int32(sfa_slots):
  3864. sSFA[
  3865. (
  3866. m_local,
  3867. k_local_sfa,
  3868. mainloop_producer_state.index,
  3869. )
  3870. ] = directSFA_mkl[
  3871. (
  3872. Int32(0),
  3873. k_coord_sfa,
  3874. tile_coord_mnl[2],
  3875. )
  3876. ]
  3877. cute.arch.fence_proxy("async.shared", space="cta")
  3878. else:
  3879. cute.copy(
  3880. tma_atom_sfa,
  3881. tAgSFA_k,
  3882. tAsSFA_pipe,
  3883. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3884. mainloop_producer_state
  3885. ),
  3886. )
  3887. if cutlass.const_expr(self.direct_sfb_representative):
  3888. lane = Int32(tidx % self.num_threads_per_warp)
  3889. if lane == Int32(0):
  3890. scale_n_tiles = (
  3891. Int32(directC_mnl.shape[1]) + Int32(127)
  3892. ) // Int32(128)
  3893. scale_k_tiles = (
  3894. Int32(directA_mkl.shape[1]) + Int32(127)
  3895. ) // Int32(128)
  3896. scale_n_tile = (
  3897. tile_coord_mnl[1] * Int32(self.tile_shape_mnk[1])
  3898. ) // Int32(128)
  3899. scale_offset = (
  3900. (tile_coord_mnl[2] * scale_n_tiles + scale_n_tile)
  3901. * scale_k_tiles
  3902. + k_tile_global
  3903. ) * Int32(512)
  3904. cp_async_bulk_g2s_mbar(
  3905. shared_ptr_to_u32(
  3906. elem_pointer(
  3907. sSFB,
  3908. (
  3909. Int32(0),
  3910. Int32(0),
  3911. mainloop_producer_state.index,
  3912. ),
  3913. )
  3914. ),
  3915. get_ptr_as_int64(directSFB_nkl, scale_offset),
  3916. Int32(16),
  3917. shared_ptr_to_u32(
  3918. mainloop_pipeline.producer_get_barrier(
  3919. mainloop_producer_state
  3920. )
  3921. ),
  3922. )
  3923. if cutlass.const_expr(self.load_path == "tma"):
  3924. if cutlass.const_expr(
  3925. not (self.fused_quant_a and self.b_tile_major)
  3926. ):
  3927. if cutlass.const_expr(self.b_tile_major):
  3928. cute.copy(
  3929. tma_atom_b,
  3930. tBgB_k,
  3931. tBsB_pipe,
  3932. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3933. mainloop_producer_state
  3934. ),
  3935. cache_policy=Int64(0x12F0000000000000),
  3936. )
  3937. else:
  3938. if cutlass.const_expr(self.occupancy > 1):
  3939. cute.copy(
  3940. tma_atom_b,
  3941. tBgB_k,
  3942. tBsB_pipe,
  3943. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3944. mainloop_producer_state
  3945. ),
  3946. cache_policy=Int64(0x12F0000000000000),
  3947. )
  3948. else:
  3949. cute.copy(
  3950. tma_atom_b,
  3951. tBgB_k,
  3952. tBsB_pipe,
  3953. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3954. mainloop_producer_state
  3955. ),
  3956. )
  3957. if cutlass.const_expr(
  3958. not self.block_fp8
  3959. and not self.manual_bk64_sf
  3960. and not self.direct_sfb_representative
  3961. ):
  3962. cute.copy(
  3963. tma_atom_sfb,
  3964. tBgSFB_k,
  3965. tBsSFB_pipe,
  3966. tma_bar_ptr=mainloop_pipeline.producer_get_barrier(
  3967. mainloop_producer_state
  3968. ),
  3969. )
  3970. if cutlass.const_expr(self.load_path == "cpasync"):
  3971. cute.arch.cp_async_commit_group()
  3972. cute.arch.cp_async_wait_group(0)
  3973. mainloop_pipeline.producer_commit(mainloop_producer_state)
  3974. mainloop_producer_state.advance()
  3975. if cutlass.const_expr(self.single_work_tile_per_cta):
  3976. work_tile = WorkTileInfo(
  3977. work_tile.tile_idx,
  3978. cutlass.Boolean(0),
  3979. )
  3980. else:
  3981. tile_sched.advance_to_next_work()
  3982. work_tile = tile_sched.get_current_work()
  3983. mainloop_pipeline.producer_tail(mainloop_producer_state)
  3984. return
  3985. @staticmethod
  3986. def _compute_stages(
  3987. tile_shape_mnk: tuple,
  3988. a_dtype,
  3989. b_dtype,
  3990. sf_dtype,
  3991. sfa_smem_layout,
  3992. sfb_smem_layout,
  3993. epi_tile: tuple,
  3994. c_dtype,
  3995. smem_capacity: int,
  3996. occupancy: int,
  3997. b_packed: bool = False,
  3998. epi_stage_cap: int = 0,
  3999. decode_stage3: bool = False,
  4000. ) -> tuple:
  4001. epi_stage_max = (tile_shape_mnk[1] // epi_tile[1]) * (
  4002. tile_shape_mnk[0] // epi_tile[0]
  4003. )
  4004. epi_stage = min(epi_stage_max, 4)
  4005. if epi_stage_cap:
  4006. epi_stage = max(1, min(epi_stage, epi_stage_cap))
  4007. c_bytes_per_stage = cute.size(epi_tile) * c_dtype.width // 8
  4008. epi_bytes = c_bytes_per_stage * epi_stage
  4009. a_shape = cute.slice_(tile_shape_mnk, (None, 0, None))
  4010. b_shape = cute.slice_(tile_shape_mnk, (0, None, None))
  4011. ab_bytes_per_stage = (
  4012. cute.size(a_shape) * a_dtype.width // 8
  4013. + cute.size(b_shape) * b_dtype.width // 8
  4014. )
  4015. # b_packed costs no extra smem: the packed TMA tile is aliased into the
  4016. # bottom of each sB stage and expanded in place.
  4017. sf_bytes_per_stage = (
  4018. cute.size(cute.filter_zeros(sfa_smem_layout).shape) * sf_dtype.width // 8
  4019. + cute.size(cute.filter_zeros(sfb_smem_layout).shape) * sf_dtype.width // 8
  4020. )
  4021. mbar_helpers_bytes = 1024
  4022. raw_ab_stage = (
  4023. (smem_capacity - occupancy * 1024) // occupancy
  4024. - mbar_helpers_bytes
  4025. - epi_bytes
  4026. ) // (ab_bytes_per_stage + sf_bytes_per_stage)
  4027. ab_stage = max(1, min(raw_ab_stage, 4))
  4028. if tile_shape_mnk[0] in (16, 64) and tile_shape_mnk[1] == 128:
  4029. ab_stage = max(1, min(raw_ab_stage, 5))
  4030. if b_packed:
  4031. # In-place packed staging freed 12 KB/stage; deeper pipelines give
  4032. # the producer the lookahead the packed consumer chain needs.
  4033. ab_stage = max(1, min(raw_ab_stage, 5))
  4034. if decode_stage3 and occupancy >= 2 and tile_shape_mnk[0] <= 16:
  4035. ab_stage = max(1, min(raw_ab_stage, 3))
  4036. return ab_stage, epi_stage
  4037. @staticmethod
  4038. def _choose_epilogue(
  4039. mma_tiler_mn: tuple,
  4040. mma_atom_tile_mn: tuple,
  4041. probe,
  4042. stages_through_smem: bool = True,
  4043. ) -> tuple:
  4044. """Choose an MX-FP6 epilogue tile without reducing mainloop depth."""
  4045. full = (mma_tiler_mn[0], mma_tiler_mn[1])
  4046. if not stages_through_smem:
  4047. # The m=1 epilogue stores straight out of registers rather than
  4048. # staging through sC (the use_m1_non_tma_c branches below), so a
  4049. # sub-tiled multi-stage buffer does not describe what it does. It
  4050. # does not fail loudly either: it writes garbage rows.
  4051. return full, 0
  4052. atom_m, atom_n = mma_atom_tile_mn
  4053. def _legal(epi_tile: tuple) -> bool:
  4054. # The epilogue walks MmaMPerEpiM = epi_m // mma_tile_m atoms per
  4055. # staged tile. An epilogue smaller than one atom floors that to
  4056. # zero, so the accumulator is never copied in and the TMA stores
  4057. # whatever was in shared memory - silent NaN, not a compile error.
  4058. return (
  4059. epi_tile[0] >= atom_m
  4060. and epi_tile[1] >= atom_n
  4061. and epi_tile[0] % atom_m == 0
  4062. and epi_tile[1] % atom_n == 0
  4063. )
  4064. full_ab, _ = probe(full, 0)
  4065. half = (mma_tiler_mn[0] // 2, mma_tiler_mn[1] // 2)
  4066. if not _legal(half):
  4067. return full, 0
  4068. # Halving the tile alone is inert - epi_stage_max rises by the same
  4069. # factor - so the cap is what actually reclaims the bytes.
  4070. half_ab, _ = probe(half, 2)
  4071. if half_ab > full_ab:
  4072. return half, 2
  4073. return full, 0
  4074. @staticmethod
  4075. def _make_smem_layouts(
  4076. tile_shape_mnk: tuple,
  4077. epi_tile: tuple,
  4078. a_dtype,
  4079. a_layout,
  4080. b_dtype,
  4081. b_layout,
  4082. ab_stage: int,
  4083. c_dtype,
  4084. c_layout,
  4085. epi_stage: int,
  4086. sf_vec_size: int,
  4087. tiled_mma,
  4088. block_fp8: bool = False,
  4089. ) -> tuple:
  4090. a_smem_shape = cute.slice_(tile_shape_mnk, (None, 0, None))
  4091. a_is_k_major = a_layout.is_k_major_a()
  4092. b_is_k_major = b_layout.is_k_major_b()
  4093. a_major_mode_size = tile_shape_mnk[2 if a_is_k_major else 0]
  4094. a_smem_layout_atom = cute.nvgpu.warpgroup.make_smem_layout_atom(
  4095. sm90_utils.get_smem_layout_atom(
  4096. a_layout,
  4097. a_dtype,
  4098. a_major_mode_size,
  4099. ),
  4100. a_dtype,
  4101. )
  4102. a_smem_layout_staged = cute.tile_to_shape(
  4103. a_smem_layout_atom,
  4104. cute.append(a_smem_shape, ab_stage),
  4105. order=(0, 1, 2) if a_is_k_major else (1, 0, 2),
  4106. )
  4107. b_smem_shape = cute.slice_(tile_shape_mnk, (0, None, None))
  4108. b_major_mode_size = tile_shape_mnk[2 if b_is_k_major else 1]
  4109. b_smem_layout_atom = cute.nvgpu.warpgroup.make_smem_layout_atom(
  4110. sm90_utils.get_smem_layout_atom(
  4111. b_layout,
  4112. b_dtype,
  4113. b_major_mode_size,
  4114. ),
  4115. b_dtype,
  4116. )
  4117. b_smem_layout_staged = cute.tile_to_shape(
  4118. b_smem_layout_atom,
  4119. cute.append(b_smem_shape, ab_stage),
  4120. order=(0, 1, 2) if b_is_k_major else (1, 0, 2),
  4121. )
  4122. if block_fp8:
  4123. sfa_smem_layout_staged = cute.make_layout((1, 1, ab_stage))
  4124. sfb_smem_layout_staged = cute.make_layout((1, 1, ab_stage))
  4125. else:
  4126. sfa_smem_layout_staged = sm120_make_smem_layout_sfa(
  4127. tiled_mma,
  4128. tile_shape_mnk,
  4129. sf_vec_size,
  4130. ab_stage,
  4131. )
  4132. sfb_smem_layout_staged = sm120_make_smem_layout_sfb(
  4133. tiled_mma,
  4134. tile_shape_mnk,
  4135. sf_vec_size,
  4136. ab_stage,
  4137. )
  4138. c_smem_shape = epi_tile
  4139. c_major_mode_size = epi_tile[1] if c_layout.is_n_major_c() else epi_tile[0]
  4140. c_smem_layout_atom = cute.nvgpu.warpgroup.make_smem_layout_atom(
  4141. sm90_utils.get_smem_layout_atom(
  4142. c_layout,
  4143. c_dtype,
  4144. c_major_mode_size,
  4145. ),
  4146. c_dtype,
  4147. )
  4148. epi_smem_layout_staged = cute.tile_to_shape(
  4149. c_smem_layout_atom,
  4150. cute.append(c_smem_shape, epi_stage),
  4151. order=(1, 0, 2) if c_layout.is_m_major_c() else (0, 1, 2),
  4152. )
  4153. return (
  4154. a_smem_layout_staged,
  4155. b_smem_layout_staged,
  4156. sfa_smem_layout_staged,
  4157. sfb_smem_layout_staged,
  4158. epi_smem_layout_staged,
  4159. )
  4160. @staticmethod
  4161. def _compute_grid(
  4162. c,
  4163. tile_shape_mnk: tuple,
  4164. max_active_clusters,
  4165. direct_one_m_tile_scheduler: bool,
  4166. split_k_slices: int,
  4167. large_m_unroll: bool,
  4168. ) -> tuple:
  4169. c_shape = cute.slice_(tile_shape_mnk, (None, None, 0))
  4170. gc = cute.zipped_divide(c, tiler=c_shape)
  4171. num_ctas_mnl = gc[(0, (None, None, None))].shape
  4172. cluster_shape_mnl = (1, 1, 1)
  4173. tile_sched_params = utils.PersistentTileSchedulerParams(
  4174. num_ctas_mnl,
  4175. cluster_shape_mnl,
  4176. swizzle_size=(
  4177. 16
  4178. if tile_shape_mnk == (128, 128, 64) and not large_m_unroll
  4179. else 1
  4180. ),
  4181. )
  4182. if cutlass.const_expr(split_k_slices > 1):
  4183. grid = (1, split_k_slices, num_ctas_mnl[1])
  4184. else:
  4185. grid = utils.StaticPersistentTileScheduler.get_grid_shape(
  4186. tile_sched_params, max_active_clusters
  4187. )
  4188. return tile_sched_params, grid
  4189. @staticmethod
  4190. def _make_tma_store_atoms_and_tensors(
  4191. tensor_c,
  4192. epi_smem_layout_staged,
  4193. epi_tile: tuple,
  4194. ) -> tuple:
  4195. epi_smem_layout = cute.slice_(epi_smem_layout_staged, (None, None, 0))
  4196. tma_atom_c, tma_tensor_c = cpasync.make_tiled_tma_atom(
  4197. cpasync.CopyBulkTensorTileS2GOp(),
  4198. tensor_c,
  4199. epi_smem_layout,
  4200. epi_tile,
  4201. )
  4202. return tma_atom_c, tma_tensor_c
  4203. @staticmethod
  4204. def _make_tma_atoms_and_tensors(
  4205. tensor,
  4206. smem_layout_staged,
  4207. smem_tile: tuple,
  4208. mcast_dim: int,
  4209. internal_type=None,
  4210. ) -> tuple:
  4211. op = (
  4212. cpasync.CopyBulkTensorTileG2SOp()
  4213. if mcast_dim == 1
  4214. else cpasync.CopyBulkTensorTileG2SMulticastOp()
  4215. )
  4216. smem_layout = cute.slice_(smem_layout_staged, (None, None, 0))
  4217. tma_atom, tma_tensor = cpasync.make_tiled_tma_atom(
  4218. op,
  4219. tensor,
  4220. smem_layout,
  4221. smem_tile,
  4222. num_multicast=mcast_dim,
  4223. internal_type=internal_type,
  4224. )
  4225. return tma_atom, tma_tensor
  4226. @staticmethod
  4227. def can_implement(
  4228. ab_dtype,
  4229. sf_dtype,
  4230. sf_vec_size: int,
  4231. c_dtype,
  4232. mma_tiler_mn: Tuple[int, int],
  4233. cluster_shape_mn: Tuple[int, int],
  4234. n: int,
  4235. k: int,
  4236. l: int,
  4237. a_major: str,
  4238. b_major: str,
  4239. c_major: str,
  4240. *,
  4241. load_path: str = "tma",
  4242. swap_ab: bool = False,
  4243. block_fp8: bool = False,
  4244. ) -> bool:
  4245. # The current target only supports cluster (1,1)
  4246. if cluster_shape_mn != (1, 1):
  4247. return False
  4248. if load_path not in _DENSE_LOAD_PATHS:
  4249. return False
  4250. if block_fp8 and (
  4251. ab_dtype != cutlass.Float8E4M3FN
  4252. or sf_dtype != cutlass.Float32
  4253. or sf_vec_size != 128
  4254. or load_path != "tma"
  4255. or swap_ab
  4256. or l != 1
  4257. or n % 128 != 0
  4258. ):
  4259. return False
  4260. if swap_ab:
  4261. if l != 1:
  4262. return False
  4263. if not (
  4264. (ab_dtype == cutlass.Float4E2M1FN and sf_vec_size == 16)
  4265. or (ab_dtype == cutlass.Float8E4M3FN and sf_vec_size == 32)
  4266. ):
  4267. return False
  4268. if load_path == "cpasync" and (
  4269. ab_dtype != cutlass.Float4E2M1FN or sf_vec_size != 16 or l != 1
  4270. ):
  4271. return False
  4272. # FP4 experiments allow narrow N tiles. The scale-factor smem paths
  4273. # still allocate full 128-element SF blocks, but the live MMA tile may
  4274. # consume only 16/32 columns.
  4275. mma_check_mn = (mma_tiler_mn[1], mma_tiler_mn[0]) if swap_ab else mma_tiler_mn
  4276. if ab_dtype == cutlass.Float8E4M3FN or is_mxfp6_ab_dtype(ab_dtype):
  4277. if mma_check_mn not in (
  4278. (16, 64),
  4279. (16, 128),
  4280. (32, 64),
  4281. (32, 128),
  4282. ):
  4283. if mma_check_mn[0] % 64 != 0 or mma_check_mn[1] % 64 != 0:
  4284. return False
  4285. elif ab_dtype == cutlass.Float4E2M1FN:
  4286. if (
  4287. mma_tiler_mn[0] % 64 != 0
  4288. or mma_tiler_mn[1] % 16 != 0
  4289. or mma_tiler_mn[1] > 128
  4290. or (mma_tiler_mn[1] < 64 and not swap_ab)
  4291. ):
  4292. return False
  4293. else:
  4294. if mma_check_mn[0] % 64 != 0 or mma_check_mn[1] % 64 != 0:
  4295. return False
  4296. # The current target supports FP4, MXFP8, and MX-FP6 warp MMA paths.
  4297. if ab_dtype not in (
  4298. cutlass.Float4E2M1FN,
  4299. cutlass.Float8E4M3FN,
  4300. cutlass.Float6E3M2FN,
  4301. cutlass.Float6E2M3FN,
  4302. ):
  4303. return False
  4304. # Current target MMA constraints:
  4305. # sf_vec_size=16 requires sf_dtype=Float8E4M3FN
  4306. # sf_vec_size=32 requires sf_dtype=Float8E8M0FNU
  4307. if not block_fp8:
  4308. if sf_vec_size == 16 and sf_dtype != cutlass.Float8E4M3FN:
  4309. return False
  4310. if sf_vec_size == 32 and sf_dtype != cutlass.Float8E8M0FNU:
  4311. return False
  4312. if ab_dtype == cutlass.Float8E4M3FN and sf_vec_size != 32:
  4313. return False
  4314. if is_mxfp6_ab_dtype(ab_dtype) and sf_vec_size != 32:
  4315. return False
  4316. # Public output is 16-bit; split-K internally uses FP32 partial output.
  4317. if c_dtype not in (cutlass.Float16, cutlass.BFloat16, cutlass.Float32):
  4318. return False
  4319. # A must be K-major, B must be K-major
  4320. if a_major != "k" or b_major != "k":
  4321. return False
  4322. # Alignment: K must be divisible by tile_k
  4323. if block_fp8:
  4324. tile_k = 128
  4325. elif ab_dtype == cutlass.Float8E4M3FN or is_mxfp6_ab_dtype(ab_dtype):
  4326. tile_k = mxfp6_tile_k() if is_mxfp6_ab_dtype(ab_dtype) else 128
  4327. else:
  4328. tile_k = sf_vec_size * 8
  4329. if k % tile_k != 0:
  4330. return False
  4331. return True
  4332. class _DenseGemmLaunch:
  4333. def __init__(
  4334. self,
  4335. n: int,
  4336. k: int,
  4337. l: int,
  4338. c_l: int,
  4339. a_major: str,
  4340. b_major: str,
  4341. c_major: str,
  4342. ab_dtype: torch.dtype,
  4343. sf_dtype: torch.dtype,
  4344. c_dtype: torch.dtype,
  4345. alpha_dtype: torch.dtype,
  4346. sf_vec_size: int,
  4347. mma_k: int,
  4348. tile_k: int,
  4349. mma_tiler_mn: Tuple[int, int],
  4350. cluster_shape_mn: Tuple[int, int],
  4351. policy: _DenseGemmPolicy,
  4352. sm_count: int,
  4353. sm_version: str,
  4354. load_path: str,
  4355. swap_ab: bool,
  4356. sfb_k_reuse: bool,
  4357. b_tile_major: bool = False,
  4358. quantize_c: bool = False,
  4359. alpha_is_one: bool = False,
  4360. direct_sfa_live16: bool = False,
  4361. direct_m1_wo_a_inputs: bool = False,
  4362. target_occupancy: int = 1,
  4363. plain_fp8: bool = False,
  4364. block_fp8: bool = False,
  4365. ):
  4366. self._n = n
  4367. self._k = k
  4368. self._l = l
  4369. self._c_l = c_l
  4370. self._a_major = a_major
  4371. self._b_major = b_major
  4372. self._c_major = c_major
  4373. self._ab_dtype = ab_dtype
  4374. self._sf_dtype = sf_dtype
  4375. self._c_dtype = c_dtype
  4376. self._alpha_dtype = alpha_dtype
  4377. self._sf_vec_size = sf_vec_size
  4378. self._mma_k = mma_k
  4379. self._tile_k = tile_k
  4380. self._mma_tiler_mn = mma_tiler_mn
  4381. self._cluster_shape_mn = cluster_shape_mn
  4382. self._policy = policy
  4383. self._sm_count = sm_count
  4384. self._sm_version = sm_version
  4385. self._load_path = load_path
  4386. self._swap_ab = swap_ab
  4387. # This experimental atom choice changes generated code. Capture the
  4388. # import-time setting on the launch so both in-process resolution and
  4389. # the persistent object cache distinguish it.
  4390. self._atom_shape_24 = _B12X_DENSE_ATOM_24
  4391. self._sfb_k_reuse = sfb_k_reuse
  4392. self._b_tile_major = b_tile_major
  4393. self._quantize_c = quantize_c
  4394. self._alpha_is_one = alpha_is_one
  4395. self._direct_sfa_live16 = direct_sfa_live16
  4396. self._direct_m1_wo_a_inputs = direct_m1_wo_a_inputs
  4397. self._target_occupancy = target_occupancy
  4398. self._plain_fp8 = bool(plain_fp8)
  4399. self._block_fp8 = bool(block_fp8)
  4400. if b_tile_major:
  4401. if (n, k, l) == (1024, 4096, 4):
  4402. self._b_tile_n = 64
  4403. elif (n, k, l) == (4096, 4096, 1):
  4404. self._b_tile_n = 128
  4405. else:
  4406. raise ValueError(
  4407. "tile-major B is restricted to production WO-A/WO-B shapes, "
  4408. f"got {(n, k, l)}"
  4409. )
  4410. else:
  4411. self._b_tile_n = 0
  4412. if not DenseGemmKernel.can_implement(
  4413. ab_dtype,
  4414. sf_dtype,
  4415. sf_vec_size,
  4416. c_dtype,
  4417. mma_tiler_mn,
  4418. cluster_shape_mn,
  4419. n,
  4420. k,
  4421. l,
  4422. a_major,
  4423. b_major,
  4424. c_major,
  4425. load_path=load_path,
  4426. swap_ab=swap_ab,
  4427. block_fp8=block_fp8,
  4428. ):
  4429. raise TypeError(
  4430. "dense_gemm launch is unsupported with "
  4431. f"{ab_dtype}, {sf_dtype}, {sf_vec_size}, {c_dtype}, "
  4432. f"{mma_tiler_mn}, {cluster_shape_mn}, {n}, {k}, {l}, "
  4433. f"{a_major}, {b_major}, {c_major}, "
  4434. f"load_path={load_path}, swap_ab={swap_ab}"
  4435. )
  4436. self._max_active_clusters = (
  4437. _max_active_clusters_for(self._cluster_shape_mn, sm_count)
  4438. * self._target_occupancy
  4439. )
  4440. if (
  4441. mma_tiler_mn == (32, 64)
  4442. and tile_k == 128
  4443. and b_tile_major
  4444. and sfb_k_reuse
  4445. and alpha_is_one
  4446. and (n, k, l) in ((1024, 4096, 4), (4096, 4096, 1))
  4447. ):
  4448. self._max_active_clusters = min(self._max_active_clusters, 40)
  4449. def compile_key(self) -> tuple[object, ...]:
  4450. """Return every value that can specialize the generated kernel."""
  4451. return (
  4452. self._n,
  4453. self._k,
  4454. self._l,
  4455. self._c_l,
  4456. self._a_major,
  4457. self._b_major,
  4458. self._c_major,
  4459. self._ab_dtype,
  4460. self._sf_dtype,
  4461. self._c_dtype,
  4462. self._alpha_dtype,
  4463. self._sf_vec_size,
  4464. self._mma_k,
  4465. self._tile_k,
  4466. self._mma_tiler_mn,
  4467. self._cluster_shape_mn,
  4468. self._policy,
  4469. self._sm_count,
  4470. self._max_active_clusters,
  4471. self._sm_version,
  4472. self._load_path,
  4473. self._swap_ab,
  4474. self._atom_shape_24,
  4475. self._sfb_k_reuse,
  4476. self._b_tile_major,
  4477. self._b_tile_n,
  4478. self._quantize_c,
  4479. self._alpha_is_one,
  4480. self._direct_sfa_live16,
  4481. self._direct_m1_wo_a_inputs,
  4482. self._target_occupancy,
  4483. self._plain_fp8,
  4484. )
  4485. @cute.jit
  4486. def __call__(
  4487. self,
  4488. a_ptr: cute.Pointer,
  4489. b_ptr: cute.Pointer,
  4490. sfa_ptr: cute.Pointer,
  4491. sfb_ptr: cute.Pointer,
  4492. c_ptr: cute.Pointer,
  4493. quant_c_values_ptr: cute.Pointer,
  4494. quant_c_scale_rows_ptr: cute.Pointer,
  4495. quant_c_scale_mma_ptr: cute.Pointer,
  4496. alpha_ptr: cute.Pointer,
  4497. m: cutlass.Int32,
  4498. current_stream: cuda.CUstream,
  4499. ):
  4500. a_tensor = cute.make_tensor(
  4501. a_ptr,
  4502. layout=cute.make_ordered_layout(
  4503. (m, self._k, self._l),
  4504. order=(0, 1, 2) if self._a_major == "m" else (1, 0, 2),
  4505. ),
  4506. )
  4507. if cutlass.const_expr(self._b_tile_major):
  4508. b_layout = cute.make_layout(
  4509. (
  4510. (self._b_tile_n, self._n // self._b_tile_n),
  4511. (128, self._k // 128),
  4512. self._l,
  4513. ),
  4514. stride=(
  4515. (128, self._b_tile_n * self._k),
  4516. (1, self._b_tile_n * 128),
  4517. self._n * self._k,
  4518. ),
  4519. )
  4520. else:
  4521. b_layout = cute.make_ordered_layout(
  4522. (self._n, self._k, self._l),
  4523. order=(0, 1, 2) if self._b_major == "n" else (1, 0, 2),
  4524. )
  4525. b_tensor = cute.make_tensor(b_ptr, layout=b_layout)
  4526. c_tensor = cute.make_tensor(
  4527. c_ptr,
  4528. layout=cute.make_ordered_layout(
  4529. (m, self._n, self._c_l),
  4530. order=(0, 1, 2) if self._c_major == "m" else (1, 0, 2),
  4531. ),
  4532. )
  4533. quant_c_width = self._n * self._l
  4534. quant_c_chunks = max(1, (quant_c_width + 31) // 32)
  4535. quant_c_k_tiles = max(1, (quant_c_width + 127) // 128)
  4536. quant_c_values_tensor = cute.make_tensor(
  4537. quant_c_values_ptr,
  4538. layout=cute.make_ordered_layout((m, quant_c_width), order=(1, 0)),
  4539. )
  4540. quant_c_scale_rows_tensor = cute.make_tensor(
  4541. quant_c_scale_rows_ptr,
  4542. layout=cute.make_ordered_layout((m, quant_c_chunks), order=(1, 0)),
  4543. )
  4544. quant_c_scale_mma_tensor = cute.make_tensor(
  4545. quant_c_scale_mma_ptr,
  4546. layout=cute.make_layout(
  4547. (32, 4, 1, 4, quant_c_k_tiles, 1),
  4548. stride=(
  4549. 16,
  4550. 4,
  4551. quant_c_k_tiles * 512,
  4552. 1,
  4553. 512,
  4554. quant_c_k_tiles * 512,
  4555. ),
  4556. ),
  4557. )
  4558. alpha_tensor = cute.make_tensor(
  4559. alpha_ptr,
  4560. layout=cute.make_ordered_layout((1,), order=(0,)),
  4561. )
  4562. if cutlass.const_expr(self._block_fp8):
  4563. sfa_tensor = cute.make_tensor(
  4564. sfa_ptr,
  4565. layout=cute.make_ordered_layout(
  4566. (m, self._k // 128, self._l), order=(1, 0, 2)
  4567. ),
  4568. )
  4569. sfb_tensor = cute.make_tensor(
  4570. sfb_ptr,
  4571. layout=cute.make_ordered_layout(
  4572. (self._n // 128, self._k // 128, self._l),
  4573. order=(1, 0, 2),
  4574. ),
  4575. )
  4576. else:
  4577. sfa_tensor = cute.make_tensor(sfa_ptr, layout=cute.make_layout((1,)))
  4578. sfb_tensor = cute.make_tensor(sfb_ptr, layout=cute.make_layout((1,)))
  4579. policy = self._policy
  4580. DenseGemmKernel(
  4581. sf_vec_size=self._sf_vec_size,
  4582. mma_tiler_mn=self._mma_tiler_mn,
  4583. cluster_shape_mn=self._cluster_shape_mn,
  4584. mma_k=self._mma_k,
  4585. tile_k=self._tile_k,
  4586. single_work_tile_per_cta=policy.single_work_tile_per_cta,
  4587. direct_one_m_tile_scheduler=policy.direct_one_m_tile_scheduler,
  4588. split_k_slices=policy.split_k_slices,
  4589. split_k_atomic_bf16=policy.split_k_atomic_bf16,
  4590. large_m_unroll=policy.large_m_unroll,
  4591. # M=1 FP8 benefits from normal TMA loads for A/SFA on the
  4592. # standalone tiny-M profile. Keep C on the direct epilogue path;
  4593. # the normal TMA store did not beat it in the DSV4F TP=2 GPU5 run.
  4594. use_m1_non_tma_a=False,
  4595. use_m1_non_tma_c=policy.use_m1_non_tma and not self._swap_ab,
  4596. use_m1_non_tma_sfa=False,
  4597. load_path=self._load_path,
  4598. swap_ab=self._swap_ab,
  4599. sfb_k_reuse=self._sfb_k_reuse,
  4600. atom_shape_24=self._atom_shape_24,
  4601. b_tile_major=self._b_tile_major,
  4602. quantize_c=self._quantize_c,
  4603. alpha_is_one=self._alpha_is_one,
  4604. direct_sfa_live16=self._direct_sfa_live16,
  4605. direct_m1_wo_a_inputs=self._direct_m1_wo_a_inputs,
  4606. target_occupancy=self._target_occupancy,
  4607. plain_fp8=self._plain_fp8,
  4608. block_fp8=self._block_fp8,
  4609. )(
  4610. a_tensor,
  4611. a_tensor,
  4612. alpha_tensor,
  4613. alpha_tensor,
  4614. b_tensor,
  4615. sfa_tensor,
  4616. sfb_tensor,
  4617. c_tensor,
  4618. quant_c_values_tensor,
  4619. quant_c_scale_rows_tensor,
  4620. quant_c_scale_mma_tensor,
  4621. alpha_tensor,
  4622. self._max_active_clusters,
  4623. current_stream,
  4624. )
  4625. class _DenseGemmMxfp6Launch(_DenseGemmLaunch):
  4626. """MX-FP6 (W6A6/W6A8) launch: FP6 codes in Float8E4M3FN byte-containers.
  4627. Adds the per-operand MX sub-formats, optional native 3:4-packed B
  4628. streaming, and the optional fused BF16->FP6 activation-quant prologue
  4629. (B12X_DENSE_FUSED_QUANT). Only the unswapped single-slice TMA plan
  4630. is wired; the constructor of DenseGemmKernel asserts the rest.
  4631. """
  4632. def __init__(
  4633. self,
  4634. *args,
  4635. mxfp6_fmt: Optional[str] = None,
  4636. mxfp6_fmt_a: Optional[str] = None,
  4637. mxfp6_fmt_b: Optional[str] = None,
  4638. b_packed: bool = False,
  4639. a_preexpanded: bool = False,
  4640. b_preexpanded: bool = False,
  4641. row_scale: bool = False,
  4642. fused_quant: Optional[bool] = None,
  4643. **kwargs,
  4644. ):
  4645. super().__init__(*args, **kwargs)
  4646. if mxfp6_fmt_a is None and mxfp6_fmt_b is None:
  4647. mxfp6_fmt_a = mxfp6_fmt
  4648. mxfp6_fmt_b = mxfp6_fmt
  4649. elif mxfp6_fmt_a is None or mxfp6_fmt_b is None:
  4650. raise ValueError(
  4651. "mxfp6_fmt_a and mxfp6_fmt_b must both be set or both None"
  4652. )
  4653. self._mxfp6_fmt_a = mxfp6_fmt_a
  4654. self._mxfp6_fmt_b = mxfp6_fmt_b
  4655. self._b_packed = bool(b_packed)
  4656. # Key-only host-side flags (do not change codegen, kept for cache
  4657. # hygiene) plus the import-time fused-quant knob, which DOES change
  4658. # the generated kernel via DenseGemmKernel.a_bf16_fused.
  4659. self._a_preexpanded = bool(a_preexpanded)
  4660. self._b_preexpanded = bool(b_preexpanded)
  4661. self._fused_quant_env = (
  4662. _DENSE_FUSED_QUANT if fused_quant is None else bool(fused_quant)
  4663. )
  4664. self._row_scale = bool(row_scale)
  4665. def compile_key(self) -> tuple[object, ...]:
  4666. return (
  4667. "mxfp6",
  4668. self._mxfp6_fmt_a,
  4669. self._mxfp6_fmt_b,
  4670. self._b_packed,
  4671. self._a_preexpanded,
  4672. self._b_preexpanded,
  4673. self._fused_quant_env,
  4674. self._row_scale,
  4675. *super().compile_key(),
  4676. )
  4677. @cute.jit
  4678. def __call__(
  4679. self,
  4680. a_ptr: cute.Pointer,
  4681. b_ptr: cute.Pointer,
  4682. sfa_ptr: cute.Pointer,
  4683. sfb_ptr: cute.Pointer,
  4684. c_ptr: cute.Pointer,
  4685. alpha_ptr: cute.Pointer,
  4686. x_bf16_ptr: cute.Pointer,
  4687. w_gscale_ptr: cute.Pointer,
  4688. row_scale_ptr: cute.Pointer,
  4689. m: cutlass.Int32,
  4690. current_stream: cuda.CUstream,
  4691. ):
  4692. a_tensor = cute.make_tensor(
  4693. a_ptr,
  4694. layout=cute.make_ordered_layout(
  4695. (m, self._k, self._l),
  4696. order=(0, 1, 2) if self._a_major == "m" else (1, 0, 2),
  4697. ),
  4698. )
  4699. # Packed B carries 3 bytes per 4 codes: gmem extent is 3K/4.
  4700. b_k_extent = self._k * 3 // 4 if self._b_packed else self._k
  4701. b_tensor = cute.make_tensor(
  4702. b_ptr,
  4703. layout=cute.make_ordered_layout(
  4704. (self._n, b_k_extent, self._l),
  4705. order=(0, 1, 2) if self._b_major == "n" else (1, 0, 2),
  4706. ),
  4707. )
  4708. c_tensor = cute.make_tensor(
  4709. c_ptr,
  4710. layout=cute.make_ordered_layout(
  4711. (m, self._n, self._c_l),
  4712. order=(0, 1, 2) if self._c_major == "m" else (1, 0, 2),
  4713. ),
  4714. )
  4715. alpha_tensor = cute.make_tensor(
  4716. alpha_ptr,
  4717. layout=cute.make_ordered_layout((1,), order=(0,)),
  4718. )
  4719. sfa_tensor = cute.make_tensor(sfa_ptr, layout=cute.make_layout((1,)))
  4720. sfb_tensor = cute.make_tensor(sfb_ptr, layout=cute.make_layout((1,)))
  4721. x_bf16_tensor = cute.make_tensor(
  4722. x_bf16_ptr,
  4723. layout=cute.make_ordered_layout((m, self._k), order=(0, 1)),
  4724. )
  4725. w_gscale_tensor = cute.make_tensor(
  4726. w_gscale_ptr,
  4727. layout=cute.make_ordered_layout((Int32(1),), order=(0,)),
  4728. )
  4729. if cutlass.const_expr(self._row_scale):
  4730. # Row scaling and quantized-C are mutually exclusive, so the
  4731. # existing quant_c_values operand carries the FP6 epilogue scale.
  4732. row_scale_tensor = cute.make_tensor(
  4733. row_scale_ptr,
  4734. layout=cute.make_ordered_layout((m,), order=(0,)),
  4735. )
  4736. else:
  4737. row_scale_tensor = alpha_tensor
  4738. policy = self._policy
  4739. DenseGemmKernel(
  4740. sf_vec_size=self._sf_vec_size,
  4741. mma_tiler_mn=self._mma_tiler_mn,
  4742. cluster_shape_mn=self._cluster_shape_mn,
  4743. mma_k=self._mma_k,
  4744. tile_k=self._tile_k,
  4745. single_work_tile_per_cta=policy.single_work_tile_per_cta,
  4746. direct_one_m_tile_scheduler=policy.direct_one_m_tile_scheduler,
  4747. split_k_slices=policy.split_k_slices,
  4748. split_k_atomic_bf16=policy.split_k_atomic_bf16,
  4749. large_m_unroll=policy.large_m_unroll,
  4750. # The M=1 FP6 shape uses a 128-row MMA/TMA tile. In this lowering,
  4751. # the narrow A/SFA/C tensor-map paths illegal-instruction instead
  4752. # of relying on OOB handling, so keep them as explicit direct paths
  4753. # (also required by the fused activation-quant prologue).
  4754. use_m1_non_tma_a=policy.use_m1_non_tma,
  4755. use_m1_non_tma_c=policy.use_m1_non_tma,
  4756. use_m1_non_tma_sfa=policy.use_m1_non_tma,
  4757. load_path=self._load_path,
  4758. swap_ab=self._swap_ab,
  4759. sfb_k_reuse=self._sfb_k_reuse,
  4760. alpha_is_one=self._alpha_is_one,
  4761. mxfp6_fmt_a=self._mxfp6_fmt_a,
  4762. mxfp6_fmt_b=self._mxfp6_fmt_b,
  4763. b_packed=self._b_packed,
  4764. target_occupancy=self._target_occupancy,
  4765. row_scale=self._row_scale,
  4766. fused_quant_bf16=self._fused_quant_env,
  4767. )(
  4768. a_tensor,
  4769. a_tensor,
  4770. alpha_tensor,
  4771. alpha_tensor,
  4772. b_tensor,
  4773. sfa_tensor,
  4774. sfb_tensor,
  4775. c_tensor,
  4776. row_scale_tensor,
  4777. alpha_tensor,
  4778. alpha_tensor,
  4779. alpha_tensor,
  4780. self._max_active_clusters,
  4781. current_stream,
  4782. x_bf16=x_bf16_tensor,
  4783. w_gscale=w_gscale_tensor,
  4784. )
  4785. @functools.cache
  4786. def _get_compiled_dense_gemm_mxfp6(
  4787. n: int,
  4788. k: int,
  4789. l: int,
  4790. c_l: int,
  4791. a_major: str,
  4792. b_major: str,
  4793. c_major: str,
  4794. ab_dtype: Type[cutlass.Numeric],
  4795. sf_dtype: Type[cutlass.Numeric],
  4796. c_dtype: Type[cutlass.Numeric],
  4797. alpha_dtype: Type[cutlass.Numeric],
  4798. sf_vec_size: int,
  4799. mma_k: int,
  4800. tile_k: int,
  4801. mma_tiler_mn: Tuple[int, int],
  4802. cluster_shape_mn: Tuple[int, int],
  4803. policy: _DenseGemmPolicy,
  4804. sm_count: int,
  4805. sm_version: str,
  4806. mxfp6_fmt_a: Optional[str],
  4807. mxfp6_fmt_b: Optional[str],
  4808. b_packed: bool,
  4809. a_preexpanded: bool,
  4810. b_preexpanded: bool,
  4811. alpha_is_one: bool,
  4812. row_scale: bool,
  4813. fused_quant: bool,
  4814. ) -> Callable:
  4815. def _make_runtime_pointers(
  4816. input_tensors: Optional[List[torch.Tensor]],
  4817. ) -> List[cute.Pointer]:
  4818. if input_tensors is None:
  4819. (
  4820. a_data_ptr,
  4821. b_data_ptr,
  4822. sfa_data_ptr,
  4823. sfb_data_ptr,
  4824. c_data_ptr,
  4825. alpha_data_ptr,
  4826. x_bf16_data_ptr,
  4827. w_gscale_data_ptr,
  4828. row_scale_data_ptr,
  4829. ) = [16 for _ in range(9)]
  4830. else:
  4831. (
  4832. a_tensor_gpu,
  4833. b_tensor_gpu,
  4834. sfa_tensor_gpu,
  4835. sfb_tensor_gpu,
  4836. c_tensor_gpu,
  4837. alpha_tensor_gpu,
  4838. x_bf16_tensor_gpu,
  4839. w_gscale_tensor_gpu,
  4840. row_scale_tensor_gpu,
  4841. ) = input_tensors
  4842. (
  4843. a_data_ptr,
  4844. b_data_ptr,
  4845. sfa_data_ptr,
  4846. sfb_data_ptr,
  4847. c_data_ptr,
  4848. alpha_data_ptr,
  4849. ) = (
  4850. a_tensor_gpu.data_ptr(),
  4851. b_tensor_gpu.data_ptr(),
  4852. sfa_tensor_gpu.data_ptr(),
  4853. sfb_tensor_gpu.data_ptr(),
  4854. c_tensor_gpu.data_ptr(),
  4855. alpha_tensor_gpu.data_ptr(),
  4856. )
  4857. x_bf16_data_ptr = (
  4858. x_bf16_tensor_gpu.data_ptr()
  4859. if x_bf16_tensor_gpu is not None
  4860. else 16
  4861. )
  4862. w_gscale_data_ptr = (
  4863. w_gscale_tensor_gpu.data_ptr()
  4864. if w_gscale_tensor_gpu is not None
  4865. else 16
  4866. )
  4867. row_scale_data_ptr = (
  4868. row_scale_tensor_gpu.data_ptr()
  4869. if row_scale_tensor_gpu is not None
  4870. else 16
  4871. )
  4872. return [
  4873. make_ptr(ab_dtype, a_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  4874. make_ptr(ab_dtype, b_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  4875. make_ptr(sf_dtype, sfa_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  4876. make_ptr(sf_dtype, sfb_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  4877. make_ptr(c_dtype, c_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  4878. make_ptr(
  4879. alpha_dtype, alpha_data_ptr, cute.AddressSpace.gmem, assumed_align=16
  4880. ),
  4881. make_ptr(
  4882. cutlass.BFloat16,
  4883. x_bf16_data_ptr,
  4884. cute.AddressSpace.gmem,
  4885. assumed_align=16,
  4886. ),
  4887. make_ptr(
  4888. cutlass.Float32,
  4889. w_gscale_data_ptr,
  4890. cute.AddressSpace.gmem,
  4891. assumed_align=16,
  4892. ),
  4893. make_ptr(
  4894. c_dtype,
  4895. row_scale_data_ptr,
  4896. cute.AddressSpace.gmem,
  4897. assumed_align=16,
  4898. ),
  4899. ]
  4900. launch = _DenseGemmMxfp6Launch(
  4901. n=n,
  4902. k=k,
  4903. l=l,
  4904. c_l=c_l,
  4905. a_major=a_major,
  4906. b_major=b_major,
  4907. c_major=c_major,
  4908. ab_dtype=ab_dtype,
  4909. sf_dtype=sf_dtype,
  4910. c_dtype=c_dtype,
  4911. alpha_dtype=alpha_dtype,
  4912. sf_vec_size=sf_vec_size,
  4913. mma_k=mma_k,
  4914. tile_k=tile_k,
  4915. mma_tiler_mn=mma_tiler_mn,
  4916. cluster_shape_mn=cluster_shape_mn,
  4917. policy=policy,
  4918. sm_count=sm_count,
  4919. sm_version=sm_version,
  4920. load_path="tma",
  4921. swap_ab=False,
  4922. sfb_k_reuse=False,
  4923. alpha_is_one=alpha_is_one,
  4924. mxfp6_fmt_a=mxfp6_fmt_a,
  4925. mxfp6_fmt_b=mxfp6_fmt_b,
  4926. b_packed=b_packed,
  4927. a_preexpanded=a_preexpanded,
  4928. b_preexpanded=b_preexpanded,
  4929. row_scale=row_scale,
  4930. fused_quant=fused_quant,
  4931. # MX-FP6 uses the same occupancy policy as the other dense kernels.
  4932. target_occupancy=_dense_gemm_target_occupancy(
  4933. n=n,
  4934. k=k,
  4935. l=l,
  4936. ab_dtype=ab_dtype,
  4937. c_dtype=c_dtype,
  4938. tile_k=tile_k,
  4939. mma_tiler_mn=mma_tiler_mn,
  4940. cluster_shape_mn=cluster_shape_mn,
  4941. sm_count=sm_count,
  4942. load_path="tma",
  4943. swap_ab=False,
  4944. b_tile_major=False,
  4945. is_mxfp6=True,
  4946. ),
  4947. )
  4948. compile_key = launch.compile_key()
  4949. raise_if_kernel_resolution_frozen(
  4950. "cute.compile",
  4951. target=launch,
  4952. cache_key=compile_key,
  4953. )
  4954. compiled_kernel = b12x_compile(
  4955. launch,
  4956. *_make_runtime_pointers(None),
  4957. 1,
  4958. current_cuda_stream(),
  4959. compile_spec=KernelCompileSpec.from_key("gemm.dense.mxfp6", 1, compile_key),
  4960. )
  4961. def tensor_api(
  4962. a_tensor_gpu: torch.Tensor,
  4963. b_tensor_gpu: torch.Tensor,
  4964. sfa_tensor_gpu: torch.Tensor,
  4965. sfb_tensor_gpu: torch.Tensor,
  4966. c_tensor_gpu: Optional[torch.Tensor] = None,
  4967. alpha_tensor_gpu: Optional[torch.Tensor] = None,
  4968. stream_int: Optional[int] = None,
  4969. x_bf16_tensor_gpu: Optional[torch.Tensor] = None,
  4970. w_gscale_tensor_gpu: Optional[torch.Tensor] = None,
  4971. row_scale_tensor_gpu: Optional[torch.Tensor] = None,
  4972. ) -> torch.Tensor:
  4973. m = a_tensor_gpu.shape[0]
  4974. if c_tensor_gpu is None:
  4975. c_tensor_gpu = _empty_dense_gemm_output(
  4976. int(m),
  4977. n,
  4978. c_l,
  4979. dtype=cutlass_to_torch_dtype(c_dtype),
  4980. device=a_tensor_gpu.device,
  4981. )
  4982. if alpha_tensor_gpu is None:
  4983. alpha_tensor_gpu = _cached_alpha_one(a_tensor_gpu.device)
  4984. nonlocal compiled_kernel
  4985. compiled_kernel(
  4986. *_make_runtime_pointers(
  4987. [
  4988. a_tensor_gpu,
  4989. b_tensor_gpu,
  4990. sfa_tensor_gpu,
  4991. sfb_tensor_gpu,
  4992. c_tensor_gpu,
  4993. alpha_tensor_gpu,
  4994. x_bf16_tensor_gpu,
  4995. w_gscale_tensor_gpu,
  4996. row_scale_tensor_gpu,
  4997. ]
  4998. ),
  4999. m,
  5000. cuda_stream_from_int_or_current(stream_int),
  5001. )
  5002. return c_tensor_gpu
  5003. return tensor_api
  5004. class _DenseGemmFusedQuantALaunch(_DenseGemmLaunch):
  5005. """Small-M MXFP8 launch that quantizes BF16 A into each CTA's stages."""
  5006. def __init__(
  5007. self,
  5008. *args,
  5009. fused_quant_a_inner_span: int = 0,
  5010. fused_quant_a_wide: bool = False,
  5011. **kwargs,
  5012. ):
  5013. super().__init__(*args, **kwargs)
  5014. self._fused_quant_a_inner_span = int(fused_quant_a_inner_span)
  5015. self._fused_quant_a_wide = bool(fused_quant_a_wide)
  5016. def compile_key(self) -> tuple[object, ...]:
  5017. # Keep the fused entry point separate even if every ordinary launch
  5018. # specialization matches. Deriving the rest from the parent key avoids
  5019. # silently omitting a future codegen field here.
  5020. return (
  5021. "fused_quant_a",
  5022. self._fused_quant_a_inner_span,
  5023. self._fused_quant_a_wide,
  5024. *super().compile_key(),
  5025. )
  5026. @cute.jit
  5027. def __call__(
  5028. self,
  5029. a_placeholder_ptr: cute.Pointer,
  5030. a_source_ptr: cute.Pointer,
  5031. b_ptr: cute.Pointer,
  5032. sfa_placeholder_ptr: cute.Pointer,
  5033. sfb_ptr: cute.Pointer,
  5034. c_ptr: cute.Pointer,
  5035. alpha_ptr: cute.Pointer,
  5036. m: cutlass.Int32,
  5037. current_stream: cuda.CUstream,
  5038. ):
  5039. a_tensor = cute.make_tensor(
  5040. a_placeholder_ptr,
  5041. layout=cute.make_ordered_layout((m, self._k, 1), order=(1, 0, 2)),
  5042. )
  5043. a_source = cute.make_tensor(
  5044. a_source_ptr,
  5045. layout=cute.make_ordered_layout((m, self._k, 1), order=(1, 0, 2)),
  5046. )
  5047. if cutlass.const_expr(self._b_tile_major):
  5048. b_layout = cute.make_layout(
  5049. (
  5050. (self._b_tile_n, self._n // self._b_tile_n),
  5051. (128, self._k // 128),
  5052. 1,
  5053. ),
  5054. stride=(
  5055. (128, self._b_tile_n * self._k),
  5056. (1, self._b_tile_n * 128),
  5057. self._n * self._k,
  5058. ),
  5059. )
  5060. else:
  5061. b_layout = cute.make_ordered_layout((self._n, self._k, 1), order=(1, 0, 2))
  5062. b_tensor = cute.make_tensor(b_ptr, layout=b_layout)
  5063. c_tensor = cute.make_tensor(
  5064. c_ptr,
  5065. layout=cute.make_ordered_layout((m, self._n, self._c_l), order=(1, 0, 2)),
  5066. )
  5067. alpha_tensor = cute.make_tensor(
  5068. alpha_ptr,
  5069. layout=cute.make_ordered_layout((1,), order=(0,)),
  5070. )
  5071. sfa_tensor = cute.make_tensor(
  5072. sfa_placeholder_ptr, layout=cute.make_layout((1,))
  5073. )
  5074. sfb_tensor = cute.make_tensor(sfb_ptr, layout=cute.make_layout((1,)))
  5075. policy = self._policy
  5076. DenseGemmKernel(
  5077. sf_vec_size=self._sf_vec_size,
  5078. mma_tiler_mn=self._mma_tiler_mn,
  5079. cluster_shape_mn=self._cluster_shape_mn,
  5080. mma_k=self._mma_k,
  5081. tile_k=self._tile_k,
  5082. single_work_tile_per_cta=policy.single_work_tile_per_cta,
  5083. direct_one_m_tile_scheduler=policy.direct_one_m_tile_scheduler,
  5084. split_k_slices=policy.split_k_slices,
  5085. split_k_atomic_bf16=policy.split_k_atomic_bf16,
  5086. large_m_unroll=False,
  5087. use_m1_non_tma_a=False,
  5088. use_m1_non_tma_c=policy.use_m1_non_tma,
  5089. use_m1_non_tma_sfa=False,
  5090. load_path="tma",
  5091. swap_ab=False,
  5092. sfb_k_reuse=self._sfb_k_reuse,
  5093. fused_quant_a=True,
  5094. fused_quant_a_inner_span=self._fused_quant_a_inner_span,
  5095. fused_quant_a_wide=self._fused_quant_a_wide,
  5096. atom_shape_24=self._atom_shape_24,
  5097. b_tile_major=self._b_tile_major,
  5098. target_occupancy=self._target_occupancy,
  5099. )(
  5100. a_tensor,
  5101. a_source,
  5102. alpha_tensor,
  5103. alpha_tensor,
  5104. b_tensor,
  5105. sfa_tensor,
  5106. sfb_tensor,
  5107. c_tensor,
  5108. a_tensor,
  5109. sfa_tensor,
  5110. sfa_tensor,
  5111. alpha_tensor,
  5112. self._max_active_clusters,
  5113. current_stream,
  5114. )
  5115. @functools.cache
  5116. def _get_compiled_dense_gemm_fused_quant_a(
  5117. n: int,
  5118. k: int,
  5119. c_dtype: Type[cutlass.Numeric],
  5120. policy: _DenseGemmPolicy,
  5121. mma_tiler_mn: Tuple[int, int],
  5122. sm_count: int,
  5123. sfb_k_reuse: bool,
  5124. b_tile_major: bool,
  5125. a_inner_span: int = 0,
  5126. kernel_c_l: int = 1,
  5127. a_wide: bool = False,
  5128. ) -> Callable:
  5129. launch = _DenseGemmFusedQuantALaunch(
  5130. n=n,
  5131. k=k,
  5132. l=1,
  5133. c_l=kernel_c_l,
  5134. a_major="k",
  5135. b_major="k",
  5136. c_major="n",
  5137. ab_dtype=cutlass.Float8E4M3FN,
  5138. sf_dtype=cutlass.Float8E8M0FNU,
  5139. c_dtype=c_dtype,
  5140. alpha_dtype=cutlass.Float32,
  5141. sf_vec_size=32,
  5142. mma_k=32,
  5143. tile_k=128,
  5144. mma_tiler_mn=mma_tiler_mn,
  5145. cluster_shape_mn=(1, 1),
  5146. policy=policy,
  5147. sm_count=sm_count,
  5148. sm_version="sm_120",
  5149. load_path="tma",
  5150. swap_ab=False,
  5151. sfb_k_reuse=sfb_k_reuse,
  5152. b_tile_major=b_tile_major,
  5153. fused_quant_a_inner_span=a_inner_span,
  5154. fused_quant_a_wide=a_wide,
  5155. )
  5156. compile_key = launch.compile_key()
  5157. raise_if_kernel_resolution_frozen(
  5158. "cute.compile",
  5159. target=launch,
  5160. cache_key=compile_key,
  5161. )
  5162. placeholders = [16] * 7
  5163. compiled = b12x_compile(
  5164. launch,
  5165. make_ptr(
  5166. cutlass.Float8E4M3FN,
  5167. placeholders[0],
  5168. cute.AddressSpace.gmem,
  5169. assumed_align=16,
  5170. ),
  5171. make_ptr(
  5172. cutlass.BFloat16, placeholders[1], cute.AddressSpace.gmem, assumed_align=16
  5173. ),
  5174. make_ptr(
  5175. cutlass.Float8E4M3FN,
  5176. placeholders[2],
  5177. cute.AddressSpace.gmem,
  5178. assumed_align=16,
  5179. ),
  5180. make_ptr(
  5181. cutlass.Float8E8M0FNU,
  5182. placeholders[3],
  5183. cute.AddressSpace.gmem,
  5184. assumed_align=16,
  5185. ),
  5186. make_ptr(
  5187. cutlass.Float8E8M0FNU,
  5188. placeholders[4],
  5189. cute.AddressSpace.gmem,
  5190. assumed_align=16,
  5191. ),
  5192. make_ptr(c_dtype, placeholders[5], cute.AddressSpace.gmem, assumed_align=16),
  5193. make_ptr(
  5194. cutlass.Float32, placeholders[6], cute.AddressSpace.gmem, assumed_align=16
  5195. ),
  5196. 1,
  5197. current_cuda_stream(),
  5198. compile_spec=KernelCompileSpec.from_key(
  5199. "gemm.dense_fused_quant_a", 4, compile_key
  5200. ),
  5201. )
  5202. def tensor_api(
  5203. source: torch.Tensor,
  5204. b: torch.Tensor,
  5205. sfb: torch.Tensor,
  5206. out: torch.Tensor,
  5207. alpha: torch.Tensor,
  5208. stream_int: Optional[int],
  5209. ) -> torch.Tensor:
  5210. source_ptr = source.data_ptr()
  5211. compiled(
  5212. make_ptr(
  5213. cutlass.Float8E4M3FN,
  5214. source_ptr,
  5215. cute.AddressSpace.gmem,
  5216. assumed_align=16,
  5217. ),
  5218. make_ptr(
  5219. cutlass.BFloat16, source_ptr, cute.AddressSpace.gmem, assumed_align=16
  5220. ),
  5221. make_ptr(
  5222. cutlass.Float8E4M3FN,
  5223. b.data_ptr(),
  5224. cute.AddressSpace.gmem,
  5225. assumed_align=16,
  5226. ),
  5227. make_ptr(
  5228. cutlass.Float8E8M0FNU,
  5229. source_ptr,
  5230. cute.AddressSpace.gmem,
  5231. assumed_align=16,
  5232. ),
  5233. make_ptr(
  5234. cutlass.Float8E8M0FNU,
  5235. sfb.data_ptr(),
  5236. cute.AddressSpace.gmem,
  5237. assumed_align=16,
  5238. ),
  5239. make_ptr(c_dtype, out.data_ptr(), cute.AddressSpace.gmem, assumed_align=16),
  5240. make_ptr(
  5241. cutlass.Float32,
  5242. alpha.data_ptr(),
  5243. cute.AddressSpace.gmem,
  5244. assumed_align=16,
  5245. ),
  5246. int(source.shape[0]),
  5247. cuda_stream_from_int_or_current(stream_int),
  5248. )
  5249. return out
  5250. return tensor_api
  5251. class _DenseGemmFusedQuantAGroupedLaunch(_DenseGemmLaunch):
  5252. """Grouped small-M MXFP8 launch quantizing a strided (optionally
  5253. inverse-RoPE) BF16 A source into each CTA's stages (WO-A)."""
  5254. def __init__(
  5255. self,
  5256. *args,
  5257. fused_quant_a_row_stride: int,
  5258. fused_quant_a_l_stride: int,
  5259. fused_quant_a_inv_rope: bool,
  5260. fused_quant_a_head_dim: int,
  5261. fused_quant_a_nope_dim: int,
  5262. fused_quant_a_rope_dim: int,
  5263. fused_quant_a_wide: bool,
  5264. positions_dtype: Type[cutlass.Numeric],
  5265. cos_sin_dtype: Type[cutlass.Numeric],
  5266. **kwargs,
  5267. ):
  5268. super().__init__(*args, **kwargs)
  5269. self._fused_quant_a_row_stride = int(fused_quant_a_row_stride)
  5270. self._fused_quant_a_l_stride = int(fused_quant_a_l_stride)
  5271. self._fused_quant_a_inv_rope = bool(fused_quant_a_inv_rope)
  5272. self._fused_quant_a_head_dim = int(fused_quant_a_head_dim)
  5273. self._fused_quant_a_nope_dim = int(fused_quant_a_nope_dim)
  5274. self._fused_quant_a_rope_dim = int(fused_quant_a_rope_dim)
  5275. self._fused_quant_a_wide = bool(fused_quant_a_wide)
  5276. self._positions_dtype = positions_dtype
  5277. self._cos_sin_dtype = cos_sin_dtype
  5278. def compile_key(self) -> tuple[object, ...]:
  5279. return (
  5280. "fused_quant_a_grouped",
  5281. self._fused_quant_a_row_stride,
  5282. self._fused_quant_a_l_stride,
  5283. self._fused_quant_a_inv_rope,
  5284. self._fused_quant_a_head_dim,
  5285. self._fused_quant_a_nope_dim,
  5286. self._fused_quant_a_rope_dim,
  5287. self._fused_quant_a_wide,
  5288. self._positions_dtype,
  5289. self._cos_sin_dtype,
  5290. *super().compile_key(),
  5291. )
  5292. @cute.jit
  5293. def __call__(
  5294. self,
  5295. a_placeholder_ptr: cute.Pointer,
  5296. a_source_ptr: cute.Pointer,
  5297. positions_ptr: cute.Pointer,
  5298. cos_sin_ptr: cute.Pointer,
  5299. b_ptr: cute.Pointer,
  5300. sfa_placeholder_ptr: cute.Pointer,
  5301. sfb_ptr: cute.Pointer,
  5302. c_ptr: cute.Pointer,
  5303. alpha_ptr: cute.Pointer,
  5304. m: cutlass.Int32,
  5305. cos_sin_len: cutlass.Int32,
  5306. current_stream: cuda.CUstream,
  5307. ):
  5308. a_tensor = cute.make_tensor(
  5309. a_placeholder_ptr,
  5310. layout=cute.make_ordered_layout((m, self._k, 1), order=(1, 0, 2)),
  5311. )
  5312. a_source = cute.make_tensor(
  5313. a_source_ptr,
  5314. layout=cute.make_ordered_layout((m, self._k, 1), order=(1, 0, 2)),
  5315. )
  5316. positions_tensor = cute.make_tensor(
  5317. positions_ptr, layout=cute.make_layout((m,))
  5318. )
  5319. cos_sin_tensor = cute.make_tensor(
  5320. cos_sin_ptr, layout=cute.make_layout((cos_sin_len,))
  5321. )
  5322. b_tensor = cute.make_tensor(
  5323. b_ptr,
  5324. layout=cute.make_ordered_layout(
  5325. (self._n, self._k, self._l), order=(1, 0, 2)
  5326. ),
  5327. )
  5328. c_tensor = cute.make_tensor(
  5329. c_ptr,
  5330. layout=cute.make_ordered_layout((m, self._n, self._c_l), order=(1, 0, 2)),
  5331. )
  5332. alpha_tensor = cute.make_tensor(
  5333. alpha_ptr,
  5334. layout=cute.make_ordered_layout((1,), order=(0,)),
  5335. )
  5336. sfa_tensor = cute.make_tensor(
  5337. sfa_placeholder_ptr, layout=cute.make_layout((1,))
  5338. )
  5339. sfb_tensor = cute.make_tensor(sfb_ptr, layout=cute.make_layout((1,)))
  5340. policy = self._policy
  5341. DenseGemmKernel(
  5342. sf_vec_size=self._sf_vec_size,
  5343. mma_tiler_mn=self._mma_tiler_mn,
  5344. cluster_shape_mn=self._cluster_shape_mn,
  5345. mma_k=self._mma_k,
  5346. tile_k=self._tile_k,
  5347. single_work_tile_per_cta=policy.single_work_tile_per_cta,
  5348. direct_one_m_tile_scheduler=policy.direct_one_m_tile_scheduler,
  5349. split_k_slices=policy.split_k_slices,
  5350. split_k_atomic_bf16=policy.split_k_atomic_bf16,
  5351. large_m_unroll=False,
  5352. use_m1_non_tma_a=False,
  5353. use_m1_non_tma_c=policy.use_m1_non_tma,
  5354. use_m1_non_tma_sfa=False,
  5355. load_path="tma",
  5356. swap_ab=False,
  5357. sfb_k_reuse=self._sfb_k_reuse,
  5358. fused_quant_a=True,
  5359. fused_quant_a_row_stride=self._fused_quant_a_row_stride,
  5360. fused_quant_a_l_stride=self._fused_quant_a_l_stride,
  5361. fused_quant_a_inv_rope=self._fused_quant_a_inv_rope,
  5362. fused_quant_a_head_dim=self._fused_quant_a_head_dim,
  5363. fused_quant_a_nope_dim=self._fused_quant_a_nope_dim,
  5364. fused_quant_a_rope_dim=self._fused_quant_a_rope_dim,
  5365. fused_quant_a_wide=self._fused_quant_a_wide,
  5366. atom_shape_24=self._atom_shape_24,
  5367. target_occupancy=self._target_occupancy,
  5368. )(
  5369. a_tensor,
  5370. a_source,
  5371. positions_tensor,
  5372. cos_sin_tensor,
  5373. b_tensor,
  5374. sfa_tensor,
  5375. sfb_tensor,
  5376. c_tensor,
  5377. a_tensor,
  5378. sfa_tensor,
  5379. sfa_tensor,
  5380. alpha_tensor,
  5381. self._max_active_clusters,
  5382. current_stream,
  5383. )
  5384. def _cutlass_positions_dtype(dtype: torch.dtype) -> Type[cutlass.Numeric]:
  5385. if dtype == torch.int64:
  5386. return cutlass.Int64
  5387. if dtype == torch.int32:
  5388. return cutlass.Int32
  5389. raise ValueError(f"fused inv-RoPE positions must be int32/int64, got {dtype}")
  5390. def _cutlass_cos_sin_dtype(dtype: torch.dtype) -> Type[cutlass.Numeric]:
  5391. if dtype == torch.bfloat16:
  5392. return cutlass.BFloat16
  5393. if dtype == torch.float32:
  5394. return cutlass.Float32
  5395. raise ValueError(f"fused inv-RoPE cos/sin cache must be bf16/fp32, got {dtype}")
  5396. @functools.cache
  5397. def _get_compiled_dense_gemm_fused_quant_a_grouped(
  5398. n: int,
  5399. k: int,
  5400. l: int,
  5401. policy: _DenseGemmPolicy,
  5402. mma_tiler_mn: Tuple[int, int],
  5403. sm_count: int,
  5404. sfb_k_reuse: bool,
  5405. a_row_stride: int,
  5406. a_l_stride: int,
  5407. inv_rope: bool,
  5408. head_dim: int,
  5409. nope_dim: int,
  5410. rope_dim: int,
  5411. a_wide: bool,
  5412. positions_dtype: Type[cutlass.Numeric],
  5413. cos_sin_dtype: Type[cutlass.Numeric],
  5414. ) -> Callable:
  5415. launch = _DenseGemmFusedQuantAGroupedLaunch(
  5416. n=n,
  5417. k=k,
  5418. l=l,
  5419. c_l=l,
  5420. a_major="k",
  5421. b_major="k",
  5422. c_major="n",
  5423. ab_dtype=cutlass.Float8E4M3FN,
  5424. sf_dtype=cutlass.Float8E8M0FNU,
  5425. c_dtype=cutlass.BFloat16,
  5426. alpha_dtype=cutlass.Float32,
  5427. sf_vec_size=32,
  5428. mma_k=32,
  5429. tile_k=128,
  5430. mma_tiler_mn=mma_tiler_mn,
  5431. cluster_shape_mn=(1, 1),
  5432. policy=policy,
  5433. sm_count=sm_count,
  5434. sm_version="sm_120",
  5435. load_path="tma",
  5436. swap_ab=False,
  5437. sfb_k_reuse=sfb_k_reuse,
  5438. fused_quant_a_row_stride=a_row_stride,
  5439. fused_quant_a_l_stride=a_l_stride,
  5440. fused_quant_a_inv_rope=inv_rope,
  5441. fused_quant_a_head_dim=head_dim,
  5442. fused_quant_a_nope_dim=nope_dim,
  5443. fused_quant_a_rope_dim=rope_dim,
  5444. fused_quant_a_wide=a_wide,
  5445. positions_dtype=positions_dtype,
  5446. cos_sin_dtype=cos_sin_dtype,
  5447. )
  5448. compile_key = launch.compile_key()
  5449. raise_if_kernel_resolution_frozen(
  5450. "cute.compile",
  5451. target=launch,
  5452. cache_key=compile_key,
  5453. )
  5454. placeholders = [16] * 9
  5455. compiled = b12x_compile(
  5456. launch,
  5457. make_ptr(
  5458. cutlass.Float8E4M3FN,
  5459. placeholders[0],
  5460. cute.AddressSpace.gmem,
  5461. assumed_align=16,
  5462. ),
  5463. make_ptr(
  5464. cutlass.BFloat16, placeholders[1], cute.AddressSpace.gmem, assumed_align=16
  5465. ),
  5466. make_ptr(
  5467. positions_dtype, placeholders[2], cute.AddressSpace.gmem, assumed_align=8
  5468. ),
  5469. make_ptr(
  5470. cos_sin_dtype, placeholders[3], cute.AddressSpace.gmem, assumed_align=4
  5471. ),
  5472. make_ptr(
  5473. cutlass.Float8E4M3FN,
  5474. placeholders[4],
  5475. cute.AddressSpace.gmem,
  5476. assumed_align=16,
  5477. ),
  5478. make_ptr(
  5479. cutlass.Float8E8M0FNU,
  5480. placeholders[5],
  5481. cute.AddressSpace.gmem,
  5482. assumed_align=16,
  5483. ),
  5484. make_ptr(
  5485. cutlass.Float8E8M0FNU,
  5486. placeholders[6],
  5487. cute.AddressSpace.gmem,
  5488. assumed_align=16,
  5489. ),
  5490. make_ptr(
  5491. cutlass.BFloat16, placeholders[7], cute.AddressSpace.gmem, assumed_align=16
  5492. ),
  5493. make_ptr(
  5494. cutlass.Float32, placeholders[8], cute.AddressSpace.gmem, assumed_align=16
  5495. ),
  5496. 1,
  5497. 1,
  5498. current_cuda_stream(),
  5499. compile_spec=KernelCompileSpec.from_key(
  5500. "gemm.dense_fused_quant_a_grouped", 2, compile_key
  5501. ),
  5502. )
  5503. def tensor_api(
  5504. source: torch.Tensor,
  5505. positions: torch.Tensor,
  5506. cos_sin: torch.Tensor,
  5507. b: torch.Tensor,
  5508. sfb: torch.Tensor,
  5509. out: torch.Tensor,
  5510. alpha: torch.Tensor,
  5511. stream_int: Optional[int],
  5512. ) -> torch.Tensor:
  5513. source_ptr = source.data_ptr()
  5514. compiled(
  5515. make_ptr(
  5516. cutlass.Float8E4M3FN,
  5517. source_ptr,
  5518. cute.AddressSpace.gmem,
  5519. assumed_align=16,
  5520. ),
  5521. make_ptr(
  5522. cutlass.BFloat16, source_ptr, cute.AddressSpace.gmem, assumed_align=16
  5523. ),
  5524. make_ptr(
  5525. positions_dtype,
  5526. positions.data_ptr(),
  5527. cute.AddressSpace.gmem,
  5528. assumed_align=8,
  5529. ),
  5530. make_ptr(
  5531. cos_sin_dtype,
  5532. cos_sin.data_ptr(),
  5533. cute.AddressSpace.gmem,
  5534. assumed_align=4,
  5535. ),
  5536. make_ptr(
  5537. cutlass.Float8E4M3FN,
  5538. b.data_ptr(),
  5539. cute.AddressSpace.gmem,
  5540. assumed_align=16,
  5541. ),
  5542. make_ptr(
  5543. cutlass.Float8E8M0FNU,
  5544. source_ptr,
  5545. cute.AddressSpace.gmem,
  5546. assumed_align=16,
  5547. ),
  5548. make_ptr(
  5549. cutlass.Float8E8M0FNU,
  5550. sfb.data_ptr(),
  5551. cute.AddressSpace.gmem,
  5552. assumed_align=16,
  5553. ),
  5554. make_ptr(
  5555. cutlass.BFloat16,
  5556. out.data_ptr(),
  5557. cute.AddressSpace.gmem,
  5558. assumed_align=16,
  5559. ),
  5560. make_ptr(
  5561. cutlass.Float32,
  5562. alpha.data_ptr(),
  5563. cute.AddressSpace.gmem,
  5564. assumed_align=16,
  5565. ),
  5566. int(source.shape[0]),
  5567. int(cos_sin.numel()),
  5568. cuda_stream_from_int_or_current(stream_int),
  5569. )
  5570. return out
  5571. return tensor_api
  5572. def dense_gemm_fused_quant_a_grouped(
  5573. source: torch.Tensor,
  5574. b: torch.Tensor,
  5575. sfb: torch.Tensor,
  5576. *,
  5577. groups: int,
  5578. out: Optional[torch.Tensor] = None,
  5579. positions: Optional[torch.Tensor] = None,
  5580. cos_sin_cache: Optional[torch.Tensor] = None,
  5581. head_dim: int = 0,
  5582. nope_dim: int = 0,
  5583. rope_dim: int = 0,
  5584. expected_m: Optional[int] = None,
  5585. sfb_k_replicated: bool = False,
  5586. mma_tiler_mn: Optional[Tuple[int, int]] = None,
  5587. stream: object = None,
  5588. ) -> torch.Tensor:
  5589. """Small-M grouped BF16-A -> MXFP8 GEMM quantizing A in each CTA (WO-A).
  5590. `source` is `[M, groups, K]` BF16 with contiguous trailing dims (rows may
  5591. be strided); logical GEMM operands are per-group `[M, K] x [N, K]`. When
  5592. `positions`/`cos_sin_cache` are given, the trailing `rope_dim` of every
  5593. `head_dim` block is inverse-RoPE-rotated before quantization.
  5594. """
  5595. if source.dtype != torch.bfloat16 or source.ndim != 3:
  5596. raise ValueError(
  5597. "fused grouped MXFP8 quantization requires BF16 [M, groups, K]"
  5598. )
  5599. m = int(source.shape[0])
  5600. k = int(source.shape[2])
  5601. if int(source.shape[1]) != groups:
  5602. raise ValueError(
  5603. f"source groups {int(source.shape[1])} != weight groups {groups}"
  5604. )
  5605. if source.stride(2) != 1 or source.stride(1) != k:
  5606. raise ValueError(
  5607. f"fused grouped MXFP8 A needs contiguous [groups, K] rows, got strides {source.stride()}"
  5608. )
  5609. row_stride = int(source.stride(0))
  5610. if m < 1 or m > 8 or k % 128 != 0 or row_stride % 8 != 0:
  5611. raise ValueError(
  5612. f"fused grouped MXFP8 quantization requires 1<=M<=8, K%128=0, row stride%8=0; "
  5613. f"got M={m}, K={k}, row_stride={row_stride}"
  5614. )
  5615. if b.ndim != 3 or int(b.shape[1]) != k or int(b.shape[2]) != groups:
  5616. raise ValueError(f"B must have shape [N,{k},{groups}], got {tuple(b.shape)}")
  5617. n = int(b.shape[0])
  5618. inv_rope = positions is not None or cos_sin_cache is not None
  5619. if inv_rope:
  5620. if positions is None or cos_sin_cache is None:
  5621. raise ValueError("inverse-RoPE needs both positions and cos_sin_cache")
  5622. if positions.shape != (m,):
  5623. raise ValueError(
  5624. f"positions must have shape {(m,)}, got {tuple(positions.shape)}"
  5625. )
  5626. if not positions.is_contiguous() or not cos_sin_cache.is_contiguous():
  5627. raise ValueError("positions and cos_sin_cache must be contiguous")
  5628. if cos_sin_cache.ndim != 2 or int(cos_sin_cache.shape[1]) != rope_dim:
  5629. raise ValueError(
  5630. f"cos_sin_cache must have shape [max_pos, {rope_dim}], got {tuple(cos_sin_cache.shape)}"
  5631. )
  5632. if (
  5633. head_dim <= 0
  5634. or nope_dim + rope_dim != head_dim
  5635. or head_dim % 32
  5636. or nope_dim % 32
  5637. or rope_dim % 32
  5638. or k % head_dim
  5639. ):
  5640. raise ValueError(
  5641. "fused inverse-RoPE requires 32-aligned head_dim = nope_dim + rope_dim "
  5642. f"dividing K, got head={head_dim}, nope={nope_dim}, rope={rope_dim}, K={k}"
  5643. )
  5644. positions_dtype = _cutlass_positions_dtype(positions.dtype)
  5645. cos_sin_dtype = _cutlass_cos_sin_dtype(cos_sin_cache.dtype)
  5646. else:
  5647. head_dim = nope_dim = rope_dim = 0
  5648. positions = source
  5649. cos_sin_cache = source
  5650. positions_dtype = cutlass.Int64
  5651. cos_sin_dtype = cutlass.BFloat16
  5652. sm_count = get_num_sm(source.device)
  5653. if mma_tiler_mn is None:
  5654. plan = _select_default_dense_gemm_plan(
  5655. m, n, k, sm_count, is_mxfp8=True, expected_m=expected_m
  5656. )
  5657. if plan.swap_ab or plan.load_path != "tma":
  5658. raise ValueError(
  5659. "fused grouped MXFP8 quantization requires the unswapped TMA plan"
  5660. )
  5661. mma_tiler_mn = plan.mma_tiler_mn
  5662. policy = _dense_gemm_policy_for(
  5663. m=m,
  5664. n=n,
  5665. k=k,
  5666. l=groups,
  5667. ab_dtype=cutlass.Float8E4M3FN,
  5668. c_dtype=cutlass.BFloat16,
  5669. mma_tiler_mn=mma_tiler_mn,
  5670. cluster_shape_mn=(1, 1),
  5671. sm_count=sm_count,
  5672. expected_m=expected_m,
  5673. )
  5674. if policy.split_k_slices != 1:
  5675. raise ValueError("fused grouped MXFP8 quantization does not support split-K")
  5676. if out is None:
  5677. out = _empty_dense_gemm_output(
  5678. m, n, groups, dtype=torch.bfloat16, device=source.device
  5679. )
  5680. if out.shape != (m, n, groups) or out.dtype != torch.bfloat16:
  5681. raise ValueError(
  5682. f"out must be BF16 with shape {(m, n, groups)}, got {out.dtype} {tuple(out.shape)}"
  5683. )
  5684. compiled = _get_compiled_dense_gemm_fused_quant_a_grouped(
  5685. n,
  5686. k,
  5687. groups,
  5688. policy,
  5689. mma_tiler_mn,
  5690. sm_count,
  5691. bool(sfb_k_replicated),
  5692. row_stride,
  5693. k,
  5694. bool(inv_rope),
  5695. int(head_dim),
  5696. int(nope_dim),
  5697. int(rope_dim),
  5698. m == 1,
  5699. positions_dtype,
  5700. cos_sin_dtype,
  5701. )
  5702. return compiled(
  5703. source,
  5704. positions,
  5705. cos_sin_cache,
  5706. b,
  5707. sfb,
  5708. out,
  5709. _cached_alpha_one(source.device),
  5710. cuda_stream_to_int(stream),
  5711. )
  5712. def _dense_gemm_target_occupancy(
  5713. *,
  5714. n: int,
  5715. k: int,
  5716. l: int,
  5717. ab_dtype: Type[cutlass.Numeric],
  5718. c_dtype: Type[cutlass.Numeric],
  5719. tile_k: int,
  5720. mma_tiler_mn: Tuple[int, int],
  5721. cluster_shape_mn: Tuple[int, int],
  5722. sm_count: int,
  5723. load_path: str,
  5724. swap_ab: bool,
  5725. b_tile_major: bool,
  5726. is_mxfp6: bool = False,
  5727. ) -> int:
  5728. tile_m, tile_n = mma_tiler_mn
  5729. n_tiles = ((n + tile_n - 1) // tile_n) * l
  5730. if (
  5731. # NOT is_mxfp6_ab_dtype(ab_dtype): the MX-FP6 path rewrites ab_dtype to
  5732. # Float8E4M3FN byte-containers before compiling, so the operand dtype
  5733. # cannot distinguish the two families here.
  5734. is_mxfp6
  5735. and c_dtype == cutlass.BFloat16
  5736. and tile_k == 128
  5737. and mma_tiler_mn == (16, 64)
  5738. and cluster_shape_mn == (1, 1)
  5739. and load_path == "tma"
  5740. and not swap_ab
  5741. and not b_tile_major
  5742. and n_tiles > sm_count
  5743. ):
  5744. # Two resident CTAs avoid a partial tail wave when the decode grid is
  5745. # larger than the SM count. Occupancy does not change MMA ordering.
  5746. return 2
  5747. return (
  5748. 2
  5749. if ab_dtype == cutlass.Float8E4M3FN
  5750. and c_dtype == cutlass.BFloat16
  5751. and tile_k == 128
  5752. and tile_m == 16
  5753. and k <= 1024
  5754. and cluster_shape_mn == (1, 1)
  5755. and load_path == "tma"
  5756. and not swap_ab
  5757. and not b_tile_major
  5758. and n_tiles >= 2 * sm_count
  5759. else 1
  5760. )
  5761. @functools.cache
  5762. def _get_compiled_dense_gemm(
  5763. n: int,
  5764. k: int,
  5765. l: int,
  5766. c_l: int,
  5767. a_major: str,
  5768. b_major: str,
  5769. c_major: str,
  5770. ab_dtype: Type[cutlass.Numeric],
  5771. sf_dtype: Type[cutlass.Numeric],
  5772. c_dtype: Type[cutlass.Numeric],
  5773. alpha_dtype: Type[cutlass.Numeric],
  5774. sf_vec_size: int,
  5775. mma_k: int,
  5776. tile_k: int,
  5777. mma_tiler_mn: Tuple[int, int],
  5778. cluster_shape_mn: Tuple[int, int],
  5779. policy: _DenseGemmPolicy,
  5780. sm_count: int,
  5781. sm_version: str,
  5782. load_path: str,
  5783. swap_ab: bool,
  5784. sfb_k_reuse: bool,
  5785. b_tile_major: bool,
  5786. quantize_c: bool = False,
  5787. alpha_is_one: bool = False,
  5788. direct_sfa_live16: bool = False,
  5789. direct_m1_wo_a_inputs: bool = False,
  5790. plain_fp8: bool = False,
  5791. block_fp8: bool = False,
  5792. ) -> Callable:
  5793. def _make_runtime_pointers(
  5794. input_tensors: Optional[List[torch.Tensor]],
  5795. quant_c_tensors: Optional[List[torch.Tensor]] = None,
  5796. ) -> List[cute.Pointer]:
  5797. if input_tensors is None:
  5798. (
  5799. a_data_ptr,
  5800. b_data_ptr,
  5801. sfa_data_ptr,
  5802. sfb_data_ptr,
  5803. c_data_ptr,
  5804. alpha_data_ptr,
  5805. ) = [16 for _ in range(6)]
  5806. else:
  5807. (
  5808. a_tensor_gpu,
  5809. b_tensor_gpu,
  5810. sfa_tensor_gpu,
  5811. sfb_tensor_gpu,
  5812. c_tensor_gpu,
  5813. alpha_tensor_gpu,
  5814. ) = input_tensors
  5815. (
  5816. a_data_ptr,
  5817. b_data_ptr,
  5818. sfa_data_ptr,
  5819. sfb_data_ptr,
  5820. c_data_ptr,
  5821. alpha_data_ptr,
  5822. ) = (
  5823. a_tensor_gpu.data_ptr(),
  5824. b_tensor_gpu.data_ptr(),
  5825. sfa_tensor_gpu.data_ptr(),
  5826. sfb_tensor_gpu.data_ptr(),
  5827. c_tensor_gpu.data_ptr(),
  5828. alpha_tensor_gpu.data_ptr(),
  5829. )
  5830. if quant_c_tensors is None:
  5831. quant_c_values_ptr = 16
  5832. quant_c_scale_rows_ptr = 16
  5833. quant_c_scale_mma_ptr = 16
  5834. else:
  5835. (
  5836. quant_c_values_gpu,
  5837. quant_c_scale_rows_gpu,
  5838. quant_c_scale_mma_gpu,
  5839. ) = quant_c_tensors
  5840. quant_c_values_ptr = quant_c_values_gpu.data_ptr()
  5841. quant_c_scale_rows_ptr = quant_c_scale_rows_gpu.data_ptr()
  5842. quant_c_scale_mma_ptr = quant_c_scale_mma_gpu.data_ptr()
  5843. return [
  5844. make_ptr(ab_dtype, a_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  5845. make_ptr(ab_dtype, b_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  5846. make_ptr(sf_dtype, sfa_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  5847. make_ptr(sf_dtype, sfb_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  5848. make_ptr(c_dtype, c_data_ptr, cute.AddressSpace.gmem, assumed_align=16),
  5849. make_ptr(
  5850. cutlass.Float8E4M3FN,
  5851. quant_c_values_ptr,
  5852. cute.AddressSpace.gmem,
  5853. assumed_align=16,
  5854. ),
  5855. make_ptr(
  5856. cutlass.Float8E8M0FNU,
  5857. quant_c_scale_rows_ptr,
  5858. cute.AddressSpace.gmem,
  5859. assumed_align=16,
  5860. ),
  5861. make_ptr(
  5862. cutlass.Float8E8M0FNU,
  5863. quant_c_scale_mma_ptr,
  5864. cute.AddressSpace.gmem,
  5865. assumed_align=16,
  5866. ),
  5867. make_ptr(
  5868. alpha_dtype, alpha_data_ptr, cute.AddressSpace.gmem, assumed_align=16
  5869. ),
  5870. ]
  5871. launch = _DenseGemmLaunch(
  5872. n=n,
  5873. k=k,
  5874. l=l,
  5875. c_l=c_l,
  5876. a_major=a_major,
  5877. b_major=b_major,
  5878. c_major=c_major,
  5879. ab_dtype=ab_dtype,
  5880. sf_dtype=sf_dtype,
  5881. c_dtype=c_dtype,
  5882. alpha_dtype=alpha_dtype,
  5883. sf_vec_size=sf_vec_size,
  5884. mma_k=mma_k,
  5885. tile_k=tile_k,
  5886. mma_tiler_mn=mma_tiler_mn,
  5887. cluster_shape_mn=cluster_shape_mn,
  5888. policy=policy,
  5889. sm_count=sm_count,
  5890. sm_version=sm_version,
  5891. load_path=load_path,
  5892. swap_ab=swap_ab,
  5893. sfb_k_reuse=sfb_k_reuse,
  5894. b_tile_major=b_tile_major,
  5895. quantize_c=quantize_c,
  5896. alpha_is_one=alpha_is_one,
  5897. direct_sfa_live16=direct_sfa_live16,
  5898. direct_m1_wo_a_inputs=direct_m1_wo_a_inputs,
  5899. plain_fp8=plain_fp8,
  5900. block_fp8=block_fp8,
  5901. target_occupancy=_dense_gemm_target_occupancy(
  5902. n=n,
  5903. k=k,
  5904. l=l,
  5905. ab_dtype=ab_dtype,
  5906. c_dtype=c_dtype,
  5907. tile_k=tile_k,
  5908. mma_tiler_mn=mma_tiler_mn,
  5909. cluster_shape_mn=cluster_shape_mn,
  5910. sm_count=sm_count,
  5911. load_path=load_path,
  5912. swap_ab=swap_ab,
  5913. b_tile_major=b_tile_major,
  5914. ),
  5915. )
  5916. compile_key = launch.compile_key()
  5917. raise_if_kernel_resolution_frozen(
  5918. "cute.compile",
  5919. target=launch,
  5920. cache_key=compile_key,
  5921. )
  5922. compiled_kernel = b12x_compile(
  5923. launch,
  5924. *_make_runtime_pointers(None),
  5925. 1,
  5926. current_cuda_stream(),
  5927. compile_spec=KernelCompileSpec.from_key("gemm.dense", 4, compile_key),
  5928. )
  5929. def tensor_api(
  5930. a_tensor_gpu: torch.Tensor,
  5931. b_tensor_gpu: torch.Tensor,
  5932. sfa_tensor_gpu: torch.Tensor,
  5933. sfb_tensor_gpu: torch.Tensor,
  5934. c_tensor_gpu: Optional[torch.Tensor] = None,
  5935. alpha_tensor_gpu: Optional[torch.Tensor] = None,
  5936. stream_int: Optional[int] = None,
  5937. quant_c_values_gpu: Optional[torch.Tensor] = None,
  5938. quant_c_scale_rows_gpu: Optional[torch.Tensor] = None,
  5939. quant_c_scale_mma_gpu: Optional[torch.Tensor] = None,
  5940. ) -> torch.Tensor:
  5941. m = a_tensor_gpu.shape[0]
  5942. if c_tensor_gpu is None:
  5943. c_tensor_gpu = torch.empty(
  5944. (m, n, c_l),
  5945. dtype=cutlass_to_torch_dtype(c_dtype),
  5946. device=a_tensor_gpu.device,
  5947. )
  5948. if alpha_tensor_gpu is None:
  5949. alpha_tensor_gpu = _cached_alpha_one(a_tensor_gpu.device)
  5950. quant_c_tensors = None
  5951. if quantize_c:
  5952. if (
  5953. quant_c_values_gpu is None
  5954. or quant_c_scale_rows_gpu is None
  5955. or quant_c_scale_mma_gpu is None
  5956. ):
  5957. raise ValueError("quantized C output tensors are required")
  5958. quant_c_tensors = [
  5959. quant_c_values_gpu,
  5960. quant_c_scale_rows_gpu,
  5961. quant_c_scale_mma_gpu,
  5962. ]
  5963. nonlocal compiled_kernel
  5964. compiled_kernel(
  5965. *_make_runtime_pointers(
  5966. [
  5967. a_tensor_gpu,
  5968. b_tensor_gpu,
  5969. sfa_tensor_gpu,
  5970. sfb_tensor_gpu,
  5971. c_tensor_gpu,
  5972. alpha_tensor_gpu,
  5973. ],
  5974. quant_c_tensors,
  5975. ),
  5976. m,
  5977. cuda_stream_from_int_or_current(stream_int),
  5978. )
  5979. return c_tensor_gpu
  5980. return tensor_api
  5981. def _dense_gemm_launch_flat(
  5982. a_tensor_gpu: torch.Tensor,
  5983. b_tensor_gpu: torch.Tensor,
  5984. sfa_tensor_gpu: torch.Tensor,
  5985. sfb_tensor_gpu: torch.Tensor,
  5986. c_tensor_gpu: torch.Tensor,
  5987. alpha_tensor_gpu: torch.Tensor,
  5988. n: int,
  5989. k: int,
  5990. l: int,
  5991. c_l: int,
  5992. ab_dtype: str,
  5993. sf_dtype: str,
  5994. c_dtype: str,
  5995. alpha_dtype: str,
  5996. sf_vec_size: int,
  5997. mma_k: int,
  5998. tile_k: int,
  5999. mma_tile_m: int,
  6000. mma_tile_n: int,
  6001. cluster_shape_m: int,
  6002. cluster_shape_n: int,
  6003. sm_count: int,
  6004. single_work_tile_per_cta: bool,
  6005. direct_one_m_tile_scheduler: bool,
  6006. use_m1_non_tma: bool,
  6007. split_k_slices: int,
  6008. split_k_atomic_bf16: bool,
  6009. large_m_unroll: bool,
  6010. load_path: str,
  6011. swap_ab: bool,
  6012. sfb_k_reuse: bool,
  6013. alpha_is_one: bool,
  6014. stream_int: Optional[int],
  6015. ) -> None:
  6016. b_tile_major = b_tensor_gpu.ndim == 5
  6017. policy = _DenseGemmPolicy(
  6018. single_work_tile_per_cta=single_work_tile_per_cta,
  6019. direct_one_m_tile_scheduler=direct_one_m_tile_scheduler,
  6020. use_m1_non_tma=use_m1_non_tma,
  6021. split_k_slices=split_k_slices,
  6022. split_k_atomic_bf16=split_k_atomic_bf16,
  6023. large_m_unroll=large_m_unroll,
  6024. )
  6025. compiled = _get_compiled_dense_gemm(
  6026. n=n,
  6027. k=k,
  6028. l=l,
  6029. c_l=c_l,
  6030. a_major="k",
  6031. b_major="k",
  6032. c_major="n",
  6033. ab_dtype=get_cutlass_dtype(ab_dtype),
  6034. sf_dtype=get_cutlass_dtype(sf_dtype),
  6035. c_dtype=get_cutlass_dtype(c_dtype),
  6036. alpha_dtype=get_cutlass_dtype(alpha_dtype),
  6037. sf_vec_size=sf_vec_size,
  6038. mma_k=mma_k,
  6039. tile_k=tile_k,
  6040. mma_tiler_mn=(mma_tile_m, mma_tile_n),
  6041. cluster_shape_mn=(cluster_shape_m, cluster_shape_n),
  6042. policy=policy,
  6043. sm_count=sm_count,
  6044. sm_version="sm_120",
  6045. load_path=load_path,
  6046. swap_ab=swap_ab,
  6047. sfb_k_reuse=sfb_k_reuse,
  6048. b_tile_major=b_tile_major,
  6049. alpha_is_one=alpha_is_one,
  6050. direct_sfa_live16=_use_direct_sfa_live16(
  6051. m=int(a_tensor_gpu.shape[0]),
  6052. n=n,
  6053. k=k,
  6054. l=l,
  6055. sf_vec_size=sf_vec_size,
  6056. tile_k=tile_k,
  6057. mma_tiler_mn=(mma_tile_m, mma_tile_n),
  6058. load_path=load_path,
  6059. swap_ab=swap_ab,
  6060. b_tile_major=b_tile_major,
  6061. sfb_k_reuse=sfb_k_reuse,
  6062. alpha_is_one=alpha_is_one,
  6063. is_mxfp8=ab_dtype == "float8_e4m3fn",
  6064. ),
  6065. direct_m1_wo_a_inputs=_use_direct_m1_wo_a_inputs(
  6066. m=int(a_tensor_gpu.shape[0]),
  6067. n=n,
  6068. k=k,
  6069. l=l,
  6070. sf_vec_size=sf_vec_size,
  6071. tile_k=tile_k,
  6072. mma_tiler_mn=(mma_tile_m, mma_tile_n),
  6073. load_path=load_path,
  6074. swap_ab=swap_ab,
  6075. b_tile_major=b_tile_major,
  6076. sfb_k_reuse=sfb_k_reuse,
  6077. is_mxfp8=ab_dtype == "float8_e4m3fn",
  6078. ),
  6079. )
  6080. compiled(
  6081. a_tensor_gpu=a_tensor_gpu,
  6082. b_tensor_gpu=b_tensor_gpu,
  6083. sfa_tensor_gpu=sfa_tensor_gpu,
  6084. sfb_tensor_gpu=sfb_tensor_gpu,
  6085. c_tensor_gpu=c_tensor_gpu,
  6086. alpha_tensor_gpu=alpha_tensor_gpu,
  6087. stream_int=stream_int,
  6088. )
  6089. @torch.library.custom_op(
  6090. "b12x::dense_gemm_launch",
  6091. mutates_args=("c_tensor_gpu",),
  6092. )
  6093. def _dense_gemm_launch_op(
  6094. a_tensor_gpu: torch.Tensor,
  6095. b_tensor_gpu: torch.Tensor,
  6096. sfa_tensor_gpu: torch.Tensor,
  6097. sfb_tensor_gpu: torch.Tensor,
  6098. c_tensor_gpu: torch.Tensor,
  6099. alpha_tensor_gpu: torch.Tensor,
  6100. n: int,
  6101. k: int,
  6102. l: int,
  6103. c_l: int,
  6104. ab_dtype: str,
  6105. sf_dtype: str,
  6106. c_dtype: str,
  6107. alpha_dtype: str,
  6108. sf_vec_size: int,
  6109. mma_k: int,
  6110. tile_k: int,
  6111. mma_tile_m: int,
  6112. mma_tile_n: int,
  6113. cluster_shape_m: int,
  6114. cluster_shape_n: int,
  6115. sm_count: int,
  6116. single_work_tile_per_cta: bool,
  6117. direct_one_m_tile_scheduler: bool,
  6118. use_m1_non_tma: bool,
  6119. split_k_slices: int,
  6120. split_k_atomic_bf16: bool,
  6121. large_m_unroll: bool,
  6122. load_path: str,
  6123. swap_ab: bool,
  6124. sfb_k_reuse: bool,
  6125. alpha_is_one: bool,
  6126. stream_int: Optional[int],
  6127. ) -> None:
  6128. _dense_gemm_launch_flat(
  6129. a_tensor_gpu,
  6130. b_tensor_gpu,
  6131. sfa_tensor_gpu,
  6132. sfb_tensor_gpu,
  6133. c_tensor_gpu,
  6134. alpha_tensor_gpu,
  6135. n,
  6136. k,
  6137. l,
  6138. c_l,
  6139. ab_dtype,
  6140. sf_dtype,
  6141. c_dtype,
  6142. alpha_dtype,
  6143. sf_vec_size,
  6144. mma_k,
  6145. tile_k,
  6146. mma_tile_m,
  6147. mma_tile_n,
  6148. cluster_shape_m,
  6149. cluster_shape_n,
  6150. sm_count,
  6151. single_work_tile_per_cta,
  6152. direct_one_m_tile_scheduler,
  6153. use_m1_non_tma,
  6154. split_k_slices,
  6155. split_k_atomic_bf16,
  6156. large_m_unroll,
  6157. load_path,
  6158. swap_ab,
  6159. sfb_k_reuse,
  6160. alpha_is_one,
  6161. stream_int,
  6162. )
  6163. @_dense_gemm_launch_op.register_fake
  6164. def _dense_gemm_launch_fake(
  6165. a_tensor_gpu: torch.Tensor,
  6166. b_tensor_gpu: torch.Tensor,
  6167. sfa_tensor_gpu: torch.Tensor,
  6168. sfb_tensor_gpu: torch.Tensor,
  6169. c_tensor_gpu: torch.Tensor,
  6170. alpha_tensor_gpu: torch.Tensor,
  6171. n: int,
  6172. k: int,
  6173. l: int,
  6174. c_l: int,
  6175. ab_dtype: str,
  6176. sf_dtype: str,
  6177. c_dtype: str,
  6178. alpha_dtype: str,
  6179. sf_vec_size: int,
  6180. mma_k: int,
  6181. tile_k: int,
  6182. mma_tile_m: int,
  6183. mma_tile_n: int,
  6184. cluster_shape_m: int,
  6185. cluster_shape_n: int,
  6186. sm_count: int,
  6187. single_work_tile_per_cta: bool,
  6188. direct_one_m_tile_scheduler: bool,
  6189. use_m1_non_tma: bool,
  6190. split_k_slices: int,
  6191. split_k_atomic_bf16: bool,
  6192. large_m_unroll: bool,
  6193. load_path: str,
  6194. swap_ab: bool,
  6195. sfb_k_reuse: bool,
  6196. alpha_is_one: bool,
  6197. stream_int: Optional[int],
  6198. ) -> None:
  6199. return None
  6200. _ALPHA_ONE_CACHE: dict = {}
  6201. def _cached_alpha_one(device: torch.device | str) -> torch.Tensor:
  6202. # Per-device cached scalar-one alpha, to avoid a per-call torch.ones((1,))
  6203. # host/device alloc on the generic FP8 dense-GEMM path. Mirrors
  6204. # wo_projection._cached_alpha_one (not imported -- wo_projection imports
  6205. # dense, so importing back would be circular).
  6206. resolved = torch.device(device)
  6207. if resolved.type == "cuda" and resolved.index is None:
  6208. resolved = torch.device("cuda", torch.cuda.current_device())
  6209. key = (resolved.type, resolved.index)
  6210. alpha = _ALPHA_ONE_CACHE.get(key)
  6211. if alpha is None or alpha.device != resolved:
  6212. alpha = torch.ones((1,), dtype=torch.float32, device=resolved)
  6213. _ALPHA_ONE_CACHE[key] = alpha
  6214. return alpha
  6215. def _empty_dense_gemm_output(
  6216. m: int,
  6217. n: int,
  6218. l: int,
  6219. *,
  6220. dtype: torch.dtype,
  6221. device: torch.device | str,
  6222. ) -> torch.Tensor:
  6223. """Allocate an `[M,N,L]` dense-GEMM output in the layout the kernel writes.
  6224. The CuTe dense GEMM hardcodes ``c_major='n'`` and builds the C tensor from
  6225. the data pointer with order ``(1,0,2)`` -- i.e. it writes the grouped output
  6226. as physical ``[L,M,N]`` (an ``[M,N,L]`` view with strides ``(N,1,M*N)``) and
  6227. ignores the runtime tensor's actual strides. A plain contiguous ``(M,N,L)``
  6228. buffer (strides ``(N*L,L,1)``) would scatter the ``L`` groups to the wrong
  6229. offsets, so back ``L>1`` with ``[L,M,N]`` physical storage. ``L==1`` is the
  6230. same either way. Mirrors ``empty_dense_gemm_mnl_view`` in wo_projection.
  6231. """
  6232. if l > 1:
  6233. return torch.empty((l, m, n), dtype=dtype, device=device).as_strided(
  6234. (m, n, l), (n, 1, m * n)
  6235. )
  6236. return torch.empty((m, n, l), dtype=dtype, device=device)
  6237. @torch.library.custom_op(
  6238. "b12x::dense_gemm_launch_functional",
  6239. mutates_args=(),
  6240. )
  6241. def _dense_gemm_launch_functional_op(
  6242. a_tensor_gpu: torch.Tensor,
  6243. b_tensor_gpu: torch.Tensor,
  6244. sfa_tensor_gpu: torch.Tensor,
  6245. sfb_tensor_gpu: torch.Tensor,
  6246. alpha_tensor_gpu: torch.Tensor,
  6247. n: int,
  6248. k: int,
  6249. l: int,
  6250. kernel_c_l: int,
  6251. ab_dtype: str,
  6252. sf_dtype: str,
  6253. c_dtype: str,
  6254. kernel_c_dtype: str,
  6255. alpha_dtype: str,
  6256. sf_vec_size: int,
  6257. mma_k: int,
  6258. tile_k: int,
  6259. mma_tile_m: int,
  6260. mma_tile_n: int,
  6261. cluster_shape_m: int,
  6262. cluster_shape_n: int,
  6263. sm_count: int,
  6264. single_work_tile_per_cta: bool,
  6265. direct_one_m_tile_scheduler: bool,
  6266. use_m1_non_tma: bool,
  6267. split_k_slices: int,
  6268. split_k_atomic_bf16: bool,
  6269. large_m_unroll: bool,
  6270. load_path: str,
  6271. swap_ab: bool,
  6272. sfb_k_reuse: bool,
  6273. alpha_is_one: bool,
  6274. stream_int: Optional[int],
  6275. ) -> torch.Tensor:
  6276. m = int(a_tensor_gpu.shape[0])
  6277. out = _empty_dense_gemm_output(
  6278. m,
  6279. n,
  6280. l,
  6281. dtype=cutlass_to_torch_dtype(get_cutlass_dtype(c_dtype)),
  6282. device=a_tensor_gpu.device,
  6283. )
  6284. split_k_output = int(split_k_slices) > 1
  6285. if split_k_output and split_k_atomic_bf16:
  6286. c_tensor_gpu = out
  6287. out.zero_()
  6288. elif split_k_output:
  6289. split_storage = torch.empty(
  6290. (split_k_slices, m, n),
  6291. dtype=torch.float32,
  6292. device=a_tensor_gpu.device,
  6293. )
  6294. c_tensor_gpu = split_storage.permute(1, 2, 0)
  6295. else:
  6296. c_tensor_gpu = out
  6297. _dense_gemm_launch_flat(
  6298. a_tensor_gpu,
  6299. b_tensor_gpu,
  6300. sfa_tensor_gpu,
  6301. sfb_tensor_gpu,
  6302. c_tensor_gpu,
  6303. alpha_tensor_gpu,
  6304. n,
  6305. k,
  6306. l,
  6307. kernel_c_l,
  6308. ab_dtype,
  6309. sf_dtype,
  6310. kernel_c_dtype,
  6311. alpha_dtype,
  6312. sf_vec_size,
  6313. mma_k,
  6314. tile_k,
  6315. mma_tile_m,
  6316. mma_tile_n,
  6317. cluster_shape_m,
  6318. cluster_shape_n,
  6319. sm_count,
  6320. single_work_tile_per_cta,
  6321. direct_one_m_tile_scheduler,
  6322. use_m1_non_tma,
  6323. split_k_slices,
  6324. split_k_atomic_bf16,
  6325. large_m_unroll,
  6326. load_path,
  6327. swap_ab,
  6328. sfb_k_reuse,
  6329. alpha_is_one,
  6330. stream_int,
  6331. )
  6332. if split_k_output and not split_k_atomic_bf16:
  6333. _reduce_split_k2_bf16(c_tensor_gpu, out, m=m, n=n)
  6334. return out
  6335. @_dense_gemm_launch_functional_op.register_fake
  6336. def _dense_gemm_launch_functional_fake(
  6337. a_tensor_gpu: torch.Tensor,
  6338. b_tensor_gpu: torch.Tensor,
  6339. sfa_tensor_gpu: torch.Tensor,
  6340. sfb_tensor_gpu: torch.Tensor,
  6341. alpha_tensor_gpu: torch.Tensor,
  6342. n: int,
  6343. k: int,
  6344. l: int,
  6345. kernel_c_l: int,
  6346. ab_dtype: str,
  6347. sf_dtype: str,
  6348. c_dtype: str,
  6349. kernel_c_dtype: str,
  6350. alpha_dtype: str,
  6351. sf_vec_size: int,
  6352. mma_k: int,
  6353. tile_k: int,
  6354. mma_tile_m: int,
  6355. mma_tile_n: int,
  6356. cluster_shape_m: int,
  6357. cluster_shape_n: int,
  6358. sm_count: int,
  6359. single_work_tile_per_cta: bool,
  6360. direct_one_m_tile_scheduler: bool,
  6361. use_m1_non_tma: bool,
  6362. split_k_slices: int,
  6363. split_k_atomic_bf16: bool,
  6364. large_m_unroll: bool,
  6365. load_path: str,
  6366. swap_ab: bool,
  6367. sfb_k_reuse: bool,
  6368. alpha_is_one: bool,
  6369. stream_int: Optional[int],
  6370. ) -> torch.Tensor:
  6371. del (
  6372. b_tensor_gpu,
  6373. sfa_tensor_gpu,
  6374. sfb_tensor_gpu,
  6375. alpha_tensor_gpu,
  6376. k,
  6377. kernel_c_l,
  6378. ab_dtype,
  6379. sf_dtype,
  6380. kernel_c_dtype,
  6381. alpha_dtype,
  6382. sf_vec_size,
  6383. mma_k,
  6384. tile_k,
  6385. mma_tile_m,
  6386. mma_tile_n,
  6387. cluster_shape_m,
  6388. cluster_shape_n,
  6389. sm_count,
  6390. single_work_tile_per_cta,
  6391. direct_one_m_tile_scheduler,
  6392. use_m1_non_tma,
  6393. split_k_slices,
  6394. split_k_atomic_bf16,
  6395. large_m_unroll,
  6396. load_path,
  6397. swap_ab,
  6398. sfb_k_reuse,
  6399. alpha_is_one,
  6400. stream_int,
  6401. )
  6402. return _empty_dense_gemm_output(
  6403. int(a_tensor_gpu.shape[0]),
  6404. n,
  6405. l,
  6406. dtype=cutlass_to_torch_dtype(get_cutlass_dtype(c_dtype)),
  6407. device=a_tensor_gpu.device,
  6408. )
  6409. def _select_default_mma_tiler_mn(
  6410. m: int,
  6411. n: int,
  6412. sm_count: int,
  6413. *,
  6414. is_mxfp8: bool,
  6415. is_mxfp6: bool = False,
  6416. expected_m: Optional[int] = None,
  6417. k: Optional[int] = None,
  6418. ) -> Tuple[int, int]:
  6419. coarse_tile = (128, 128)
  6420. if is_mxfp6:
  6421. # Decode and speculative verification fit in one 16-row M tile. A
  6422. # declared expected-M regime hint owns the decision when present.
  6423. plan_m = expected_m if expected_m is not None else m
  6424. if n > 1536 and plan_m <= 16:
  6425. return _FP6_DECODE_TILE
  6426. if n > 1536:
  6427. # The wide-N prefill choice is independent of live M, preserving
  6428. # one kernel per expected-M regime and (N, K) shape.
  6429. return _FP6_PREFILL_TILE
  6430. return coarse_tile
  6431. # The serving WO-B prefill GEMM is [M,4096] x [4096,4096]. DeepGEMM's
  6432. # specialized O-projection dispatch switches it from BM64/BK128 to
  6433. # BM128/BK64 at M>=2048, and the same exact-shape switch wins in b12x.
  6434. # WO-A is deliberately excluded: its four grouped [M,512]x[1024,512]
  6435. # GEMMs remain faster with BM64/BK128 on this kernel.
  6436. if (
  6437. is_mxfp8
  6438. and expected_m is not None
  6439. and expected_m >= 2048
  6440. and k is not None
  6441. and (n, k) == (4096, 4096)
  6442. ):
  6443. return (128, 128)
  6444. if is_mxfp8 and n > 1536:
  6445. # DeepGEMM-style regime hint. When a caller declares expected_m, pick the
  6446. # per-regime optimal tile and key the compile on it: ONE kernel per
  6447. # (N,K,expected_m), reused for every live M in that regime under frozen
  6448. # resolution (M-independent within the regime). Probe optima
  6449. # (benchmarks/probe_dense_fp8_tile_sweep.py): exact M=1 -> 16x64
  6450. # (flushed common-shape decode sweep); expected_m=2..8 -> 16x128.
  6451. # The DSV4 TP2 q_b shape keeps that tile through M=16; its 16-row tile
  6452. # sustains two resident CTAs per SM, while 32x128 drops to one and loses
  6453. # the evict-first short-K load policy. Other wide-N shapes retain the
  6454. # existing 32x128 small-batch regime.
  6455. # <=128 (small batch) -> 32x128 (~25% faster than 64x128 at M=32..128);
  6456. # else -> 64x128 (the M-independent default, good to prefill).
  6457. if expected_m is not None:
  6458. # BM64/BK128 is the 20-SM Spark q_b winner. The 188-SM RTX audit
  6459. # keeps the following BM128/BK64 specialization instead.
  6460. if (
  6461. expected_m >= 2048
  6462. and (n, k) == (16384, 1024)
  6463. and _dense_spark_policy_for_sm_count(sm_count)
  6464. ):
  6465. return (64, 128)
  6466. if expected_m >= 2048 and n >= 16384 and k is not None and k <= 1024:
  6467. return (128, 128)
  6468. if expected_m == 1:
  6469. return (16, 64)
  6470. # 48-SM Spark q_b decode: (16,128) yields 128 N-tiles over the 96
  6471. # resident CTAs (occupancy 2), so the remainder wave streams B with
  6472. # only 32 CTAs and drops below the sustained-read ceiling. (16,64)
  6473. # quantizes the tail at 64 columns with 2/3 of CTAs still active,
  6474. # matching the M=1 (16,64) profile that already runs at ceiling.
  6475. # RTX keeps the probe-swept (16,128).
  6476. if (
  6477. expected_m <= 16
  6478. and (n, k) == (16384, 1024)
  6479. and _dense_spark_policy_for_sm_count(sm_count)
  6480. ):
  6481. return (16, 64)
  6482. if expected_m <= 8 or (expected_m <= 16 and (n, k) == (16384, 1024)):
  6483. return (16, 128)
  6484. if expected_m <= 128:
  6485. return (32, 128)
  6486. return (64, 128)
  6487. # No regime hint: keep the true single-token decode specialization and
  6488. # use the decode-tuned tile for tiny standalone graph shapes. Broader
  6489. # live-M reuse still falls back to the M-independent prefill-safe tile.
  6490. if m == 1:
  6491. return (16, 64)
  6492. if m <= 8:
  6493. return (16, 128)
  6494. # Wide-N MXFP8: the 128x128 pin spans only ceil(N/128) column tiles, so
  6495. # at small/medium M it launches ~32-64 CTAs and runs flat (~80us, B-BW
  6496. # starved). It is in fact the WORST tile at every M (geomean ~121us over
  6497. # M=2..4096; see benchmarks/probe_dense_fp8_tile_sweep.py). 64x128 is the
  6498. # best M-INDEPENDENT tile: it beats 128x128 at every M (1.1x-2.4x; geomean
  6499. # ~69us) with byte-identical output. M-independence is required because
  6500. # dense serving warms one kernel per (N,K) and reuses it for all live M
  6501. # under frozen resolution (see test_block_fp8_linear_small_live_m_reuses_
  6502. # prefill_dense_kernel) -- an M-dependent tile forces an illegal recompile
  6503. # mid-serve. (Smaller 32x128/16x128 are faster at M<=128 but regress
  6504. # prefill M>=2k and would break that one-kernel-per-(N,K) reuse contract.)
  6505. return (64, 128)
  6506. if is_mxfp8:
  6507. # Narrow-N MXFP8 (n <= 1536; the n > 1536 case returned above). The
  6508. # (128,128) coarse tile spans only ceil(N/128) column tiles (<=12 at
  6509. # N<=1536), so at M>=512 it launches ~32-48 CTAs on a 188-SM part and
  6510. # runs CTA-starved -- 2x-3.5x slower than a CTA-multiplying tile
  6511. # (probe_dense_fp8_tile_sweep.py: N=1024 M=512 (128,128)=63.5us vs
  6512. # (64,64)=18.4us; N=1536 M=512 (128,128)=65.5us vs (64,128)=24.6us).
  6513. # Mirror the wide-N expected_m design where we have data. Exact M=1
  6514. # gets the flushed common-shape decode winner (16,64). Declared prefill
  6515. # (expected_m>128) -> (64,128): the best narrow-N tile at M>=512 for
  6516. # both N=1024 and N=1536 across M=512..8192 (probe sweep), recovering
  6517. # both the M~512 cliff and the large-M tail (N=1024 M=4096:
  6518. # (64,128)=80us vs (64,64)=105us vs (128,128)=125us). Other
  6519. # decode/small and the no-hint default use the M-independent (64,64)
  6520. # (max CTAs; best at M<=512), preserving the one-kernel-per-(N,K) reuse
  6521. # contract.
  6522. if expected_m == 1 or (expected_m is None and m == 1):
  6523. return (16, 64)
  6524. if expected_m is not None and expected_m > 128:
  6525. return (64, 128)
  6526. return (64, 64)
  6527. plan_m = expected_m if expected_m is not None else m
  6528. if plan_m == 1 and k is not None:
  6529. # Flushed M=1 FP4 probe (benchmarks/probe_dense_fp4_tile_load_sweep.py)
  6530. # across the repo's common shapes:
  6531. # * wide/medium N: (64,128)/TMA has the best geomean and wins nearly all
  6532. # shapes.
  6533. # * N=1024,K=5376: (64,64)/TMA wins the boundary by a small margin.
  6534. # * N<=512 with long K: (64,32)/TMA+swap_ab is the only clear tiny-N win.
  6535. # Keep the tile selector tile-only; the launch planner below attaches
  6536. # swap_ab to the narrow tile.
  6537. if n <= 512 and k >= 4096:
  6538. return (64, 32)
  6539. if n <= 1024:
  6540. return (64, 64)
  6541. return (64, 128)
  6542. coarse_tiles = ((m + coarse_tile[0] - 1) // coarse_tile[0]) * (
  6543. (n + coarse_tile[1] - 1) // coarse_tile[1]
  6544. )
  6545. # The coarse CTA-count heuristic misses exact-small-M, wide-N cases: a wide
  6546. # N dimension can generate plenty of CTAs even while each 128-row M tile is
  6547. # mostly empty. Keep using the narrower 64x128 tile while the 128x128 plan
  6548. # still leaves the GPU below the existing half-SM occupancy proxy.
  6549. if n > 1536:
  6550. if m <= 64:
  6551. return (64, 128)
  6552. if m <= 256 and coarse_tiles < max(1, sm_count // 2):
  6553. return (64, 128)
  6554. if m <= 128 and coarse_tiles < max(1, sm_count // 2):
  6555. if n > 1536:
  6556. return (64, 128)
  6557. medium_tile = (128, 64)
  6558. medium_tiles = ((m + medium_tile[0] - 1) // medium_tile[0]) * (
  6559. (n + medium_tile[1] - 1) // medium_tile[1]
  6560. )
  6561. if medium_tiles < max(1, sm_count // 2):
  6562. return (64, 64)
  6563. return (128, 64)
  6564. return coarse_tile
  6565. def _select_mxfp8_tile_k(
  6566. m: int,
  6567. n: int,
  6568. k: int,
  6569. expected_m: Optional[int],
  6570. sm_count: int,
  6571. ) -> int:
  6572. # Keep tile-M and tile-K as one hardware-specific q_b plan: Spark uses
  6573. # BM64/BK128, while the RTX specialization below remains BM128/BK64.
  6574. if (
  6575. expected_m is not None
  6576. and expected_m >= 2048
  6577. and (n, k) == (16384, 1024)
  6578. and _dense_spark_policy_for_sm_count(sm_count)
  6579. ):
  6580. return 128
  6581. # BK64 is an explicitly hinted production specialization. Choosing it from
  6582. # live M when expected_m is absent would change both tile K and generated
  6583. # code at M=2048, violating the no-hint frozen-resolution reuse contract.
  6584. hinted_bk64 = (
  6585. expected_m is not None
  6586. and expected_m >= 2048
  6587. and ((n >= 16384 and k <= 1024) or (n, k) == (4096, 4096))
  6588. )
  6589. return 64 if hinted_bk64 else 128
  6590. def _validate_mxfp8_bk64_plan(
  6591. tile_k: int,
  6592. mma_tiler_mn: Tuple[int, int],
  6593. swap_ab: bool,
  6594. ) -> None:
  6595. if tile_k == 64 and (mma_tiler_mn[0] != 128 or swap_ab):
  6596. raise ValueError(
  6597. "MXFP8 BK64 packed-scale staging requires an unswapped "
  6598. f"128-row tile, got tile={mma_tiler_mn}, swap_ab={swap_ab}"
  6599. )
  6600. def _select_default_dense_gemm_plan(
  6601. m: int,
  6602. n: int,
  6603. k: int,
  6604. sm_count: int,
  6605. *,
  6606. is_mxfp8: bool,
  6607. is_mxfp6: bool = False,
  6608. expected_m: Optional[int] = None,
  6609. ) -> _DenseGemmPlan:
  6610. tile = _select_default_mma_tiler_mn(
  6611. m,
  6612. n,
  6613. sm_count,
  6614. is_mxfp8=is_mxfp8,
  6615. is_mxfp6=is_mxfp6,
  6616. expected_m=expected_m,
  6617. k=k,
  6618. )
  6619. return _DenseGemmPlan(
  6620. mma_tiler_mn=tile,
  6621. load_path="tma",
  6622. swap_ab=(not is_mxfp8 and not is_mxfp6 and tile[1] < 64),
  6623. )
  6624. def dense_gemm_fused_quant_a(
  6625. source: torch.Tensor,
  6626. b: torch.Tensor,
  6627. sfb: torch.Tensor,
  6628. *,
  6629. out: Optional[torch.Tensor] = None,
  6630. expected_m: Optional[int] = None,
  6631. sfb_k_replicated: bool = False,
  6632. rhs_values_tiled: Optional[torch.Tensor] = None,
  6633. a_inner_span: int = 0,
  6634. mma_tiler_mn: Optional[Tuple[int, int]] = None,
  6635. _atomic_output_precleared: bool = False,
  6636. stream: object = None,
  6637. ) -> torch.Tensor:
  6638. """Small-M BF16-A -> MXFP8 GEMM with activation quantization in each CTA.
  6639. a_inner_span > 0 reads A from an L-blocked source instead of contiguous
  6640. rows: `source` is the `[M, span, K/span]` dense-GEMM mnl view over physical
  6641. `[K/span, M, span]` storage (the WO tmp group-major layout). Follows the
  6642. dense_gemm split-K policy (FP32 partials + fused reduce) instead of forcing
  6643. a single un-split kernel, which loses ~2x at M=1 for N,K >= 4096.
  6644. """
  6645. a_inner_span = int(a_inner_span)
  6646. if source.dtype != torch.bfloat16:
  6647. raise ValueError("fused MXFP8 activation quantization requires BF16 A")
  6648. if a_inner_span == 0:
  6649. if source.ndim != 2 or not source.is_contiguous():
  6650. raise ValueError(
  6651. "fused MXFP8 activation quantization requires contiguous BF16 [M,K]"
  6652. )
  6653. m, k = map(int, source.shape)
  6654. else:
  6655. if a_inner_span % 32 != 0:
  6656. raise ValueError(
  6657. f"fused MXFP8 a_inner_span must be a multiple of 32, got {a_inner_span}"
  6658. )
  6659. if source.ndim != 3 or int(source.shape[1]) != a_inner_span:
  6660. raise ValueError(
  6661. "L-blocked fused MXFP8 A requires an [M, span, K/span] view, "
  6662. f"got {tuple(source.shape)} for span={a_inner_span}"
  6663. )
  6664. m = int(source.shape[0])
  6665. k = a_inner_span * int(source.shape[2])
  6666. if source.stride() != (a_inner_span, 1, m * a_inner_span):
  6667. raise ValueError(
  6668. "L-blocked fused MXFP8 A must be a dense-GEMM mnl view over "
  6669. f"physical [K/span, M, span] storage, got strides {source.stride()}"
  6670. )
  6671. if m < 1 or m > 8 or k % 128 != 0:
  6672. raise ValueError(
  6673. f"fused MXFP8 activation quantization requires 1<=M<=8 and K%128=0, got M={m}, K={k}"
  6674. )
  6675. if b.ndim != 3 or int(b.shape[1]) != k or int(b.shape[2]) != 1:
  6676. raise ValueError(f"B must have shape [N,{k},1], got {tuple(b.shape)}")
  6677. n = int(b.shape[0])
  6678. sm_count = get_num_sm(source.device)
  6679. if mma_tiler_mn is None:
  6680. plan = _select_default_dense_gemm_plan(
  6681. m, n, k, sm_count, is_mxfp8=True, expected_m=expected_m
  6682. )
  6683. if plan.swap_ab or plan.load_path != "tma":
  6684. raise ValueError(
  6685. "fused MXFP8 activation quantization requires the unswapped TMA plan"
  6686. )
  6687. mma_tiler_mn = plan.mma_tiler_mn
  6688. b_launch = b
  6689. if rhs_values_tiled is not None:
  6690. expected_tiled_shape = (1, 32, 32, 128, 128)
  6691. if (n, k) != (4096, 4096) or mma_tiler_mn not in (
  6692. (16, 64),
  6693. (16, 128),
  6694. ):
  6695. raise ValueError(
  6696. "tile-major fused-quant RHS is restricted to the production "
  6697. "WO-B 16xN/BK128 plans"
  6698. )
  6699. if (
  6700. rhs_values_tiled.shape != expected_tiled_shape
  6701. or rhs_values_tiled.dtype != b.dtype
  6702. or rhs_values_tiled.device != b.device
  6703. or not rhs_values_tiled.is_contiguous()
  6704. ):
  6705. raise ValueError(
  6706. "tile-major fused-quant RHS must be contiguous with shape "
  6707. f"{expected_tiled_shape}, dtype {b.dtype}, and device {b.device}; "
  6708. f"got shape={tuple(rhs_values_tiled.shape)}, "
  6709. f"dtype={rhs_values_tiled.dtype}, device={rhs_values_tiled.device}"
  6710. )
  6711. b_launch = rhs_values_tiled
  6712. policy = _dense_gemm_policy_for(
  6713. m=m,
  6714. n=n,
  6715. k=k,
  6716. l=1,
  6717. ab_dtype=cutlass.Float8E4M3FN,
  6718. c_dtype=cutlass.BFloat16,
  6719. mma_tiler_mn=mma_tiler_mn,
  6720. cluster_shape_mn=(1, 1),
  6721. sm_count=sm_count,
  6722. expected_m=expected_m,
  6723. )
  6724. split_k_slices = policy.split_k_slices
  6725. split_k_output = split_k_slices > 1
  6726. split_k_atomic_bf16 = split_k_output and policy.split_k_atomic_bf16
  6727. if out is None:
  6728. if _atomic_output_precleared:
  6729. raise ValueError("a precleared fused-quant output must be caller-owned")
  6730. out = torch.empty((m, n, 1), dtype=torch.bfloat16, device=source.device)
  6731. if out.shape != (m, n, 1) or out.dtype != torch.bfloat16:
  6732. raise ValueError(
  6733. f"out must be BF16 with shape {(m, n, 1)}, got {out.dtype} {tuple(out.shape)}"
  6734. )
  6735. split_storage = None
  6736. if split_k_atomic_bf16:
  6737. if not _atomic_output_precleared:
  6738. out.zero_()
  6739. kernel_c_l = 1
  6740. kernel_c_dtype = cutlass.BFloat16
  6741. c_tensor_gpu = out
  6742. elif split_k_output:
  6743. split_storage = torch.empty(
  6744. (split_k_slices, m, n), dtype=torch.float32, device=source.device
  6745. )
  6746. kernel_c_l = split_k_slices
  6747. kernel_c_dtype = cutlass.Float32
  6748. c_tensor_gpu = split_storage
  6749. else:
  6750. kernel_c_l = 1
  6751. kernel_c_dtype = cutlass.BFloat16
  6752. c_tensor_gpu = out
  6753. compiled = _get_compiled_dense_gemm_fused_quant_a(
  6754. n,
  6755. k,
  6756. kernel_c_dtype,
  6757. policy,
  6758. mma_tiler_mn,
  6759. sm_count,
  6760. bool(sfb_k_replicated),
  6761. rhs_values_tiled is not None,
  6762. a_inner_span,
  6763. kernel_c_l,
  6764. m == 1,
  6765. )
  6766. compiled(
  6767. source,
  6768. b_launch,
  6769. sfb,
  6770. c_tensor_gpu,
  6771. _cached_alpha_one(source.device),
  6772. cuda_stream_to_int(stream),
  6773. )
  6774. if split_storage is not None:
  6775. _reduce_split_k2_bf16(split_storage.permute(1, 2, 0), out, m=m, n=n)
  6776. return out
  6777. def _expand_packed_mxfp6_ab(t: torch.Tensor, num_codes: int) -> torch.Tensor:
  6778. """Expand a 3:4-packed FP6 operand ``(X, packed_k, L)`` to byte-containers.
  6779. Returns an ``(X, num_codes, L)`` uint8 view whose underlying memory is laid out
  6780. K-major (matching ``a_major``/``b_major == "k"``), so the compiled kernel reads
  6781. each FP6 code from one byte. The launch only uses ``data_ptr`` plus the compiled
  6782. ``(X, K, L)`` layout, so the returned view's strides need not be contiguous; the
  6783. contiguous backing buffer is kept alive by the returned view.
  6784. """
  6785. t_lxk = t.permute(2, 0, 1).contiguous() # (L, X, packed_k), packed_k fastest
  6786. e_lxk = expand_mxfp6_packed_to_bytes(t_lxk, num_codes) # (L, X, num_codes)
  6787. return e_lxk.permute(1, 2, 0) # (X, num_codes, L), K stride 1
  6788. def dense_gemm(
  6789. lhs: Tuple[torch.Tensor, torch.Tensor],
  6790. rhs: Tuple[torch.Tensor, torch.Tensor],
  6791. out: Optional[torch.Tensor] = None,
  6792. *,
  6793. ab_dtype: str,
  6794. sf_dtype: str,
  6795. c_dtype: str,
  6796. sf_vec_size: int,
  6797. sm_count: Optional[int] = None,
  6798. mma_tiler_mn: Optional[Tuple[int, int]] = None,
  6799. cluster_shape_mn: Tuple[int, int] = (1, 1),
  6800. alpha: Optional[torch.Tensor] = None,
  6801. alpha_dtype: Optional[str] = None,
  6802. expected_m: Optional[int] = None,
  6803. load_path: Optional[Literal["tma", "cpasync"]] = None,
  6804. swap_ab: Optional[bool] = None,
  6805. sfb_k_replicated: bool = False,
  6806. rhs_values_tiled: Optional[torch.Tensor] = None,
  6807. _quantized_c: Optional[Tuple[torch.Tensor, torch.Tensor, torch.Tensor]] = None,
  6808. stream: object = None,
  6809. a_preexpanded: bool = False,
  6810. b_preexpanded: bool = False,
  6811. b_packed: bool = False,
  6812. a_fmt: Optional[str] = None,
  6813. b_fmt: Optional[str] = None,
  6814. x_bf16: Optional[torch.Tensor] = None,
  6815. w_gscale: Optional[torch.Tensor] = None,
  6816. plain_fp8: bool = False,
  6817. row_scale: Optional[torch.Tensor] = None,
  6818. block_fp8: bool = False,
  6819. ) -> torch.Tensor:
  6820. """Execute dense block-scaled GEMM for one expert-major batch stack.
  6821. expected_m: optional regime hint (DeepGEMM-style). When set, the default tile
  6822. is chosen for that representative M instead of being M-independent, giving a
  6823. per-regime-optimal kernel that is still reused across all live M in the regime
  6824. (e.g. expected_m<=128 selects a decode-tuned tile). Ignored when mma_tiler_mn
  6825. is given. Live M stays a runtime arg; only the tile (a compile key) changes.
  6826. MX-FP6 (``ab_dtype`` in ``float6_e3m2fn``/``float6_e2m3fn``) parameters:
  6827. ``b_preexpanded``: when True the RHS is already in 1-byte-per-code FP8
  6828. container layout (the output of ``_expand_packed_mxfp6_ab``) and the
  6829. per-call weight expansion is skipped. Callers hoist the expansion to load
  6830. time so the static weight is unpacked once instead of every token.
  6831. ``a_preexpanded``: same for the LHS — its shape is then ``(M, K, L)`` with
  6832. one code per byte (e.g. the quantizer's ``emit="bytes"`` output), so the
  6833. logical K is read directly from the shape and no expansion runs.
  6834. ``b_packed``: native packed-FP6 streaming. The RHS stays in the 3:4-packed
  6835. wire format ``(N, 3K/4, L)``; the kernel TMA-streams the packed bytes and
  6836. expands to byte-containers in smem. 25% less B HBM traffic than the
  6837. byte-container layout and no expanded copy resident in VRAM. MX-FP6 only;
  6838. mutually exclusive with ``b_preexpanded``.
  6839. ``a_fmt`` / ``b_fmt``: optional per-operand MX sub-formats (``e2m3`` /
  6840. ``e3m2`` / ``e4m3``). When omitted, both operands use the format implied by
  6841. ``ab_dtype``. W6A8 dense passes ``a_fmt="e4m3"`` (activations) and
  6842. ``b_fmt="e2m3"`` (weights) with ``ab_dtype="float6_e2m3fn"`` so the weight
  6843. packing / expansion path stays on the MX-FP6 branch.
  6844. ``x_bf16`` / ``w_gscale``: fused BF16 activation-quant inputs, only used
  6845. when B12X_DENSE_FUSED_QUANT is enabled on an m=1 MX-FP6 launch.
  6846. ``plain_fp8``: emit non-block-scaled E4M3 warp MMA while reusing the
  6847. SM12x dense pipeline. The scalar ``alpha`` carries the combined activation
  6848. and weight dequantization scale.
  6849. ``row_scale``: optional contiguous ``(M,)`` tensor in the C dtype, applied
  6850. per output row in the epilogue. MX-FP6 only.
  6851. ``block_fp8``: accumulate ordinary E4M3 MMA over each K128 block, then
  6852. apply compact FP32 activation ``[M,K/128]`` and weight
  6853. ``[N/128,K/128]`` scales before adding it to the final accumulator.
  6854. """
  6855. a_torch, sfa_torch = lhs
  6856. b_torch, sfb_torch = rhs
  6857. if load_path is not None and load_path not in _DENSE_LOAD_PATHS:
  6858. raise ValueError(
  6859. f"dense_gemm load_path must be one of {_DENSE_LOAD_PATHS}, got {load_path!r}"
  6860. )
  6861. if b_packed and b_preexpanded:
  6862. raise ValueError("b_packed and b_preexpanded are mutually exclusive")
  6863. m, k, l = a_torch.shape
  6864. n, _, _ = b_torch.shape
  6865. if sm_count is None:
  6866. sm_count = get_num_sm(a_torch.device)
  6867. mxfp6_fmt_a: Optional[str] = None
  6868. mxfp6_fmt_b: Optional[str] = None
  6869. if ab_dtype == "float4_e2m1fn":
  6870. is_mxfp8 = False
  6871. is_mxfp6 = False
  6872. k *= 2
  6873. mma_k = 64
  6874. tile_k = sf_vec_size * 8
  6875. elif ab_dtype == "float8_e4m3fn":
  6876. is_mxfp8 = True
  6877. is_mxfp6 = False
  6878. mma_k = 32
  6879. tile_k = (
  6880. 128
  6881. if block_fp8
  6882. else _select_mxfp8_tile_k(m, n, k, expected_m, sm_count)
  6883. )
  6884. elif ab_dtype in ("float6_e3m2fn", "float6_e2m3fn"):
  6885. is_mxfp8 = False
  6886. is_mxfp6 = True
  6887. if sf_vec_size != 32:
  6888. raise ValueError("MX-FP6 dense_gemm requires sf_vec_size=32")
  6889. if sf_dtype != "float8_e8m0fnu":
  6890. raise ValueError("MX-FP6 dense_gemm requires sf_dtype='float8_e8m0fnu'")
  6891. if not a_preexpanded:
  6892. k = mxfp6_logical_k_from_packed_bytes(k)
  6893. mma_k = 32
  6894. tile_k = mxfp6_tile_k(sf_vec_size)
  6895. weight_fmt = "e3m2" if ab_dtype == "float6_e3m2fn" else "e2m3"
  6896. mxfp6_fmt_b = b_fmt if b_fmt is not None else weight_fmt
  6897. mxfp6_fmt_a = a_fmt if a_fmt is not None else weight_fmt
  6898. for name, fmt in (("a_fmt", mxfp6_fmt_a), ("b_fmt", mxfp6_fmt_b)):
  6899. if fmt not in ("e2m3", "e3m2", "e4m3"):
  6900. raise ValueError(f"unsupported {name}={fmt!r}")
  6901. else:
  6902. raise TypeError(f"dense_gemm unsupported ab_dtype: {ab_dtype}")
  6903. if block_fp8:
  6904. plain_fp8 = True
  6905. expected_sfa_shape = (m, k // 128)
  6906. expected_sfb_shape = (n // 128, k // 128)
  6907. if (
  6908. sf_dtype != "float32"
  6909. or sf_vec_size != 128
  6910. or l != 1
  6911. or n % 128 != 0
  6912. or k % 128 != 0
  6913. or sfa_torch.shape != expected_sfa_shape
  6914. or sfb_torch.shape != expected_sfb_shape
  6915. or sfa_torch.dtype != torch.float32
  6916. or sfb_torch.dtype != torch.float32
  6917. or not sfa_torch.is_contiguous()
  6918. or not sfb_torch.is_contiguous()
  6919. ):
  6920. raise ValueError(
  6921. "block_fp8 requires L=1, N/K divisible by 128, "
  6922. "sf_dtype='float32', sf_vec_size=128, and contiguous FP32 "
  6923. f"scales shaped {expected_sfa_shape} and {expected_sfb_shape}"
  6924. )
  6925. if (
  6926. load_path not in (None, "tma")
  6927. or swap_ab not in (None, False)
  6928. or rhs_values_tiled is not None
  6929. or _quantized_c is not None
  6930. or sfb_k_replicated
  6931. ):
  6932. raise ValueError(
  6933. "block_fp8 currently requires unswapped TMA operands without "
  6934. "tiled RHS, quantized output, or replicated weight scales"
  6935. )
  6936. load_path = "tma"
  6937. swap_ab = False
  6938. if plain_fp8 and not is_mxfp8:
  6939. raise ValueError("plain_fp8 requires ab_dtype='float8_e4m3fn'")
  6940. if plain_fp8 and (
  6941. rhs_values_tiled is not None or _quantized_c is not None or sfb_k_replicated
  6942. ):
  6943. raise ValueError(
  6944. "plain_fp8 does not support tiled/quantized output or replicated scales"
  6945. )
  6946. if b_packed:
  6947. if mxfp6_fmt_b is None:
  6948. raise ValueError("b_packed requires an MX-FP6 ab_dtype")
  6949. if b_torch.shape[1] * 4 != k * 3:
  6950. raise ValueError(
  6951. f"b_packed expects (N, 3K/4, L); got packed K bytes "
  6952. f"{b_torch.shape[1]} for logical K {k}"
  6953. )
  6954. if not is_mxfp6 and (
  6955. a_preexpanded
  6956. or b_preexpanded
  6957. or a_fmt is not None
  6958. or b_fmt is not None
  6959. or x_bf16 is not None
  6960. or w_gscale is not None
  6961. ):
  6962. raise ValueError(
  6963. "a_preexpanded/b_preexpanded/a_fmt/b_fmt/x_bf16/w_gscale are only "
  6964. f"supported with an MX-FP6 ab_dtype, got ab_dtype={ab_dtype!r}"
  6965. )
  6966. if (x_bf16 is None) != (w_gscale is None):
  6967. raise ValueError("x_bf16 and w_gscale must be provided together")
  6968. if x_bf16 is not None and w_gscale is not None:
  6969. if not is_mxfp6:
  6970. raise ValueError("fused quantization inputs require an MX-FP6 ab_dtype")
  6971. if (
  6972. x_bf16.shape != (m, k)
  6973. or x_bf16.dtype != torch.bfloat16
  6974. or not x_bf16.is_contiguous()
  6975. or x_bf16.device != a_torch.device
  6976. or x_bf16.data_ptr() % 16 != 0
  6977. ):
  6978. raise ValueError(
  6979. "x_bf16 must be a contiguous, 16-byte-aligned BF16 tensor "
  6980. f"with shape {(m, k)} on {a_torch.device}; got shape "
  6981. f"{tuple(x_bf16.shape)}, dtype {x_bf16.dtype}, device "
  6982. f"{x_bf16.device}, data_ptr alignment {x_bf16.data_ptr() % 16}"
  6983. )
  6984. if (
  6985. w_gscale.shape != (1,)
  6986. or w_gscale.dtype != torch.float32
  6987. or not w_gscale.is_contiguous()
  6988. or w_gscale.device != a_torch.device
  6989. or w_gscale.data_ptr() % 16 != 0
  6990. ):
  6991. raise ValueError(
  6992. "w_gscale must be a contiguous, 16-byte-aligned float32 tensor "
  6993. f"with shape (1,) on {a_torch.device}; got shape "
  6994. f"{tuple(w_gscale.shape)}, dtype {w_gscale.dtype}, device "
  6995. f"{w_gscale.device}, data_ptr alignment {w_gscale.data_ptr() % 16}"
  6996. )
  6997. ab_cutlass_dtype = get_cutlass_dtype(ab_dtype)
  6998. if mxfp6_fmt_a is not None:
  6999. # Stage 1: carry MX codes in Float8E4M3FN byte-containers so the kernel
  7000. # uses cutlass's native 8-bit smem/TMA/ldmatrix path (cutlass cannot build
  7001. # a 6-bit smem layout). Expand the 3:4-packed inputs to one code per byte
  7002. # at this load boundary; the on-disk/wire format stays packed. E4M3
  7003. # activations are already one-byte-per-code (a_preexpanded required).
  7004. ab_cutlass_dtype = cutlass.Float8E4M3FN
  7005. if not a_preexpanded:
  7006. if mxfp6_fmt_a == "e4m3":
  7007. raise ValueError("e4m3 activations require a_preexpanded=True")
  7008. a_torch = _expand_packed_mxfp6_ab(a_torch, k)
  7009. if not (b_preexpanded or b_packed):
  7010. b_torch = _expand_packed_mxfp6_ab(b_torch, k)
  7011. c_cutlass_dtype = get_cutlass_dtype(c_dtype)
  7012. if mma_tiler_mn is None or load_path is None or swap_ab is None:
  7013. default_plan = _select_default_dense_gemm_plan(
  7014. m,
  7015. n,
  7016. k,
  7017. sm_count,
  7018. is_mxfp8=is_mxfp8,
  7019. is_mxfp6=is_mxfp6,
  7020. expected_m=expected_m,
  7021. )
  7022. if mma_tiler_mn is None:
  7023. mma_tiler_mn = default_plan.mma_tiler_mn
  7024. if load_path is None:
  7025. load_path = default_plan.load_path
  7026. if swap_ab is None:
  7027. swap_ab = default_plan.swap_ab if mma_tiler_mn[1] < 64 else False
  7028. assert load_path is not None
  7029. assert swap_ab is not None
  7030. if is_mxfp6:
  7031. # Only the unswapped single-slice TMA mainloop is wired for the FP6
  7032. # byte-container path; fail loudly instead of silently miscomputing.
  7033. if swap_ab:
  7034. raise ValueError("MX-FP6 dense_gemm does not support swap_ab")
  7035. if load_path != "tma":
  7036. raise ValueError(
  7037. "MX-FP6 dense_gemm only supports load_path='tma', got "
  7038. f"{load_path!r}"
  7039. )
  7040. if _quantized_c is not None:
  7041. raise ValueError("MX-FP6 dense_gemm does not support quantized C output")
  7042. b_launch_torch = b_torch
  7043. if rhs_values_tiled is not None:
  7044. tile_n = 0
  7045. supported_plan = False
  7046. if (n, k, l) == (1024, 4096, 4):
  7047. tile_n = 64
  7048. supported_plan = mma_tiler_mn in ((16, 64), (32, 64), (64, 64))
  7049. elif (n, k, l) == (4096, 4096, 1):
  7050. tile_n = 128
  7051. supported_plan = mma_tiler_mn in (
  7052. (16, 128),
  7053. (32, 64),
  7054. (32, 128),
  7055. )
  7056. if not is_mxfp8 or not supported_plan or swap_ab or load_path != "tma":
  7057. raise ValueError(
  7058. "tile-major MXFP8 RHS is restricted to production WO-A/WO-B "
  7059. "Ntile/BK128 TMA plans"
  7060. )
  7061. expected_tiled_shape = (l, n // tile_n, k // 128, tile_n, 128)
  7062. if (
  7063. rhs_values_tiled.shape != expected_tiled_shape
  7064. or rhs_values_tiled.dtype != b_torch.dtype
  7065. or rhs_values_tiled.device != b_torch.device
  7066. or not rhs_values_tiled.is_contiguous()
  7067. ):
  7068. raise ValueError(
  7069. "tile-major MXFP8 RHS must be contiguous with shape "
  7070. f"{expected_tiled_shape}, dtype {b_torch.dtype}, and device "
  7071. f"{b_torch.device}; got shape={tuple(rhs_values_tiled.shape)}, "
  7072. f"dtype={rhs_values_tiled.dtype}, device={rhs_values_tiled.device}"
  7073. )
  7074. b_launch_torch = rhs_values_tiled
  7075. if is_mxfp8:
  7076. _validate_mxfp8_bk64_plan(tile_k, mma_tiler_mn, swap_ab)
  7077. # k-reuse relies on SFB being the 128x128-block weight operand; with
  7078. # swap_ab the smem B slot holds activations, so force it off there.
  7079. sfb_k_reuse = bool(sfb_k_replicated) and not swap_ab and is_mxfp8
  7080. if alpha_dtype is None:
  7081. alpha_dtype = "float32" if alpha is None else str(alpha.dtype).split(".")[-1]
  7082. policy = _dense_gemm_policy_for(
  7083. m=m,
  7084. n=n,
  7085. k=k,
  7086. l=l,
  7087. ab_dtype=ab_cutlass_dtype,
  7088. c_dtype=c_cutlass_dtype,
  7089. mma_tiler_mn=mma_tiler_mn,
  7090. cluster_shape_mn=cluster_shape_mn,
  7091. sm_count=sm_count,
  7092. expected_m=expected_m,
  7093. )
  7094. split_k_slices = policy.split_k_slices
  7095. if swap_ab and split_k_slices != 1:
  7096. policy = _DenseGemmPolicy(
  7097. single_work_tile_per_cta=policy.single_work_tile_per_cta,
  7098. direct_one_m_tile_scheduler=policy.direct_one_m_tile_scheduler,
  7099. use_m1_non_tma=policy.use_m1_non_tma,
  7100. split_k_slices=1,
  7101. split_k_atomic_bf16=False,
  7102. large_m_unroll=policy.large_m_unroll,
  7103. )
  7104. split_k_slices = 1
  7105. if is_mxfp6 and (policy.split_k_slices != 1 or policy.large_m_unroll):
  7106. # The policy helper sees the FP8 byte-container dtype and may pick
  7107. # MXFP8 tactics that are not wired for MX-FP6.
  7108. policy = _DenseGemmPolicy(
  7109. single_work_tile_per_cta=policy.single_work_tile_per_cta,
  7110. direct_one_m_tile_scheduler=policy.direct_one_m_tile_scheduler,
  7111. use_m1_non_tma=policy.use_m1_non_tma,
  7112. split_k_slices=1,
  7113. split_k_atomic_bf16=False,
  7114. large_m_unroll=False,
  7115. )
  7116. split_k_slices = 1
  7117. split_k_output = split_k_slices > 1
  7118. split_k_atomic_bf16 = split_k_output and policy.split_k_atomic_bf16
  7119. if split_k_atomic_bf16:
  7120. kernel_c_l = l
  7121. elif split_k_output:
  7122. kernel_c_l = split_k_slices
  7123. else:
  7124. kernel_c_l = l
  7125. alpha_is_one = alpha is None
  7126. if alpha is None:
  7127. alpha = _cached_alpha_one(a_torch.device)
  7128. stream_int = cuda_stream_to_int(stream)
  7129. kernel_c_dtype_name = (
  7130. "float32" if split_k_output and not split_k_atomic_bf16 else c_dtype
  7131. )
  7132. if row_scale is not None:
  7133. if not is_mxfp6:
  7134. raise ValueError("row_scale is only wired for the MX-FP6 path")
  7135. if (
  7136. row_scale.dim() != 1
  7137. or row_scale.shape[0] != m
  7138. or not row_scale.is_contiguous()
  7139. or row_scale.dtype != cutlass_to_torch_dtype(c_cutlass_dtype)
  7140. # The epilogue reads it through a raw device pointer, so a
  7141. # host or wrong-device tensor is an illegal access, not an error.
  7142. or row_scale.device != a_torch.device
  7143. or row_scale.data_ptr() % 16 != 0
  7144. ):
  7145. raise ValueError(
  7146. "row_scale must be a contiguous 1-D tensor of shape (M,) in the "
  7147. f"C dtype on the operand device; got shape "
  7148. f"{tuple(row_scale.shape)} dtype {row_scale.dtype} device "
  7149. f"{row_scale.device} for M={m}, C dtype "
  7150. f"{cutlass_to_torch_dtype(c_cutlass_dtype)}, operand device "
  7151. f"{a_torch.device}"
  7152. )
  7153. if is_mxfp6:
  7154. # Dedicated MX-FP6 launch path (bypasses the torch custom ops, like
  7155. # the quantized-C path): the byte-container operands plus the
  7156. # x_bf16/w_gscale fused-quant tensors flow straight into the compiled
  7157. # launch, and the FP6 fmt/packed axes key the compile cache.
  7158. compiled_mxfp6 = _get_compiled_dense_gemm_mxfp6(
  7159. n=n,
  7160. k=k,
  7161. l=l,
  7162. c_l=l,
  7163. a_major="k",
  7164. b_major="k",
  7165. c_major="n",
  7166. ab_dtype=ab_cutlass_dtype,
  7167. sf_dtype=get_cutlass_dtype(sf_dtype),
  7168. c_dtype=c_cutlass_dtype,
  7169. alpha_dtype=get_cutlass_dtype(alpha_dtype),
  7170. sf_vec_size=sf_vec_size,
  7171. mma_k=mma_k,
  7172. tile_k=tile_k,
  7173. mma_tiler_mn=mma_tiler_mn,
  7174. cluster_shape_mn=cluster_shape_mn,
  7175. policy=policy,
  7176. sm_count=sm_count,
  7177. sm_version="sm_120",
  7178. mxfp6_fmt_a=mxfp6_fmt_a,
  7179. mxfp6_fmt_b=mxfp6_fmt_b,
  7180. b_packed=b_packed,
  7181. a_preexpanded=a_preexpanded,
  7182. b_preexpanded=b_preexpanded,
  7183. alpha_is_one=alpha_is_one,
  7184. row_scale=row_scale is not None,
  7185. # Fused quantization changes code generation and is part of the
  7186. # compiled-kernel key.
  7187. fused_quant=_DENSE_FUSED_QUANT and x_bf16 is not None,
  7188. )
  7189. if out is None:
  7190. out = _empty_dense_gemm_output(
  7191. m,
  7192. n,
  7193. l,
  7194. dtype=cutlass_to_torch_dtype(c_cutlass_dtype),
  7195. device=a_torch.device,
  7196. )
  7197. return compiled_mxfp6(
  7198. a_tensor_gpu=a_torch,
  7199. b_tensor_gpu=b_torch,
  7200. sfa_tensor_gpu=sfa_torch,
  7201. sfb_tensor_gpu=sfb_torch,
  7202. c_tensor_gpu=out,
  7203. alpha_tensor_gpu=alpha,
  7204. stream_int=stream_int,
  7205. x_bf16_tensor_gpu=x_bf16,
  7206. w_gscale_tensor_gpu=w_gscale,
  7207. row_scale_tensor_gpu=row_scale,
  7208. )
  7209. if _quantized_c is not None:
  7210. quant_c_values, quant_c_scale_rows, quant_c_scale_mma = _quantized_c
  7211. quant_c_width = n * l
  7212. expected_scale_mma_shape = (
  7213. 32,
  7214. 4,
  7215. 1,
  7216. 4,
  7217. quant_c_width // 128,
  7218. 1,
  7219. )
  7220. if (
  7221. split_k_output
  7222. or swap_ab
  7223. or c_dtype != "bfloat16"
  7224. or m < 1
  7225. or m > 16
  7226. or n % 32 != 0
  7227. or quant_c_width % 128 != 0
  7228. or mma_tiler_mn[1] != 64
  7229. or quant_c_values.shape != (m, quant_c_width)
  7230. or not quant_c_values.is_contiguous()
  7231. or quant_c_values.dtype != torch.float8_e4m3fn
  7232. or quant_c_scale_rows.shape != (m, quant_c_width // 32)
  7233. or not quant_c_scale_rows.is_contiguous()
  7234. or quant_c_scale_rows.dtype != torch.float8_e8m0fnu
  7235. or quant_c_scale_mma.shape != expected_scale_mma_shape
  7236. or quant_c_scale_mma.dtype != torch.float8_e8m0fnu
  7237. ):
  7238. raise ValueError("quantized C is restricted to the BF16 WO-A decode layout")
  7239. if out is None:
  7240. out = _empty_dense_gemm_output(
  7241. m,
  7242. n,
  7243. l,
  7244. dtype=torch.bfloat16,
  7245. device=a_torch.device,
  7246. )
  7247. compiled_quant_c = _get_compiled_dense_gemm(
  7248. n=n,
  7249. k=k,
  7250. l=l,
  7251. c_l=kernel_c_l,
  7252. a_major="k",
  7253. b_major="k",
  7254. c_major="n",
  7255. ab_dtype=ab_cutlass_dtype,
  7256. sf_dtype=get_cutlass_dtype(sf_dtype),
  7257. c_dtype=c_cutlass_dtype,
  7258. alpha_dtype=get_cutlass_dtype(alpha_dtype),
  7259. sf_vec_size=sf_vec_size,
  7260. mma_k=mma_k,
  7261. tile_k=tile_k,
  7262. mma_tiler_mn=mma_tiler_mn,
  7263. cluster_shape_mn=cluster_shape_mn,
  7264. policy=policy,
  7265. sm_count=sm_count,
  7266. sm_version="sm_120",
  7267. load_path=load_path,
  7268. swap_ab=swap_ab,
  7269. sfb_k_reuse=sfb_k_reuse,
  7270. b_tile_major=rhs_values_tiled is not None,
  7271. quantize_c=True,
  7272. alpha_is_one=alpha_is_one,
  7273. direct_sfa_live16=_use_direct_sfa_live16(
  7274. m=m,
  7275. n=n,
  7276. k=k,
  7277. l=l,
  7278. sf_vec_size=sf_vec_size,
  7279. tile_k=tile_k,
  7280. mma_tiler_mn=mma_tiler_mn,
  7281. load_path=load_path,
  7282. swap_ab=swap_ab,
  7283. b_tile_major=rhs_values_tiled is not None,
  7284. sfb_k_reuse=sfb_k_reuse,
  7285. alpha_is_one=alpha_is_one,
  7286. is_mxfp8=is_mxfp8,
  7287. ),
  7288. )
  7289. return compiled_quant_c(
  7290. a_tensor_gpu=a_torch,
  7291. b_tensor_gpu=b_launch_torch,
  7292. sfa_tensor_gpu=sfa_torch,
  7293. sfb_tensor_gpu=sfb_torch,
  7294. c_tensor_gpu=out,
  7295. alpha_tensor_gpu=alpha,
  7296. stream_int=stream_int,
  7297. quant_c_values_gpu=quant_c_values,
  7298. quant_c_scale_rows_gpu=quant_c_scale_rows,
  7299. quant_c_scale_mma_gpu=quant_c_scale_mma,
  7300. )
  7301. if out is None and not plain_fp8:
  7302. # No caller-owned output buffer: functional launch (allocate + return
  7303. # inside the opaque op). The compile graph then carries no
  7304. # auto_functionalized dense node mutating a (possibly strided) caller
  7305. # view -- which inductor's decompose pass cannot remove. No is_compiling;
  7306. # purely caller-intent, behaviorally identical to the eager out=None path.
  7307. return torch.ops.b12x.dense_gemm_launch_functional(
  7308. a_torch,
  7309. b_launch_torch,
  7310. sfa_torch,
  7311. sfb_torch,
  7312. alpha,
  7313. n,
  7314. k,
  7315. l,
  7316. kernel_c_l,
  7317. ab_dtype,
  7318. sf_dtype,
  7319. c_dtype,
  7320. kernel_c_dtype_name,
  7321. alpha_dtype,
  7322. sf_vec_size,
  7323. mma_k,
  7324. tile_k,
  7325. mma_tiler_mn[0],
  7326. mma_tiler_mn[1],
  7327. cluster_shape_mn[0],
  7328. cluster_shape_mn[1],
  7329. sm_count,
  7330. policy.single_work_tile_per_cta,
  7331. policy.direct_one_m_tile_scheduler,
  7332. policy.use_m1_non_tma,
  7333. policy.split_k_slices,
  7334. policy.split_k_atomic_bf16,
  7335. policy.large_m_unroll,
  7336. load_path,
  7337. swap_ab,
  7338. sfb_k_reuse,
  7339. alpha_is_one,
  7340. stream_int,
  7341. )
  7342. split_storage = None
  7343. split_scratch = None
  7344. if split_k_output:
  7345. if out is None:
  7346. out = torch.empty(
  7347. (m, n, l),
  7348. dtype=cutlass_to_torch_dtype(c_cutlass_dtype),
  7349. device=a_torch.device,
  7350. )
  7351. if split_k_atomic_bf16:
  7352. out.zero_()
  7353. else:
  7354. split_storage = torch.empty(
  7355. (split_k_slices, m, n),
  7356. dtype=torch.float32,
  7357. device=a_torch.device,
  7358. )
  7359. split_scratch = split_storage.permute(1, 2, 0)
  7360. elif out is None:
  7361. out = torch.empty(
  7362. (m, n, l),
  7363. dtype=cutlass_to_torch_dtype(c_cutlass_dtype),
  7364. device=a_torch.device,
  7365. )
  7366. if alpha is None:
  7367. alpha = _cached_alpha_one(a_torch.device)
  7368. t0 = time.perf_counter() if _B12X_TIMING else 0.0
  7369. cache_before = (
  7370. _get_compiled_dense_gemm.cache_info() if _B12X_TIMING else None
  7371. )
  7372. t_compiled = t0
  7373. kernel_c_dtype_name = (
  7374. "float32" if split_k_output and not split_k_atomic_bf16 else c_dtype
  7375. )
  7376. c_tensor_gpu = (
  7377. out if split_k_atomic_bf16 else split_scratch if split_k_output else out
  7378. )
  7379. assert c_tensor_gpu is not None
  7380. if plain_fp8:
  7381. compiled_plain_fp8 = _get_compiled_dense_gemm(
  7382. n=n,
  7383. k=k,
  7384. l=l,
  7385. c_l=kernel_c_l,
  7386. a_major="k",
  7387. b_major="k",
  7388. c_major="n",
  7389. ab_dtype=ab_cutlass_dtype,
  7390. sf_dtype=get_cutlass_dtype(sf_dtype),
  7391. c_dtype=get_cutlass_dtype(kernel_c_dtype_name),
  7392. alpha_dtype=get_cutlass_dtype(alpha_dtype),
  7393. sf_vec_size=sf_vec_size,
  7394. mma_k=mma_k,
  7395. tile_k=tile_k,
  7396. mma_tiler_mn=mma_tiler_mn,
  7397. cluster_shape_mn=cluster_shape_mn,
  7398. policy=policy,
  7399. sm_count=sm_count,
  7400. sm_version="sm_120",
  7401. load_path=load_path,
  7402. swap_ab=swap_ab,
  7403. sfb_k_reuse=False,
  7404. b_tile_major=False,
  7405. alpha_is_one=alpha_is_one,
  7406. plain_fp8=True,
  7407. block_fp8=block_fp8,
  7408. )
  7409. compiled_plain_fp8(
  7410. a_tensor_gpu=a_torch,
  7411. b_tensor_gpu=b_launch_torch,
  7412. sfa_tensor_gpu=sfa_torch,
  7413. sfb_tensor_gpu=sfb_torch,
  7414. c_tensor_gpu=c_tensor_gpu,
  7415. alpha_tensor_gpu=alpha,
  7416. stream_int=stream_int,
  7417. )
  7418. else:
  7419. torch.ops.b12x.dense_gemm_launch(
  7420. a_torch,
  7421. b_launch_torch,
  7422. sfa_torch,
  7423. sfb_torch,
  7424. c_tensor_gpu,
  7425. alpha,
  7426. n,
  7427. k,
  7428. l,
  7429. kernel_c_l,
  7430. ab_dtype,
  7431. sf_dtype,
  7432. kernel_c_dtype_name,
  7433. alpha_dtype,
  7434. sf_vec_size,
  7435. mma_k,
  7436. tile_k,
  7437. mma_tiler_mn[0],
  7438. mma_tiler_mn[1],
  7439. cluster_shape_mn[0],
  7440. cluster_shape_mn[1],
  7441. sm_count,
  7442. policy.single_work_tile_per_cta,
  7443. policy.direct_one_m_tile_scheduler,
  7444. policy.use_m1_non_tma,
  7445. policy.split_k_slices,
  7446. policy.split_k_atomic_bf16,
  7447. policy.large_m_unroll,
  7448. load_path,
  7449. swap_ab,
  7450. sfb_k_reuse,
  7451. alpha_is_one,
  7452. stream_int,
  7453. )
  7454. result = out
  7455. if split_k_output and not split_k_atomic_bf16:
  7456. assert split_scratch is not None
  7457. assert out is not None
  7458. _reduce_split_k2_bf16(split_scratch, out, m=m, n=n)
  7459. result = out
  7460. if _B12X_TIMING:
  7461. t_launch = time.perf_counter()
  7462. cache_after = _get_compiled_dense_gemm.cache_info()
  7463. assert cache_before is not None
  7464. compile_ms = (t_compiled - t0) * 1000.0
  7465. launch_ms = (t_launch - t_compiled) * 1000.0
  7466. total_ms = (t_launch - t0) * 1000.0
  7467. if total_ms >= _B12X_TIMING_THRESHOLD_MS:
  7468. logger.warning(
  7469. "b12x_dense_gemm timing m=%d n=%d k=%d l=%d ab=%s sf=%s c=%s "
  7470. "tile=%s load=%s swap_ab=%s cache_hit=%s compile_or_lookup=%.3fms "
  7471. "launch_enqueue=%.3fms total=%.3fms cache=%s",
  7472. m,
  7473. n,
  7474. k,
  7475. l,
  7476. ab_dtype,
  7477. sf_dtype,
  7478. c_dtype,
  7479. mma_tiler_mn,
  7480. load_path,
  7481. swap_ab,
  7482. cache_after.hits > cache_before.hits,
  7483. compile_ms,
  7484. launch_ms,
  7485. total_ms,
  7486. cache_after,
  7487. )
  7488. return result