| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246 |
- // Copyright (c) 2015-2016 The Khronos Group Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Validation tests for Control Flow Graph
- #include <array>
- #include <functional>
- #include <sstream>
- #include <string>
- #include <utility>
- #include <vector>
- #include "gmock/gmock.h"
- #include "source/spirv_target_env.h"
- #include "source/val/validate.h"
- #include "test/test_fixture.h"
- #include "test/unit_spirv.h"
- #include "test/val/val_fixtures.h"
- namespace spvtools {
- namespace val {
- namespace {
- using ::testing::HasSubstr;
- using ::testing::MatchesRegex;
- using ValidateCFG = spvtest::ValidateBase<spv::Capability>;
- using spvtest::ScopedContext;
- std::string nameOps() { return ""; }
- template <typename... Args>
- std::string nameOps(std::pair<std::string, std::string> head, Args... names) {
- return "OpName %" + head.first + " \"" + head.second + "\"\n" +
- nameOps(names...);
- }
- template <typename... Args>
- std::string nameOps(std::string head, Args... names) {
- return "OpName %" + head + " \"" + head + "\"\n" + nameOps(names...);
- }
- /// This class allows the easy creation of complex control flow without writing
- /// SPIR-V. This class is used in the test cases below.
- class Block {
- std::string label_;
- std::string body_;
- spv::Op type_;
- std::vector<Block> successors_;
- public:
- /// Creates a Block with a given label
- ///
- /// @param[in]: label the label id of the block
- /// @param[in]: type the branch instruction that ends the block
- explicit Block(std::string label, spv::Op type = spv::Op::OpBranch)
- : label_(label), body_(), type_(type), successors_() {}
- /// Sets the instructions which will appear in the body of the block
- Block& SetBody(std::string body) {
- body_ = body;
- return *this;
- }
- Block& AppendBody(std::string body) {
- body_ += body;
- return *this;
- }
- /// Converts the block into a SPIR-V string
- operator std::string() {
- std::stringstream out;
- out << std::setw(8) << "%" + label_ + " = OpLabel \n";
- if (!body_.empty()) {
- out << body_;
- }
- switch (type_) {
- case spv::Op::OpBranchConditional:
- out << "OpBranchConditional %cond ";
- for (Block& b : successors_) {
- out << "%" + b.label_ + " ";
- }
- break;
- case spv::Op::OpSwitch: {
- out << "OpSwitch %one %" + successors_.front().label_;
- std::stringstream ss;
- for (size_t i = 1; i < successors_.size(); i++) {
- ss << " " << i << " %" << successors_[i].label_;
- }
- out << ss.str();
- } break;
- case spv::Op::OpLoopMerge: {
- assert(successors_.size() == 2);
- out << "OpLoopMerge %" + successors_[0].label_ + " %" +
- successors_[0].label_ + "None";
- } break;
- case spv::Op::OpReturn:
- assert(successors_.size() == 0);
- out << "OpReturn\n";
- break;
- case spv::Op::OpUnreachable:
- assert(successors_.size() == 0);
- out << "OpUnreachable\n";
- break;
- case spv::Op::OpBranch:
- assert(successors_.size() == 1);
- out << "OpBranch %" + successors_.front().label_;
- break;
- case spv::Op::OpKill:
- assert(successors_.size() == 0);
- out << "OpKill\n";
- break;
- default:
- assert(1 == 0 && "Unhandled");
- }
- out << "\n";
- return out.str();
- }
- friend Block& operator>>(Block& curr, std::vector<Block> successors);
- friend Block& operator>>(Block& lhs, Block& successor);
- };
- /// Assigns the successors for the Block on the lhs
- Block& operator>>(Block& lhs, std::vector<Block> successors) {
- if (lhs.type_ == spv::Op::OpBranchConditional) {
- assert(successors.size() == 2);
- } else if (lhs.type_ == spv::Op::OpSwitch) {
- assert(successors.size() > 1);
- }
- lhs.successors_ = successors;
- return lhs;
- }
- /// Assigns the successor for the Block on the lhs
- Block& operator>>(Block& lhs, Block& successor) {
- assert(lhs.type_ == spv::Op::OpBranch);
- lhs.successors_.push_back(successor);
- return lhs;
- }
- const std::string& GetDefaultHeader(spv::Capability cap) {
- static const std::string shader_header =
- "OpCapability Shader\n"
- "OpCapability Linkage\n"
- "OpMemoryModel Logical GLSL450\n";
- static const std::string kernel_header =
- "OpCapability Kernel\n"
- "OpCapability Linkage\n"
- "OpMemoryModel Logical OpenCL\n";
- return (cap == spv::Capability::Shader) ? shader_header : kernel_header;
- }
- const std::string& types_consts() {
- static const std::string types =
- "%voidt = OpTypeVoid\n"
- "%boolt = OpTypeBool\n"
- "%intt = OpTypeInt 32 0\n"
- "%one = OpConstant %intt 1\n"
- "%two = OpConstant %intt 2\n"
- "%ptrt = OpTypePointer Function %intt\n"
- "%funct = OpTypeFunction %voidt\n";
- return types;
- }
- INSTANTIATE_TEST_SUITE_P(StructuredControlFlow, ValidateCFG,
- ::testing::Values(spv::Capability::Shader,
- spv::Capability::Kernel));
- TEST_P(ValidateCFG, LoopReachableFromEntryButNeverLeadingToReturn) {
- // In this case, the loop is reachable from a node without a predecessor,
- // but never reaches a node with a return.
- //
- // This motivates the need for the pseudo-exit node to have a node
- // from a cycle in its predecessors list. Otherwise the validator's
- // post-dominance calculation will go into an infinite loop.
- //
- // For more motivation, see
- // https://github.com/KhronosGroup/SPIRV-Tools/issues/279
- std::string str = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %entry "entry"
- OpName %loop "loop"
- OpName %exit "exit"
- %voidt = OpTypeVoid
- %funct = OpTypeFunction %voidt
- %main = OpFunction %voidt None %funct
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %loop None
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
- }
- TEST_P(ValidateCFG, LoopUnreachableFromEntryButLeadingToReturn) {
- // In this case, the loop is not reachable from a node without a
- // predecessor, but eventually reaches a node with a return.
- //
- // This motivates the need for the pseudo-entry node to have a node
- // from a cycle in its successors list. Otherwise the validator's
- // dominance calculation will go into an infinite loop.
- //
- // For more motivation, see
- // https://github.com/KhronosGroup/SPIRV-Tools/issues/279
- // Before that fix, we'd have an infinite loop when calculating
- // post-dominators.
- std::string str = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %entry "entry"
- OpName %loop "loop"
- OpName %cont "cont"
- OpName %exit "exit"
- %voidt = OpTypeVoid
- %funct = OpTypeFunction %voidt
- %boolt = OpTypeBool
- %false = OpConstantFalse %boolt
- %main = OpFunction %voidt None %funct
- %entry = OpLabel
- OpReturn
- %loop = OpLabel
- OpLoopMerge %exit %cont None
- OpBranch %cont
- %cont = OpLabel
- OpBranchConditional %false %loop %exit
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions())
- << str << getDiagnosticString();
- }
- TEST_P(ValidateCFG, Simple) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block cont("cont");
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop.SetBody("OpLoopMerge %merge %cont None\n");
- }
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("loop", "entry", "cont", "merge",
- std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({cont, merge});
- str += cont >> loop;
- str += merge;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, Variable) {
- Block entry("entry");
- Block cont("cont");
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%var = OpVariable %ptrt Function\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps(std::make_pair("func", "Main")) + types_consts() +
- " %func = OpFunction %voidt None %funct\n";
- str += entry >> cont;
- str += cont >> exit;
- str += exit;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, VariableNotInFirstBlockBad) {
- Block entry("entry");
- Block cont("cont");
- Block exit("exit", spv::Op::OpReturn);
- // This operation should only be performed in the entry block
- cont.SetBody("%var = OpVariable %ptrt Function\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps(std::make_pair("func", "Main")) + types_consts() +
- " %func = OpFunction %voidt None %funct\n";
- str += entry >> cont;
- str += cont >> exit;
- str += exit;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("All OpVariable instructions in a function must be the "
- "first instructions in the first block"));
- }
- TEST_P(ValidateCFG, BlockSelfLoopIsOk) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("loop", "merge", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- // loop branches to itself, but does not trigger an error.
- str += loop >> std::vector<Block>({merge, loop});
- str += merge;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
- }
- TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block cont("cont");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("cont", "branch", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += cont >> merge; // cont appears before its dominator
- str += branch >> std::vector<Block>({cont, merge});
- str += merge;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("Block '.\\[%cont\\]' appears in the binary "
- "before its dominator '.\\[%branch\\]'\n"
- " %branch = OpLabel\n"));
- }
- TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop");
- Block selection("selection", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
- // cannot share the same merge
- if (is_shader) selection.SetBody("OpSelectionMerge %merge None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("merge", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> selection;
- str += selection >> std::vector<Block>({loop, merge});
- str += merge;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("Block '.\\[%merge\\]' is already a merge block "
- "for another header\n"
- " %Main = OpFunction %void None %9\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block selection("selection", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n");
- // cannot share the same merge
- if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("merge", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> selection;
- str += selection >> std::vector<Block>({merge, loop});
- str += loop >> std::vector<Block>({loop, merge});
- str += merge;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("Block '.\\[%merge\\]' is already a merge block "
- "for another header\n"
- " %Main = OpFunction %void None %9\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceEntryBlock) {
- Block entry("entry");
- Block bad("bad");
- Block end("end", spv::Op::OpReturn);
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "bad", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> bad;
- str += bad >> entry; // Cannot target entry block
- str += end;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("First block '.\\[%entry\\]' of function "
- "'.\\[%Main\\]' is targeted by block '.\\[%bad\\]'\n"
- " %Main = OpFunction %void None %10\n"));
- }
- TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceValue) {
- Block entry("entry");
- entry.SetBody("%undef = OpUndef %boolt\n");
- Block bad("bad");
- Block end("end", spv::Op::OpReturn);
- Block badvalue("undef"); // This references the OpUndef.
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "bad", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> bad;
- str +=
- bad >> badvalue; // Check branch to a function value (it's not a block!)
- str += end;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("'Target Label' operands for OpBranch must "
- "be the ID of an OpLabel instruction"));
- }
- TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) {
- Block entry("entry");
- Block bad("bad", spv::Op::OpBranchConditional);
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- bad.SetBody(" OpLoopMerge %entry %exit None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "bad", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> bad;
- str += bad >> std::vector<Block>({entry, exit}); // cannot target entry block
- str += exit;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' "
- "is targeted by block '.\\[%bad\\]'\n"
- " %Main = OpFunction %void None %10\n"));
- }
- TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) {
- Block entry("entry");
- Block bad("bad", spv::Op::OpBranchConditional);
- Block t("t");
- Block merge("merge");
- Block end("end", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- bad.SetBody("OpLoopMerge %merge %cont None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "bad", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> bad;
- str += bad >> std::vector<Block>({t, entry});
- str += merge >> end;
- str += end;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' "
- "is targeted by block '.\\[%bad\\]'\n"
- " %Main = OpFunction %void None %10\n"));
- }
- TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) {
- Block entry("entry");
- Block bad("bad", spv::Op::OpSwitch);
- Block block1("block1");
- Block block2("block2");
- Block block3("block3");
- Block def("def"); // default block
- Block merge("merge");
- Block end("end", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- bad.SetBody("OpSelectionMerge %merge None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "bad", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> bad;
- str += bad >> std::vector<Block>({def, block1, block2, block3, entry});
- str += def >> merge;
- str += block1 >> merge;
- str += block2 >> merge;
- str += block3 >> merge;
- str += merge >> end;
- str += end;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("First block '.\\[%entry\\]' of function '.\\[%Main\\]' "
- "is targeted by block '.\\[%bad\\]'\n"
- " %Main = OpFunction %void None %10\n"));
- }
- TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) {
- Block entry("entry");
- Block middle("middle", spv::Op::OpBranchConditional);
- Block end("end", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- middle.SetBody("OpSelectionMerge %end None\n");
- Block entry2("entry2");
- Block middle2("middle2");
- Block end2("end2", spv::Op::OpReturn);
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("middle2", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> middle;
- str += middle >> std::vector<Block>({end, middle2});
- str += end;
- str += "OpFunctionEnd\n";
- str += "%func2 = OpFunction %voidt None %funct\n";
- str += entry2 >> middle2;
- str += middle2 >> end2;
- str += end2;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex(
- "Block\\(s\\) \\{'.\\[%middle2\\]'\\} are referenced but not "
- "defined in function '.\\[%Main\\]'\n"
- " %Main = OpFunction %void None %9\n"));
- }
- TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
- // If a merge block is reachable, then it must be strictly dominated by
- // its header block.
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block head("head", spv::Op::OpBranchConditional);
- Block exit("exit", spv::Op::OpReturn);
- head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) head.AppendBody("OpSelectionMerge %head None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("head", "exit", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += head >> std::vector<Block>({exit, exit});
- str += exit;
- str += "OpFunctionEnd\n";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex(
- "The selection construct with the selection header "
- "'.\\[%head\\]' does not strictly structurally dominate the "
- "merge block "
- "'.\\[%head\\]'\n %head = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
- }
- }
- std::string GetUnreachableMergeNoMergeInst(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpReturn);
- Block f("f", spv::Op::OpReturn);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpSelectionMerge %merge None\n");
- std::string str = header;
- str += nameOps("branch", "merge", std::make_pair("func", "Main"));
- str += types_consts() + "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += merge;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeNoMergeInst) {
- CompileSuccessfully(GetUnreachableMergeNoMergeInst(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableMergeTerminatedBy(spv::Capability cap, spv::Op op) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpReturn);
- Block f("f", spv::Op::OpReturn);
- Block merge("merge", op);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpSelectionMerge %merge None\n");
- str += nameOps("branch", "merge", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += merge;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) {
- CompileSuccessfully(
- GetUnreachableMergeTerminatedBy(GetParam(), spv::Op::OpUnreachable));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) {
- CompileSuccessfully(GetUnreachableMergeTerminatedBy(spv::Capability::Shader,
- spv::Op::OpKill));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) {
- CompileSuccessfully(
- GetUnreachableMergeTerminatedBy(GetParam(), spv::Op::OpReturn));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableContinueTerminatedBy(spv::Capability cap,
- spv::Op op) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranch);
- Block merge("merge", spv::Op::OpReturn);
- Block target("target", op);
- if (op == spv::Op::OpBranch) target >> branch;
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpLoopMerge %merge %target None\n");
- str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({merge});
- str += merge;
- str += target;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableContinueTerminatedByOpUnreachable) {
- CompileSuccessfully(
- GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpUnreachable));
- if (GetParam() == spv::Capability::Shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("targeted by 0 back-edge blocks"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_F(ValidateCFG, UnreachableContinueTerminatedByOpKill) {
- CompileSuccessfully(GetUnreachableContinueTerminatedBy(
- spv::Capability::Shader, spv::Op::OpKill));
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("targeted by 0 back-edge blocks"));
- }
- TEST_P(ValidateCFG, UnreachableContinueTerminatedByOpReturn) {
- CompileSuccessfully(
- GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpReturn));
- if (GetParam() == spv::Capability::Shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("targeted by 0 back-edge blocks"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, UnreachableContinueTerminatedByOpBranch) {
- CompileSuccessfully(
- GetUnreachableContinueTerminatedBy(GetParam(), spv::Op::OpBranch));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableMergeUnreachableMergeInst(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block body("body", spv::Op::OpReturn);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpReturn);
- Block f("f", spv::Op::OpReturn);
- Block merge("merge", spv::Op::OpUnreachable);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpSelectionMerge %merge None\n");
- str += nameOps("branch", "merge", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += body;
- str += merge;
- str += entry >> branch;
- str += branch >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeUnreachableMergeInst) {
- CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableContinueUnreachableLoopInst(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block body("body", spv::Op::OpReturn);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranch);
- Block merge("merge", spv::Op::OpReturn);
- Block target("target", spv::Op::OpBranch);
- target >> branch;
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpLoopMerge %merge %target None\n");
- str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += body;
- str += target;
- str += merge;
- str += entry >> branch;
- str += branch >> std::vector<Block>({merge});
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
- CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableMergeWithComplexBody(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpReturn);
- Block f("f", spv::Op::OpReturn);
- Block merge("merge", spv::Op::OpUnreachable);
- entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
- entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
- merge.AppendBody("OpStore %placeholder %one\n");
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpSelectionMerge %merge None\n");
- str += nameOps("branch", "merge", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%intptrt = OpTypePointer Function %intt\n";
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += merge;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeWithComplexBody) {
- CompileSuccessfully(GetUnreachableMergeWithComplexBody(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableContinueWithComplexBody(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranch);
- Block merge("merge", spv::Op::OpReturn);
- Block target("target", spv::Op::OpBranch);
- target >> branch;
- entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
- target.AppendBody("OpStore %placeholder %one\n");
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpLoopMerge %merge %target None\n");
- str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%intptrt = OpTypePointer Function %intt\n";
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({merge});
- str += merge;
- str += target;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableContinueWithComplexBody) {
- CompileSuccessfully(GetUnreachableContinueWithComplexBody(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableMergeWithBranchUse(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpBranch);
- Block f("f", spv::Op::OpReturn);
- Block merge("merge", spv::Op::OpUnreachable);
- entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpSelectionMerge %merge None\n");
- str += nameOps("branch", "merge", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({t, f});
- str += t >> merge;
- str += f;
- str += merge;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
- CompileSuccessfully(GetUnreachableMergeWithBranchUse(GetParam()));
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableMergeWithMultipleUses(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpReturn);
- Block f("f", spv::Op::OpReturn);
- Block merge("merge", spv::Op::OpUnreachable);
- Block duplicate("duplicate", spv::Op::OpBranchConditional);
- entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
- std::string str = header;
- if (cap == spv::Capability::Shader) {
- branch.AppendBody("OpSelectionMerge %merge None\n");
- duplicate.AppendBody("OpSelectionMerge %merge None\n");
- }
- str += nameOps("branch", "merge", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({t, f});
- str += duplicate >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += merge;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) {
- CompileSuccessfully(GetUnreachableMergeWithMultipleUses(GetParam()));
- if (GetParam() == spv::Capability::Shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("is already a merge block for another header"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- std::string GetUnreachableContinueWithBranchUse(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranch);
- Block merge("merge", spv::Op::OpReturn);
- Block target("target", spv::Op::OpBranch);
- target >> branch;
- entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
- std::string str = header;
- if (cap == spv::Capability::Shader)
- branch.AppendBody("OpLoopMerge %merge %target None\n");
- str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%intptrt = OpTypePointer Function %intt\n";
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({merge});
- str += merge;
- str += target;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableContinueWithBranchUse) {
- CompileSuccessfully(GetUnreachableContinueWithBranchUse(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetReachableMergeAndContinue(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranch);
- Block merge("merge", spv::Op::OpReturn);
- Block target("target", spv::Op::OpBranch);
- Block body("body", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpBranch);
- Block f("f", spv::Op::OpBranch);
- target >> branch;
- body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- t >> merge;
- f >> target;
- std::string str = header;
- if (cap == spv::Capability::Shader) {
- branch.AppendBody("OpLoopMerge %merge %target None\n");
- body.AppendBody("OpSelectionMerge %f None\n");
- }
- str += nameOps("branch", "merge", "target", "body", "t", "f",
- std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({body});
- str += body >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += merge;
- str += target;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, ReachableMergeAndContinue) {
- CompileSuccessfully(GetReachableMergeAndContinue(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableMergeAndContinue(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block branch("branch", spv::Op::OpBranch);
- Block merge("merge", spv::Op::OpReturn);
- Block target("target", spv::Op::OpBranch);
- Block body("body", spv::Op::OpBranchConditional);
- Block t("t", spv::Op::OpReturn);
- Block f("f", spv::Op::OpReturn);
- Block pre_target("pre_target", spv::Op::OpBranch);
- target >> branch;
- body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- std::string str = header;
- if (cap == spv::Capability::Shader) {
- branch.AppendBody("OpLoopMerge %merge %target None\n");
- body.AppendBody("OpSelectionMerge %pre_target None\n");
- }
- str += nameOps("branch", "merge", "pre_target", "target", "body", "t", "f",
- std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> branch;
- str += branch >> std::vector<Block>({body});
- str += body >> std::vector<Block>({t, f});
- str += t;
- str += f;
- str += merge;
- str += pre_target >> target;
- str += target;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableMergeAndContinue) {
- CompileSuccessfully(GetUnreachableMergeAndContinue(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableBlock(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block unreachable("unreachable");
- Block exit("exit", spv::Op::OpReturn);
- std::string str = header;
- str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> exit;
- str += unreachable >> exit;
- str += exit;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableBlock) {
- CompileSuccessfully(GetUnreachableBlock(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- std::string GetUnreachableBranch(spv::Capability cap) {
- std::string header = GetDefaultHeader(cap);
- Block entry("entry");
- Block unreachable("unreachable", spv::Op::OpBranchConditional);
- Block unreachablechildt("unreachablechildt");
- Block unreachablechildf("unreachablechildf");
- Block merge("merge");
- Block exit("exit", spv::Op::OpReturn);
- unreachable.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (cap == spv::Capability::Shader)
- unreachable.AppendBody("OpSelectionMerge %merge None\n");
- std::string str = header;
- str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
- str += types_consts();
- str += "%func = OpFunction %voidt None %funct\n";
- str += entry >> exit;
- str +=
- unreachable >> std::vector<Block>({unreachablechildt, unreachablechildf});
- str += unreachablechildt >> merge;
- str += unreachablechildf >> merge;
- str += merge >> exit;
- str += exit;
- str += "OpFunctionEnd\n";
- return str;
- }
- TEST_P(ValidateCFG, UnreachableBranch) {
- CompileSuccessfully(GetUnreachableBranch(GetParam()));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, EmptyFunction) {
- std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
- R"(%func = OpFunction %voidt None %funct
- %l = OpLabel
- OpReturn
- OpFunctionEnd)";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, SingleBlockLoop) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n");
- std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({loop, exit});
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, NestedLoops) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop1("loop1");
- Block loop1_cont_break_block("loop1_cont_break_block",
- spv::Op::OpBranchConditional);
- Block loop2("loop2", spv::Op::OpBranchConditional);
- Block loop2_merge("loop2_merge");
- Block loop1_merge("loop1_merge");
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop1.SetBody("OpLoopMerge %loop1_merge %loop2 None\n");
- loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
- }
- std::string str =
- GetDefaultHeader(GetParam()) +
- nameOps("loop1", "loop1_cont_break_block", "loop2", "loop2_merge") +
- types_consts() + "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop1;
- str += loop1 >> loop1_cont_break_block;
- str += loop1_cont_break_block >> std::vector<Block>({loop1_merge, loop2});
- str += loop2 >> std::vector<Block>({loop2, loop2_merge});
- str += loop2_merge >> loop1;
- str += loop1_merge >> exit;
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, NestedSelection) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- const int N = 256;
- std::vector<Block> if_blocks;
- std::vector<Block> merge_blocks;
- Block inner("inner");
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if_blocks.emplace_back("if0", spv::Op::OpBranchConditional);
- if (is_shader) if_blocks[0].SetBody("OpSelectionMerge %if_merge0 None\n");
- merge_blocks.emplace_back("if_merge0", spv::Op::OpReturn);
- for (int i = 1; i < N; i++) {
- std::stringstream ss;
- ss << i;
- if_blocks.emplace_back("if" + ss.str(), spv::Op::OpBranchConditional);
- if (is_shader)
- if_blocks[i].SetBody("OpSelectionMerge %if_merge" + ss.str() + " None\n");
- merge_blocks.emplace_back("if_merge" + ss.str(), spv::Op::OpBranch);
- }
- std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> if_blocks[0];
- for (int i = 0; i < N - 1; i++) {
- str +=
- if_blocks[i] >> std::vector<Block>({if_blocks[i + 1], merge_blocks[i]});
- }
- str += if_blocks.back() >> std::vector<Block>({inner, merge_blocks.back()});
- str += inner >> merge_blocks.back();
- for (int i = N - 1; i > 0; i--) {
- str += merge_blocks[i] >> merge_blocks[i - 1];
- }
- str += merge_blocks[0];
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop1("loop1", spv::Op::OpBranchConditional);
- Block loop2("loop2", spv::Op::OpBranchConditional);
- Block loop2_merge("loop2_merge");
- Block loop1_cont("loop1_cont", spv::Op::OpBranchConditional);
- Block be_block("be_block");
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
- loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
- }
- std::string str =
- GetDefaultHeader(GetParam()) +
- nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
- types_consts() + "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop1;
- str += loop1 >> std::vector<Block>({loop2, exit});
- str += loop2 >> std::vector<Block>({loop2, loop2_merge});
- str += loop2_merge >> loop1_cont;
- str += loop1_cont >> std::vector<Block>({be_block, exit});
- str += be_block >> loop1;
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (GetParam() == spv::Capability::Shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex(
- "The continue construct with the continue target "
- "'.\\[%loop1_cont\\]' is not structurally post dominated by the "
- "back-edge block '.\\[%be_block\\]'\n"
- " %be_block = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block split("split", spv::Op::OpBranchConditional);
- Block t("t");
- Block f("f");
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
- std::string str = GetDefaultHeader(GetParam()) + nameOps("split", "f") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> split;
- str += split >> std::vector<Block>({t, f});
- str += t >> exit;
- str += f >> split;
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("Back-edges \\('.\\[%f\\]' -> '.\\[%split\\]'\\) can only "
- "be formed between a block and a loop header.\n"
- " %f = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block split("split", spv::Op::OpBranchConditional);
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
- std::string str = GetDefaultHeader(GetParam()) + nameOps("split") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> split;
- str += split >> std::vector<Block>({split, exit});
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex(
- "Back-edges \\('.\\[%split\\]' -> '.\\[%split\\]'\\) can only be "
- "formed between a block and a loop header.\n %split = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block back0("back0");
- Block back1("back1");
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.SetBody("OpLoopMerge %merge %back0 None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("loop", "back0", "back1") + types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({back0, back1});
- str += back0 >> loop;
- str += back1 >> loop;
- str += merge;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex(
- "Loop header '.\\[%loop\\]' is targeted by 2 back-edge blocks but "
- "the standard requires exactly one\n %loop = OpLabel\n"))
- << str;
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block cheader("cheader", spv::Op::OpBranchConditional);
- Block be_block("be_block");
- Block merge("merge", spv::Op::OpReturn);
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n");
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("cheader", "be_block") + types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({cheader, merge});
- str += cheader >> std::vector<Block>({exit, be_block});
- str += exit; // Branches out of a continue construct
- str += be_block >> loop;
- str += merge;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex(
- "The continue construct with the continue target "
- "'.\\[%cheader\\]' is not structurally post dominated by the "
- "back-edge block '.\\[%be_block\\]'\n"
- " %be_block = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block cont("cont", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
- std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({cont, merge});
- str += cont >> std::vector<Block>({loop, merge});
- str += merge;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- "'.\\[%loop\\]' is not structurally post dominated by the "
- "back-edge block '.\\[%cont\\]'\n"
- " %cont = OpLabel\n"))
- << str;
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, BranchOutOfConstructBad) {
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block cont("cont", spv::Op::OpBranchConditional);
- Block merge("merge");
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
- std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({cont, merge});
- str += cont >> std::vector<Block>({loop, exit});
- str += merge >> exit;
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- "'.\\[%loop\\]' is not structurally post dominated by the "
- "back-edge block '.\\[%cont\\]'\n"
- " %cont = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_F(ValidateCFG, OpSwitchToUnreachableBlock) {
- Block entry("entry", spv::Op::OpSwitch);
- Block case0("case0");
- Block case1("case1");
- Block case2("case2");
- Block def("default", spv::Op::OpUnreachable);
- Block phi("phi", spv::Op::OpReturn);
- std::string str = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main" %id
- OpExecutionMode %main LocalSize 1 1 1
- OpSource GLSL 430
- OpName %main "main"
- OpDecorate %id BuiltIn GlobalInvocationId
- %void = OpTypeVoid
- %voidf = OpTypeFunction %void
- %u32 = OpTypeInt 32 0
- %f32 = OpTypeFloat 32
- %uvec3 = OpTypeVector %u32 3
- %fvec3 = OpTypeVector %f32 3
- %uvec3ptr = OpTypePointer Input %uvec3
- %id = OpVariable %uvec3ptr Input
- %one = OpConstant %u32 1
- %three = OpConstant %u32 3
- %main = OpFunction %void None %voidf
- )";
- entry.SetBody(
- "%idval = OpLoad %uvec3 %id\n"
- "%x = OpCompositeExtract %u32 %idval 0\n"
- "%selector = OpUMod %u32 %x %three\n"
- "OpSelectionMerge %phi None\n");
- str += entry >> std::vector<Block>({def, case0, case1, case2});
- str += case1 >> phi;
- str += def;
- str += phi;
- str += case0 >> phi;
- str += case2 >> phi;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
- std::string str = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
- OpName %loop "loop"
- %voidt = OpTypeVoid
- %funct = OpTypeFunction %voidt
- %main = OpFunction %voidt None %funct
- %loop = OpLabel
- OpLoopMerge %exit %loop None
- OpBranch %exit
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(str);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("Loop header '.\\[%loop\\]' is targeted by "
- "0 back-edge blocks but the standard requires exactly "
- "one\n %loop = OpLabel\n"));
- }
- TEST_F(ValidateCFG, LoopWithBackEdgeFromUnreachableContinueConstructGood) {
- std::string str = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
- OpName %loop "loop"
- %voidt = OpTypeVoid
- %funct = OpTypeFunction %voidt
- %floatt = OpTypeFloat 32
- %boolt = OpTypeBool
- %one = OpConstant %floatt 1
- %two = OpConstant %floatt 2
- %main = OpFunction %voidt None %funct
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %cont None
- OpBranch %16
- %16 = OpLabel
- %cond = OpFOrdLessThan %boolt %one %two
- OpBranchConditional %cond %body %exit
- %body = OpLabel
- OpReturn
- %cont = OpLabel ; Reachable only from OpLoopMerge ContinueTarget parameter
- OpBranch %loop ; Should be considered a back-edge
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
- }
- TEST_P(ValidateCFG,
- NestedConstructWithUnreachableMergeBlockBranchingToOuterMergeBlock) {
- // Test for https://github.com/KhronosGroup/SPIRV-Tools/issues/297
- // The nested construct has an unreachable merge block. In the
- // augmented CFG that merge block
- // we still determine that the
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry", spv::Op::OpBranchConditional);
- Block inner_head("inner_head", spv::Op::OpBranchConditional);
- Block inner_true("inner_true", spv::Op::OpReturn);
- Block inner_false("inner_false", spv::Op::OpReturn);
- Block inner_merge("inner_merge");
- Block exit("exit", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- entry.AppendBody("OpSelectionMerge %exit None\n");
- inner_head.SetBody("OpSelectionMerge %inner_merge None\n");
- }
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "inner_merge", "exit") + types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> std::vector<Block>({inner_head, exit});
- str += inner_head >> std::vector<Block>({inner_true, inner_false});
- str += inner_true;
- str += inner_false;
- str += inner_merge >> exit;
- str += exit;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
- }
- TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
- // The continue construct cannot be the merge target of a nested selection
- // because the loop construct must contain "if_merge" because it contains
- // "if_head".
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop");
- Block if_head("if_head", spv::Op::OpBranchConditional);
- Block if_true("if_true");
- Block if_merge("if_merge", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop.SetBody("OpLoopMerge %merge %if_merge None\n");
- if_head.SetBody("OpSelectionMerge %if_merge None\n");
- }
- std::string str =
- GetDefaultHeader(GetParam()) +
- nameOps("entry", "loop", "if_head", "if_true", "if_merge", "merge") +
- types_consts() + "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> if_head;
- str += if_head >> std::vector<Block>({if_true, if_merge});
- str += if_true >> if_merge;
- str += if_merge >> std::vector<Block>({loop, merge});
- str += merge;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- if (is_shader) {
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Header block '3[%if_head]' is contained in the loop construct "
- "headed "
- "by '2[%loop]', but its merge block '5[%if_merge]' is not"));
- } else {
- EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
- }
- }
- TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
- // This test case ensures we allow both branches of a loop latch block
- // to go back to the loop header. It still counts as a single back edge.
- bool is_shader = GetParam() == spv::Capability::Shader;
- Block entry("entry");
- Block loop("loop", spv::Op::OpBranchConditional);
- Block latch("latch", spv::Op::OpBranchConditional);
- Block merge("merge", spv::Op::OpReturn);
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop.SetBody("OpLoopMerge %merge %latch None\n");
- }
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "loop", "latch", "merge") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
- str += entry >> loop;
- str += loop >> std::vector<Block>({latch, merge});
- str += latch >> std::vector<Block>({loop, loop}); // This is the key
- str += merge;
- str += "OpFunctionEnd";
- CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions())
- << str << getDiagnosticString();
- }
- // Unit test to check the case where a basic block is the entry block of 2
- // different constructs. In this case, the basic block is the entry block of a
- // continue construct as well as a selection construct. See issue# 517 for more
- // details.
- TEST_F(ValidateCFG, BasicBlockIsEntryBlockOfTwoConstructsGood) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %int = OpTypeInt 32 1
- %void_func = OpTypeFunction %void
- %int_0 = OpConstant %int 0
- %testfun = OpFunction %void None %void_func
- %label_1 = OpLabel
- OpBranch %start
- %start = OpLabel
- %cond = OpSLessThan %bool %int_0 %int_0
- ;
- ; Note: In this case, the "target" block is both the entry block of
- ; the continue construct of the loop as well as the entry block of
- ; the selection construct.
- ;
- OpLoopMerge %loop_merge %target None
- OpBranchConditional %cond %target %loop_merge
- %loop_merge = OpLabel
- OpReturn
- %target = OpLabel
- OpSelectionMerge %selection_merge None
- OpBranchConditional %cond %do_stuff %do_other_stuff
- %do_other_stuff = OpLabel
- OpBranch %selection_merge
- %selection_merge = OpLabel
- OpBranch %start
- %do_stuff = OpLabel
- OpBranch %selection_merge
- OpFunctionEnd
- )";
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, OpReturnInNonVoidFunc) {
- std::string spirv = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %int = OpTypeInt 32 1
- %int_func = OpTypeFunction %int
- %testfun = OpFunction %int None %int_func
- %label_1 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(spirv);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "OpReturn can only be called from a function with void return type.\n"
- " OpReturn"));
- }
- TEST_F(ValidateCFG, StructuredCFGBranchIntoSelectionBody) {
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %func "func"
- OpExecutionMode %func OriginUpperLeft
- %void = OpTypeVoid
- %bool = OpTypeBool
- %true = OpConstantTrue %bool
- %functy = OpTypeFunction %void
- %func = OpFunction %void None %functy
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpBranchConditional %true %then %merge
- %merge = OpLabel
- OpBranch %then
- %then = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("branches to the selection construct, but not to the "
- "selection header <ID> 6\n %7 = OpLabel"));
- }
- TEST_F(ValidateCFG, SwitchDefaultOnly) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpFunction %1 None %4
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpSwitch %3 %7
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, SwitchSingleCase) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpFunction %1 None %4
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpSwitch %3 %7 0 %8
- %8 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MultipleFallThroughBlocks) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %11 1 %12
- %10 = OpLabel
- OpBranchConditional %6 %11 %12
- %11 = OpLabel
- OpBranch %9
- %12 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Case construct that targets '10[%10]' has branches to multiple "
- "other "
- "case construct targets '12[%12]' and '11[%11]'\n %10 = OpLabel"));
- }
- TEST_F(ValidateCFG, MultipleFallThroughToDefault) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %11 1 %12
- %10 = OpLabel
- OpBranch %9
- %11 = OpLabel
- OpBranch %10
- %12 = OpLabel
- OpBranch %10
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Multiple case constructs have branches to the case construct "
- "that targets '10[%10]'\n %10 = OpLabel"));
- }
- TEST_F(ValidateCFG, MultipleFallThroughToNonDefault) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %11 1 %12
- %10 = OpLabel
- OpBranch %12
- %11 = OpLabel
- OpBranch %12
- %12 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Multiple case constructs have branches to the case construct "
- "that targets '12[%12]'\n %12 = OpLabel"));
- }
- TEST_F(ValidateCFG, DuplicateTargetWithFallThrough) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %10 1 %11
- %10 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, OpSwitchTargetCannotBeOuterLoopMergeBlock) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeFunction %1
- %3 = OpTypeBool
- %4 = OpUndef %3
- %5 = OpTypeInt 32 0
- %6 = OpConstant %5 0
- %7 = OpFunction %1 None %2
- %8 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpLoopMerge %10 %11 None
- OpBranch %12
- %12 = OpLabel
- OpSelectionMerge %13 None
- OpSwitch %6 %13 0 %10 1 %14
- %14 = OpLabel
- OpBranch %13
- %13 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranch %9
- %10 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Switch header '12[%12]' does not structurally dominate its case construct '10[%10]'\n"
- " %12 = OpLabel"));
- }
- TEST_F(ValidateCFG, OpSwitchTargetCannotBeOuterLoopContinueBlock) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeFunction %1
- %3 = OpTypeBool
- %4 = OpUndef %3
- %5 = OpTypeInt 32 0
- %6 = OpConstant %5 0
- %7 = OpFunction %1 None %2
- %8 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpLoopMerge %10 %11 None
- OpBranch %12
- %12 = OpLabel
- OpSelectionMerge %13 None
- OpSwitch %6 %13 0 %11 1 %14
- %14 = OpLabel
- OpBranch %13
- %13 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranch %9
- %10 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Switch header '12[%12]' does not structurally dominate its case construct '11[%11]'\n"
- " %12 = OpLabel"));
- }
- TEST_F(ValidateCFG, WrongOperandList) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %11 1 %12
- %10 = OpLabel
- OpBranch %9
- %12 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Case construct that targets '12[%12]' has branches to the case "
- "construct that targets '11[%11]', but does not immediately "
- "precede it in the OpSwitch's target list\n"
- " OpSwitch %uint_0 %10 0 %11 1 %12"));
- }
- TEST_F(ValidateCFG, WrongOperandListThroughDefault) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %11 1 %12
- %10 = OpLabel
- OpBranch %11
- %12 = OpLabel
- OpBranch %10
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Case construct that targets '12[%12]' has branches to the case "
- "construct that targets '11[%11]', but does not immediately "
- "precede it in the OpSwitch's target list\n"
- " OpSwitch %uint_0 %10 0 %11 1 %12"));
- }
- TEST_F(ValidateCFG, WrongOperandListNotLast) {
- std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeInt 32 0
- %3 = OpConstant %2 0
- %4 = OpTypeFunction %1
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpFunction %1 None %4
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %3 %10 0 %11 1 %12 2 %13
- %10 = OpLabel
- OpBranch %9
- %12 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranch %9
- %13 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Case construct that targets '12[%12]' has branches to the case "
- "construct that targets '11[%11]', but does not immediately "
- "precede it in the OpSwitch's target list\n"
- " OpSwitch %uint_0 %10 0 %11 1 %12 2 %13"));
- }
- TEST_F(ValidateCFG, GoodUnreachableSwitch) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %2 "main"
- OpExecutionMode %2 OriginUpperLeft
- %3 = OpTypeVoid
- %4 = OpTypeFunction %3
- %5 = OpTypeBool
- %6 = OpConstantTrue %5
- %7 = OpTypeInt 32 1
- %9 = OpConstant %7 0
- %2 = OpFunction %3 None %4
- %10 = OpLabel
- OpSelectionMerge %11 None
- OpBranchConditional %6 %12 %13
- %12 = OpLabel
- OpReturn
- %13 = OpLabel
- OpReturn
- %11 = OpLabel
- OpSelectionMerge %14 None
- OpSwitch %9 %14 0 %15
- %15 = OpLabel
- OpBranch %14
- %14 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, InvalidCaseExit) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %1 "func"
- OpExecutionMode %1 OriginUpperLeft
- %2 = OpTypeVoid
- %3 = OpTypeInt 32 0
- %4 = OpTypeFunction %2
- %5 = OpConstant %3 0
- %1 = OpFunction %2 None %4
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpSwitch %5 %7 0 %8 1 %9
- %8 = OpLabel
- OpBranch %10
- %9 = OpLabel
- OpBranch %10
- %10 = OpLabel
- OpReturn
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Case construct that targets '8[%8]' has invalid branch "
- "to block '10[%10]' (not another case construct, "
- "corresponding merge, outer loop merge or outer loop "
- "continue)"));
- }
- TEST_F(ValidateCFG, GoodCaseExitsToOuterConstructs) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %func "func"
- OpExecutionMode %func OriginUpperLeft
- %void = OpTypeVoid
- %bool = OpTypeBool
- %true = OpConstantTrue %bool
- %int = OpTypeInt 32 0
- %int0 = OpConstant %int 0
- %func_ty = OpTypeFunction %void
- %func = OpFunction %void None %func_ty
- %1 = OpLabel
- OpBranch %2
- %2 = OpLabel
- OpLoopMerge %7 %6 None
- OpBranch %3
- %3 = OpLabel
- OpSelectionMerge %5 None
- OpSwitch %int0 %5 0 %4
- %4 = OpLabel
- OpBranchConditional %true %6 %7
- %5 = OpLabel
- OpBranchConditional %true %6 %7
- %6 = OpLabel
- OpBranch %2
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, SwitchCaseOrderingBad1) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %default "default"
- OpName %other "other"
- %void = OpTypeVoid
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %undef %default 0 %other 1 %default
- %default = OpLabel
- OpBranch %other
- %other = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Case construct that targets '1[%default]' has branches to the "
- "case construct that targets '2[%other]', but does not "
- "immediately precede it in the OpSwitch's target list"));
- }
- TEST_F(ValidateCFG, SwitchCaseOrderingBad2) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %default "default"
- OpName %other "other"
- %void = OpTypeVoid
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %undef %default 0 %default 1 %other
- %other = OpLabel
- OpBranch %default
- %default = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Case construct that targets '2[%other]' has branches to the "
- "case construct that targets '1[%default]', but does not "
- "immediately precede it in the OpSwitch's target list"));
- }
- TEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %first "first"
- OpName %second "second"
- OpName %third "third"
- %void = OpTypeVoid
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %undef %second 0 %first 1 %second 2 %third
- %first = OpLabel
- OpBranch %second
- %second = OpLabel
- OpBranch %third
- %third = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %first "first"
- OpName %second "second"
- OpName %third "third"
- %void = OpTypeVoid
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %undef %second 0 %second 1 %first 2 %third
- %first = OpLabel
- OpBranch %second
- %second = OpLabel
- OpBranch %third
- %third = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- }
- TEST_F(ValidateCFG, GoodUnreachableSelection) {
- const std::string text = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
- %void = OpTypeVoid
- %8 = OpTypeFunction %void
- %bool = OpTypeBool
- %false = OpConstantFalse %bool
- %main = OpFunction %void None %8
- %15 = OpLabel
- OpBranch %16
- %16 = OpLabel
- OpLoopMerge %17 %18 None
- OpBranch %19
- %19 = OpLabel
- OpBranchConditional %false %21 %17
- %21 = OpLabel
- OpSelectionMerge %22 None
- OpBranchConditional %false %23 %22
- %23 = OpLabel
- OpBranch %24
- %24 = OpLabel
- OpLoopMerge %25 %26 None
- OpBranch %27
- %27 = OpLabel
- OpReturn
- %26 = OpLabel
- OpBranchConditional %false %24 %25
- %25 = OpLabel
- OpSelectionMerge %28 None
- OpBranchConditional %false %18 %28
- %28 = OpLabel
- OpBranch %22
- %22 = OpLabel
- OpBranch %18
- %18 = OpLabel
- OpBranch %16
- %17 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, ShaderWithPhiPtr) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "main"
- OpExecutionMode %1 LocalSize 1 1 1
- OpSource HLSL 600
- %bool = OpTypeBool
- %_ptr_Function_bool = OpTypePointer Function %bool
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %6 = OpLabel
- %7 = OpVariable %_ptr_Function_bool Function
- %8 = OpVariable %_ptr_Function_bool Function
- %9 = OpUndef %bool
- OpSelectionMerge %10 None
- OpBranchConditional %9 %11 %10
- %11 = OpLabel
- OpBranch %10
- %10 = OpLabel
- %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Using pointers with OpPhi requires capability "
- "VariablePointers or VariablePointersStorageBuffer"));
- }
- TEST_F(ValidateCFG, VarPtrShaderWithPhiPtr) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability VariablePointers
- OpExtension "SPV_KHR_variable_pointers"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "main"
- OpExecutionMode %1 LocalSize 1 1 1
- OpSource HLSL 600
- %bool = OpTypeBool
- %_ptr_Function_bool = OpTypePointer Function %bool
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %6 = OpLabel
- %7 = OpVariable %_ptr_Function_bool Function
- %8 = OpVariable %_ptr_Function_bool Function
- %9 = OpUndef %bool
- OpSelectionMerge %10 None
- OpBranchConditional %9 %11 %10
- %11 = OpLabel
- OpBranch %10
- %10 = OpLabel
- %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, VarPtrStgBufShaderWithPhiStgBufPtr) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability VariablePointersStorageBuffer
- OpExtension "SPV_KHR_variable_pointers"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "main"
- OpExecutionMode %1 LocalSize 1 1 1
- OpSource HLSL 600
- %bool = OpTypeBool
- %float = OpTypeFloat 32
- %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
- %7 = OpVariable %_ptr_StorageBuffer_float StorageBuffer
- %8 = OpVariable %_ptr_StorageBuffer_float StorageBuffer
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %6 = OpLabel
- %9 = OpUndef %bool
- OpSelectionMerge %10 None
- OpBranchConditional %9 %11 %10
- %11 = OpLabel
- OpBranch %10
- %10 = OpLabel
- %12 = OpPhi %_ptr_StorageBuffer_float %7 %6 %8 %11
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, KernelWithPhiPtr) {
- const std::string text = R"(
- OpCapability Kernel
- OpCapability Addresses
- OpMemoryModel Physical32 OpenCL
- OpEntryPoint Kernel %1 "main"
- OpExecutionMode %1 LocalSize 1 1 1
- OpSource HLSL 600
- %bool = OpTypeBool
- %_ptr_Function_bool = OpTypePointer Function %bool
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %6 = OpLabel
- %7 = OpVariable %_ptr_Function_bool Function
- %8 = OpVariable %_ptr_Function_bool Function
- %9 = OpUndef %bool
- OpSelectionMerge %10 None
- OpBranchConditional %9 %11 %10
- %11 = OpLabel
- OpBranch %10
- %10 = OpLabel
- %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, SwitchTargetMustBeLabel) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "foo"
- %uint = OpTypeInt 32 0
- %uint_0 = OpConstant %uint 0
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %6 = OpLabel
- %7 = OpCopyObject %uint %uint_0
- OpSelectionMerge %8 None
- OpSwitch %uint_0 %8 0 %7
- %8 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("'Target Label' operands for OpSwitch must "
- "be IDs of an OpLabel instruction"));
- }
- TEST_F(ValidateCFG, BranchTargetMustBeLabel) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "foo"
- %uint = OpTypeInt 32 0
- %uint_0 = OpConstant %uint 0
- %void = OpTypeVoid
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %2 = OpLabel
- %7 = OpCopyObject %uint %uint_0
- OpBranch %7
- %8 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("'Target Label' operands for OpBranch must "
- "be the ID of an OpLabel instruction"));
- }
- TEST_F(ValidateCFG, ReachableOpUnreachableOneBlock) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %block
- %block = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %block None
- OpBranchConditional %undef %block %unreachable
- %block = OpLabel
- OpReturn
- %unreachable = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %block1 None
- OpSwitch %undef %block1 0 %unreachable 1 %block2
- %block1 = OpLabel
- OpReturn
- %unreachable = OpLabel
- OpUnreachable
- %block2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, ReachableOpUnreachableLoop) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %unreachable %loop None
- OpBranchConditional %undef %loop %unreachable
- %unreachable = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, UnreachableLoopBadBackedge) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %2 "main"
- OpExecutionMode %2 OriginUpperLeft
- %4 = OpTypeVoid
- %5 = OpTypeFunction %4
- %8 = OpTypeBool
- %13 = OpConstantTrue %8
- %2 = OpFunction %4 None %5
- %14 = OpLabel
- OpSelectionMerge %15 None
- OpBranchConditional %13 %15 %15
- %16 = OpLabel
- OpLoopMerge %17 %18 None
- OpBranch %17
- %18 = OpLabel
- OpBranch %17
- %17 = OpLabel
- OpBranch %15
- %15 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- // The back-edge in this test is bad, but the validator fails to identify it
- // because it is in an entirely unreachable section of code. Prior to #2488
- // this code failed an assert in Construct::blocks().
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, OneContinueTwoBackedges) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %1 "main"
- OpExecutionMode %1 LocalSize 1 1 1
- %void = OpTypeVoid
- %bool = OpTypeBool
- %true = OpConstantTrue %bool
- %5 = OpTypeFunction %void
- %1 = OpFunction %void None %5
- %6 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpLoopMerge %8 %9 None
- OpBranch %10
- %10 = OpLabel
- OpLoopMerge %11 %9 None
- OpBranchConditional %true %11 %9
- %9 = OpLabel
- OpBranchConditional %true %10 %7
- %11 = OpLabel
- OpBranch %8
- %8 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Back-edges ('10[%10]' -> '9[%9]') can only be formed "
- "between a block and a loop header"));
- }
- TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %1 = OpLabel
- OpLoopMerge %undef %2 None
- OpBranchConditional %undef %2 %2
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Merge Block '1[%undef]' must be an OpLabel"));
- }
- TEST_F(ValidateCFG, LoopMergeContinueTargetNotLabel) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %1 = OpLabel
- OpLoopMerge %2 %undef None
- OpBranchConditional %undef %2 %2
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Continue Target '1[%undef]' must be an OpLabel"));
- }
- TEST_F(ValidateCFG, LoopMergeMergeBlockContinueTargetSameLabel) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %1 = OpLabel
- OpLoopMerge %2 %2 None
- OpBranchConditional %undef %2 %2
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Merge Block and Continue Target must be different ids"));
- }
- TEST_F(ValidateCFG, LoopMergeUnrollAndDontUnroll) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %5 = OpLabel
- OpBranch %1
- %1 = OpLabel
- OpLoopMerge %2 %3 Unroll|DontUnroll
- OpBranchConditional %undef %2 %3
- %3 = OpLabel
- OpBranch %1
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "Unroll and DontUnroll loop controls must not both be specified"));
- }
- TEST_F(ValidateCFG, LoopMergePeelCountAndDontUnroll) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %5 = OpLabel
- OpBranch %1
- %1 = OpLabel
- OpLoopMerge %2 %3 DontUnroll|PeelCount 1
- OpBranchConditional %undef %2 %3
- %3 = OpLabel
- OpBranch %1
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA,
- ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "PeelCount and DontUnroll loop controls must not both be specified"));
- }
- TEST_F(ValidateCFG, LoopMergePartialCountAndDontUnroll) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %5 = OpLabel
- OpBranch %1
- %1 = OpLabel
- OpLoopMerge %2 %3 DontUnroll|PartialCount 1
- OpBranchConditional %undef %2 %3
- %3 = OpLabel
- OpBranch %1
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA,
- ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("PartialCount and DontUnroll loop controls must not "
- "both be specified"));
- }
- TEST_F(ValidateCFG, LoopMergeIterationMultipleZero) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %5 = OpLabel
- OpBranch %1
- %1 = OpLabel
- OpLoopMerge %2 %3 IterationMultiple 0
- OpBranchConditional %undef %2 %3
- %3 = OpLabel
- OpBranch %1
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA,
- ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "IterationMultiple loop control operand must be greater than zero"));
- }
- TEST_F(ValidateCFG, LoopMergeIterationMultipleZeroMoreOperands) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %undef "undef"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %5 = OpLabel
- OpBranch %1
- %1 = OpLabel
- OpLoopMerge %2 %3 MaxIterations|IterationMultiple 4 0
- OpBranchConditional %undef %2 %3
- %3 = OpLabel
- OpBranch %1
- %2 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA,
- ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "IterationMultiple loop control operand must be greater than zero"));
- }
- TEST_F(ValidateCFG, LoopMergeTargetsHeader) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %loop %continue None
- OpBranch %body
- %continue = OpLabel
- OpBranch %loop
- %body = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Merge Block may not be the block containing the OpLoopMerge"));
- }
- TEST_F(ValidateCFG, InvalidSelectionExit) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %1 "main"
- OpExecutionMode %1 OriginUpperLeft
- %2 = OpTypeVoid
- %3 = OpTypeBool
- %4 = OpConstantTrue %3
- %5 = OpTypeFunction %2
- %1 = OpFunction %2 None %5
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpBranchConditional %4 %7 %8
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpBranchConditional %4 %10 %9
- %10 = OpLabel
- OpBranch %7
- %9 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("block <ID> '10[%10]' exits the selection headed by <ID> "
- "'8[%8]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, InvalidLoopExit) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %1 "main"
- OpExecutionMode %1 OriginUpperLeft
- %2 = OpTypeVoid
- %3 = OpTypeBool
- %4 = OpConstantTrue %3
- %5 = OpTypeFunction %2
- %1 = OpFunction %2 None %5
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpBranchConditional %4 %7 %8
- %8 = OpLabel
- OpLoopMerge %9 %10 None
- OpBranchConditional %4 %9 %11
- %11 = OpLabel
- OpBranchConditional %4 %7 %10
- %10 = OpLabel
- OpBranch %8
- %9 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> '11[%11]' exits the loop headed by <ID> "
- "'8[%8]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, InvalidContinueExit) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %1 "main"
- OpExecutionMode %1 OriginUpperLeft
- %2 = OpTypeVoid
- %3 = OpTypeBool
- %4 = OpConstantTrue %3
- %5 = OpTypeFunction %2
- %1 = OpFunction %2 None %5
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpBranchConditional %4 %7 %8
- %8 = OpLabel
- OpLoopMerge %9 %10 None
- OpBranchConditional %4 %9 %10
- %10 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranchConditional %4 %8 %7
- %9 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("block <ID> '11[%11]' exits the continue headed by <ID> "
- "'10[%10]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, InvalidSelectionExitBackedge) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeBool
- %3 = OpUndef %2
- %4 = OpTypeFunction %1
- %5 = OpFunction %1 None %4
- %6 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpLoopMerge %8 %9 None
- OpBranchConditional %3 %8 %9
- %9 = OpLabel
- OpSelectionMerge %10 None
- OpBranchConditional %3 %11 %12
- %11 = OpLabel
- OpBranch %13
- %12 = OpLabel
- OpBranch %13
- %13 = OpLabel
- OpBranch %7
- %10 = OpLabel
- OpUnreachable
- %8 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "The continue construct with the continue target '9[%9]' is not "
- "structurally post dominated by the back-edge block '13[%13]'"));
- }
- TEST_F(ValidateCFG, BreakFromSwitch) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeBool
- %3 = OpTypeInt 32 0
- %4 = OpUndef %2
- %5 = OpUndef %3
- %6 = OpTypeFunction %1
- %7 = OpFunction %1 None %6
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %5 %9 0 %10
- %10 = OpLabel
- OpSelectionMerge %11 None
- OpBranchConditional %4 %11 %12
- %12 = OpLabel
- OpBranch %9
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, InvalidBreakFromSwitch) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeBool
- %3 = OpTypeInt 32 0
- %4 = OpUndef %2
- %5 = OpUndef %3
- %6 = OpTypeFunction %1
- %7 = OpFunction %1 None %6
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %5 %9 0 %10
- %10 = OpLabel
- OpSelectionMerge %11 None
- OpSwitch %5 %11 0 %12
- %12 = OpLabel
- OpBranch %9
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("block <ID> '12[%12]' exits the selection headed by <ID> "
- "'10[%10]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, BreakToOuterSwitch) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeBool
- %3 = OpTypeInt 32 0
- %4 = OpUndef %2
- %5 = OpUndef %3
- %6 = OpTypeFunction %1
- %7 = OpFunction %1 None %6
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %5 %9 0 %10
- %10 = OpLabel
- OpSelectionMerge %11 None
- OpSwitch %5 %11 0 %12
- %12 = OpLabel
- OpSelectionMerge %13 None
- OpBranchConditional %4 %13 %14
- %14 = OpLabel
- OpBranch %9
- %13 = OpLabel
- OpBranch %11
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("block <ID> '14[%14]' exits the selection headed by <ID> "
- "'10[%10]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, BreakToOuterLoop) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeBool
- %3 = OpUndef %2
- %4 = OpTypeFunction %1
- %5 = OpFunction %1 None %4
- %6 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpLoopMerge %8 %9 None
- OpBranch %10
- %10 = OpLabel
- OpLoopMerge %11 %12 None
- OpBranch %13
- %13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %3 %14 %15
- %15 = OpLabel
- OpBranch %8
- %14 = OpLabel
- OpBranch %12
- %12 = OpLabel
- OpBranchConditional %3 %10 %11
- %11 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpBranchConditional %3 %7 %8
- %8 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> '15[%15]' exits the loop headed by <ID> "
- "'10[%10]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, ContinueFromNestedSelection) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %4 = OpFunction %void None %void_fn
- %5 = OpLabel
- OpBranch %48
- %48 = OpLabel
- OpLoopMerge %47 %50 None
- OpBranch %10
- %10 = OpLabel
- OpLoopMerge %12 %37 None
- OpBranchConditional %undef %11 %12
- %11 = OpLabel
- OpSelectionMerge %31 None
- OpBranchConditional %undef %30 %31
- %30 = OpLabel
- OpSelectionMerge %38 None
- OpBranchConditional %undef %36 %38
- %36 = OpLabel
- OpBranch %38
- %38 = OpLabel
- OpBranch %37
- %37 = OpLabel
- OpBranch %10
- %31 = OpLabel
- OpBranch %12
- %12 = OpLabel
- OpSelectionMerge %55 None
- OpBranchConditional %undef %47 %55
- %55 = OpLabel
- OpBranch %47
- %50 = OpLabel
- OpBranch %48
- %47 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranchConditional %undef %then %else
- %then = OpLabel
- OpReturn
- %else = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
- }
- TEST_F(ValidateCFG, LoopConditionalBranchWithoutExitBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %continue None
- OpBranchConditional %undef %then %else
- %then = OpLabel
- OpBranch %continue
- %else = OpLabel
- OpBranch %exit
- %continue = OpLabel
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
- }
- TEST_F(ValidateCFG, MissingMergeSwitchBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSwitch %undef %then 0 %else
- %then = OpLabel
- OpReturn
- %else = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "OpSwitch must be preceded by an OpSelectionMerge instruction"));
- }
- TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSwitch %undef %then 0 %then 1 %then 2 %else
- %then = OpLabel
- OpReturn
- %else = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "OpSwitch must be preceded by an OpSelectionMerge instruction"));
- }
- TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %b3 None
- OpBranchConditional %undef %b1 %b2
- %b1 = OpLabel
- OpBranchConditional %undef %b2 %b3
- %b2 = OpLabel
- OpBranch %b3
- %b3 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranchConditional %undef %then %then
- %then = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeOneTargetSwitchBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %int = OpTypeInt 32 0
- %undef = OpUndef %int
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSwitch %undef %then 0 %then 1 %then
- %then = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "OpSwitch must be preceded by an OpSelectionMerge instruction"));
- }
- TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %int = OpTypeInt 32 0
- %undef_int = OpUndef %int
- %bool = OpTypeBool
- %undef_bool = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpBranchConditional %undef_bool %merge %b1
- %b1 = OpLabel
- OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2
- %b2 = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "OpSwitch must be preceded by an OpSelectionMerge instruction"));
- }
- TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %continue None
- OpBranch %body
- %body = OpLabel
- OpBranchConditional %undef %body2 %exit
- %body2 = OpLabel
- OpBranch %continue
- %continue = OpLabel
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeLoopContinueGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %continue None
- OpBranch %body
- %body = OpLabel
- OpBranchConditional %undef %body2 %continue
- %body2 = OpLabel
- OpBranch %continue
- %continue = OpLabel
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %int = OpTypeInt 32 0
- %int_0 = OpConstant %int 0
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %int_0 %merge 1 %b1
- %b1 = OpLabel
- OpBranchConditional %undef %merge %b2
- %b2 = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %int = OpTypeInt 32 0
- %int_0 = OpConstant %int 0
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %int_0 %b1 1 %b2
- %b1 = OpLabel
- OpBranchConditional %undef %b3 %b2
- %b2 = OpLabel
- OpBranch %merge
- %b3 = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MissingMergeInALoopBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %continue None
- OpBranch %body
- %body = OpLabel
- OpBranchConditional %undef %b1 %b2
- %b1 = OpLabel
- OpBranch %exit
- %b2 = OpLabel
- OpBranch %continue
- %continue = OpLabel
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
- }
- TEST_F(ValidateCFG, MissingMergeCrissCrossBad) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %merge None
- OpBranchConditional %undef %b1 %b2
- %b1 = OpLabel
- OpBranchConditional %undef %b3 %b4
- %b2 = OpLabel
- OpBranchConditional %undef %b3 %b4
- %b3 = OpLabel
- OpBranch %merge
- %b4 = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
- }
- TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %loop "loop"
- OpName %continue "continue"
- OpName %body "body"
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %continue None
- OpBranch %body
- %body = OpLabel
- OpSelectionMerge %continue None
- OpBranchConditional %undef %exit %continue
- %continue = OpLabel
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Header block '3[%body]' is contained in the loop construct "
- "headed by "
- "'1[%loop]', but its merge block '2[%continue]' is not"));
- }
- TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %loop "loop"
- OpName %continue "continue"
- OpName %inner "inner"
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %exit %continue None
- OpBranchConditional %undef %exit %inner
- %inner = OpLabel
- OpLoopMerge %continue %inner None
- OpBranchConditional %undef %inner %continue
- %continue = OpLabel
- OpBranch %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Header block '3[%inner]' is contained in the loop construct "
- "headed by "
- "'1[%loop]', but its merge block '2[%continue]' is not"));
- }
- TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %2 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %4 = OpUndef %int
- %bool = OpTypeBool
- %6 = OpUndef %bool
- %7 = OpFunction %void None %2
- %8 = OpLabel
- OpSelectionMerge %9 None
- OpSwitch %4 %10 0 %11
- %10 = OpLabel
- OpBranch %9
- %11 = OpLabel
- OpBranch %12
- %12 = OpLabel
- OpLoopMerge %13 %14 None
- OpBranch %15
- %15 = OpLabel
- OpSelectionMerge %16 None
- OpSwitch %4 %17 1 %18 2 %19
- %17 = OpLabel
- OpBranch %16
- %18 = OpLabel
- OpBranch %14
- %19 = OpLabel
- OpBranch %16
- %16 = OpLabel
- OpBranch %14
- %14 = OpLabel
- OpBranchConditional %6 %12 %13
- %13 = OpLabel
- OpSelectionMerge %20 None
- OpBranchConditional %6 %21 %20
- %21 = OpLabel
- OpBranch %9
- %20 = OpLabel
- OpBranch %10
- %9 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge2) {
- const std::string text = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %2 "main"
- OpExecutionMode %2 OriginUpperLeft
- %void = OpTypeVoid
- %4 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %6 = OpUndef %int
- %bool = OpTypeBool
- %8 = OpUndef %bool
- %2 = OpFunction %void None %4
- %9 = OpLabel
- OpSelectionMerge %10 None
- OpSwitch %6 %11 0 %12
- %11 = OpLabel
- OpBranch %10
- %12 = OpLabel
- OpBranch %13
- %13 = OpLabel
- OpLoopMerge %14 %15 None
- OpBranch %16
- %16 = OpLabel
- OpSelectionMerge %17 None
- OpSwitch %6 %18 1 %19 2 %20
- %18 = OpLabel
- OpBranch %17
- %19 = OpLabel
- OpBranch %15
- %20 = OpLabel
- OpBranch %17
- %17 = OpLabel
- OpBranch %15
- %15 = OpLabel
- OpBranchConditional %8 %13 %14
- %14 = OpLabel
- OpSelectionMerge %21 None
- OpBranchConditional %8 %22 %21
- %22 = OpLabel
- OpSelectionMerge %23 None
- OpBranchConditional %8 %24 %23
- %24 = OpLabel
- OpBranch %10
- %23 = OpLabel
- OpBranch %21
- %21 = OpLabel
- OpBranch %11
- %10 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, PhiResultInvalidSampler) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %f32 = OpTypeFloat 32
- %sampler = OpTypeSampler
- %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
- %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
- %undef_bool = OpUndef %bool
- %undef_sampler = OpUndef %sampler
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- %ld_sampler = OpLoad %sampler %sampler_var
- OpBranch %loop
- %loop = OpLabel
- %phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
- OpLoopMerge %exit %loop None
- OpBranchConditional %undef_bool %exit %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Result type cannot be OpTypeSampler"));
- }
- TEST_F(ValidateCFG, PhiResultInvalidImage) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %f32 = OpTypeFloat 32
- %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
- %ptr_uc_image = OpTypePointer UniformConstant %image
- %image_var = OpVariable %ptr_uc_image UniformConstant
- %undef_bool = OpUndef %bool
- %undef_image = OpUndef %image
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- %ld_image = OpLoad %image %image_var
- OpBranch %loop
- %loop = OpLabel
- %phi = OpPhi %image %undef_image %entry %ld_image %loop
- OpLoopMerge %exit %loop None
- OpBranchConditional %undef_bool %exit %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Result type cannot be OpTypeImage"));
- }
- TEST_F(ValidateCFG, PhiResultInvalidSampledImage) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %f32 = OpTypeFloat 32
- %sampler = OpTypeSampler
- %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
- %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
- %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
- %ptr_uc_image = OpTypePointer UniformConstant %image
- %image_var = OpVariable %ptr_uc_image UniformConstant
- %sampled_image = OpTypeSampledImage %image
- %undef_bool = OpUndef %bool
- %undef_sampled_image = OpUndef %sampled_image
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- %ld_image = OpLoad %image %image_var
- %ld_sampler = OpLoad %sampler %sampler_var
- OpBranch %loop
- %loop = OpLabel
- %phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
- %sample = OpSampledImage %sampled_image %ld_image %ld_sampler
- OpLoopMerge %exit %loop None
- OpBranchConditional %undef_bool %exit %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Result type cannot be OpTypeSampledImage"));
- }
- TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %f32 = OpTypeFloat 32
- %sampler = OpTypeSampler
- %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
- %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
- %undef_bool = OpUndef %bool
- %undef_sampler = OpUndef %sampler
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- %ld_sampler = OpLoad %sampler %sampler_var
- OpBranch %loop
- %loop = OpLabel
- %phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
- OpLoopMerge %exit %loop None
- OpBranchConditional %undef_bool %exit %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- options_->before_hlsl_legalization = true;
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %f32 = OpTypeFloat 32
- %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
- %ptr_uc_image = OpTypePointer UniformConstant %image
- %image_var = OpVariable %ptr_uc_image UniformConstant
- %undef_bool = OpUndef %bool
- %undef_image = OpUndef %image
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- %ld_image = OpLoad %image %image_var
- OpBranch %loop
- %loop = OpLabel
- %phi = OpPhi %image %undef_image %entry %ld_image %loop
- OpLoopMerge %exit %loop None
- OpBranchConditional %undef_bool %exit %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- options_->before_hlsl_legalization = true;
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %f32 = OpTypeFloat 32
- %sampler = OpTypeSampler
- %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
- %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
- %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
- %ptr_uc_image = OpTypePointer UniformConstant %image
- %image_var = OpVariable %ptr_uc_image UniformConstant
- %sampled_image = OpTypeSampledImage %image
- %undef_bool = OpUndef %bool
- %undef_sampled_image = OpUndef %sampled_image
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %entry = OpLabel
- %ld_image = OpLoad %image %image_var
- %ld_sampler = OpLoad %sampler %sampler_var
- OpBranch %loop
- %loop = OpLabel
- %phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
- %sample = OpSampledImage %sampled_image %ld_image %ld_sampler
- OpLoopMerge %exit %loop None
- OpBranchConditional %undef_bool %exit %loop
- %exit = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- options_->before_hlsl_legalization = true;
- CompileSuccessfully(text);
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) {
- // In this test, we try to make a case where the false branches
- // to %20 and %60 from blocks %10 and %50 must be registered
- // during the validity check for sturctured selections.
- // However, an error is caught earlier in the flow, that the
- // branches from %100 to %20 and %60 violate dominance.
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical Simple
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
-
- %void = OpTypeVoid
- %void_fn = OpTypeFunction %void
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %main = OpFunction %void None %void_fn
- %1 = OpLabel
- OpSelectionMerge %999 None
- OpBranchConditional %cond %10 %100
- %10 = OpLabel
- OpSelectionMerge %30 None ; force registration of %30
- OpBranchConditional %cond %30 %20 ; %20 should be registered too
- %20 = OpLabel
- OpBranch %30
- %30 = OpLabel ; merge for first if
- OpBranch %50
- %50 = OpLabel
- OpSelectionMerge %70 None ; force registration of %70
- OpBranchConditional %cond %70 %60 ; %60 should be registered
- %60 = OpLabel
- OpBranch %70
- %70 = OpLabel ; merge for second if
- OpBranch %999
- %100 = OpLabel
- OpBranchConditional %cond %20 %60 ; should require a merge
- %999 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("The selection construct with the selection header "
- "'8[%8]' does not structurally dominate the merge "
- "block '10[%10]'\n"));
- }
- TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeFunction %1
- %3 = OpFunction %1 None %2
- %4 = OpLabel
- OpBranch %5
- %5 = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
- auto f = vstate_->function(3);
- auto entry = f->GetBlock(4).first;
- ASSERT_TRUE(entry->reachable());
- auto end = f->GetBlock(5).first;
- ASSERT_TRUE(end->reachable());
- }
- TEST_F(ValidateCFG, BlockOrderDoesNotAffectReachability) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeFunction %1
- %3 = OpTypeBool
- %4 = OpUndef %3
- %5 = OpFunction %1 None %2
- %6 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpSelectionMerge %8 None
- OpBranchConditional %4 %9 %10
- %8 = OpLabel
- OpReturn
- %9 = OpLabel
- OpBranch %8
- %10 = OpLabel
- OpBranch %8
- %11 = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
- auto f = vstate_->function(5);
- auto b6 = f->GetBlock(6).first;
- auto b7 = f->GetBlock(7).first;
- auto b8 = f->GetBlock(8).first;
- auto b9 = f->GetBlock(9).first;
- auto b10 = f->GetBlock(10).first;
- auto b11 = f->GetBlock(11).first;
- ASSERT_TRUE(b6->reachable());
- ASSERT_TRUE(b7->reachable());
- ASSERT_TRUE(b8->reachable());
- ASSERT_TRUE(b9->reachable());
- ASSERT_TRUE(b10->reachable());
- ASSERT_FALSE(b11->reachable());
- }
- TEST_F(ValidateCFG, PhiInstructionWithDuplicateIncomingEdges) {
- const std::string text = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 320
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeBool
- %7 = OpConstantTrue %6
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- OpSelectionMerge %10 None
- OpBranchConditional %7 %8 %9
- %8 = OpLabel
- OpBranch %10
- %9 = OpLabel
- OpBranch %10
- %10 = OpLabel
- %11 = OpPhi %6 %7 %8 %7 %8
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("OpPhi references incoming basic block <id> "));
- EXPECT_THAT(getDiagnosticString(), HasSubstr("multiple times."));
- }
- TEST_F(ValidateCFG, PhiOnVoid) {
- const std::string text = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 320
- OpName %4 "main"
- OpName %6 "foo("
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- %8 = OpFunctionCall %2 %6
- OpBranch %20
- %20 = OpLabel
- %21 = OpPhi %2 %8 %20
- OpReturn
- OpFunctionEnd
- %6 = OpFunction %2 None %3
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("OpPhi must not have void result type"));
- }
- TEST_F(ValidateCFG, InvalidExitSingleBlockLoop) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- OpName %5 "BAD"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %fn = OpFunction %void None %void_fn
- %1 = OpLabel
- OpBranch %2
- %2 = OpLabel
- OpLoopMerge %3 %4 None
- OpBranchConditional %undef %3 %5
- %5 = OpLabel
- OpLoopMerge %6 %5 None
- OpBranchConditional %undef %5 %4
- %6 = OpLabel
- OpReturn
- %4 = OpLabel
- OpBranch %2
- %3 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("block <ID> '1[%BAD]' exits the continue headed by <ID> "
- "'1[%BAD]', but not via a structured exit"));
- }
- TEST_F(ValidateCFG, SwitchSelectorNotAnInt) {
- const std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- %void = OpTypeVoid
- %float = OpTypeFloat 32
- %float_1 = OpConstant %float 1
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %default None
- OpSwitch %float_1 %default
- %default = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Selector type must be OpTypeInt"));
- }
- TEST_F(ValidateCFG, SwitchDefaultNotALabel) {
- const std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- %void = OpTypeVoid
- %int = OpTypeInt 32 0
- %int_1 = OpConstant %int 1
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %entry = OpLabel
- OpSelectionMerge %default None
- OpSwitch %int_1 %int_1
- %default = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Default must be an OpLabel instruction"));
- }
- TEST_F(ValidateCFG, BlockDepthRecursion) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %1 = OpLabel
- OpBranch %2
- %2 = OpLabel
- OpLoopMerge %3 %4 None
- OpBranchConditional %undef %3 %4
- %4 = OpLabel
- OpBranch %2
- %3 = OpLabel
- OpBranch %5
- %5 = OpLabel
- OpSelectionMerge %2 None
- OpBranchConditional %undef %6 %7
- %6 = OpLabel
- OpReturn
- %7 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- }
- TEST_F(ValidateCFG, BadStructuredExitBackwardsMerge) {
- const std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %1 = OpLabel
- OpBranch %2
- %2 = OpLabel
- OpLoopMerge %4 %5 None
- OpBranchConditional %undef %4 %6
- %6 = OpLabel
- OpSelectionMerge %7 None
- OpBranchConditional %undef %8 %9
- %7 = OpLabel
- OpReturn
- %8 = OpLabel
- OpBranch %5
- %9 = OpLabel
- OpSelectionMerge %6 None
- OpBranchConditional %undef %5 %5
- %5 = OpLabel
- OpBranch %2
- %4 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- }
- TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPre1p6) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranchConditional %undef %target %target
- %target = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
- }
- TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPost1p6) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %void = OpTypeVoid
- %bool = OpTypeBool
- %undef = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %entry = OpLabel
- OpBranchConditional %undef %target %target
- %target = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("In SPIR-V 1.6 or later, True Label and False Label "
- "must be different labels"));
- }
- TEST_F(ValidateCFG, BadBackEdgeUnreachableContinue) {
- const std::string text = R"(
- OpCapability Shader
- OpCapability Linkage
- OpMemoryModel Logical GLSL450
- %1 = OpTypeVoid
- %2 = OpTypeFunction %1
- %3 = OpFunction %1 None %2
- %4 = OpLabel
- OpBranch %5
- %5 = OpLabel
- OpLoopMerge %6 %7 None
- OpBranch %8
- %8 = OpLabel
- OpBranch %5
- %7 = OpLabel
- OpUnreachable
- %6 = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("The continue construct with the continue target '7[%7]' "
- "does not structurally dominate the back-edge block '8[%8]'"));
- }
- TEST_F(ValidateCFG, BadLoop) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical Simple
- OpEntryPoint Fragment %2 " "
- OpExecutionMode %2 OriginUpperLeft
- OpName %49 "loop"
- %void = OpTypeVoid
- %12 = OpTypeFunction %void
- %2 = OpFunction %void None %12
- %33 = OpLabel
- OpBranch %49
- %50 = OpLabel
- OpBranch %49
- %49 = OpLabel
- OpLoopMerge %33 %50 Unroll
- OpBranch %49
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Loop header '2[%loop]' is targeted by 2 back-edge "
- "blocks but the standard requires exactly one"));
- }
- TEST_F(ValidateCFG, BadSwitch) {
- const std::string text = R"(
- OpCapability StorageImageExtendedFormats
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %2 "blah" %58
- OpExecutionMode %2 OriginUpperLeft
- OpName %BAD "BAD"
- %11 = OpTypeVoid
- %12 = OpTypeFunction %11
- %19 = OpTypeInt 32 1
- %21 = OpConstant %19 555758549
- %2 = OpFunction %11 None %12
- %4 = OpLabel
- OpBranch %33
- %33 = OpLabel
- OpLoopMerge %34 %35 None
- OpBranch %55
- %BAD = OpLabel
- OpSelectionMerge %53 None
- OpSwitch %21 %34 196153896 %53 20856160 %34 33570306 %34 593494531 %52
- %55 = OpLabel
- OpLoopMerge %52 %58 DontUnroll
- OpBranch %35
- %58 = OpLabel
- OpSelectionMerge %58 None
- OpSwitch %21 %52 178168 %55 608223677 %34 604111047 %34 -553516825 %34 -106432813 %BAD 6946864 %55 1257373689 %55 973090296 %35 -113180668 %55 537002232 %BAD 13762553 %BAD 1030172152 %35 -553516825 %55 -262137 %35 -1091822332 %BAD 131320 %52 131321 %35 131320 %52 131321 %35 -1091822332 %BAD
- %53 = OpLabel
- OpBranch %35
- %52 = OpLabel
- OpBranch %34
- %35 = OpLabel
- OpBranch %33
- %34 = OpLabel
- OpKill
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("exits the selection headed by <ID> '3[%BAD]', but not "
- "via a structured exit"));
- }
- TEST_F(ValidateCFG,
- MaximalReconvergenceBranchConditionalSameTargetNotInCallTree) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %func_entry = OpLabel
- OpBranchConditional %cond %func_exit %func_exit
- %func_exit = OpLabel
- OpReturn
- OpFunctionEnd
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
- }
- TEST_F(ValidateCFG, MaximalReconvergenceBranchConditionalSameTargetInCallTree) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %func_entry = OpLabel
- OpBranchConditional %cond %func_exit %func_exit
- %func_exit = OpLabel
- OpReturn
- OpFunctionEnd
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- %call = OpFunctionCall %void %func
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
- EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("In entry points using the MaximallyReconvergesKHR "
- "execution mode, True "
- "Label and False Label must be different labels"));
- }
- TEST_F(ValidateCFG, MaximalReconvergenceEarlyReconvergenceNotInCallTree) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %func_entry = OpLabel
- OpSelectionMerge %func_exit None
- OpBranchConditional %cond %then %else
- %then = OpLabel
- OpBranch %merge
- %else = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpBranch %func_exit
- %func_exit = OpLabel
- OpReturn
- OpFunctionEnd
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MaximalReconvergenceEarlyReconvergenceInCallTree) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %func = OpFunction %void None %void_fn
- %func_entry = OpLabel
- OpSelectionMerge %func_exit None
- OpBranchConditional %cond %then %else
- %then = OpLabel
- OpBranch %merge
- %else = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpBranch %func_exit
- %func_exit = OpLabel
- OpReturn
- OpFunctionEnd
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- %call = OpFunctionCall %void %func
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "In entry points using the MaximallyReconvergesKHR execution mode, "
- "this basic block must not have multiple unique predecessors"));
- }
- TEST_F(ValidateCFG, MaximalReconvergenceLoopMultiplePredsOk) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %merge %loop None
- OpBranchConditional %cond %loop %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MaximalReconvergenceLoopMultiplePredsOk2) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %merge %cont None
- OpBranch %body
- %body = OpLabel
- OpBranch %cont
- %cont = OpLabel
- OpBranchConditional %cond %loop %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MaximalReconvergenceSelectionMergeMultiplePredsOk) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpSelectionMerge %merge None
- OpBranchConditional %cond %then %else
- %then = OpLabel
- OpBranch %merge
- %else = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MaximalReconvergenceSelectionMergeMultiplePredsOk2) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- OpName %merge "merge"
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpSelectionMerge %merge None
- OpBranchConditional %cond %then %else
- %then = OpLabel
- OpBranch %merge
- %else = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MaximalReconvergenceLoopMergeMultiplePredsOk) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpBranch %loop
- %loop = OpLabel
- OpLoopMerge %merge %continue None
- OpBranchConditional %cond %merge %continue
- %continue = OpLabel
- OpBranchConditional %cond %loop %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, MaximalReconvergenceCaseFallthroughMultiplePredsOk) {
- const std::string text = R"(
- OpCapability Shader
- OpExtension "SPV_KHR_maximal_reconvergence"
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpExecutionMode %main MaximallyReconvergesKHR
- %void = OpTypeVoid
- %bool = OpTypeBool
- %cond = OpUndef %bool
- %int = OpTypeInt 32 0
- %val = OpUndef %int
- %void_fn = OpTypeFunction %void
- %main = OpFunction %void None %void_fn
- %main_entry = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %val %merge 0 %case1 1 %case2
- %case1 = OpLabel
- OpBranch %case2
- %case2 = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, StructurallyUnreachableContinuePredecessor) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
- OpSource ESSL 310
- OpName %main "main"
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %int = OpTypeInt 32 1
- %int_1 = OpConstant %int 1
- %int_n7 = OpConstant %int -7
- %bool = OpTypeBool
- %main = OpFunction %void None %3
- %8 = OpLabel
- OpBranch %9
- %9 = OpLabel
- %10 = OpPhi %int %int_1 %8 %int_n7 %15
- %12 = OpSGreaterThan %bool %10 %int_n7
- OpLoopMerge %13 %15 None
- OpBranchConditional %12 %14 %13
- %14 = OpLabel
- OpBranch %15
- %15 = OpLabel
- OpBranch %9
- %17 = OpLabel
- OpBranch %15
- %13 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, FullyLoopPrecedingSwitchToContinue) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
- OpName %main "main"
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %bool = OpTypeBool
- %true = OpConstantTrue %bool
- %int = OpTypeInt 32 1
- %int_0 = OpConstant %int 0
- %int_1 = OpConstant %int 1
- %main = OpFunction %void None %3
- %4 = OpLabel
- OpBranch %7
- %7 = OpLabel
- OpLoopMerge %8 %6 None
- OpBranch %5
- %5 = OpLabel
- OpSelectionMerge %9 None
- OpBranchConditional %true %10 %9
- %10 = OpLabel
- OpSelectionMerge %16 None
- OpSwitch %int_0 %13
- %13 = OpLabel
- OpBranch %19
- %19 = OpLabel
- OpLoopMerge %20 %18 None
- OpBranch %17
- %17 = OpLabel
- OpReturn
- %18 = OpLabel
- OpBranch %19
- %20 = OpLabel
- OpSelectionMerge %23 None
- OpSwitch %int_1 %21
- %21 = OpLabel
- OpBranch %6
- %23 = OpLabel
- OpBranch %16
- %16 = OpLabel
- OpBranch %9
- %9 = OpLabel
- OpBranch %6
- %6 = OpLabel
- OpBranch %7
- %8 = OpLabel
- OpUnreachable
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- TEST_F(ValidateCFG, CaseBreak) {
- const std::string text = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %main "main"
- OpExecutionMode %main OriginUpperLeft
- OpName %main "main"
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %bool = OpTypeBool
- %true = OpConstantTrue %bool
- %int = OpTypeInt 32 1
- %int_0 = OpConstant %int 0
- %int_1 = OpConstant %int 1
- %main = OpFunction %void None %3
- %4 = OpLabel
- OpSelectionMerge %merge None
- OpSwitch %int_1 %case 2 %merge
- %case = OpLabel
- OpBranch %merge
- %merge = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
- } // namespace
- } // namespace val
- } // namespace spvtools
|